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:
@@ -130,7 +130,7 @@ export default function CampaignDetailPanel({ campaign, onClose, onSave, onDelet
|
||||
form.status === 'paused' ? 'bg-amber-100 text-amber-700' :
|
||||
form.status === 'completed' ? 'bg-blue-100 text-blue-700' :
|
||||
form.status === 'cancelled' ? 'bg-red-100 text-red-700' :
|
||||
'bg-gray-100 text-gray-600'
|
||||
'bg-gray-100 text-text-secondary'
|
||||
}`}>
|
||||
{statusOptions.find(s => s.value === form.status)?.label}
|
||||
</span>
|
||||
@@ -226,7 +226,7 @@ export default function CampaignDetailPanel({ campaign, onClose, onSave, onDelet
|
||||
{/* Platforms */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">{t('campaigns.platforms')}</label>
|
||||
<div className="flex flex-wrap gap-2 p-2 border border-border rounded-lg bg-white min-h-[38px]">
|
||||
<div className="flex flex-wrap gap-2 p-2 border border-border rounded-lg bg-surface min-h-[38px]">
|
||||
{Object.entries(PLATFORMS).map(([k, v]) => {
|
||||
const checked = (form.platforms || []).includes(k)
|
||||
return (
|
||||
@@ -281,7 +281,7 @@ export default function CampaignDetailPanel({ campaign, onClose, onSave, onDelet
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-text-tertiary mb-1">
|
||||
{t('campaigns.budget')} ({currencySymbol})
|
||||
{!permissions?.canSetBudget && <span className="text-[10px] text-text-tertiary ml-1">(Superadmin only)</span>}
|
||||
{!permissions?.canSetBudget && <span className="text-[10px] text-text-tertiary ms-1">(Superadmin only)</span>}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
|
||||
Reference in New Issue
Block a user