Files
iistwin/.superpowers/sdd/di2-restyle/FinanceBottomNav.reference.tsx
Ильяс Султанов 66f4e5ef53 DI2: удаление старого модуля финансов — пул БД и аудит перенесены в finance-di2
- 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)
2026-09-06 21:53:47 +03:00

145 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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