- server/finance/ (19 файлов) удалён: пул финансовой БД перенесён в server/finance-di2/db-core.ts, db-client.ts теперь реэкспортирует из ./db-core - Шедулер аудита перенесён в server/finance-di2/audit-scheduler.ts (03:00 МСК, setInterval, без node-cron), запуск из registerDi2Routes с защитой от повторного старта - История аудита DI2 теперь персистентна: загрузка/запись data/finance-audit-history.json в audit-agent.ts (порт из удалённого server/finance/audit.ts) - registerFinanceRoutes убран из server/routes/index.ts, registerDi2Routes остался на его месте - Клиент: удалены pages/finance/, components/finance/, hooks/useFinance.ts, hooks/useColumnLabels.ts, lib/format.ts, pages/salary.tsx; роуты /finance*, /income*, /expenses*, /salary и lazy-импорты убраны из App.tsx - Навигация: «Финансы» в Sidebar и MobileBottomNav ведёт на субапп /di2 (внешняя ссылка), гейт moduleAccess.finance + finance.view сохранён - index.css: удалён блок .hover-elevate/.hover-elevate-2/.active-elevate (использовался только старым модульным DataTable) - FinanceBottomNav.tsx сохранён как референс для этапа 3 (.superpowers/sdd/di2-restyle/FinanceBottomNav.reference.tsx) - Не тронуто: client-di2/, сиды прав finance.view/manage, finance_access (module-access)
145 lines
5.3 KiB
TypeScript
145 lines
5.3 KiB
TypeScript
import { Link, useLocation } from "wouter";
|
||
import { cn } from "@/lib/utils";
|
||
import {
|
||
LayoutDashboard,
|
||
Wallet,
|
||
Calculator,
|
||
ShieldCheck,
|
||
Tags,
|
||
TrendingUp,
|
||
TrendingDown,
|
||
Settings,
|
||
Menu,
|
||
ExternalLink,
|
||
} from "lucide-react";
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuSeparator,
|
||
DropdownMenuTrigger,
|
||
} from "@/components/ui/dropdown-menu";
|
||
import type { FinanceTab } from "./FinanceNav";
|
||
|
||
interface FinanceBottomNavProps {
|
||
active: FinanceTab;
|
||
onChange: (tab: FinanceTab) => void;
|
||
}
|
||
|
||
export function FinanceBottomNav({ active, onChange }: FinanceBottomNavProps) {
|
||
const [location, setLocation] = useLocation();
|
||
// путь в wouter может включать search-параметр
|
||
const onDashboard = location.split("?")[0] === "/finance";
|
||
|
||
const goDashboardTab = (tab: FinanceTab) => {
|
||
if (onDashboard) {
|
||
onChange(tab);
|
||
} else {
|
||
setLocation(tab === "dashboard" ? "/finance" : `/finance?tab=${tab}`);
|
||
}
|
||
};
|
||
|
||
const btnClass = (isActive: boolean) =>
|
||
cn(
|
||
"flex flex-col items-center justify-center gap-0.5 flex-1 h-full rounded-lg transition-colors",
|
||
isActive ? "text-primary" : "text-muted-foreground hover:text-foreground"
|
||
);
|
||
|
||
const sectionsActive =
|
||
active === "debt" ||
|
||
active === "calculations" ||
|
||
active === "audit" ||
|
||
active === "income" ||
|
||
active === "expenses" ||
|
||
active === "income-categories" ||
|
||
active === "expense-categories";
|
||
|
||
return (
|
||
<div className="fixed bottom-0 left-0 right-0 bg-background border-t border-border z-50 md:hidden safe-area-bottom">
|
||
<nav className="flex items-stretch justify-around h-14 px-2" aria-label="Навигация по финансам">
|
||
<button
|
||
onClick={() => goDashboardTab("dashboard")}
|
||
aria-label="Обзор"
|
||
className={btnClass(active === "dashboard")}
|
||
>
|
||
<LayoutDashboard className="w-5 h-5" aria-hidden="true" />
|
||
<span className="text-[10px] font-medium">Обзор</span>
|
||
</button>
|
||
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<button
|
||
aria-label="Разделы"
|
||
className={cn(btnClass(false), "relative", sectionsActive && "text-primary")}
|
||
>
|
||
<Menu className="w-5 h-5" aria-hidden="true" />
|
||
<span className="text-[10px] font-medium">Разделы</span>
|
||
{sectionsActive && (
|
||
<span
|
||
aria-hidden="true"
|
||
className="absolute top-1 right-1/2 translate-x-4 w-1.5 h-1.5 rounded-full bg-primary"
|
||
/>
|
||
)}
|
||
</button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent side="top" align="center" className="w-56 mb-2">
|
||
<DropdownMenuItem className="text-xs cursor-pointer" onSelect={() => goDashboardTab("income")}>
|
||
<TrendingUp className="w-3.5 h-3.5 mr-2" />
|
||
Доходы
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem className="text-xs cursor-pointer" onSelect={() => goDashboardTab("expenses")}>
|
||
<TrendingDown className="w-3.5 h-3.5 mr-2" />
|
||
Расходы
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<Link href="/finance/debt">
|
||
<DropdownMenuItem className="text-xs cursor-pointer">
|
||
<Wallet className="w-3.5 h-3.5 mr-2" />
|
||
Дебиторка
|
||
</DropdownMenuItem>
|
||
</Link>
|
||
<Link href="/finance/calculations">
|
||
<DropdownMenuItem className="text-xs cursor-pointer">
|
||
<Calculator className="w-3.5 h-3.5 mr-2" />
|
||
Расчёты
|
||
</DropdownMenuItem>
|
||
</Link>
|
||
<Link href="/finance/audit">
|
||
<DropdownMenuItem className="text-xs cursor-pointer">
|
||
<ShieldCheck className="w-3.5 h-3.5 mr-2" />
|
||
Аудит
|
||
</DropdownMenuItem>
|
||
</Link>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem className="text-xs cursor-pointer" onSelect={() => goDashboardTab("income-categories")}>
|
||
<Tags className="w-3.5 h-3.5 mr-2" />
|
||
Категории доходов
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem className="text-xs cursor-pointer" onSelect={() => goDashboardTab("expense-categories")}>
|
||
<Tags className="w-3.5 h-3.5 mr-2" />
|
||
Категории расходов
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
{/* Внешняя ссылка на субапп Data-Insight2 — полная версия финансов */}
|
||
<a href="/di2" target="_blank" rel="noopener">
|
||
<DropdownMenuItem className="text-xs cursor-pointer">
|
||
<ExternalLink className="w-3.5 h-3.5 mr-2" />
|
||
Data-Insight (полная версия)
|
||
</DropdownMenuItem>
|
||
</a>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
<Link
|
||
href="/finance/settings"
|
||
aria-label="Настройки"
|
||
className={btnClass(active === "settings")}
|
||
>
|
||
<Settings className="w-5 h-5" aria-hidden="true" />
|
||
<span className="text-[10px] font-medium">Настройки</span>
|
||
</Link>
|
||
</nav>
|
||
</div>
|
||
);
|
||
}
|