forked from baron/baron-sso
fix(admin): add missing Tabs import and refine import result UI type safety
This commit is contained in:
@@ -76,6 +76,7 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "../../../components/ui/table";
|
} from "../../../components/ui/table";
|
||||||
|
import { Tabs, TabsList, TabsTrigger } from "../../../components/ui/tabs";
|
||||||
import { toast } from "../../../components/ui/use-toast";
|
import { toast } from "../../../components/ui/use-toast";
|
||||||
import type { UserProfileResponse } from "../../../lib/adminApi";
|
import type { UserProfileResponse } from "../../../lib/adminApi";
|
||||||
import {
|
import {
|
||||||
@@ -288,6 +289,17 @@ function TenantListPage() {
|
|||||||
const [importResult, setImportResult] =
|
const [importResult, setImportResult] =
|
||||||
React.useState<TenantImportResult | null>(null);
|
React.useState<TenantImportResult | null>(null);
|
||||||
const [importResultOpen, setImportResultOpen] = React.useState(false);
|
const [importResultOpen, setImportResultOpen] = React.useState(false);
|
||||||
|
const [importResultFilter, setImportResultFilter] = React.useState<
|
||||||
|
"all" | "created" | "updated" | "failed" | "skipped"
|
||||||
|
>("all");
|
||||||
|
|
||||||
|
const filteredImportDetails = React.useMemo(() => {
|
||||||
|
if (!importResult) return [];
|
||||||
|
if (importResultFilter === "all") return importResult.details;
|
||||||
|
if (importResultFilter === "failed")
|
||||||
|
return importResult.details.filter((d) => !d.success);
|
||||||
|
return importResult.details.filter((d) => d.action === importResultFilter);
|
||||||
|
}, [importResult, importResultFilter]);
|
||||||
const [selectedBulkStatus, setSelectedBulkStatus] = React.useState("");
|
const [selectedBulkStatus, setSelectedBulkStatus] = React.useState("");
|
||||||
const _tenantTableScrollRef = React.useRef<HTMLDivElement | null>(null);
|
const _tenantTableScrollRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
@@ -1009,22 +1021,60 @@ function TenantListPage() {
|
|||||||
"가져오기 결과 리포트",
|
"가져오기 결과 리포트",
|
||||||
)}
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
|
||||||
{importResult &&
|
|
||||||
t(
|
|
||||||
"msg.admin.tenants.import_result.summary",
|
|
||||||
"총 {{total}}건 처리: 생성 {{created}}, 갱신 {{updated}}, 실패 {{failed}}",
|
|
||||||
{
|
|
||||||
total: importResult.details.length,
|
|
||||||
created: importResult.created,
|
|
||||||
updated: importResult.updated,
|
|
||||||
failed: importResult.failed,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="max-h-[60vh] overflow-auto rounded-md border">
|
{importResult && (
|
||||||
|
<div className="grid grid-cols-4 gap-4 py-4">
|
||||||
|
<div className="flex flex-col items-center rounded-lg border bg-muted/30 p-3">
|
||||||
|
<span className="text-xs text-muted-foreground uppercase">
|
||||||
|
Total
|
||||||
|
</span>
|
||||||
|
<span className="text-2xl font-bold">
|
||||||
|
{importResult.details.length}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center rounded-lg border border-success/20 bg-success/5 p-3">
|
||||||
|
<span className="text-xs text-success uppercase">Created</span>
|
||||||
|
<span className="text-2xl font-bold text-success">
|
||||||
|
{importResult.created}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center rounded-lg border border-warning/20 bg-warning/5 p-3">
|
||||||
|
<span className="text-xs text-warning uppercase">Updated</span>
|
||||||
|
<span className="text-2xl font-bold text-warning">
|
||||||
|
{importResult.updated}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center rounded-lg border border-destructive/20 bg-destructive/5 p-3">
|
||||||
|
<span className="text-xs text-destructive uppercase">
|
||||||
|
Failed
|
||||||
|
</span>
|
||||||
|
<span className="text-2xl font-bold text-destructive">
|
||||||
|
{importResult.failed}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
value={importResultFilter}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setImportResultFilter(
|
||||||
|
v as "all" | "created" | "updated" | "failed" | "skipped",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<TabsList className="grid w-full grid-cols-5">
|
||||||
|
<TabsTrigger value="all">ALL</TabsTrigger>
|
||||||
|
<TabsTrigger value="created">CREATED</TabsTrigger>
|
||||||
|
<TabsTrigger value="updated">UPDATED</TabsTrigger>
|
||||||
|
<TabsTrigger value="failed">FAILED</TabsTrigger>
|
||||||
|
<TabsTrigger value="skipped">SKIPPED</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<div className="max-h-[50vh] overflow-auto rounded-md border">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader className={commonStickyTableHeaderClass}>
|
<TableHeader className={commonStickyTableHeaderClass}>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -1037,10 +1087,7 @@ function TenantListPage() {
|
|||||||
<TableHead>
|
<TableHead>
|
||||||
{t("ui.admin.tenants.table.slug", "SLUG")}
|
{t("ui.admin.tenants.table.slug", "SLUG")}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead>
|
<TableHead className="w-[120px]">
|
||||||
{t("ui.admin.tenants.import_result.action", "작업")}
|
|
||||||
</TableHead>
|
|
||||||
<TableHead>
|
|
||||||
{t("ui.admin.tenants.import_result.status", "상태")}
|
{t("ui.admin.tenants.import_result.status", "상태")}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
@@ -1049,67 +1096,69 @@ function TenantListPage() {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{importResult?.details.map((detail) => (
|
{filteredImportDetails.length === 0 ? (
|
||||||
<TableRow key={detail.row}>
|
<TableRow>
|
||||||
<TableCell className="font-mono text-xs">
|
<TableCell
|
||||||
{detail.row}
|
colSpan={5}
|
||||||
</TableCell>
|
className="h-24 text-center text-muted-foreground"
|
||||||
<TableCell className="font-medium">{detail.name}</TableCell>
|
>
|
||||||
<TableCell className="font-mono text-xs">
|
{t("ui.common.no_results", "표시할 결과가 없습니다.")}
|
||||||
{detail.slug}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
detail.action === "created"
|
|
||||||
? "success"
|
|
||||||
: detail.action === "updated"
|
|
||||||
? "warning"
|
|
||||||
: detail.action === "skipped"
|
|
||||||
? "outline"
|
|
||||||
: "destructive"
|
|
||||||
}
|
|
||||||
className="text-[10px]"
|
|
||||||
>
|
|
||||||
{detail.action.toUpperCase()}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
{detail.success ? (
|
|
||||||
<Badge variant="success" className="text-[10px]">
|
|
||||||
SUCCESS
|
|
||||||
</Badge>
|
|
||||||
) : (
|
|
||||||
<Badge variant="destructive" className="text-[10px]">
|
|
||||||
FAILED
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-xs">
|
|
||||||
{detail.message}
|
|
||||||
{detail.modifiedFields &&
|
|
||||||
detail.modifiedFields.length > 0 && (
|
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
|
||||||
<span className="mr-1 text-muted-foreground">
|
|
||||||
{t(
|
|
||||||
"ui.admin.tenants.import_result.modified",
|
|
||||||
"수정됨:",
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
{detail.modifiedFields.map((field) => (
|
|
||||||
<Badge
|
|
||||||
key={field}
|
|
||||||
variant="outline"
|
|
||||||
className="bg-muted text-[10px]"
|
|
||||||
>
|
|
||||||
{field}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
) : (
|
||||||
|
filteredImportDetails.map((detail) => (
|
||||||
|
<TableRow key={detail.row}>
|
||||||
|
<TableCell className="font-mono text-xs text-muted-foreground">
|
||||||
|
{detail.row}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-medium">
|
||||||
|
{detail.name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-mono text-xs">
|
||||||
|
{detail.slug}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge
|
||||||
|
variant={
|
||||||
|
detail.action === "created"
|
||||||
|
? "success"
|
||||||
|
: detail.action === "updated"
|
||||||
|
? "warning"
|
||||||
|
: detail.action === "skipped"
|
||||||
|
? "outline"
|
||||||
|
: "destructive"
|
||||||
|
}
|
||||||
|
className="w-full justify-center text-[10px]"
|
||||||
|
>
|
||||||
|
{detail.action.toUpperCase()}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-xs">
|
||||||
|
{detail.message}
|
||||||
|
{detail.modifiedFields &&
|
||||||
|
detail.modifiedFields.length > 0 && (
|
||||||
|
<div className="mt-1 flex flex-wrap gap-1">
|
||||||
|
<span className="mr-1 text-[10px] text-muted-foreground">
|
||||||
|
{t(
|
||||||
|
"ui.admin.tenants.import_result.modified",
|
||||||
|
"수정됨:",
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
{detail.modifiedFields.map((field) => (
|
||||||
|
<Badge
|
||||||
|
key={field}
|
||||||
|
variant="outline"
|
||||||
|
className="h-4 bg-muted px-1 text-[9px] font-normal"
|
||||||
|
>
|
||||||
|
{field}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user