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:
+3
-1
@@ -37,6 +37,7 @@ const PublicIssueSubmit = lazy(() => import('./pages/PublicIssueSubmit'))
|
||||
const PublicIssueTracker = lazy(() => import('./pages/PublicIssueTracker'))
|
||||
const Translations = lazy(() => import('./pages/Translations'))
|
||||
const PublicTranslationReview = lazy(() => import('./pages/PublicTranslationReview'))
|
||||
const PublicBudgetApproval = lazy(() => import('./pages/PublicBudgetApproval'))
|
||||
const ForgotPassword = lazy(() => import('./pages/ForgotPassword'))
|
||||
const ResetPassword = lazy(() => import('./pages/ResetPassword'))
|
||||
|
||||
@@ -161,7 +162,7 @@ function AppContent() {
|
||||
<AppContext.Provider value={{ currentUser: user, teamMembers, brands, loadTeam, getBrandName, teams, loadTeams, roles, loadRoles }}>
|
||||
{/* Profile completion prompt */}
|
||||
{showProfilePrompt && (
|
||||
<div className="fixed top-4 right-4 z-50 bg-amber-50 border-2 border-amber-400 rounded-xl shadow-lg p-4 max-w-md animate-fade-in">
|
||||
<div className="fixed top-4 end-4 z-50 bg-amber-50 border-2 border-amber-400 rounded-xl shadow-lg p-4 max-w-md animate-fade-in">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-amber-400 flex items-center justify-center text-white shrink-0">
|
||||
⚠️
|
||||
@@ -298,6 +299,7 @@ function AppContent() {
|
||||
<Route path="/submit-issue" element={<PublicIssueSubmit />} />
|
||||
<Route path="/track/:token" element={<PublicIssueTracker />} />
|
||||
<Route path="/review-translation/:token" element={<PublicTranslationReview />} />
|
||||
<Route path="/approve-budget/:token" element={<PublicBudgetApproval />} />
|
||||
<Route path="/" element={user ? <Layout /> : <Navigate to="/login" replace />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
{hasModule('marketing') && <>
|
||||
|
||||
Reference in New Issue
Block a user