- Fix Accounts page: include investment accounts in Est. Monthly Interest calc, add Fund column to investment table, split summary cards into Operating/Reserve - Fix Cash Flow: ending balance now respects includeInvestments toggle - Fix Budget Manager: separate operating/reserve income in summary cards - Fix Projects: default sort by planned_date instead of name - Add Vendors: last_negotiated date field with migration, CSV import/export - New Quarterly Financial Report: budget vs actuals, over-budget flagging, YTD - Enhance Dashboard: separate Operating/Reserve fund cards, expanded Quick Stats with monthly interest, YTD interest earned, planned capital spend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
147 lines
6.3 KiB
TypeScript
147 lines
6.3 KiB
TypeScript
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
import { useAuthStore } from './stores/authStore';
|
|
import { AppLayout } from './components/layout/AppLayout';
|
|
import { LoginPage } from './pages/auth/LoginPage';
|
|
import { RegisterPage } from './pages/auth/RegisterPage';
|
|
import { SelectOrgPage } from './pages/auth/SelectOrgPage';
|
|
import { DashboardPage } from './pages/dashboard/DashboardPage';
|
|
import { AccountsPage } from './pages/accounts/AccountsPage';
|
|
import { TransactionsPage } from './pages/transactions/TransactionsPage';
|
|
import { BudgetsPage } from './pages/budgets/BudgetsPage';
|
|
import { UnitsPage } from './pages/units/UnitsPage';
|
|
import { InvoicesPage } from './pages/invoices/InvoicesPage';
|
|
import { PaymentsPage } from './pages/payments/PaymentsPage';
|
|
import { VendorsPage } from './pages/vendors/VendorsPage';
|
|
import { ProjectsPage } from './pages/projects/ProjectsPage';
|
|
import { InvestmentsPage } from './pages/investments/InvestmentsPage';
|
|
import { CapitalProjectsPage } from './pages/capital-projects/CapitalProjectsPage';
|
|
import { BalanceSheetPage } from './pages/reports/BalanceSheetPage';
|
|
import { IncomeStatementPage } from './pages/reports/IncomeStatementPage';
|
|
import { BudgetVsActualPage } from './pages/reports/BudgetVsActualPage';
|
|
import { SankeyPage } from './pages/reports/SankeyPage';
|
|
import { CashFlowPage } from './pages/reports/CashFlowPage';
|
|
import { AgingReportPage } from './pages/reports/AgingReportPage';
|
|
import { YearEndPage } from './pages/reports/YearEndPage';
|
|
import { QuarterlyReportPage } from './pages/reports/QuarterlyReportPage';
|
|
import { SettingsPage } from './pages/settings/SettingsPage';
|
|
import { UserPreferencesPage } from './pages/preferences/UserPreferencesPage';
|
|
import { OrgMembersPage } from './pages/org-members/OrgMembersPage';
|
|
import { AdminPage } from './pages/admin/AdminPage';
|
|
import { AssessmentGroupsPage } from './pages/assessment-groups/AssessmentGroupsPage';
|
|
import { CashFlowForecastPage } from './pages/cash-flow/CashFlowForecastPage';
|
|
import { MonthlyActualsPage } from './pages/monthly-actuals/MonthlyActualsPage';
|
|
import { InvestmentPlanningPage } from './pages/investment-planning/InvestmentPlanningPage';
|
|
|
|
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
|
const token = useAuthStore((s) => s.token);
|
|
if (!token) return <Navigate to="/login" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function OrgRequiredRoute({ children }: { children: React.ReactNode }) {
|
|
const token = useAuthStore((s) => s.token);
|
|
const currentOrg = useAuthStore((s) => s.currentOrg);
|
|
if (!token) return <Navigate to="/login" replace />;
|
|
if (!currentOrg) return <Navigate to="/select-org" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function SuperAdminRoute({ children }: { children: React.ReactNode }) {
|
|
const token = useAuthStore((s) => s.token);
|
|
const user = useAuthStore((s) => s.user);
|
|
if (!token) return <Navigate to="/login" replace />;
|
|
if (!user?.isSuperadmin) return <Navigate to="/dashboard" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function AuthRoute({ children }: { children: React.ReactNode }) {
|
|
const token = useAuthStore((s) => s.token);
|
|
const user = useAuthStore((s) => s.user);
|
|
const currentOrg = useAuthStore((s) => s.currentOrg);
|
|
const organizations = useAuthStore((s) => s.organizations);
|
|
if (token && currentOrg) return <Navigate to="/" replace />;
|
|
// Platform owner / superadmin with no org memberships → admin panel
|
|
if (token && user?.isSuperadmin && (!organizations || organizations.length === 0)) {
|
|
return <Navigate to="/admin" replace />;
|
|
}
|
|
if (token && !currentOrg) return <Navigate to="/select-org" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
export function App() {
|
|
return (
|
|
<Routes>
|
|
<Route
|
|
path="/login"
|
|
element={
|
|
<AuthRoute>
|
|
<LoginPage />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/register"
|
|
element={
|
|
<AuthRoute>
|
|
<RegisterPage />
|
|
</AuthRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/select-org"
|
|
element={
|
|
<ProtectedRoute>
|
|
<SelectOrgPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/admin"
|
|
element={
|
|
<SuperAdminRoute>
|
|
<AppLayout />
|
|
</SuperAdminRoute>
|
|
}
|
|
>
|
|
<Route index element={<AdminPage />} />
|
|
</Route>
|
|
<Route
|
|
path="/*"
|
|
element={
|
|
<OrgRequiredRoute>
|
|
<AppLayout />
|
|
</OrgRequiredRoute>
|
|
}
|
|
>
|
|
<Route index element={<Navigate to="/dashboard" replace />} />
|
|
<Route path="dashboard" element={<DashboardPage />} />
|
|
<Route path="accounts" element={<AccountsPage />} />
|
|
<Route path="transactions" element={<TransactionsPage />} />
|
|
<Route path="budgets/:year" element={<BudgetsPage />} />
|
|
<Route path="units" element={<UnitsPage />} />
|
|
<Route path="invoices" element={<InvoicesPage />} />
|
|
<Route path="payments" element={<PaymentsPage />} />
|
|
<Route path="vendors" element={<VendorsPage />} />
|
|
<Route path="projects" element={<ProjectsPage />} />
|
|
<Route path="investments" element={<InvestmentsPage />} />
|
|
<Route path="capital-projects" element={<CapitalProjectsPage />} />
|
|
<Route path="investment-planning" element={<InvestmentPlanningPage />} />
|
|
<Route path="assessment-groups" element={<AssessmentGroupsPage />} />
|
|
<Route path="cash-flow" element={<CashFlowForecastPage />} />
|
|
<Route path="monthly-actuals" element={<MonthlyActualsPage />} />
|
|
<Route path="reports/balance-sheet" element={<BalanceSheetPage />} />
|
|
<Route path="reports/income-statement" element={<IncomeStatementPage />} />
|
|
<Route path="reports/budget-vs-actual" element={<BudgetVsActualPage />} />
|
|
<Route path="reports/cash-flow" element={<CashFlowPage />} />
|
|
<Route path="reports/aging" element={<AgingReportPage />} />
|
|
<Route path="reports/sankey" element={<SankeyPage />} />
|
|
<Route path="reports/year-end" element={<YearEndPage />} />
|
|
<Route path="reports/quarterly" element={<QuarterlyReportPage />} />
|
|
<Route path="settings" element={<SettingsPage />} />
|
|
<Route path="preferences" element={<UserPreferencesPage />} />
|
|
<Route path="org-members" element={<OrgMembersPage />} />
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
}
|