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,10 +1,11 @@
import { useState } from 'react'
import { Settings as SettingsIcon, Play, CheckCircle, Languages } from 'lucide-react'
import { Settings as SettingsIcon, Play, CheckCircle, Languages, Coins } from 'lucide-react'
import { api } from '../utils/api'
import { useLanguage } from '../i18n/LanguageContext'
import { CURRENCIES } from '../i18n/LanguageContext'
export default function Settings() {
const { t, lang, setLang } = useLanguage()
const { t, lang, setLang, currency, setCurrency } = useLanguage()
const [restarting, setRestarting] = useState(false)
const [success, setSuccess] = useState(false)
@@ -57,6 +58,26 @@ export default function Settings() {
<option value="ar">{t('settings.arabic')}</option>
</select>
</div>
{/* Currency Selector */}
<div>
<label className="block text-sm font-medium text-text-primary mb-2 flex items-center gap-2">
<Coins className="w-4 h-4" />
{t('settings.currency')}
</label>
<select
value={currency}
onChange={(e) => setCurrency(e.target.value)}
className="w-full max-w-xs px-4 py-2.5 border border-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary bg-white"
>
{CURRENCIES.map(c => (
<option key={c.code} value={c.code}>
{c.symbol} {lang === 'ar' ? c.labelAr : c.labelEn}
</option>
))}
</select>
<p className="text-xs text-text-tertiary mt-1.5">{t('settings.currencyHint')}</p>
</div>
</div>
</div>