1
0
forked from baron/baron-sso

fix(admin): add missing Tabs import and refine import result UI type safety

This commit is contained in:
2026-06-04 11:20:21 +09:00
parent fbdfb97c3e
commit c6c79f7306

View File

@@ -76,6 +76,7 @@ import {
TableHeader,
TableRow,
} from "../../../components/ui/table";
import { Tabs, TabsList, TabsTrigger } from "../../../components/ui/tabs";
import { toast } from "../../../components/ui/use-toast";
import type { UserProfileResponse } from "../../../lib/adminApi";
import {
@@ -288,6 +289,17 @@ function TenantListPage() {
const [importResult, setImportResult] =
React.useState<TenantImportResult | null>(null);
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 _tenantTableScrollRef = React.useRef<HTMLDivElement | null>(null);
@@ -1009,22 +1021,60 @@ function TenantListPage() {
"가져오기 결과 리포트",
)}
</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>
<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>
<TableHeader className={commonStickyTableHeaderClass}>
<TableRow>
@@ -1037,10 +1087,7 @@ function TenantListPage() {
<TableHead>
{t("ui.admin.tenants.table.slug", "SLUG")}
</TableHead>
<TableHead>
{t("ui.admin.tenants.import_result.action", "작업")}
</TableHead>
<TableHead>
<TableHead className="w-[120px]">
{t("ui.admin.tenants.import_result.status", "상태")}
</TableHead>
<TableHead>
@@ -1049,67 +1096,69 @@ function TenantListPage() {
</TableRow>
</TableHeader>
<TableBody>
{importResult?.details.map((detail) => (
<TableRow key={detail.row}>
<TableCell className="font-mono text-xs">
{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="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>
)}
{filteredImportDetails.length === 0 ? (
<TableRow>
<TableCell
colSpan={5}
className="h-24 text-center text-muted-foreground"
>
{t("ui.common.no_results", "표시할 결과가 없습니다.")}
</TableCell>
</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>
</Table>
</div>