From 57f3d421335247f3fe2273298e4535e8a1b69198 Mon Sep 17 00:00:00 2001 From: grabowski Date: Thu, 16 Apr 2026 16:06:19 +0700 Subject: [PATCH] Redesign company overview: 4 compact KPIs, side-by-side projects + recent expenses Co-Authored-By: Claude Opus 4.6 (1M context) --- .../(app)/companies/[companyId]/+page.svelte | 272 +++++++++++------- 1 file changed, 171 insertions(+), 101 deletions(-) diff --git a/src/routes/(app)/companies/[companyId]/+page.svelte b/src/routes/(app)/companies/[companyId]/+page.svelte index 10f29c8..d3c06eb 100644 --- a/src/routes/(app)/companies/[companyId]/+page.svelte +++ b/src/routes/(app)/companies/[companyId]/+page.svelte @@ -2,7 +2,7 @@ import type { PageData } from './$types'; import { formatCurrency } from '$lib/utils/currency.js'; - let { data } = $props(); + let { data }: { data: PageData } = $props(); const currency = $derived(data.company.currency); const allocated = $derived(data.projects.reduce((s, p) => s + parseFloat(p.allocatedBudget), 0)); @@ -10,126 +10,196 @@ const total = $derived(parseFloat(data.company.totalBudget)); const remaining = $derived(total - spent); const remainingPct = $derived(total > 0 ? (remaining / total) * 100 : 0); + + const tone = $derived(remaining < 0 ? 'red' : remainingPct < 20 ? 'amber' : 'green'); + + const toneRing: Record = { + green: 'border-green-300 dark:border-green-700', + amber: 'border-amber-300 dark:border-amber-700', + red: 'border-red-300 dark:border-red-700' + }; + const toneText: Record = { + green: 'text-green-700 dark:text-green-400', + amber: 'text-amber-700 dark:text-amber-400', + red: 'text-red-700 dark:text-red-400' + }; + const toneBar: Record = { + green: 'bg-green-500', + amber: 'bg-amber-500', + red: 'bg-red-500' + }; {data.company.name} - {data.appName} -
- -
-

Remaining Budget

-
- {formatCurrency(remaining, currency)} +
+ +
+
+

+ Remaining +

+

+ {formatCurrency(remaining, currency)} +

+
+
+
+

+ {remainingPct.toFixed(1)}% of total +

-
-
-
-

{remainingPct.toFixed(1)}% remaining

-
-
- Total budget - {formatCurrency(total, currency)} -
-
- Total spent - {formatCurrency(spent, currency)} -
-
- Allocated - {formatCurrency(allocated, currency)} -
+
+

+ Total Budget +

+

+ {formatCurrency(total, currency)} +

+

Company-wide

+
+ +
+

+ Spent +

+

+ {formatCurrency(spent, currency)} +

+

+ Across {data.projects.length} {data.projects.length === 1 ? 'project' : 'projects'} +

+
+ +
+

+ Allocated +

+

+ {formatCurrency(allocated, currency)} +

+

+ {total > 0 ? ((allocated / total) * 100).toFixed(1) : '0'}% of total +

- -
-
-

Projects

- {#if data.companyRoles.some(r => r === 'admin' || r === 'manager' || r === 'user' || r === 'hr')} - + +
+
+

- + New Project - + Projects +

+ {#if data.companyRoles.some((r) => r === 'admin' || r === 'manager' || r === 'user' || r === 'hr')} + + + New Project + + {/if} +
+ + {#if data.projects.length === 0} +

No projects yet.

+ {:else} +
+ {#each data.projects as project (project.id)} + {@const budgetNum = parseFloat(project.allocatedBudget)} + {@const spentNum = parseFloat(project.spent)} + {@const pct = budgetNum > 0 ? Math.min((spentNum / budgetNum) * 100, 100) : 0} + +
+ {project.name} + + {formatCurrency(project.spent, currency)} / {formatCurrency( + project.allocatedBudget, + currency + )} + +
+
+
+
+ {#if project.pendingCount > 0} +

{project.pendingCount} pending

+ {/if} +
+ {/each} +
{/if}
- {#if data.projects.length === 0} -

No projects yet.

- {:else} -
- {#each data.projects as project} - {@const budgetNum = parseFloat(project.allocatedBudget)} - {@const spentNum = parseFloat(project.spent)} - {@const pct = budgetNum > 0 ? Math.min((spentNum / budgetNum) * 100, 100) : 0} - -
- {project.name} - - {formatCurrency(project.spent, currency)} / {formatCurrency(project.allocatedBudget, currency)} - -
-
-
-
- {#if project.pendingCount > 0} -

{project.pendingCount} pending

- {/if} -
- {/each} + +
+
+

+ Recent Expenses +

+ + View all → +
- {/if} -
- - -
-

Recent Expenses

- {#if data.recentExpenses.length === 0} -

No expenses yet.

- {:else} - - - - - - - - - - - - {#each data.recentExpenses as expense} - - - - - - - + + {/each} - -
TitleProjectAmountDateStatus
{expense.title}{expense.projectName}{formatCurrency(expense.amount, currency)}{expense.expenseDate} + {#if data.recentExpenses.length === 0} +

No expenses yet.

+ {:else} +
    + {#each data.recentExpenses as expense (expense.id)} +
  • +
    +

    + {expense.title} +

    +

    + {expense.projectName} · {expense.expenseDate} +

    +
    +
    + + {formatCurrency(expense.amount, currency)} + {expense.status} -
- {/if} + + {/if} +