Dashboard fix, expense system, currency settings, visual upgrade

- Fix Dashboard stat card: show "Budget Remaining" instead of "Budget Spent"
  with correct remaining value accounting for campaign allocations
- Add expense system: budget entries now have income/expense type with
  server-side split, per-campaign and per-project expense tracking,
  colored amounts, type filters, and summary bar in Budgets page
- Add configurable currency in Settings (SAR ⃁ default, supports 10
  currencies) replacing all hardcoded SAR references across the app
- Replace PiggyBank icon with Landmark (culturally appropriate for KSA)
- Visual upgrade: mesh background, gradient text, premium stat cards with
  accent bars, section-card containers, sidebar active glow
- UX polish: consistent text-2xl headers, skeleton loaders for Finance
  and Budgets pages
- Finance page: expenses column in campaign/project breakdown tables,
  ROI accounts for expenses, expense stat card

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
fahed
2026-02-15 15:49:28 +03:00
parent f3e6fc848d
commit e76be78498
17 changed files with 2817 additions and 1379 deletions

View File

@@ -1,20 +1,22 @@
export default function StatCard({ icon: Icon, label, value, subtitle, color = 'brand-primary', trend }) {
const colorMap = {
'brand-primary': 'from-indigo-500 to-indigo-600',
'brand-secondary': 'from-pink-500 to-pink-600',
'brand-tertiary': 'from-amber-500 to-amber-600',
'brand-quaternary': 'from-emerald-500 to-emerald-600',
const accentMap = {
'brand-primary': 'accent-primary',
'brand-secondary': 'accent-secondary',
'brand-tertiary': 'accent-tertiary',
'brand-quaternary': 'accent-quaternary',
}
const iconBgMap = {
'brand-primary': 'bg-indigo-50 text-indigo-600',
'brand-secondary': 'bg-pink-50 text-pink-600',
'brand-tertiary': 'bg-amber-50 text-amber-600',
'brand-quaternary': 'bg-emerald-50 text-emerald-600',
'brand-primary': 'bg-indigo-50 text-indigo-600 shadow-lg shadow-indigo-500/20',
'brand-secondary': 'bg-pink-50 text-pink-600 shadow-lg shadow-pink-500/20',
'brand-tertiary': 'bg-amber-50 text-amber-600 shadow-lg shadow-amber-500/20',
'brand-quaternary': 'bg-emerald-50 text-emerald-600 shadow-lg shadow-emerald-500/20',
}
const accentClass = accentMap[color] || 'accent-primary'
return (
<div className="bg-white rounded-xl border border-border p-5 card-hover">
<div className={`stat-card-premium ${accentClass} bg-white rounded-xl border border-border p-5 card-hover`}>
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-medium text-text-tertiary">{label}</p>