From 30c0007f40594e679f5d997219985107de784cf4 Mon Sep 17 00:00:00 2001
From: Luca <102960244+Luca-Timo@users.noreply.github.com>
Date: Thu, 11 Jun 2026 00:04:40 +0200
Subject: [PATCH] feat(accounting): Accounting nav section + relocate Tax
report out of CRM
Adds the `accounting` feature flag to the frontend (type, context default) and
a Settings -> Features toggle card. When enabled:
- A new top-level "Accounting" sidebar entry appears (gated by `accounting` +
accounting.view), with an AccountingLayout sub-nav mirroring ClientsLayout.
- The Tax report relocates: it is HIDDEN from the CRM (Clients) sub-nav and
shown under Accounting instead, at /admin/accounting/tax-report. When
accounting is OFF, Tax stays under CRM exactly as before.
Tax visibility still depends on `taxReport` (which depends on `bills`), so the
relocation only changes WHERE the menu item lives, not whether it exists.
Files: featureFlags.service.ts (+'accounting'), FeatureFlagsContext default,
AdminSidebar entry, new AccountingLayout, ClientsLayout filter, App.tsx route,
FeaturesTab card, en/de i18n (navigation.accounting, accounting.*,
settings.features.accounting; DE authored natively).
Verified: `npm run build` green; en/de JSON valid.
---
frontend/src/App.tsx | 15 ++
.../src/components/admin/AccountingLayout.tsx | 141 ++++++++++++++++++
.../src/components/admin/AdminSidebar.tsx | 11 ++
.../src/components/admin/ClientsLayout.tsx | 8 +-
frontend/src/contexts/FeatureFlagsContext.tsx | 4 +
.../features/settings/tabs/FeaturesTab.tsx | 15 ++
frontend/src/i18n/locales/de.json | 18 +++
frontend/src/i18n/locales/en.json | 18 +++
frontend/src/services/featureFlags.service.ts | 7 +-
9 files changed, 235 insertions(+), 2 deletions(-)
create mode 100644 frontend/src/components/admin/AccountingLayout.tsx
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index a4d72da6..e78f7e4c 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -64,6 +64,7 @@ import {
import { CustomerAuthProvider } from './contexts/CustomerAuthContext';
import { AdminLayout, AdminAuthWrapper } from './components/admin';
import { ClientsLayout } from './components/admin/ClientsLayout';
+import { AccountingLayout } from './components/admin/AccountingLayout';
import { RequireFeature } from './components/admin/RequireFeature';
import { PageErrorBoundary, OfflineIndicator, SkipLink, DynamicFavicon, RobotsMetaTags, CMSContentBlock, Loading } from './components/common';
import { MaintenanceWrapper } from './components/MaintenanceWrapper';
@@ -260,6 +261,20 @@ function App() {
+ {/* Accounting section (migration 122). Parent gated by
+ the `accounting` flag. Hosts the Tax report — which
+ relocates here from the CRM sub-nav when accounting
+ is on — plus the future inbound-invoice / expenses
+ pages. Each sub-route is independently flagged. */}
+ }>
+ }>
+ }>
+ } />
+
+ } />
+
+
+
{/* Old /admin/customers paths now live under
/admin/clients/accounts. Kept indefinitely as
redirects so existing bookmarks and email links
diff --git a/frontend/src/components/admin/AccountingLayout.tsx b/frontend/src/components/admin/AccountingLayout.tsx
new file mode 100644
index 00000000..e098b44b
--- /dev/null
+++ b/frontend/src/components/admin/AccountingLayout.tsx
@@ -0,0 +1,141 @@
+/**
+ * Accounting section layout (migration 122).
+ *
+ * Wraps /admin/accounting/* routes with a Settings-style left sub-nav,
+ * mirroring ClientsLayout. Today it hosts the Tax report (relocated here
+ * from CRM when the `accounting` flag is on); the inbound-document inbox and
+ * expenses pages slot in as additional sub-nav entries when their UIs land.
+ */
+import React from 'react';
+import { NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { Landmark, Calculator } from 'lucide-react';
+import type { LucideIcon } from 'lucide-react';
+import { useFeatureFlags, type FeatureKey } from '../../contexts/FeatureFlagsContext';
+
+interface NavItem {
+ key: string;
+ to: string;
+ label: string;
+ icon: LucideIcon;
+ /** Feature flag that must be ON for this entry to render. */
+ featureFlag: FeatureKey;
+}
+
+export const AccountingLayout: React.FC = () => {
+ const { t } = useTranslation();
+ const location = useLocation();
+ const navigate = useNavigate();
+ const { flags } = useFeatureFlags();
+
+ const navItems: NavItem[] = [
+ {
+ key: 'tax-report',
+ to: '/admin/accounting/tax-report',
+ label: t('accounting.subnav.taxReport', 'Tax'),
+ icon: Calculator,
+ featureFlag: 'taxReport',
+ },
+ // Future Accounting sub-features (inbound inbox, expenses) slot in here
+ // once their pages land, e.g.:
+ // { key: 'inbox', to: '/admin/accounting/inbox', featureFlag: 'accounting' }
+ // { key: 'expenses', to: '/admin/accounting/expenses', featureFlag: 'accounting' }
+ ];
+
+ const enabledItems = navItems.filter((item) => flags[item.featureFlag]);
+
+ const header = (
+
+
+ {t('accounting.title', 'Accounting')}
+
+
+ {t('accounting.subtitle', 'Inbound supplier invoices, expenses and reporting.')}
+
+
+ );
+
+ if (enabledItems.length === 0) {
+ return (
+
+ {header}
+
+
+
+ {t('accounting.empty.title', 'No accounting features enabled')}
+
+
+ {t('accounting.empty.body', 'Enable the Tax report (or another accounting sub-feature) under Settings → Features to get started.')}
+
+
+
+ );
+ }
+
+ return (
+
+ {header}
+
+
+ {/* Mobile: native select dropdown */}
+
+
+
+
+
+ {/* Desktop: sticky left rail */}
+
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/components/admin/AdminSidebar.tsx b/frontend/src/components/admin/AdminSidebar.tsx
index 79aefddf..01a4d837 100644
--- a/frontend/src/components/admin/AdminSidebar.tsx
+++ b/frontend/src/components/admin/AdminSidebar.tsx
@@ -10,6 +10,7 @@ import {
X,
Users,
Briefcase,
+ Landmark,
PanelLeftClose,
PanelLeftOpen,
} from 'lucide-react';
@@ -96,6 +97,16 @@ const navigation: NavItem[] = [
'taxReport', 'hoursLogging', 'contracts', 'calendar',
],
},
+ // Accounting section (migration 122) — inbound supplier invoices,
+ // expenses + re-bill, and the tax report (which relocates here from
+ // the CRM sub-nav when `accounting` is on). Gated by the `accounting`
+ // master flag; the sub-pages inside AccountingLayout are each
+ // independently feature-gated.
+ {
+ nameKey: 'navigation.accounting', href: '/admin/accounting', icon: Landmark,
+ permission: 'accounting.view',
+ featureFlag: 'accounting',
+ },
];
export const AdminSidebar: React.FC = ({ isOpen, onClose, collapsed = false, onToggleCollapse }) => {
diff --git a/frontend/src/components/admin/ClientsLayout.tsx b/frontend/src/components/admin/ClientsLayout.tsx
index d3de2e1a..be53b2e4 100644
--- a/frontend/src/components/admin/ClientsLayout.tsx
+++ b/frontend/src/components/admin/ClientsLayout.tsx
@@ -99,7 +99,13 @@ export const ClientsLayout: React.FC = () => {
},
];
- const enabledItems = navItems.filter((item) => flags[item.featureFlag]);
+ const enabledItems = navItems.filter((item) => {
+ if (!flags[item.featureFlag]) return false;
+ // When the Accounting area is enabled, the tax report relocates out
+ // of CRM and under Accounting — hide it here so it isn't in both.
+ if (item.key === 'tax-report' && flags.accounting) return false;
+ return true;
+ });
// When the parent `clients` flag is on but no sub-feature is enabled,
// there's nothing to render. Settings → Features is one click away
diff --git a/frontend/src/contexts/FeatureFlagsContext.tsx b/frontend/src/contexts/FeatureFlagsContext.tsx
index b0da55dc..4b80eb53 100644
--- a/frontend/src/contexts/FeatureFlagsContext.tsx
+++ b/frontend/src/contexts/FeatureFlagsContext.tsx
@@ -45,6 +45,10 @@ export const DEFAULT_FLAGS: FeatureFlags = {
// Settings → Features once they've reviewed the seeded block
// library with their lawyer.
contracts: false,
+ // Accounting (migration 122). Top-level Accounting area (inbound
+ // supplier invoices, expenses + re-bill). When ON, the tax report
+ // moves out of the CRM sub-nav and under Accounting. Strictly opt-in.
+ accounting: false,
};
export const FEATURE_FLAGS_QUERY_KEY = ['feature-flags'] as const;
diff --git a/frontend/src/features/settings/tabs/FeaturesTab.tsx b/frontend/src/features/settings/tabs/FeaturesTab.tsx
index b5cefdc7..6563af5c 100644
--- a/frontend/src/features/settings/tabs/FeaturesTab.tsx
+++ b/frontend/src/features/settings/tabs/FeaturesTab.tsx
@@ -16,6 +16,7 @@ import {
Briefcase,
Wrench,
Calculator,
+ Landmark,
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button, Card } from '../../../components/common';
@@ -278,6 +279,20 @@ export const FeaturesTab: React.FC = () => {
) : undefined}
/>
+ setFlag('accounting', next)}
+ />
+
;