feat: comprehensive UI overhaul + budget allocation redesign

Audit & Quality:
- RTL: replaced 121 LTR-only utilities (text-left, pl-, left-) with logical properties
- A11y: focus traps + ARIA on Modal/SlidePanel/TabbedModal, clickable divs→buttons
- Theming: bg-white→bg-surface (170 instances), text-gray→semantic tokens
- Performance: useMemo on filters, loading="lazy" on 24 images
- CSS: prefers-reduced-motion, removed dead animations

Component Splits:
- PostDetailPanel: 1332→623 lines + 4 sub-components
- ArtefactDetailPanel: 972→590 lines + 1 sub-component

Brand Identity — Rawaj (رواج):
- New name, DM Sans font, deep teal palette (#0d9488)
- Custom SVG logo, forest-tinted dark mode
- All emails branded with app name in subject line

Design Refinement:
- Dashboard: merged posts+deadlines into tabbed ActivityFeed, inline stats
- Quieter: removed card lift, brand glow, gradient text, mesh backgrounds
- CampaignDetail: prominent budget card, compact team avatars, Lucide icons
- Consistent page titles via Header.jsx, standardized section headers
- Finance page fully i18n'd (20+ hardcoded strings replaced)

Budget Allocation Redesign:
- Single source of truth: BudgetEntries (Campaign.budget deprecated)
- Validation at all levels: main→campaign→track, expenses blocked if insufficient
- Budget request workflow with CEO approval via public link
- BudgetRequests table, CRUD routes, public approval page
- Budget mutex for race condition prevention
- Idempotent migration for existing campaign budgets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
fahed
2026-03-15 15:36:19 +03:00
parent 3c857856c5
commit e1d1c392eb
77 changed files with 4351 additions and 2108 deletions
+6 -6
View File
@@ -100,7 +100,7 @@ export default function TaskCalendarView({ tasks, onTaskClick }) {
if (p === 'urgent') return 'bg-red-500 text-white'
if (p === 'high') return 'bg-orange-400 text-white'
if (p === 'medium') return 'bg-amber-400 text-amber-900'
return 'bg-gray-300 text-gray-700'
return 'bg-gray-300 text-text-secondary'
}
return (
@@ -124,14 +124,14 @@ export default function TaskCalendarView({ tasks, onTaskClick }) {
<div className="flex bg-surface-tertiary rounded-lg p-0.5">
<button
onClick={() => setCalView('month')}
className={`px-2.5 py-1 text-[11px] font-medium rounded-md transition-colors flex items-center gap-1 ${calView === 'month' ? 'bg-white shadow-sm text-text-primary' : 'text-text-tertiary'}`}
className={`px-2.5 py-1 text-[11px] font-medium rounded-md transition-colors flex items-center gap-1 ${calView === 'month' ? 'bg-surface shadow-sm text-text-primary' : 'text-text-tertiary'}`}
>
<CalendarIcon className="w-3 h-3" />
Month
</button>
<button
onClick={() => setCalView('week')}
className={`px-2.5 py-1 text-[11px] font-medium rounded-md transition-colors flex items-center gap-1 ${calView === 'week' ? 'bg-white shadow-sm text-text-primary' : 'text-text-tertiary'}`}
className={`px-2.5 py-1 text-[11px] font-medium rounded-md transition-colors flex items-center gap-1 ${calView === 'week' ? 'bg-surface shadow-sm text-text-primary' : 'text-text-tertiary'}`}
>
<CalendarDays className="w-3 h-3" />
Week
@@ -162,7 +162,7 @@ export default function TaskCalendarView({ tasks, onTaskClick }) {
<div
key={i}
className={`border-r border-b border-border ${calView === 'week' ? 'min-h-[300px]' : 'min-h-[90px]'} p-1 ${
cell.current ? 'bg-white' : 'bg-surface-secondary/50'
cell.current ? 'bg-surface' : 'bg-surface-secondary/50'
}`}
>
<div className={`text-[11px] font-medium mb-0.5 w-6 h-6 flex items-center justify-center rounded-full ${
@@ -175,7 +175,7 @@ export default function TaskCalendarView({ tasks, onTaskClick }) {
<button
key={task._id || task.id}
onClick={() => onTaskClick(task)}
className={`w-full text-left text-[10px] px-1.5 py-0.5 rounded truncate font-medium hover:opacity-80 transition-opacity ${
className={`w-full text-start text-[10px] px-1.5 py-0.5 rounded truncate font-medium hover:opacity-80 transition-opacity ${
task.status === 'done' ? 'bg-emerald-100 text-emerald-700 line-through' : getPillColor(task)
}`}
title={task.title}
@@ -206,7 +206,7 @@ export default function TaskCalendarView({ tasks, onTaskClick }) {
<button
key={task._id || task.id}
onClick={() => onTaskClick(task)}
className="w-full text-left bg-white border border-border rounded-lg p-2 hover:border-brand-primary/30 transition-colors"
className="w-full text-start bg-surface border border-border rounded-lg p-2 hover:border-brand-primary/30 transition-colors"
>
<div className="flex items-center gap-1.5">
<div className={`w-2 h-2 rounded-full ${priority.color} shrink-0`} />