feat: Accounting web module — COA, journals, expenses, accounts, reports + nav (FIBEROPS-234-239)
- Chart of Accounts (FIBEROPS-234): type-filtered table, add/seed/toggle-active - Journal Entries (FIBEROPS-235): double-entry form with debit=credit validation, read-only view modal - Expenses (FIBEROPS-236): date-range filtered table, record expense against expense accounts - Company Accounts + Transfers (FIBEROPS-237): balance cards, add account modal, transfer modal, transfers table - Financial Reports (FIBEROPS-238): Trial Balance, P&L, Balance Sheet, Cash Flow with date pickers - Sidebar nav entry (FIBEROPS-239): Accounting link with BookOpen icon, placed between Reports and Settings - AccountingNav horizontal sub-nav component shared across all accounting pages - New types: Account, JournalEntry, Expense, CompanyAccount, Transfer and report types added to src/types/index.ts
This commit is contained in:
41
src/components/accounting/AccountingNav.tsx
Normal file
41
src/components/accounting/AccountingNav.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { BookOpen, BookText, Receipt, Building2, BarChart3 } from "lucide-react";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/accounting", label: "Chart of Accounts", icon: BookOpen, exact: true },
|
||||
{ href: "/accounting/journal-entries", label: "Journal Entries", icon: BookText },
|
||||
{ href: "/accounting/expenses", label: "Expenses", icon: Receipt },
|
||||
{ href: "/accounting/company-accounts", label: "Company Accounts", icon: Building2 },
|
||||
{ href: "/accounting/reports", label: "Reports", icon: BarChart3 },
|
||||
];
|
||||
|
||||
export function AccountingNav() {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="flex gap-1 border-b border-gray-200 mb-6 overflow-x-auto">
|
||||
{NAV_ITEMS.map(({ href, label, icon: Icon, exact }) => {
|
||||
const isActive = exact ? pathname === href : pathname === href || pathname.startsWith(href + "/");
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 -mb-px whitespace-nowrap transition-colors",
|
||||
isActive
|
||||
? "border-blue-600 text-blue-700"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
Wifi,
|
||||
Briefcase,
|
||||
BarChart2,
|
||||
BookOpen,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
||||
@@ -32,6 +33,7 @@ const navItems = [
|
||||
{ href: "/users", label: "Users", icon: UserCog },
|
||||
{ href: "/audit-log", label: "Audit Log", icon: ClipboardList },
|
||||
{ href: "/reports", label: "Reports", icon: BarChart2 },
|
||||
{ href: "/accounting", label: "Accounting", icon: BookOpen },
|
||||
{ href: "/settings", label: "Settings", icon: Settings },
|
||||
];
|
||||
|
||||
|
||||
@@ -211,6 +211,110 @@ export interface LegacyPaginatedResponse<T> {
|
||||
limit: number;
|
||||
}
|
||||
|
||||
// ─── Accounting ────────────────────────────────────────────────────────────────
|
||||
|
||||
export type AccountType = 'ASSET' | 'LIABILITY' | 'EQUITY' | 'REVENUE' | 'EXPENSE';
|
||||
|
||||
export interface Account {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
type: AccountType;
|
||||
isActive: boolean;
|
||||
balance?: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface JournalEntryLine {
|
||||
id?: string;
|
||||
accountId: string;
|
||||
account?: Account;
|
||||
debit: number;
|
||||
credit: number;
|
||||
memo?: string;
|
||||
}
|
||||
|
||||
export interface JournalEntry {
|
||||
id: string;
|
||||
date: string;
|
||||
reference?: string;
|
||||
description: string;
|
||||
sourceType?: string;
|
||||
lines: JournalEntryLine[];
|
||||
totalDebit?: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Expense {
|
||||
id: string;
|
||||
date: string;
|
||||
vendor?: string;
|
||||
accountId: string;
|
||||
account?: Account;
|
||||
amount: number;
|
||||
description?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export type CompanyAccountType = 'CASH' | 'BANK' | 'EWALLET';
|
||||
|
||||
export interface CompanyAccount {
|
||||
id: string;
|
||||
name: string;
|
||||
type: CompanyAccountType;
|
||||
bankName?: string;
|
||||
accountNumber?: string;
|
||||
balance: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Transfer {
|
||||
id: string;
|
||||
fromAccountId: string;
|
||||
fromAccount?: CompanyAccount;
|
||||
toAccountId: string;
|
||||
toAccount?: CompanyAccount;
|
||||
amount: number;
|
||||
date: string;
|
||||
note?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface TrialBalanceLine {
|
||||
code: string;
|
||||
name: string;
|
||||
debit: number;
|
||||
credit: number;
|
||||
balance: number;
|
||||
}
|
||||
|
||||
export interface ProfitLossReport {
|
||||
revenue: Array<{ name: string; amount: number }>;
|
||||
expenses: Array<{ name: string; amount: number }>;
|
||||
totalRevenue: number;
|
||||
totalExpenses: number;
|
||||
netIncome: number;
|
||||
}
|
||||
|
||||
export interface BalanceSheetReport {
|
||||
assets: Array<{ name: string; amount: number }>;
|
||||
liabilities: Array<{ name: string; amount: number }>;
|
||||
equity: Array<{ name: string; amount: number }>;
|
||||
totalAssets: number;
|
||||
totalLiabilities: number;
|
||||
totalEquity: number;
|
||||
}
|
||||
|
||||
export interface CashFlowReport {
|
||||
operating: Array<{ name: string; amount: number }>;
|
||||
investing: Array<{ name: string; amount: number }>;
|
||||
financing: Array<{ name: string; amount: number }>;
|
||||
netOperating: number;
|
||||
netInvesting: number;
|
||||
netFinancing: number;
|
||||
netCashFlow: number;
|
||||
}
|
||||
|
||||
export interface Lead {
|
||||
id: string;
|
||||
firstName: string;
|
||||
|
||||
Reference in New Issue
Block a user