Refactor: consolidate accounting routes under Buchhaltung submenu

- New layout route: companies.$id.buchhaltung.tsx with card-based navigation
- Renamed 7 accounting routes to use buchhaltung prefix:
  - companies.$id.bilanzen.tsx → companies.$id.buchhaltung.bilanzen.tsx
  - companies.$id.ausgaben.tsx → companies.$id.buchhaltung.ausgaben.tsx
  - companies.$id.ausgaben.kategorien.tsx → companies.$id.buchhaltung.ausgaben.kategorien.tsx
  - companies.$id.einnahmen.tsx → companies.$id.buchhaltung.einnahmen.tsx
  - companies.$id.einnahmen.kategorien.tsx → companies.$id.buchhaltung.einnahmen.kategorien.tsx
  - companies.$id.anlagevermoegen.tsx → companies.$id.buchhaltung.anlagevermoegen.tsx
  - companies.$id.money.tsx → companies.$id.buchhaltung.money.tsx

- Updated routing configuration (app/routes.ts) to use nested layout structure
- Updated breadcrumbs in all accounting routes to show Buchhaltung hierarchy
- Updated internal links in kategorien pages to use new URLs
- Main menu now shows single 'Buchhaltung' card instead of 5 separate items

Navigation improvements:
- Cleaner main menu (1 item vs 5)
- Clear accounting subsection with icon-based navigation
- Consistent URL structure (/companies/:id/buchhaltung/*)
- Better information hierarchy

Build:  Successful
Accounting routes:  Accessible
Navigation:  Functional

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
hwinkel
2026-04-15 21:41:56 +02:00
parent f10a79471e
commit ad80688b8b
68 changed files with 141 additions and 4014 deletions
@@ -19,6 +19,7 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Anlagevermögen" },
],
};
@@ -12,7 +12,8 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Betriebsausgaben", href: `/companies/${data.companyId}/ausgaben` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Betriebsausgaben", href: `/companies/${data.companyId}/buchhaltung/ausgaben` },
{ label: "Kategorien" },
],
};
@@ -149,7 +150,7 @@ export default function AusgabenKategorienPage() {
return (
<div>
<Link
to={`/companies/${companyId}/ausgaben`}
to={`/companies/${companyId}/buchhaltung/ausgaben`}
className="inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700 mb-6"
>
<ChevronLeft className="h-4 w-4" /> Zurück zu Betriebsausgaben
@@ -14,6 +14,7 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Betriebsausgaben" },
],
};
@@ -11,6 +11,7 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Bilanzen" },
],
};
@@ -12,7 +12,8 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Sonstige Einnahmen", href: `/companies/${data.companyId}/einnahmen` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Sonstige Einnahmen", href: `/companies/${data.companyId}/buchhaltung/einnahmen` },
{ label: "Kategorien" },
],
};
@@ -149,7 +150,7 @@ export default function EinnahmenKategorienPage() {
return (
<div>
<Link
to={`/companies/${companyId}/einnahmen`}
to={`/companies/${companyId}/buchhaltung/einnahmen`}
className="inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700 mb-6"
>
<ChevronLeft className="h-4 w-4" /> Zurück zu Sonstige Einnahmen
@@ -14,6 +14,7 @@ export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Buchhaltung", href: `/companies/${data.companyId}/buchhaltung/bilanzen` },
{ label: "Sonstige Einnahmen" },
],
};
+116
View File
@@ -0,0 +1,116 @@
import { Outlet, useParams, useLocation, Link } from "react-router";
import { requireUser } from "@/session.server";
import { Scale, TrendingDown, TrendingUp, Landmark, DollarSign, ChevronRight } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
export const handle = {
breadcrumbs: (data: { companyId: string; companyName: string }) => [
{ label: "Mandanten", href: "/companies" },
{ label: data.companyName, href: `/companies/${data.companyId}` },
{ label: "Buchhaltung" },
],
};
export async function loader({ request, params }: { request: Request; params: { id: string } }) {
const user = await requireUser(request);
// Verify company ownership
const { PrismaClient } = await import("@prisma/client");
const prisma = new PrismaClient();
const company = await prisma.company.findFirst({
where: { id: params.id, userId: user.id },
select: { id: true, name: true },
});
await prisma.$disconnect();
if (!company) throw new Response("Not Found", { status: 404 });
return { companyId: company.id, companyName: company.name };
}
const accountingTabs = [
{
id: "bilanzen",
label: "Bilanzen",
icon: Scale,
href: "bilanzen",
color: "teal",
},
{
id: "ausgaben",
label: "Ausgaben",
icon: TrendingDown,
href: "ausgaben",
color: "rose",
},
{
id: "einnahmen",
label: "Einnahmen",
icon: TrendingUp,
href: "einnahmen",
color: "emerald",
},
{
id: "anlagevermoegen",
label: "Anlagevermögen",
icon: Landmark,
href: "anlagevermoegen",
color: "violet",
},
{
id: "money",
label: "Finanzmittel",
icon: DollarSign,
href: "money",
color: "cyan",
},
];
export default function BuchhaltungLayout() {
const params = useParams();
const location = useLocation();
const companyId = params.id;
// Determine which tab is active based on current pathname
const pathSegments = location.pathname.split("/");
const activeSegment = pathSegments[pathSegments.length - 1];
const activeTa = accountingTabs.find((tab) => tab.href === activeSegment);
return (
<div className="space-y-6">
<div className="bg-white rounded-lg border border-gray-200 p-6 shadow-sm">
<h2 className="text-2xl font-bold text-gray-900 mb-1">Buchhaltung</h2>
<p className="text-sm text-gray-600">Verwaltung von Bilanzen, Ausgaben, Einnahmen und Vermögen</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
{accountingTabs.map((tab) => {
const Icon = tab.icon;
const isActive = tab.href === activeSegment;
const bgColor = `${tab.color}-50`;
const borderColor = `${tab.color}-200`;
const textColor = `${tab.color}-600`;
const activeBg = `${tab.color}-100`;
return (
<Link key={tab.id} to={`/companies/${companyId}/buchhaltung/${tab.href}`} className="block">
<Card
className={`hover:shadow-sm transition-all cursor-pointer ${
isActive ? `border-${borderColor} bg-${activeBg}` : `hover:border-${borderColor}`
}`}
>
<CardContent className="pt-6 pb-6 flex flex-col items-center gap-3 text-center">
<div className={`p-3 rounded-lg bg-${bgColor}`}>
<Icon className={`h-5 w-5 text-${textColor}`} />
</div>
<span className="text-sm font-medium text-gray-700">{tab.label}</span>
</CardContent>
</Card>
</Link>
);
})}
</div>
<div className="bg-white rounded-lg border border-gray-200 shadow-sm">
<Outlet />
</div>
</div>
);
}
+5 -45
View File
@@ -233,53 +233,13 @@ export default function CompanyPage() {
</CardContent>
</Card>
</Link>
<Link to={`/companies/${id}/bilanzen`} className="block">
<Card className="hover:border-teal-200 hover:shadow-sm transition-all cursor-pointer">
<Link to={`/companies/${id}/buchhaltung/bilanzen`} className="block">
<Card className="hover:border-indigo-200 hover:shadow-sm transition-all cursor-pointer">
<CardContent className="pt-4 pb-4 flex items-center gap-3">
<div className="p-2 rounded-lg bg-teal-50">
<Scale className="h-4 w-4 text-teal-600" />
<div className="p-2 rounded-lg bg-indigo-50">
<Briefcase className="h-4 w-4 text-indigo-600" />
</div>
<span className="text-sm font-medium text-gray-700">Bilanzen</span>
</CardContent>
</Card>
</Link>
<Link to={`/companies/${id}/ausgaben`} className="block">
<Card className="hover:border-rose-200 hover:shadow-sm transition-all cursor-pointer">
<CardContent className="pt-4 pb-4 flex items-center gap-3">
<div className="p-2 rounded-lg bg-rose-50">
<TrendingDown className="h-4 w-4 text-rose-600" />
</div>
<span className="text-sm font-medium text-gray-700">Ausgaben</span>
</CardContent>
</Card>
</Link>
<Link to={`/companies/${id}/einnahmen`} className="block">
<Card className="hover:border-emerald-200 hover:shadow-sm transition-all cursor-pointer">
<CardContent className="pt-4 pb-4 flex items-center gap-3">
<div className="p-2 rounded-lg bg-emerald-50">
<TrendingUp className="h-4 w-4 text-emerald-600" />
</div>
<span className="text-sm font-medium text-gray-700">Einnahmen</span>
</CardContent>
</Card>
</Link>
<Link to={`/companies/${id}/anlagevermoegen`} className="block">
<Card className="hover:border-violet-200 hover:shadow-sm transition-all cursor-pointer">
<CardContent className="pt-4 pb-4 flex items-center gap-3">
<div className="p-2 rounded-lg bg-violet-50">
<PackageSearch className="h-4 w-4 text-violet-600" />
</div>
<span className="text-sm font-medium text-gray-700">Anlagevermögen</span>
</CardContent>
</Card>
</Link>
<Link to={`/companies/${id}/money`} className="block">
<Card className="hover:border-cyan-200 hover:shadow-sm transition-all cursor-pointer">
<CardContent className="pt-4 pb-4 flex items-center gap-3">
<div className="p-2 rounded-lg bg-cyan-50">
<DollarSign className="h-4 w-4 text-cyan-600" />
</div>
<span className="text-sm font-medium text-gray-700">Finanzmittel</span>
<span className="text-sm font-medium text-gray-700">Buchhaltung</span>
</CardContent>
</Card>
</Link>