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)}
+ />
+
;