From 4035f5fad4dd60037bb9450adb07b35e4ddacbf6 Mon Sep 17 00:00:00 2001 From: JCBSComputer Date: Mon, 6 Jul 2026 09:13:45 +0200 Subject: [PATCH] Fixed Resources, Added Error Logging/Messages, Fixed Pumkin Spice Background --- Web_Backgrounds/pumpkin-theme.css | 429 ++++++++++-------- src/app/(dashboard)/ai-assistant/page.tsx | 42 +- src/app/(dashboard)/calendar/page.tsx | 10 +- src/app/(dashboard)/campaigns/[id]/page.tsx | 22 +- src/app/(dashboard)/chats/page.tsx | 18 +- src/app/(dashboard)/dashboard/page.tsx | 4 +- src/app/(dashboard)/emails/page.tsx | 2 +- src/app/(dashboard)/leads/[id]/page.tsx | 4 +- .../(dashboard)/resource-planning/page.tsx | 47 +- src/app/(dashboard)/users/page.tsx | 4 +- src/app/globals.css | 54 +++ 11 files changed, 372 insertions(+), 264 deletions(-) diff --git a/Web_Backgrounds/pumpkin-theme.css b/Web_Backgrounds/pumpkin-theme.css index 2f11e7f..3793f9d 100644 --- a/Web_Backgrounds/pumpkin-theme.css +++ b/Web_Backgrounds/pumpkin-theme.css @@ -1,70 +1,71 @@ .dark.theme-pumpkin { - --background: 0 30% 4%; - --foreground: 20 50% 95%; - --card: 0 25% 8%; - --card-foreground: 20 50% 95%; - --popover: 0 25% 8%; - --popover-foreground: 20 50% 95%; - --primary: 10 100% 50%; - --primary-foreground: 0 0% 100%; - --secondary: 30 100% 50%; - --secondary-foreground: 0 0% 0%; - --muted: 0 12% 15%; - --muted-foreground: 20 10% 55%; - --accent: 10 100% 50%; + --background: 30 40% 3%; + --foreground: 30 30% 92%; + --card: 25 30% 7%; + --card-foreground: 30 30% 92%; + --popover: 25 30% 7%; + --popover-foreground: 30 30% 92%; + --primary: 25 95% 50%; + --primary-foreground: 30 20% 5%; + --secondary: 35 100% 45%; + --secondary-foreground: 30 20% 5%; + --muted: 25 15% 12%; + --muted-foreground: 30 12% 60%; + --accent: 15 100% 45%; --accent-foreground: 0 0% 100%; --destructive: 0 70% 50%; - --destructive-foreground: 210 40% 98%; - --border: 10 30% 18%; - --input: 10 30% 18%; - --ring: 10 100% 50%; + --destructive-foreground: 0 0% 100%; + --border: 25 20% 16%; + --input: 25 20% 16%; + --ring: 25 95% 50%; --radius: 0.5rem; - --sidebar: 0 30% 4%; - --sidebar-foreground: 20 50% 95%; - --sidebar-primary: 10 100% 50%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 10 100% 50%; + --sidebar: 25 35% 4%; + --sidebar-foreground: 30 30% 92%; + --sidebar-primary: 25 95% 50%; + --sidebar-primary-foreground: 30 20% 5%; + --sidebar-accent: 15 100% 45%; --sidebar-accent-foreground: 0 0% 100%; - --sidebar-border: 10 30% 15%; - --sidebar-ring: 10 100% 50%; + --sidebar-border: 25 18% 14%; + --sidebar-ring: 25 95% 50%; } .light.theme-pumpkin { - --background: 20 30% 96%; - --foreground: 10 50% 10%; + --background: 30 30% 94%; + --foreground: 25 40% 12%; --card: 0 0% 100%; - --card-foreground: 10 50% 10%; + --card-foreground: 25 40% 12%; --popover: 0 0% 100%; - --popover-foreground: 10 50% 10%; - --primary: 10 90% 45%; + --popover-foreground: 25 40% 12%; + --primary: 25 90% 42%; --primary-foreground: 0 0% 100%; - --secondary: 30 90% 45%; + --secondary: 35 90% 42%; --secondary-foreground: 0 0% 100%; - --muted: 20 15% 88%; - --muted-foreground: 10 10% 45%; - --accent: 10 90% 45%; + --muted: 30 15% 86%; + --muted-foreground: 25 10% 42%; + --accent: 15 90% 42%; --accent-foreground: 0 0% 100%; --destructive: 0 80% 50%; --destructive-foreground: 0 0% 100%; - --border: 20 15% 82%; - --input: 20 15% 82%; - --ring: 10 90% 45%; + --border: 25 15% 80%; + --input: 25 15% 80%; + --ring: 25 90% 42%; --radius: 0.5rem; --sidebar: 0 0% 100%; - --sidebar-foreground: 10 50% 10%; - --sidebar-primary: 10 90% 45%; + --sidebar-foreground: 25 40% 12%; + --sidebar-primary: 25 90% 42%; --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 10 90% 45%; + --sidebar-accent: 15 90% 42%; --sidebar-accent-foreground: 0 0% 100%; - --sidebar-border: 20 15% 82%; - --sidebar-ring: 10 90% 45%; + --sidebar-border: 25 15% 80%; + --sidebar-ring: 25 90% 42%; } +/* Warm ambient glow */ .dark.theme-pumpkin body { background: transparent - radial-gradient(ellipse 120% 60% at 20% 30%, rgba(200, 0, 0, 0.2) 0%, transparent 60%), - radial-gradient(ellipse 80% 50% at 80% 70%, rgba(255, 100, 0, 0.15) 0%, transparent 50%), - radial-gradient(ellipse 60% 40% at 50% 50%, rgba(150, 0, 0, 0.1) 0%, transparent 50%); + radial-gradient(ellipse 100% 55% at 20% 25%, rgba(200, 80, 0, 0.18) 0%, transparent 55%), + radial-gradient(ellipse 80% 45% at 80% 75%, rgba(255, 140, 0, 0.12) 0%, transparent 45%), + radial-gradient(ellipse 60% 35% at 50% 50%, rgba(180, 60, 0, 0.08) 0%, transparent 40%); } .dark.theme-pumpkin body > div { @@ -78,195 +79,209 @@ z-index: -1; pointer-events: none; background: - radial-gradient(ellipse 80% 50% at 15% 20%, rgba(200, 50, 0, 0.04) 0%, transparent 60%), - radial-gradient(ellipse 80% 50% at 85% 80%, rgba(255, 100, 0, 0.03) 0%, transparent 60%); + radial-gradient(ellipse 70% 45% at 15% 20%, rgba(200, 80, 0, 0.05) 0%, transparent 55%), + radial-gradient(ellipse 70% 45% at 85% 80%, rgba(255, 140, 0, 0.04) 0%, transparent 55%); } +/* Pumpkin Spice text glow */ .dark.theme-pumpkin .text-glow { - text-shadow: 0 0 10px hsl(10, 100%, 50%), 0 0 30px hsl(10, 100%, 50%), 0 0 60px rgba(255, 0, 0, 0.5); + text-shadow: 0 0 10px hsl(25, 95%, 50%), 0 0 30px hsla(25, 95%, 50%, 0.6), 0 0 60px rgba(200, 80, 0, 0.3); } .light.theme-pumpkin .text-glow { - text-shadow: 0 0 8px hsl(10, 90%, 45%), 0 0 20px hsl(10, 90%, 45%), 0 0 40px rgba(200, 50, 0, 0.3); + text-shadow: 0 0 8px hsl(25, 90%, 42%), 0 0 20px hsla(25, 90%, 42%, 0.4), 0 0 40px rgba(200, 80, 0, 0.15); } -.dark.theme-pumpkin h1, .dark.theme-pumpkin h2, .dark.theme-pumpkin h3 { - background: linear-gradient(135deg, hsl(10, 100%, 50%), hsl(30, 100%, 50%)); +/* Gradient headings — warm orange to amber */ +.dark.theme-pumpkin h1:not(.text-primary):not(.text-foreground), +.dark.theme-pumpkin h2:not(.text-primary):not(.text-foreground), +.dark.theme-pumpkin h3:not(.text-primary):not(.text-foreground) { + background: linear-gradient(135deg, hsl(25, 95%, 55%), hsl(35, 100%, 50%)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } -.light.theme-pumpkin h1, .light.theme-pumpkin h2, .light.theme-pumpkin h3 { - background: linear-gradient(135deg, hsl(10, 90%, 40%), hsl(30, 90%, 40%)); +.light.theme-pumpkin h1:not(.text-primary):not(.text-foreground), +.light.theme-pumpkin h2:not(.text-primary):not(.text-foreground), +.light.theme-pumpkin h3:not(.text-primary):not(.text-foreground) { + background: linear-gradient(135deg, hsl(25, 90%, 40%), hsl(35, 90%, 40%)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } -.dark.theme-pumpkin a { - color: hsl(10, 100%, 55%); -} - -.light.theme-pumpkin a { - color: hsl(10, 90%, 40%); -} - -.dark.theme-pumpkin a:hover { - color: hsl(30, 100%, 55%); - text-shadow: 0 0 8px rgba(255, 100, 0, 0.4); -} - -.light.theme-pumpkin a:hover { - color: hsl(30, 90%, 45%); -} +/* Links */ +.dark.theme-pumpkin a { color: hsl(25, 95%, 55%); } +.light.theme-pumpkin a { color: hsl(25, 90%, 40%); } +.dark.theme-pumpkin a:hover { color: hsl(35, 100%, 55%); text-shadow: 0 0 8px rgba(255, 140, 0, 0.35); } +.light.theme-pumpkin a:hover { color: hsl(35, 90%, 45%); } +/* Blockquotes */ .dark.theme-pumpkin blockquote { - border-left: 3px solid hsl(10, 100%, 50%); - color: hsl(20, 15%, 70%); + border-left: 3px solid hsl(25, 95%, 50%); + color: hsl(30, 15%, 70%); font-style: italic; } - .light.theme-pumpkin blockquote { - border-left: 3px solid hsl(10, 90%, 45%); - color: hsl(10, 10%, 45%); + border-left: 3px solid hsl(25, 90%, 42%); + color: hsl(25, 10%, 42%); font-style: italic; } +/* Code */ .dark.theme-pumpkin code { - color: hsl(10, 100%, 55%); - background: rgba(200, 0, 0, 0.12); - border: 1px solid rgba(200, 0, 0, 0.15); + color: hsl(25, 95%, 55%); + background: rgba(200, 80, 0, 0.1); + border: 1px solid rgba(200, 80, 0, 0.15); } - .light.theme-pumpkin code { - color: hsl(10, 90%, 40%); - background: rgba(200, 50, 0, 0.05); - border: 1px solid rgba(200, 50, 0, 0.1); + color: hsl(25, 90%, 40%); + background: rgba(200, 80, 0, 0.04); + border: 1px solid rgba(200, 80, 0, 0.1); } +.dark.theme-pumpkin pre { background: rgba(25, 12, 0, 0.9); border: 1px solid rgba(200, 80, 0, 0.1); } +.light.theme-pumpkin pre { background: rgba(200, 80, 0, 0.03); border: 1px solid rgba(200, 80, 0, 0.08); } -.dark.theme-pumpkin pre { - background: rgba(20, 0, 0, 0.9); - border: 1px solid rgba(200, 0, 0, 0.1); -} +/* HR */ +.dark.theme-pumpkin hr { border-color: rgba(200, 80, 0, 0.12); } +.light.theme-pumpkin hr { border-color: rgba(200, 80, 0, 0.08); } -.light.theme-pumpkin pre { - background: rgba(200, 50, 0, 0.03); - border: 1px solid rgba(200, 50, 0, 0.08); -} +/* Tables */ +.dark.theme-pumpkin table th { color: hsl(25, 95%, 55%); } +.light.theme-pumpkin table th { color: hsl(25, 90%, 40%); } +.dark.theme-pumpkin table td { border-color: rgba(200, 80, 0, 0.1); } +.light.theme-pumpkin table td { border-color: rgba(200, 80, 0, 0.08); } -.dark.theme-pumpkin hr { - border-color: rgba(200, 0, 0, 0.15); -} +/* Selection */ +.dark.theme-pumpkin ::selection { background: rgba(200, 80, 0, 0.2); color: hsl(0, 0%, 100%); } +.light.theme-pumpkin ::selection { background: rgba(200, 80, 0, 0.1); color: hsl(0, 0%, 0%); } -.light.theme-pumpkin hr { - border-color: rgba(200, 50, 0, 0.1); -} +/* Placeholders */ +.dark.theme-pumpkin input::placeholder, .dark.theme-pumpkin textarea::placeholder { color: hsl(25, 10%, 40%); } +.light.theme-pumpkin input::placeholder, .light.theme-pumpkin textarea::placeholder { color: hsl(25, 10%, 62%); } -.dark.theme-pumpkin table th { - color: hsl(10, 100%, 55%); -} - -.light.theme-pumpkin table th { - color: hsl(10, 90%, 40%); -} - -.dark.theme-pumpkin table td { - border-color: rgba(200, 0, 0, 0.12); -} - -.light.theme-pumpkin table td { - border-color: rgba(200, 50, 0, 0.1); -} - -.dark.theme-pumpkin ::selection { - background: rgba(200, 0, 0, 0.2); - color: hsl(0, 0%, 100%); -} - -.light.theme-pumpkin ::selection { - background: rgba(200, 50, 0, 0.12); - color: hsl(0, 0%, 0%); -} - -.dark.theme-pumpkin input::placeholder, -.dark.theme-pumpkin textarea::placeholder { - color: hsl(20, 10%, 40%); -} - -.light.theme-pumpkin input::placeholder, -.light.theme-pumpkin textarea::placeholder { - color: hsl(10, 10%, 65%); -} - -.dark.theme-pumpkin .page-header-title { - color: hsl(10, 100%, 55%); -} - -.light.theme-pumpkin .page-header-title { - color: hsl(10, 90%, 40%); -} - -.dark.theme-pumpkin .stat-card-value { - color: hsl(10, 100%, 55%); -} - -.light.theme-pumpkin .stat-card-value { - color: hsl(10, 90%, 40%); -} - -.dark.theme-pumpkin .badge { - color: hsl(10, 100%, 55%); -} - -.light.theme-pumpkin .badge { - color: hsl(10, 90%, 40%); -} +/* Page header / stat card / badge */ +.dark.theme-pumpkin .page-header-title, .dark.theme-pumpkin .stat-card-value, .dark.theme-pumpkin .badge { color: hsl(25, 95%, 55%); } +.light.theme-pumpkin .page-header-title, .light.theme-pumpkin .stat-card-value, .light.theme-pumpkin .badge { color: hsl(25, 90%, 40%); } +/* ============================================================ + Dashboard stat card pipeline bars — Pumpkin Spice palette + ============================================================ */ .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .h-1.w-full { - background: linear-gradient(to right, hsl(10, 100%, 50%), hsl(10, 100%, 65%), hsl(10, 100%, 50%)) !important; + background: linear-gradient(to right, hsl(25, 95%, 50%), hsl(25, 95%, 65%), hsl(25, 95%, 50%)) !important; } .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .h-1.w-full { - background: linear-gradient(to right, hsl(30, 100%, 50%), hsl(30, 100%, 65%), hsl(30, 100%, 50%)) !important; + background: linear-gradient(to right, hsl(35, 100%, 45%), hsl(35, 100%, 60%), hsl(35, 100%, 45%)) !important; } .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 { - background: hsla(10, 100%, 50%, 0.1) !important; + background: hsla(25, 95%, 50%, 0.12) !important; } - .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 { - background: hsla(30, 100%, 50%, 0.1) !important; + background: hsla(35, 100%, 45%, 0.12) !important; } .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 svg { - color: hsl(10, 100%, 50%) !important; + color: hsl(25, 95%, 50%) !important; } - .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 svg { - color: hsl(30, 100%, 50%) !important; + color: hsl(35, 100%, 45%) !important; } .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .absolute.bottom-0.h-1 { - background: linear-gradient(to right, transparent, hsla(10, 100%, 50%, 0.25), transparent) !important; + background: linear-gradient(to right, transparent, hsla(25, 95%, 50%, 0.25), transparent) !important; } - .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .absolute.bottom-0.h-1 { - background: linear-gradient(to right, transparent, hsla(30, 100%, 50%, 0.25), transparent) !important; + background: linear-gradient(to right, transparent, hsla(35, 100%, 45%, 0.25), transparent) !important; } .dark.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 .rounded-full.bg-gradient-to-r { - background: linear-gradient(to right, hsl(10, 100%, 50%), hsl(30, 100%, 50%)) !important; + background: linear-gradient(to right, hsl(25, 95%, 50%), hsl(35, 100%, 45%)) !important; } .light.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .h-1.w-full { - background: linear-gradient(to right, hsl(10, 90%, 45%), hsl(10, 90%, 55%), hsl(10, 90%, 45%)) !important; + background: linear-gradient(to right, hsl(25, 90%, 42%), hsl(25, 90%, 55%), hsl(25, 90%, 42%)) !important; } - .light.theme-pumpkin .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .h-1.w-full { - background: linear-gradient(to right, hsl(30, 90%, 45%), hsl(30, 90%, 55%), hsl(30, 90%, 45%)) !important; + background: linear-gradient(to right, hsl(35, 90%, 42%), hsl(35, 90%, 55%), hsl(35, 90%, 42%)) !important; } +/* ============================================================ + Chart color overrides — Pumpkin Spice palette + ============================================================ */ +.dark.theme-pumpkin [class*="recharts-"] text, +.light.theme-pumpkin [class*="recharts-"] text { + fill: hsl(30, 12%, 60%) !important; +} + +/* Donut chart gradient stops */ +.dark.theme-pumpkin .recharts-pie-sector:nth-child(1) path, +.light.theme-pumpkin .recharts-pie-sector:nth-child(1) path { + fill: hsl(25, 95%, 50%) !important; +} +.dark.theme-pumpkin .recharts-pie-sector:nth-child(2) path, +.light.theme-pumpkin .recharts-pie-sector:nth-child(2) path { + fill: hsl(35, 100%, 45%) !important; +} +.dark.theme-pumpkin .recharts-pie-sector:nth-child(3) path, +.light.theme-pumpkin .recharts-pie-sector:nth-child(3) path { + fill: hsl(15, 100%, 42%) !important; +} + +/* Stat card icon colors */ +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(1) .h-8.w-8, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(1) .h-8.w-8 { + background: hsla(25, 95%, 50%, 0.12) !important; + color: hsl(25, 95%, 50%) !important; +} +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(2) .h-8.w-8, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(2) .h-8.w-8 { + background: hsla(35, 100%, 45%, 0.12) !important; + color: hsl(35, 100%, 45%) !important; +} +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(3) .h-8.w-8, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(3) .h-8.w-8 { + background: hsla(15, 100%, 42%, 0.12) !important; + color: hsl(15, 100%, 42%) !important; +} + +/* Stat card value colors */ +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(1) .text-2xl, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(1) .text-2xl { + color: hsl(25, 95%, 50%) !important; +} +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(2) .text-2xl, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(2) .text-2xl { + color: hsl(35, 100%, 45%) !important; +} +.dark.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(3) .text-2xl, +.light.theme-pumpkin .grid.grid-cols-1.sm\:grid-cols-2.lg\:grid-cols-4.gap-4 > div:nth-child(3) .text-2xl { + color: hsl(15, 100%, 42%) !important; +} + +/* Bar chart bar fills */ +.dark.theme-pumpkin .recharts-bar-rectangle:nth-child(1) rect, +.light.theme-pumpkin .recharts-bar-rectangle:nth-child(1) rect { + fill: hsl(25, 95%, 50%) !important; +} +.dark.theme-pumpkin .recharts-bar-rectangle:nth-child(2) rect, +.light.theme-pumpkin .recharts-bar-rectangle:nth-child(2) rect { + fill: hsl(35, 100%, 45%) !important; +} + +/* ============================================================ + Card gradient accent — subtle pumpkin glow on hover + ============================================================ */ +.dark.theme-pumpkin .rounded-xl.border.border-border { + border-color: hsla(25, 20%, 18%, 0.8) !important; +} +.dark.theme-pumpkin .rounded-xl.border.border-border:hover { + border-color: hsla(25, 95%, 50%, 0.25) !important; +} + +/* Growth word on login page — keep brand blue */ .dark.theme-pumpkin .growth-word, .light.theme-pumpkin .growth-word { -webkit-text-fill-color: #1BB0CE !important; @@ -274,70 +289,88 @@ background: none !important; } -/* ============================================================================ - Client Portal Overrides — Background for portal pages - ============================================================================ */ - -/* Make the outer portal container transparent so body::before background shows */ +/* ============================================================ + Client Portal Overrides + ============================================================ */ .dark.theme-pumpkin .min-h-screen.bg-background { background: transparent !important; } -/* Portal sidebar — dark glass */ .dark.theme-pumpkin aside.bg-card.border-r.border-border { - background: hsl(240, 15%, 3%) !important; - border-right-color: hsl(270, 25%, 10%) !important; + background: hsl(25, 35%, 4%) !important; + border-right-color: hsl(25, 20%, 14%) !important; } -/* Portal sidebar nav items — muted text */ .dark.theme-pumpkin aside .text-muted-foreground { - color: hsl(240, 8%, 50%) !important; + color: hsl(30, 10%, 50%) !important; } .dark.theme-pumpkin aside button:hover { - background: hsla(330, 100%, 50%, 0.08) !important; + background: hsla(25, 95%, 50%, 0.08) !important; color: hsl(0, 0%, 90%) !important; } -/* Portal sidebar active nav item */ .dark.theme-pumpkin aside .bg-primary\/10.text-primary { - background: hsl(330, 100%, 50%) !important; + background: hsl(25, 95%, 50%) !important; color: hsl(0, 0%, 100%) !important; - box-shadow: 0 0 10px rgba(255, 0, 127, 0.3), 0 0 20px rgba(255, 0, 127, 0.1); - border-color: hsl(330, 100%, 50%) !important; + box-shadow: 0 0 10px hsla(25, 95%, 50%, 0.3), 0 0 20px hsla(25, 95%, 50%, 0.1); + border-color: hsl(25, 95%, 50%) !important; } .dark.theme-pumpkin aside .bg-primary\/10.text-primary svg { color: hsl(0, 0%, 100%) !important; } -/* Portal header — dark glass */ .dark.theme-pumpkin header.bg-card\/80 { - background: hsl(240, 15%, 3%) !important; - border-bottom-color: hsl(270, 25%, 10%) !important; + background: hsl(25, 35%, 4%) !important; + border-bottom-color: hsl(25, 20%, 14%) !important; } -/* Portal card backgrounds — semi-transparent glass */ .dark.theme-pumpkin .bg-card { - background: hsla(240, 15%, 7%, 0.65) !important; + background: hsla(25, 30%, 7%, 0.65) !important; backdrop-filter: blur(10px) !important; - border-color: hsla(270, 30%, 20%, 0.35) !important; + border-color: hsla(25, 20%, 16%, 0.35) !important; } -/* Portal borders */ .dark.theme-pumpkin .border-border { - border-color: hsl(270, 25%, 12%) !important; + border-color: hsl(25, 18%, 14%) !important; } -/* Portal input fields */ .dark.theme-pumpkin .bg-muted { - background: hsl(240, 12%, 9%) !important; + background: hsl(25, 15%, 10%) !important; } -/* Portal primary buttons */ .dark.theme-pumpkin .bg-primary { - background: hsl(330, 100%, 50%) !important; - box-shadow: 0 0 10px rgba(255, 0, 127, 0.2); + background: hsl(25, 95%, 50%) !important; + box-shadow: 0 0 10px hsla(25, 95%, 50%, 0.2); } .dark.theme-pumpkin .bg-primary:hover { - background: hsl(330, 100%, 55%) !important; - box-shadow: 0 0 15px rgba(255, 0, 127, 0.4); + background: hsl(25, 95%, 55%) !important; + box-shadow: 0 0 15px hsla(25, 95%, 50%, 0.35); +} + +/* Light mode portal */ +.light.theme-pumpkin .min-h-screen.bg-background { + background: transparent !important; +} + +.light.theme-pumpkin aside.bg-card.border-r.border-border { + background: hsl(0, 0%, 100%) !important; + border-right-color: hsl(25, 15%, 82%) !important; +} + +.light.theme-pumpkin header.bg-card\/80 { + background: hsl(0, 0%, 98%) !important; + border-bottom-color: hsl(25, 15%, 82%) !important; +} + +.light.theme-pumpkin .bg-card { + background: hsla(0, 0%, 100%, 0.65) !important; + backdrop-filter: blur(10px) !important; + border-color: hsla(25, 15%, 80%, 0.5) !important; +} + +.light.theme-pumpkin .bg-primary { + background: hsl(25, 90%, 42%) !important; +} +.light.theme-pumpkin .bg-primary:hover { + background: hsl(25, 90%, 48%) !important; } diff --git a/src/app/(dashboard)/ai-assistant/page.tsx b/src/app/(dashboard)/ai-assistant/page.tsx index e70bc7f..9cbe06c 100644 --- a/src/app/(dashboard)/ai-assistant/page.tsx +++ b/src/app/(dashboard)/ai-assistant/page.tsx @@ -73,9 +73,12 @@ Example format: [{"companyName":"Acme Corp","contactName":"John Smith","email":"john@acme.com","phone":"555-0100","description":"Brief description"}] Use realistic company names, names, and emails for the ${job.industry} industry.` - aiChatRef.current?.addAssistantMessage(`🔍 Finding leads for **${job.job_title}**...`) + + const addMsg = (text: string) => aiChatRef.current?.addAssistantMessage(text) try { + addMsg(`🔍 Step 1/3: Asking AI to generate leads for **${job.job_title}**...`) + const res = await fetch("/api/ai/chat", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -84,11 +87,22 @@ Use realistic company names, names, and emails for the ${job.industry} industry. if (!res.ok) { const data = await res.json().catch(() => ({})) - throw new Error(data.error || `Error ${res.status}`) + const statusInfo = `HTTP ${res.status}: ${data.error || res.statusText}` + addMsg(`❌ Step 1/3 failed — AI chat returned ${statusInfo}. Make sure Ollama is running (port 11434) and the AI service on port 3001 is up.`) + setSearching(false) + return } const data = await res.json() const raw = data.response || "" + if (!raw) { + addMsg("❌ Step 1/3 failed — AI returned an empty response.") + setSearching(false) + return + } + + addMsg(`✅ Step 1/3 complete. Parsing AI response...`) + let leads: any[] try { let text = raw.trim() @@ -97,11 +111,19 @@ Use realistic company names, names, and emails for the ${job.industry} industry. leads = JSON.parse(text) if (!Array.isArray(leads)) leads = [leads] } catch (e) { - aiChatRef.current?.addAssistantMessage(`⚠️ AI response wasn't valid JSON. Showing raw output:\n\n${raw.slice(0, 1500)}`) + addMsg(`⚠️ Step 2/3 failed — AI response wasn't valid JSON. Raw output:\n\n${raw.slice(0, 1500)}`) setSearching(false) return } + if (leads.length === 0) { + addMsg("⚠️ Step 2/3 — AI returned an empty array. No leads to create.") + setSearching(false) + return + } + + addMsg(`✅ Step 2/3 — Parsed **${leads.length}** lead${leads.length > 1 ? "s" : ""} from AI response. Step 3/3: Saving to database...`) + let created = 0 let lastError = "" for (const lead of leads.slice(0, 5)) { @@ -119,18 +141,20 @@ Use realistic company names, names, and emails for the ${job.industry} industry. }), }) if (r.ok) created++ - else { const e = await r.json().catch(() => ({})); lastError = e.error || r.statusText; } - } catch (e) { lastError = "Network error" } + else { const e = await r.json().catch(() => ({})); lastError = `API ${res.status} ${e.error || r.statusText}`; } + } catch (e) { + lastError = `Network error: ${e instanceof Error ? e.message : "Failed to reach server"}` + } } if (created > 0) { - aiChatRef.current?.addAssistantMessage(`✅ Created **${created}** lead${created > 1 ? "s" : ""} for **${job.job_title}**! Go to the Leads tab to see them.`) + addMsg(`✅ Created **${created}** lead${created > 1 ? "s" : ""} for **${job.job_title}**! Go to the **Leads** tab to view and manage them.${lastError ? `\n\n⚠️ ${leads.length - created} lead${leads.length - created > 1 ? "s" : ""} failed: ${lastError}` : ""}`) } else { - aiChatRef.current?.addAssistantMessage(`❌ Failed to create leads. Last error: ${lastError || "Unknown"}. Make sure you're logged in with an account that has permission to create leads.`) + addMsg(`❌ Step 3/3 failed — Could not save any leads.\n\nLast error: ${lastError || "Unknown"}\n\nPossible causes:\n• Your session expired — try logging out and back in\n• You don't have permission to create leads\n• The database connection is down`) } } catch (err) { - const msg = err instanceof Error ? err.message : "Search failed" - aiChatRef.current?.addAssistantMessage(`❌ Error: ${msg}. Make sure Ollama is running with the model loaded.`) + const msg = err instanceof Error ? err.message : "Unknown error" + addMsg(`❌ Unexpected error: ${msg}\n\nThis might be a network issue or the AI service (port 3001) is not responding. Try again.`) } finally { setSearching(false) } diff --git a/src/app/(dashboard)/calendar/page.tsx b/src/app/(dashboard)/calendar/page.tsx index 164f88d..e62560a 100644 --- a/src/app/(dashboard)/calendar/page.tsx +++ b/src/app/(dashboard)/calendar/page.tsx @@ -126,11 +126,11 @@ export default function CalendarPage() { fetch("/api/event-users") .then((r) => r.json()) .then((data) => setUsers(data.users || [])) - .catch(() => {}) + .catch((err) => console.error("Failed to fetch event users:", err)) fetch("/api/leads?limit=200") .then((r) => r.json()) .then((data) => setLeads((Array.isArray(data) ? data : data?.leads || []).map((l: any) => ({ id: l.id, contactName: l.contactName, companyName: l.companyName })))) - .catch(() => {}) + .catch((err) => console.error("Failed to fetch leads:", err)) }, [user, router]) useEffect(() => { @@ -139,13 +139,13 @@ export default function CalendarPage() { }, [fetchMonthEvents]) useEffect(() => { - fetchUpcomingEvents().catch(() => {}) + fetchUpcomingEvents().catch((err) => console.error("Failed to fetch upcoming events:", err)) }, [fetchUpcomingEvents]) useEffect(() => { const interval = setInterval(() => { - fetchMonthEvents().catch(() => {}) - fetchUpcomingEvents().catch(() => {}) + fetchMonthEvents().catch((err) => console.error("Failed to fetch month events:", err)) + fetchUpcomingEvents().catch((err) => console.error("Failed to fetch upcoming events:", err)) }, 15000) return () => clearInterval(interval) }, [fetchMonthEvents, fetchUpcomingEvents]) diff --git a/src/app/(dashboard)/campaigns/[id]/page.tsx b/src/app/(dashboard)/campaigns/[id]/page.tsx index 0ca20a3..d72eaac 100644 --- a/src/app/(dashboard)/campaigns/[id]/page.tsx +++ b/src/app/(dashboard)/campaigns/[id]/page.tsx @@ -39,8 +39,11 @@ export default function CampaignDetailPage({ params }: { params: Promise<{ id: s const searchLeads = async (q: string) => { setLeadSearch(q) if (q.length < 2) { setSearchResults([]); return } - const res = await fetch(`/api/leads?search=${encodeURIComponent(q)}&limit=10`) - if (res.ok) setSearchResults(await res.json()) + try { + const res = await fetch(`/api/leads?search=${encodeURIComponent(q)}&limit=10`) + if (res.ok) setSearchResults(await res.json()) + else console.error("Failed to search leads:", res.status, res.statusText) + } catch (err) { console.error("Failed to search leads:", err) } } const addStep = async () => { @@ -84,12 +87,15 @@ export default function CampaignDetailPage({ params }: { params: Promise<{ id: s } const updateStatus = async (status: string) => { - await fetch(`/api/campaigns/${id}`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ status }), - }) - fetchCampaign() + try { + const res = await fetch(`/api/campaigns/${id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ status }), + }) + if (!res.ok) console.error("Failed to update campaign status:", res.status, res.statusText) + fetchCampaign() + } catch (err) { console.error("Failed to update campaign status:", err) } } if (loading) return
Loading...
diff --git a/src/app/(dashboard)/chats/page.tsx b/src/app/(dashboard)/chats/page.tsx index 96d67a2..dfae018 100644 --- a/src/app/(dashboard)/chats/page.tsx +++ b/src/app/(dashboard)/chats/page.tsx @@ -235,8 +235,8 @@ const formatPreviewContent = (content: string) => { setUnreadMap(new Map(convs.map((c: any) => [c.id, c.unread]))) if (convs.length > 0) setActiveChat(convs[0].id) } - } catch { - console.warn("Failed to fetch conversations in chats page") + } catch (err) { + console.error("Failed to fetch conversations:", err) } finally { setLoadingChats(false) } @@ -297,11 +297,11 @@ const formatPreviewContent = (content: string) => { return next }) } - } catch { - // ignore + } catch (err) { + console.error("Failed to fetch messages:", err) } // Mark conversation as read - fetch(`/api/conversations/${activeChat}/read`, { method: "POST" }).catch(() => {}) + fetch(`/api/conversations/${activeChat}/read`, { method: "POST" }).catch((err) => console.error("Failed to mark read:", err)) setUnreadMap((prev) => { const m = new Map(prev); m.set(activeChat, 0); return m }) } fetchMsgs() @@ -320,7 +320,7 @@ const formatPreviewContent = (content: string) => { const existingIds = new Set(conversations.map((c) => otherParticipant(c).id)) setSearchResults(data.users.filter((u: any) => !existingIds.has(u.id))) } - } catch { console.warn("Failed to search users in chats page") } + } catch (err) { console.error("Failed to search users:", err) } setSearchingUsers(false) }, 300) return () => clearTimeout(timer) @@ -799,8 +799,8 @@ const formatPreviewContent = (content: string) => { prev.map((c) => (c.id === convId ? { ...c, pinned: !pinned } : c)) ) } - } catch { - console.warn("Failed to toggle pin") + } catch (err) { + console.error("Failed to toggle pin:", err) } } @@ -912,7 +912,7 @@ const formatPreviewContent = (content: string) => { setSearchQuery("") } } - } catch { console.warn("Failed to create conversation in chats page") } + } catch (err) { console.error("Failed to create conversation:", err) } }} className="w-full flex items-center gap-3 p-4 text-left transition-colors hover:bg-muted/50" > diff --git a/src/app/(dashboard)/dashboard/page.tsx b/src/app/(dashboard)/dashboard/page.tsx index cb288a3..9b427ea 100644 --- a/src/app/(dashboard)/dashboard/page.tsx +++ b/src/app/(dashboard)/dashboard/page.tsx @@ -39,8 +39,8 @@ export default function DashboardPage() { const data = await res.json() setStats(data) } - } catch { - console.warn("Failed to fetch dashboard stats") + } catch (err) { + console.error("Failed to fetch dashboard stats:", err) } } diff --git a/src/app/(dashboard)/emails/page.tsx b/src/app/(dashboard)/emails/page.tsx index 8a6b905..628e659 100644 --- a/src/app/(dashboard)/emails/page.tsx +++ b/src/app/(dashboard)/emails/page.tsx @@ -13,7 +13,7 @@ export default function EmailsPage() { useEffect(() => { if (!user) { router.push("/login"); return } if (user.role !== "admin" && user.role !== "super_admin") { router.push("/dashboard"); return } - fetch("/api/emails").then((r) => r.ok ? r.json() : { emails: [] }).then((d) => setEmails(d.emails || [])) + fetch("/api/emails").then((r) => r.ok ? r.json() : { emails: [] }).then((d) => setEmails(d.emails || [])).catch((err) => console.error("Failed to fetch emails:", err)) }, [user, router]) if (!user || (user.role !== "admin" && user.role !== "super_admin")) return null diff --git a/src/app/(dashboard)/leads/[id]/page.tsx b/src/app/(dashboard)/leads/[id]/page.tsx index babf5da..7080192 100644 --- a/src/app/(dashboard)/leads/[id]/page.tsx +++ b/src/app/(dashboard)/leads/[id]/page.tsx @@ -39,8 +39,8 @@ export default function LeadDetailsPage({ params }: { params: Promise<{ id: stri fetchNotes(), ]) if (leadRes.ok) setLead(await leadRes.json()) - } catch { - console.warn("Failed to fetch lead details") + } catch (err) { + console.error("Failed to fetch lead details:", err) } setLoading(false) } diff --git a/src/app/(dashboard)/resource-planning/page.tsx b/src/app/(dashboard)/resource-planning/page.tsx index bb778c2..4d2e174 100644 --- a/src/app/(dashboard)/resource-planning/page.tsx +++ b/src/app/(dashboard)/resource-planning/page.tsx @@ -42,33 +42,24 @@ interface Totals { teamUtilization: number } -function utilizationColor(pct: number): string { - if (pct > 100) return "bg-red-500" - if (pct > 90) return "bg-orange-500" - if (pct > 70) return "bg-yellow-500" - return "bg-emerald-500" -} - -function utilizationTextColor(pct: number): string { - if (pct > 100) return "text-red-600" - if (pct > 90) return "text-orange-600" - if (pct > 70) return "text-yellow-600" - return "text-emerald-600" -} - -function utilizationBgColor(pct: number): string { - if (pct > 100) return "bg-red-50 border-red-200" - if (pct > 90) return "bg-orange-50 border-orange-200" - if (pct > 70) return "bg-yellow-50 border-yellow-200" - return "bg-emerald-50 border-emerald-200" +function statusKey(pct: number): string { + if (pct > 100) return "overloaded" + if (pct > 90) return "near-capacity" + if (pct > 70) return "busy" + if (pct > 50) return "available" + return "underutilized" } function statusLabel(pct: number): { label: string; color: string } { - if (pct > 100) return { label: "Overloaded", color: "bg-red-100 text-red-700 border-red-300" } - if (pct > 90) return { label: "Near Capacity", color: "bg-orange-100 text-orange-700 border-orange-300" } - if (pct > 70) return { label: "Busy", color: "bg-yellow-100 text-yellow-700 border-yellow-300" } - if (pct > 50) return { label: "Available", color: "bg-emerald-100 text-emerald-700 border-emerald-300" } - return { label: "Underutilized", color: "bg-slate-100 text-slate-600 border-slate-300" } + const key = statusKey(pct) + const labels: Record = { + overloaded: "Overloaded", + "near-capacity": "Near Capacity", + busy: "Busy", + available: "Available", + underutilized: "Underutilized", + } + return { label: labels[key], color: `util-badge-${key}` } } export default function ResourcePlanningPage() { @@ -120,7 +111,7 @@ export default function ResourcePlanningPage() {
Utilisation
-

+

{totals?.teamUtilization ?? 0}%

@@ -157,7 +148,7 @@ export default function ResourcePlanningPage() { {members.map((m) => { const status = statusLabel(m.utilization) return ( - +
@@ -179,11 +170,11 @@ export default function ResourcePlanningPage() {
Capacity: {m.capacityHours}h Logged: {m.totalHours}h - {m.utilization}% + {m.utilization}%
diff --git a/src/app/(dashboard)/users/page.tsx b/src/app/(dashboard)/users/page.tsx index 8d43d4b..e7df377 100644 --- a/src/app/(dashboard)/users/page.tsx +++ b/src/app/(dashboard)/users/page.tsx @@ -24,8 +24,8 @@ export default function UsersPage() { const data = await res.json() setUsers(data.users || []) } - } catch { - console.warn("Failed to fetch users in users page") + } catch (err) { + console.error("Failed to fetch users:", err) } finally { setLoading(false) } diff --git a/src/app/globals.css b/src/app/globals.css index b0e72a3..3e0bd82 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -758,4 +758,58 @@ main { opacity: 1 !important; } +/* ============================================================ + Utilisation status card backgrounds (theme-aware) + ============================================================ */ +.util-card-overloaded { + background-color: color-mix(in srgb, hsl(0 80% 50% / 0.1), hsl(var(--card))) !important; + border-color: hsl(0 70% 50% / 0.3) !important; +} +.util-card-near-capacity { + background-color: color-mix(in srgb, hsl(25 95% 50% / 0.1), hsl(var(--card))) !important; + border-color: hsl(25 85% 45% / 0.3) !important; +} +.util-card-busy { + background-color: color-mix(in srgb, hsl(45 90% 50% / 0.08), hsl(var(--card))) !important; + border-color: hsl(45 80% 45% / 0.25) !important; +} +.util-card-available { + background-color: color-mix(in srgb, hsl(150 65% 40% / 0.08), hsl(var(--card))) !important; + border-color: hsl(150 60% 40% / 0.25) !important; +} +.util-card-underutilized { + /* Default card styling — no tint needed */ +} + +/* Utilisation status text */ +.util-text-overloaded { @apply text-red-600 dark:text-red-400; } +.util-text-near-capacity { @apply text-orange-600 dark:text-orange-400; } +.util-text-busy { @apply text-yellow-600 dark:text-yellow-400; } +.util-text-available { @apply text-emerald-600 dark:text-emerald-400; } +.util-text-underutilized { @apply text-slate-600 dark:text-slate-400; } + +/* Utilisation status badge */ +.util-badge-overloaded { + @apply bg-red-100 text-red-700 border-red-300 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800; +} +.util-badge-near-capacity { + @apply bg-orange-100 text-orange-700 border-orange-300 dark:bg-orange-900/30 dark:text-orange-300 dark:border-orange-800; +} +.util-badge-busy { + @apply bg-yellow-100 text-yellow-700 border-yellow-300 dark:bg-yellow-900/25 dark:text-yellow-300 dark:border-yellow-800; +} +.util-badge-available { + @apply bg-emerald-100 text-emerald-700 border-emerald-300 dark:bg-emerald-900/25 dark:text-emerald-300 dark:border-emerald-800; +} +.util-badge-underutilized { + @apply bg-slate-100 text-slate-600 border-slate-300 dark:bg-slate-800/30 dark:text-slate-400 dark:border-slate-700; +} + +/* Utilisation bar fill */ +.util-bar-overloaded { @apply bg-red-500; } +.util-bar-near-capacity { @apply bg-orange-500; } +.util-bar-busy { @apply bg-yellow-500; } +.util-bar-available { @apply bg-emerald-500; } +.util-bar-underutilized { @apply bg-slate-400 dark:bg-slate-500; } +