Unify entities navigation, redesign time clock, and add invoice PDF preview.
Combine clients and businesses under entities, polish the web time clock, and show live invoice PDF preview with tighter line-item editing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+12
-3
@@ -3,7 +3,6 @@ import {
|
||||
LayoutDashboard,
|
||||
Users,
|
||||
FileText,
|
||||
Building,
|
||||
Receipt,
|
||||
BarChart2,
|
||||
Shield,
|
||||
@@ -22,14 +21,24 @@ export interface NavSection {
|
||||
links: NavLink[];
|
||||
}
|
||||
|
||||
export function isNavLinkActive(pathname: string, href: string): boolean {
|
||||
if (href === "/dashboard/entities") {
|
||||
return (
|
||||
pathname === href ||
|
||||
pathname.startsWith("/dashboard/clients") ||
|
||||
pathname.startsWith("/dashboard/businesses")
|
||||
);
|
||||
}
|
||||
return pathname === href;
|
||||
}
|
||||
|
||||
export const navigationConfig: NavSection[] = [
|
||||
{
|
||||
title: "Main",
|
||||
links: [
|
||||
{ name: "Dashboard", href: "/dashboard", icon: LayoutDashboard },
|
||||
{ name: "Time clock", href: "/dashboard/time-clock", icon: Clock },
|
||||
{ name: "Clients", href: "/dashboard/clients", icon: Users },
|
||||
{ name: "Businesses", href: "/dashboard/businesses", icon: Building },
|
||||
{ name: "Entities", href: "/dashboard/entities", icon: Users },
|
||||
{ name: "Invoices", href: "/dashboard/invoices", icon: FileText },
|
||||
{ name: "Recurring", href: "/dashboard/invoices/recurring", icon: RefreshCw },
|
||||
{ name: "Expenses", href: "/dashboard/expenses", icon: Receipt },
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const STORAGE_KEY = "beenvoice:time-clock:last-client";
|
||||
|
||||
export function getLastTimeClockClientId(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem(STORAGE_KEY);
|
||||
}
|
||||
|
||||
export function setLastTimeClockClientId(clientId: string): void {
|
||||
if (!clientId || typeof window === "undefined") return;
|
||||
localStorage.setItem(STORAGE_KEY, clientId);
|
||||
}
|
||||
@@ -1,3 +1,29 @@
|
||||
export const DEFAULT_CLOCK_DESCRIPTION = "Professional services";
|
||||
|
||||
export function resolveEffectiveHourlyRate(
|
||||
enteredRate: number,
|
||||
client?: { defaultHourlyRate?: number | null } | null,
|
||||
): number {
|
||||
if (Number.isFinite(enteredRate) && enteredRate > 0) return enteredRate;
|
||||
const clientRate = client?.defaultHourlyRate ?? 0;
|
||||
if (Number.isFinite(clientRate) && clientRate > 0) return clientRate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function startedAtFromMinutesAgo(minutes: number): Date {
|
||||
return new Date(Date.now() - minutes * 60 * 1000);
|
||||
}
|
||||
|
||||
export function resolveClockDescription(
|
||||
title: string,
|
||||
existingDescription?: string | null,
|
||||
): string {
|
||||
const trimmed = title.trim();
|
||||
if (trimmed) return trimmed;
|
||||
if (existingDescription?.trim()) return existingDescription.trim();
|
||||
return DEFAULT_CLOCK_DESCRIPTION;
|
||||
}
|
||||
|
||||
export type ClockOutOutcome =
|
||||
| "linked_to_invoice"
|
||||
| "saved_no_invoice"
|
||||
|
||||
Reference in New Issue
Block a user