feat: unified Data Import page — merge Dispo + Skill imports

New /admin/imports page with tabs:
- "Dispo Import" tab: renders DispoImportClient (lazy-loaded)
- "Skill Matrix" tab: renders BatchSkillImport (lazy-loaded)
- Tab state via ?tab= URL param

Routing:
- /admin/dispo-imports → redirects to /admin/imports?tab=dispo
- /admin/skill-import → redirects to /admin/imports?tab=skills
- /admin/dispo-imports/[batchId] detail routes still work

Sidebar: single "Data Import" link under ACN-Orga (was 2 links)

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-23 10:38:03 +01:00
parent cefd3e0bf8
commit 52d425043b
4 changed files with 70 additions and 8 deletions
@@ -1,5 +1,5 @@
import { DispoImportClient } from "~/components/admin/DispoImportClient.js"; import { redirect } from "next/navigation";
export default function DispoImportsPage() { export default function DispoImportRedirect() {
return <DispoImportClient />; redirect("/admin/imports?tab=dispo");
} }
@@ -0,0 +1,63 @@
"use client";
import { useState } from "react";
import dynamic from "next/dynamic";
import { useSearchParams } from "next/navigation";
const DispoImportClient = dynamic(
() => import("~/components/admin/DispoImportClient.js").then((m) => m.DispoImportClient),
{ loading: () => <div className="p-6"><div className="h-8 w-48 shimmer-skeleton rounded" /><div className="mt-4 h-64 shimmer-skeleton rounded-xl" /></div> },
);
const BatchSkillImport = dynamic(
() => import("~/components/admin/BatchSkillImport.js").then((m) => m.BatchSkillImport),
{ loading: () => <div className="p-6"><div className="h-8 w-48 shimmer-skeleton rounded" /><div className="mt-4 h-64 shimmer-skeleton rounded-xl" /></div> },
);
type Tab = "dispo" | "skills";
const TABS: { key: Tab; label: string; description: string }[] = [
{ key: "dispo", label: "Dispo Import", description: "Import planning data from Dispo V2 workbooks" },
{ key: "skills", label: "Skill Matrix", description: "Import skill matrices from XLSX files" },
];
export default function ImportsPage() {
const searchParams = useSearchParams();
const initialTab = (searchParams.get("tab") as Tab) ?? "dispo";
const [activeTab, setActiveTab] = useState<Tab>(initialTab);
return (
<div className="app-page flex h-full flex-col gap-5 pb-6">
<div className="app-page-header">
<div>
<h1 className="app-page-title">Data Import</h1>
<p className="app-page-subtitle mt-1">Import planning data and skill matrices</p>
</div>
</div>
{/* Tab bar */}
<div className="flex border-b border-gray-200 dark:border-gray-700">
{TABS.map((tab) => (
<button
key={tab.key}
type="button"
onClick={() => setActiveTab(tab.key)}
className={`px-5 py-2.5 text-sm font-medium border-b-2 -mb-px transition-colors ${
activeTab === tab.key
? "border-brand-500 text-brand-600 dark:text-brand-400"
: "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}`}
>
{tab.label}
</button>
))}
</div>
{/* Tab content */}
<div className="flex-1 min-h-0">
{activeTab === "dispo" && <DispoImportClient />}
{activeTab === "skills" && <BatchSkillImport />}
</div>
</div>
);
}
@@ -1,5 +1,5 @@
import { BatchSkillImport } from "~/components/admin/BatchSkillImport.js"; import { redirect } from "next/navigation";
export default function BatchSkillImportPage() { export default function SkillImportRedirect() {
return <BatchSkillImport />; redirect("/admin/imports?tab=skills");
} }
+1 -2
View File
@@ -182,8 +182,7 @@ const adminNavEntries: AdminEntry[] = [
{ href: "/admin/org-units", label: "Org Units", icon: <AdminIcon /> }, { href: "/admin/org-units", label: "Org Units", icon: <AdminIcon /> },
{ href: "/admin/utilization-categories", label: "Util. Categories", icon: <AdminIcon /> }, { href: "/admin/utilization-categories", label: "Util. Categories", icon: <AdminIcon /> },
{ href: "/admin/management-levels", label: "Mgmt Levels", icon: <AdminIcon /> }, { href: "/admin/management-levels", label: "Mgmt Levels", icon: <AdminIcon /> },
{ href: "/admin/dispo-imports", label: "Dispo Import", icon: <AdminIcon /> }, { href: "/admin/imports", label: "Data Import", icon: <AdminIcon /> },
{ href: "/admin/skill-import", label: "Skill Import", icon: <AdminIcon /> },
], ],
}, },
{ href: "/admin/calculation-rules", label: "Calc. Rules", icon: <AdminIcon /> }, { href: "/admin/calculation-rules", label: "Calc. Rules", icon: <AdminIcon /> },