mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:08:46 +02:00
Add nav config module, budget-cycle helper, me/recent API helpers
navigation.ts becomes the single source of truth for sidebar and breadcrumbs (Inicio entry added; '/' is exact-match). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,8 @@ export async function logout() {
|
|||||||
await fetch("/api/auth/logout", { method: "POST", credentials: "same-origin" });
|
await fetch("/api/auth/logout", { method: "POST", credentials: "same-origin" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getMe = () => api.get<{ username: string }>("/auth/me");
|
||||||
|
|
||||||
// ─── Types ──────────────────────────────────────────────────────────────────
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export type Account = Schema<'AccountRead'>;
|
export type Account = Schema<'AccountRead'>;
|
||||||
@@ -240,6 +242,11 @@ export const upsertBalanceOverride = (
|
|||||||
export const deleteBalanceOverride = (year: number, month: number) =>
|
export const deleteBalanceOverride = (year: number, month: number) =>
|
||||||
api.delete(`/budget/balance-override/${year}/${month}`);
|
api.delete(`/budget/balance-override/${year}/${month}`);
|
||||||
|
|
||||||
|
// --- Transactions ---
|
||||||
|
|
||||||
|
export const getRecentTransactions = (limit = 5) =>
|
||||||
|
api.get<Transaction[]>("/transactions/recent", { params: { limit } });
|
||||||
|
|
||||||
// --- Installment Plans (Tasa Cero) ---
|
// --- Installment Plans (Tasa Cero) ---
|
||||||
|
|
||||||
export type InstallmentPlan = Schema<'InstallmentPlanRead'>;
|
export type InstallmentPlan = Schema<'InstallmentPlanRead'>;
|
||||||
|
|||||||
@@ -18,6 +18,29 @@ export function formatShortDate(dateStr: string): string {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Budget month M = the credit-card cycle ENDING on the 18th of M
|
||||||
|
* (matches get_cycle_range in the backend). After the 18th we are already
|
||||||
|
* in next month's cycle — including the Dec 19 → January-next-year wrap. */
|
||||||
|
export function currentBudgetCycle(today = new Date()): {
|
||||||
|
year: number;
|
||||||
|
month: number;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
} {
|
||||||
|
let year = today.getFullYear();
|
||||||
|
let month = today.getMonth() + 1;
|
||||||
|
if (today.getDate() > 18) {
|
||||||
|
month += 1;
|
||||||
|
if (month === 13) {
|
||||||
|
month = 1;
|
||||||
|
year += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = new Date(year, month - 1, 18);
|
||||||
|
const start = new Date(year, month - 2, 18);
|
||||||
|
return { year, month, start, end };
|
||||||
|
}
|
||||||
|
|
||||||
/** "hace 3 días" / "hace 2 horas" / "hace un momento" */
|
/** "hace 3 días" / "hace 2 horas" / "hace un momento" */
|
||||||
export function formatRelativeAge(iso: string): string {
|
export function formatRelativeAge(iso: string): string {
|
||||||
const ms = Date.now() - new Date(iso).getTime();
|
const ms = Date.now() - new Date(iso).getTime();
|
||||||
|
|||||||
71
frontend/src/lib/navigation.ts
Normal file
71
frontend/src/lib/navigation.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
BarChart3,
|
||||||
|
Calculator,
|
||||||
|
Droplets,
|
||||||
|
Home,
|
||||||
|
Landmark,
|
||||||
|
Layers,
|
||||||
|
PiggyBank,
|
||||||
|
RefreshCw,
|
||||||
|
Sparkles,
|
||||||
|
Telescope,
|
||||||
|
TrendingUp,
|
||||||
|
type LucideIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
/** Single source of truth for the sidebar nav and header breadcrumbs. */
|
||||||
|
|
||||||
|
export interface NavItem {
|
||||||
|
to: string;
|
||||||
|
icon: LucideIcon;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NavSection {
|
||||||
|
label: string;
|
||||||
|
items: NavItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const navSections: NavSection[] = [
|
||||||
|
{
|
||||||
|
label: "General",
|
||||||
|
items: [
|
||||||
|
{ to: "/", icon: Home, label: "Inicio" },
|
||||||
|
{ to: "/asistente", icon: Sparkles, label: "Asistente" },
|
||||||
|
{ to: "/sync", icon: RefreshCw, label: "Sincronización" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Finanzas",
|
||||||
|
items: [
|
||||||
|
{ to: "/budget", icon: Calculator, label: "Presupuesto" },
|
||||||
|
{ to: "/salarios", icon: Landmark, label: "Salarios" },
|
||||||
|
{ to: "/financiamientos", icon: Layers, label: "Financiamientos" },
|
||||||
|
{ to: "/pensions", icon: PiggyBank, label: "Pensiones" },
|
||||||
|
{ to: "/proyecciones", icon: TrendingUp, label: "Proyecciones" },
|
||||||
|
{ to: "/planificador", icon: Telescope, label: "Planificador" },
|
||||||
|
{ to: "/analytics", icon: BarChart3, label: "Analytics" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Servicios",
|
||||||
|
items: [
|
||||||
|
{ to: "/servicios-municipales", icon: Droplets, label: "Municipalidad" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/** "/" must match exactly — prefix-matching it would mark Inicio active everywhere. */
|
||||||
|
export function isNavActive(to: string, pathname: string): boolean {
|
||||||
|
if (to === "/") return pathname === "/";
|
||||||
|
return pathname === to || pathname.startsWith(`${to}/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function titleFor(pathname: string): string {
|
||||||
|
for (const section of navSections) {
|
||||||
|
for (const item of section.items) {
|
||||||
|
if (isNavActive(item.to, pathname)) return item.label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "WealthySmart";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user