Add client portal themes (Cyberpunk etc.), settings page, semantic Tailwind classes
Build & Auto-Repair / build (push) Has been cancelled
Build & Auto-Repair / build (push) Has been cancelled
This commit is contained in:
@@ -318,3 +318,71 @@
|
|||||||
color: #1BB0CE !important;
|
color: #1BB0CE !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.dark.theme-bw .min-h-screen.bg-background {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar — dark glass */
|
||||||
|
.dark.theme-bw aside.bg-card.border-r.border-border {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-right-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-bw aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-bw aside button:hover {
|
||||||
|
background: hsla(330, 100%, 50%, 0.08) !important;
|
||||||
|
color: hsl(0, 0%, 90%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar active nav item */
|
||||||
|
.dark.theme-bw aside .bg-primary\/10.text-primary {
|
||||||
|
background: hsl(330, 100%, 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;
|
||||||
|
}
|
||||||
|
.dark.theme-bw aside .bg-primary\/10.text-primary svg {
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal header — dark glass */
|
||||||
|
.dark.theme-bw header.bg-card\/80 {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-bottom-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-bw .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-bw .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-bw .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary buttons */
|
||||||
|
.dark.theme-bw .bg-primary {
|
||||||
|
background: hsl(330, 100%, 50%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 127, 0.2);
|
||||||
|
}
|
||||||
|
.dark.theme-bw .bg-primary:hover {
|
||||||
|
background: hsl(330, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 127, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -287,3 +287,71 @@
|
|||||||
color: #1BB0CE !important;
|
color: #1BB0CE !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.dark.theme-cosmic .min-h-screen.bg-background {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar — dark glass */
|
||||||
|
.dark.theme-cosmic aside.bg-card.border-r.border-border {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-right-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-cosmic aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-cosmic aside button:hover {
|
||||||
|
background: hsla(330, 100%, 50%, 0.08) !important;
|
||||||
|
color: hsl(0, 0%, 90%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar active nav item */
|
||||||
|
.dark.theme-cosmic aside .bg-primary\/10.text-primary {
|
||||||
|
background: hsl(330, 100%, 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;
|
||||||
|
}
|
||||||
|
.dark.theme-cosmic aside .bg-primary\/10.text-primary svg {
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal header — dark glass */
|
||||||
|
.dark.theme-cosmic header.bg-card\/80 {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-bottom-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-cosmic .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-cosmic .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-cosmic .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary buttons */
|
||||||
|
.dark.theme-cosmic .bg-primary {
|
||||||
|
background: hsl(330, 100%, 50%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 127, 0.2);
|
||||||
|
}
|
||||||
|
.dark.theme-cosmic .bg-primary:hover {
|
||||||
|
background: hsl(330, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 127, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -318,3 +318,71 @@
|
|||||||
color: #1BB0CE !important;
|
color: #1BB0CE !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.dark.theme-cyber2 .min-h-screen.bg-background {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar — dark glass */
|
||||||
|
.dark.theme-cyber2 aside.bg-card.border-r.border-border {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-right-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-cyber2 aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-cyber2 aside button:hover {
|
||||||
|
background: hsla(330, 100%, 50%, 0.08) !important;
|
||||||
|
color: hsl(0, 0%, 90%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar active nav item */
|
||||||
|
.dark.theme-cyber2 aside .bg-primary\/10.text-primary {
|
||||||
|
background: hsl(330, 100%, 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;
|
||||||
|
}
|
||||||
|
.dark.theme-cyber2 aside .bg-primary\/10.text-primary svg {
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal header — dark glass */
|
||||||
|
.dark.theme-cyber2 header.bg-card\/80 {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-bottom-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-cyber2 .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-cyber2 .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-cyber2 .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary buttons */
|
||||||
|
.dark.theme-cyber2 .bg-primary {
|
||||||
|
background: hsl(330, 100%, 50%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 127, 0.2);
|
||||||
|
}
|
||||||
|
.dark.theme-cyber2 .bg-primary:hover {
|
||||||
|
background: hsl(330, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 127, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1233,3 +1233,84 @@
|
|||||||
.light.theme-cyberpunk span.h-1\.5.w-1\.5.rounded-full.bg-zinc-500 {
|
.light.theme-cyberpunk span.h-1\.5.w-1\.5.rounded-full.bg-zinc-500 {
|
||||||
background: hsl(0, 0%, 40%) !important;
|
background: hsl(0, 0%, 40%) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Cyberpunk background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.dark.theme-cyberpunk .min-h-screen.bg-background {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar — dark glass */
|
||||||
|
.dark.theme-cyberpunk aside.bg-card.border-r.border-border {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-right-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-cyberpunk aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-cyberpunk aside button:hover {
|
||||||
|
background: hsla(330, 100%, 50%, 0.08) !important;
|
||||||
|
color: hsl(0, 0%, 90%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar active nav item — neon pink pill */
|
||||||
|
.dark.theme-cyberpunk aside .bg-primary\/10.text-primary {
|
||||||
|
background: hsl(330, 100%, 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;
|
||||||
|
}
|
||||||
|
.dark.theme-cyberpunk aside .bg-primary\/10.text-primary svg {
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal header — dark glass */
|
||||||
|
.dark.theme-cyberpunk header.bg-card\/80 {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-bottom-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-cyberpunk .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-cyberpunk .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-cyberpunk .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-cyberpunk .border-border:focus {
|
||||||
|
border-color: hsl(330, 100%, 50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary buttons */
|
||||||
|
.dark.theme-cyberpunk .bg-primary {
|
||||||
|
background: hsl(330, 100%, 50%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 127, 0.2);
|
||||||
|
}
|
||||||
|
.dark.theme-cyberpunk .bg-primary:hover {
|
||||||
|
background: hsl(330, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 127, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary text highlights */
|
||||||
|
.dark.theme-cyberpunk .text-primary {
|
||||||
|
color: hsl(330, 100%, 55%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal muted text */
|
||||||
|
.dark.theme-cyberpunk .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -273,3 +273,71 @@
|
|||||||
color: #1BB0CE !important;
|
color: #1BB0CE !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-pumpkin aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-pumpkin aside button:hover {
|
||||||
|
background: hsla(330, 100%, 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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-pumpkin .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-pumpkin .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-pumpkin .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !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);
|
||||||
|
}
|
||||||
|
.dark.theme-pumpkin .bg-primary:hover {
|
||||||
|
background: hsl(330, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 127, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -183,3 +183,71 @@
|
|||||||
color: #1BB0CE !important;
|
color: #1BB0CE !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Client Portal Overrides — Background for portal pages
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* Make the outer portal container transparent so body::before background shows */
|
||||||
|
.dark.theme-spidey .min-h-screen.bg-background {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar — dark glass */
|
||||||
|
.dark.theme-spidey aside.bg-card.border-r.border-border {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-right-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar nav items — muted text */
|
||||||
|
.dark.theme-spidey aside .text-muted-foreground {
|
||||||
|
color: hsl(240, 8%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-spidey aside button:hover {
|
||||||
|
background: hsla(0, 100%, 50%, 0.08) !important;
|
||||||
|
color: hsl(0, 0%, 90%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal sidebar active nav item */
|
||||||
|
.dark.theme-spidey aside .bg-primary\/10.text-primary {
|
||||||
|
background: hsl(0, 100%, 50%) !important;
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 0, 0.3), 0 0 20px rgba(255, 0, 0, 0.1);
|
||||||
|
border-color: hsl(0, 100%, 50%) !important;
|
||||||
|
}
|
||||||
|
.dark.theme-spidey aside .bg-primary\/10.text-primary svg {
|
||||||
|
color: hsl(0, 0%, 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal header — dark glass */
|
||||||
|
.dark.theme-spidey header.bg-card\/80 {
|
||||||
|
background: hsl(240, 15%, 3%) !important;
|
||||||
|
border-bottom-color: hsl(270, 25%, 10%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal card backgrounds — semi-transparent glass */
|
||||||
|
.dark.theme-spidey .bg-card {
|
||||||
|
background: hsla(240, 15%, 7%, 0.65) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
border-color: hsla(270, 30%, 20%, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal borders */
|
||||||
|
.dark.theme-spidey .border-border {
|
||||||
|
border-color: hsl(270, 25%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal input fields */
|
||||||
|
.dark.theme-spidey .bg-muted {
|
||||||
|
background: hsl(240, 12%, 9%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Portal primary buttons */
|
||||||
|
.dark.theme-spidey .bg-primary {
|
||||||
|
background: hsl(0, 100%, 50%) !important;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
.dark.theme-spidey .bg-primary:hover {
|
||||||
|
background: hsl(0, 100%, 55%) !important;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ export default function ClientActivity() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getIcon = (type: string) => {
|
const getIcon = (type: string) => {
|
||||||
if (type === "milestone") return <GitMerge className="h-4 w-4 text-[#1BB0CE]" />
|
if (type === "milestone") return <GitMerge className="h-4 w-4 text-primary" />
|
||||||
if (type === "invoice") return <FileText className="h-4 w-4 text-[#a855f7]" />
|
if (type === "invoice") return <FileText className="h-4 w-4 text-purple-400" />
|
||||||
return <LifeBuoy className="h-4 w-4 text-[#f59e0b]" />
|
return <LifeBuoy className="h-4 w-4 text-amber-400" />
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusColor = (type: string, status: string) => {
|
const getStatusColor = (type: string, status: string) => {
|
||||||
@@ -59,35 +59,35 @@ export default function ClientActivity() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Activity</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Activity</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">Recent project updates and changes</p>
|
<p className="text-sm text-muted-foreground mb-6">Recent project updates and changes</p>
|
||||||
|
|
||||||
{events.length === 0 ? (
|
{events.length === 0 ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<Activity className="h-10 w-10 text-[#2a2a35] mx-auto mb-3" />
|
<Activity className="h-10 w-10 text-muted-foreground mx-auto mb-3" />
|
||||||
<p className="text-sm text-[#6a6a75]">No recent activity</p>
|
<p className="text-sm text-muted-foreground">No recent activity</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute left-[17px] top-2 bottom-2 w-px bg-[#1a1a24]" />
|
<div className="absolute left-[17px] top-2 bottom-2 w-px bg-muted" />
|
||||||
<div className="space-y-0">
|
<div className="space-y-0">
|
||||||
{(events || []).map((e, i) => (
|
{(events || []).map((e, i) => (
|
||||||
<button
|
<button
|
||||||
key={`${e.event_type}-${e.id}-${i}`}
|
key={`${e.event_type}-${e.id}-${i}`}
|
||||||
onClick={() => handleClick(e)}
|
onClick={() => handleClick(e)}
|
||||||
className="w-full flex items-start gap-4 py-3 pl-0 pr-4 text-left hover:bg-[#0d1117]/50 rounded-xl transition-all group"
|
className="w-full flex items-start gap-4 py-3 pl-0 pr-4 text-left hover:bg-card/50 rounded-xl transition-all group"
|
||||||
>
|
>
|
||||||
<div className="relative z-10 w-9 h-9 rounded-full bg-[#0a0a0f] border-2 border-[#1a1a24] flex items-center justify-center flex-shrink-0">
|
<div className="relative z-10 w-9 h-9 rounded-full bg-background border-2 border-border flex items-center justify-center flex-shrink-0">
|
||||||
{getIcon(e.event_type)}
|
{getIcon(e.event_type)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0 pt-0.5">
|
<div className="flex-1 min-w-0 pt-0.5">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<p className="text-sm text-white font-medium truncate">
|
<p className="text-sm text-foreground font-medium truncate">
|
||||||
{e.event_type === "milestone" && (e.name || "Milestone updated")}
|
{e.event_type === "milestone" && (e.name || "Milestone updated")}
|
||||||
{e.event_type === "invoice" && `${e.invoice_number || "Invoice"} — ${e.currency || ""} ${Number(e.total_amount || 0).toLocaleString()}`}
|
{e.event_type === "invoice" && `${e.invoice_number || "Invoice"} — ${e.currency || ""} ${Number(e.total_amount || 0).toLocaleString()}`}
|
||||||
{e.event_type === "ticket" && (e.title || "Ticket created")}
|
{e.event_type === "ticket" && (e.title || "Ticket created")}
|
||||||
</p>
|
</p>
|
||||||
<span className="text-[11px] text-[#6a6a75] flex-shrink-0">{formatDate(e.date)}</span>
|
<span className="text-[11px] text-muted-foreground flex-shrink-0">{formatDate(e.date)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 mt-0.5">
|
<div className="flex items-center gap-2 mt-0.5">
|
||||||
<span className={`text-[11px] capitalize ${getStatusColor(e.event_type, e.status || e.status_name || "")}`}>
|
<span className={`text-[11px] capitalize ${getStatusColor(e.event_type, e.status || e.status_name || "")}`}>
|
||||||
@@ -95,13 +95,13 @@ export default function ClientActivity() {
|
|||||||
</span>
|
</span>
|
||||||
{e.event_type === "milestone" && e.project_name && (
|
{e.event_type === "milestone" && e.project_name && (
|
||||||
<>
|
<>
|
||||||
<span className="text-[#2a2a35] text-[10px]">•</span>
|
<span className="text-muted-foreground text-[10px]">•</span>
|
||||||
<span className="text-[11px] text-[#6a6a75] truncate">{e.project_name}</span>
|
<span className="text-[11px] text-muted-foreground truncate">{e.project_name}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="h-4 w-4 text-[#2a2a35] group-hover:text-[#1BB0CE] transition-colors flex-shrink-0 mt-1" />
|
<ChevronRight className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors flex-shrink-0 mt-1" />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ export default function ClientDashboard() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Welcome, {name}</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Welcome, {name}</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">Your project overview at a glance</p>
|
<p className="text-sm text-muted-foreground mb-6">Your project overview at a glance</p>
|
||||||
|
|
||||||
{/* Count cards */}
|
{/* Count cards */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
||||||
@@ -71,15 +71,15 @@ export default function ClientDashboard() {
|
|||||||
<button
|
<button
|
||||||
key={card.label}
|
key={card.label}
|
||||||
onClick={() => router.push(card.href)}
|
onClick={() => router.push(card.href)}
|
||||||
className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 text-left hover:border-[#1BB0CE]/30 transition-all group"
|
className="bg-card border border-border rounded-xl p-5 text-left hover:border-primary/30 transition-all group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3 mb-3">
|
<div className="flex items-center gap-3 mb-3">
|
||||||
<div className="w-10 h-10 rounded-lg flex items-center justify-center" style={{ backgroundColor: `${card.color}15` }}>
|
<div className="w-10 h-10 rounded-lg flex items-center justify-center" style={{ backgroundColor: `${card.color}15` }}>
|
||||||
<Icon className="h-5 w-5" style={{ color: card.color }} />
|
<Icon className="h-5 w-5" style={{ color: card.color }} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-2xl font-bold text-white">{card.value}</span>
|
<span className="text-2xl font-bold text-foreground">{card.value}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-[#8a8a95]">{card.label}</p>
|
<p className="text-sm text-muted-foreground">{card.label}</p>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -87,24 +87,24 @@ export default function ClientDashboard() {
|
|||||||
|
|
||||||
{/* Financial stats */}
|
{/* Financial stats */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5">
|
<div className="bg-card border border-border rounded-xl p-5">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<DollarSign className="h-4 w-4 text-[#1BB0CE]" />
|
<DollarSign className="h-4 w-4 text-primary" />
|
||||||
<span className="text-xs text-[#6a6a75] uppercase tracking-wider">Total Billed</span>
|
<span className="text-xs text-muted-foreground uppercase tracking-wider">Total Billed</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xl font-bold text-white">{formatCurrency(stats.totalBilled)}</p>
|
<p className="text-xl font-bold text-foreground">{formatCurrency(stats.totalBilled)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5">
|
<div className="bg-card border border-border rounded-xl p-5">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<TrendingUp className="h-4 w-4 text-emerald-400" />
|
<TrendingUp className="h-4 w-4 text-emerald-400" />
|
||||||
<span className="text-xs text-[#6a6a75] uppercase tracking-wider">Total Paid</span>
|
<span className="text-xs text-muted-foreground uppercase tracking-wider">Total Paid</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xl font-bold text-emerald-400">{formatCurrency(stats.totalPaid)}</p>
|
<p className="text-xl font-bold text-emerald-400">{formatCurrency(stats.totalPaid)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5">
|
<div className="bg-card border border-border rounded-xl p-5">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<AlertTriangle className="h-4 w-4 text-red-400" />
|
<AlertTriangle className="h-4 w-4 text-red-400" />
|
||||||
<span className="text-xs text-[#6a6a75] uppercase tracking-wider">Overdue</span>
|
<span className="text-xs text-muted-foreground uppercase tracking-wider">Overdue</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xl font-bold text-red-400">{formatCurrency(stats.totalOverdue)}</p>
|
<p className="text-xl font-bold text-red-400">{formatCurrency(stats.totalOverdue)}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,24 +112,24 @@ export default function ClientDashboard() {
|
|||||||
|
|
||||||
{/* Upcoming Deadlines */}
|
{/* Upcoming Deadlines */}
|
||||||
{stats.upcomingDeadlines.length > 0 && (
|
{stats.upcomingDeadlines.length > 0 && (
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5">
|
<div className="bg-card border border-border rounded-xl p-5">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<div className="flex items-center gap-2 mb-4">
|
||||||
<Calendar className="h-4 w-4 text-[#f59e0b]" />
|
<Calendar className="h-4 w-4 text-amber-400" />
|
||||||
<span className="text-sm font-semibold text-white">Upcoming Deadlines</span>
|
<span className="text-sm font-semibold text-foreground">Upcoming Deadlines</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{(stats.upcomingDeadlines || []).map((d, i) => (
|
{(stats.upcomingDeadlines || []).map((d, i) => (
|
||||||
<div key={`${d.type}-${d.id}-${i}`} className="flex items-center justify-between">
|
<div key={`${d.type}-${d.id}-${i}`} className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className={`w-2 h-2 rounded-full ${
|
<div className={`w-2 h-2 rounded-full ${
|
||||||
d.type === "invoice" ? "bg-[#a855f7]" : "bg-[#1BB0CE]"
|
d.type === "invoice" ? "bg-purple-400" : "bg-primary"
|
||||||
}`} />
|
}`} />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-white">{d.name}</p>
|
<p className="text-sm text-foreground">{d.name}</p>
|
||||||
<p className="text-[11px] text-[#6a6a75] capitalize">{d.type} · {d.status_name?.replace("_", " ")}</p>
|
<p className="text-[11px] text-muted-foreground capitalize">{d.type} · {d.status_name?.replace("_", " ")}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-[#6a6a75]">
|
<span className="text-xs text-muted-foreground">
|
||||||
{new Date(d.date).toLocaleDateString()}
|
{new Date(d.date).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default function InvoiceDetail() {
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="w-8 h-8 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -157,10 +157,10 @@ export default function InvoiceDetail() {
|
|||||||
return (
|
return (
|
||||||
<div ref={printRef}>
|
<div ref={printRef}>
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<h1 className="text-xl font-bold text-white">Invoice {invoice.invoice_number}</h1>
|
<h1 className="text-xl font-bold text-foreground">Invoice {invoice.invoice_number}</h1>
|
||||||
<button
|
<button
|
||||||
onClick={handlePrint}
|
onClick={handlePrint}
|
||||||
className="flex items-center gap-1.5 bg-[#1BB0CE] hover:bg-[#1BB0CE]/80 text-white text-xs px-3 py-2 rounded-lg transition-all"
|
className="flex items-center gap-1.5 bg-primary hover:bg-primary/80 text-foreground text-xs px-3 py-2 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
<Printer className="h-3.5 w-3.5" />
|
<Printer className="h-3.5 w-3.5" />
|
||||||
Print / PDF
|
Print / PDF
|
||||||
@@ -175,40 +175,40 @@ export default function InvoiceDetail() {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4 mt-6 mb-6">
|
<div className="grid grid-cols-2 gap-4 mt-6 mb-6">
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
<span className="text-[11px] text-[#6a6a75] uppercase tracking-wider">Issued</span>
|
<span className="text-[11px] text-muted-foreground uppercase tracking-wider">Issued</span>
|
||||||
<p className="text-white text-sm mt-1">
|
<p className="text-foreground text-sm mt-1">
|
||||||
{invoice.issued_date ? new Date(invoice.issued_date).toLocaleDateString() : "-"}
|
{invoice.issued_date ? new Date(invoice.issued_date).toLocaleDateString() : "-"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
<span className="text-[11px] text-[#6a6a75] uppercase tracking-wider">Due</span>
|
<span className="text-[11px] text-muted-foreground uppercase tracking-wider">Due</span>
|
||||||
<p className="text-white text-sm mt-1">
|
<p className="text-foreground text-sm mt-1">
|
||||||
{invoice.due_date ? new Date(invoice.due_date).toLocaleDateString() : "-"}
|
{invoice.due_date ? new Date(invoice.due_date).toLocaleDateString() : "-"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-base font-semibold text-white mb-3">Items</h2>
|
<h2 className="text-base font-semibold text-foreground mb-3">Items</h2>
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl overflow-hidden mb-6 print:hidden">
|
<div className="bg-card border border-border rounded-xl overflow-hidden mb-6 print:hidden">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-[#1a1a24]">
|
<tr className="border-b border-border">
|
||||||
<th className="text-left text-[11px] text-[#6a6a75] uppercase tracking-wider px-4 py-3">Description</th>
|
<th className="text-left text-[11px] text-muted-foreground uppercase tracking-wider px-4 py-3">Description</th>
|
||||||
<th className="text-right text-[11px] text-[#6a6a75] uppercase tracking-wider px-4 py-3">Qty</th>
|
<th className="text-right text-[11px] text-muted-foreground uppercase tracking-wider px-4 py-3">Qty</th>
|
||||||
<th className="text-right text-[11px] text-[#6a6a75] uppercase tracking-wider px-4 py-3">Unit Price</th>
|
<th className="text-right text-[11px] text-muted-foreground uppercase tracking-wider px-4 py-3">Unit Price</th>
|
||||||
<th className="text-right text-[11px] text-[#6a6a75] uppercase tracking-wider px-4 py-3">Total</th>
|
<th className="text-right text-[11px] text-muted-foreground uppercase tracking-wider px-4 py-3">Total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{(items || []).map((item, i) => (
|
{(items || []).map((item, i) => (
|
||||||
<tr key={i} className="border-b border-[#1a1a24]/50">
|
<tr key={i} className="border-b border-border/50">
|
||||||
<td className="text-sm text-white px-4 py-3">{item.description}</td>
|
<td className="text-sm text-foreground px-4 py-3">{item.description}</td>
|
||||||
<td className="text-sm text-white px-4 py-3 text-right">{item.quantity}</td>
|
<td className="text-sm text-foreground px-4 py-3 text-right">{item.quantity}</td>
|
||||||
<td className="text-sm text-white px-4 py-3 text-right">
|
<td className="text-sm text-foreground px-4 py-3 text-right">
|
||||||
{invoice.currency} {Number(item.unit_price).toLocaleString()}
|
{invoice.currency} {Number(item.unit_price).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-sm text-white px-4 py-3 text-right">
|
<td className="text-sm text-foreground px-4 py-3 text-right">
|
||||||
{invoice.currency} {Number(item.total_price).toLocaleString()}
|
{invoice.currency} {Number(item.total_price).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -217,23 +217,23 @@ export default function InvoiceDetail() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
{invoice.subtotal && (
|
{invoice.subtotal && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={3} className="text-right text-sm text-[#8a8a95] px-4 py-2">Subtotal</td>
|
<td colSpan={3} className="text-right text-sm text-muted-foreground px-4 py-2">Subtotal</td>
|
||||||
<td className="text-right text-sm text-white px-4 py-2">
|
<td className="text-right text-sm text-foreground px-4 py-2">
|
||||||
{invoice.currency} {Number(invoice.subtotal).toLocaleString()}
|
{invoice.currency} {Number(invoice.subtotal).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
{invoice.tax_amount > 0 && (
|
{invoice.tax_amount > 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={3} className="text-right text-sm text-[#8a8a95] px-4 py-2">Tax ({invoice.tax_percent}%)</td>
|
<td colSpan={3} className="text-right text-sm text-muted-foreground px-4 py-2">Tax ({invoice.tax_percent}%)</td>
|
||||||
<td className="text-right text-sm text-white px-4 py-2">
|
<td className="text-right text-sm text-foreground px-4 py-2">
|
||||||
{invoice.currency} {Number(invoice.tax_amount).toLocaleString()}
|
{invoice.currency} {Number(invoice.tax_amount).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
<tr className="border-t border-[#1a1a24]">
|
<tr className="border-t border-border">
|
||||||
<td colSpan={3} className="text-right text-sm font-semibold text-white px-4 py-3">Total</td>
|
<td colSpan={3} className="text-right text-sm font-semibold text-foreground px-4 py-3">Total</td>
|
||||||
<td className="text-right text-sm font-bold text-[#1BB0CE] px-4 py-3">
|
<td className="text-right text-sm font-bold text-primary px-4 py-3">
|
||||||
{invoice.currency} {Number(invoice.total_amount).toLocaleString()}
|
{invoice.currency} {Number(invoice.total_amount).toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -243,15 +243,15 @@ export default function InvoiceDetail() {
|
|||||||
|
|
||||||
{receipts.length > 0 && (
|
{receipts.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 className="text-base font-semibold text-white mb-3">Receipts</h2>
|
<h2 className="text-base font-semibold text-foreground mb-3">Receipts</h2>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{(receipts || []).map((r) => (
|
{(receipts || []).map((r) => (
|
||||||
<div key={r.id} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-4 flex items-center justify-between">
|
<div key={r.id} className="bg-card border border-border rounded-xl p-4 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FileText className="h-4 w-4 text-[#1BB0CE]" />
|
<FileText className="h-4 w-4 text-primary" />
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm text-white">{r.receipt_number}</span>
|
<span className="text-sm text-foreground">{r.receipt_number}</span>
|
||||||
<p className="text-[11px] text-[#6a6a75]">
|
<p className="text-[11px] text-muted-foreground">
|
||||||
{new Date(r.issued_at).toLocaleDateString()}
|
{new Date(r.issued_at).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -260,7 +260,7 @@ export default function InvoiceDetail() {
|
|||||||
<a
|
<a
|
||||||
href={r.file_url}
|
href={r.file_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex items-center gap-1.5 text-xs text-[#1BB0CE] hover:underline"
|
className="flex items-center gap-1.5 text-xs text-primary hover:underline"
|
||||||
>
|
>
|
||||||
<Download className="h-3.5 w-3.5" />
|
<Download className="h-3.5 w-3.5" />
|
||||||
Download
|
Download
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ export default function ClientInvoices() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Invoices</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Invoices</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">View and download your invoices and receipts</p>
|
<p className="text-sm text-muted-foreground mb-6">View and download your invoices and receipts</p>
|
||||||
|
|
||||||
{invoices.length === 0 ? (
|
{invoices.length === 0 ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<FileText className="h-10 w-10 text-[#2a2a35] mx-auto mb-3" />
|
<FileText className="h-10 w-10 text-muted-foreground mx-auto mb-3" />
|
||||||
<p className="text-sm text-[#6a6a75]">No invoices yet</p>
|
<p className="text-sm text-muted-foreground">No invoices yet</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -42,12 +42,12 @@ export default function ClientInvoices() {
|
|||||||
<button
|
<button
|
||||||
key={inv.id}
|
key={inv.id}
|
||||||
onClick={() => router.push(`/client-portal/invoices/${inv.id}`)}
|
onClick={() => router.push(`/client-portal/invoices/${inv.id}`)}
|
||||||
className="w-full bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 text-left hover:border-[#1BB0CE]/30 transition-all group"
|
className="w-full bg-card border border-border rounded-xl p-5 text-left hover:border-primary/30 transition-all group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
<h3 className="text-white font-semibold">{inv.invoice_number}</h3>
|
<h3 className="text-foreground font-semibold">{inv.invoice_number}</h3>
|
||||||
<span className={`text-[10px] font-medium px-2 py-0.5 rounded-full ${
|
<span className={`text-[10px] font-medium px-2 py-0.5 rounded-full ${
|
||||||
inv.status_name === "Paid" ? "bg-emerald-500/10 text-emerald-400" :
|
inv.status_name === "Paid" ? "bg-emerald-500/10 text-emerald-400" :
|
||||||
inv.status_name === "Overdue" ? "bg-red-500/10 text-red-400" :
|
inv.status_name === "Overdue" ? "bg-red-500/10 text-red-400" :
|
||||||
@@ -57,17 +57,17 @@ export default function ClientInvoices() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4 mt-1">
|
<div className="flex gap-4 mt-1">
|
||||||
<span className="text-sm text-white font-medium">
|
<span className="text-sm text-foreground font-medium">
|
||||||
{inv.currency} {Number(inv.total_amount).toLocaleString()}
|
{inv.currency} {Number(inv.total_amount).toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
{inv.issued_date && (
|
{inv.issued_date && (
|
||||||
<span className="text-[11px] text-[#6a6a75]">
|
<span className="text-[11px] text-muted-foreground">
|
||||||
Issued: {new Date(inv.issued_date).toLocaleDateString()}
|
Issued: {new Date(inv.issued_date).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="h-5 w-5 text-[#2a2a35] group-hover:text-[#1BB0CE] transition-colors" />
|
<ChevronRight className="h-5 w-5 text-muted-foreground group-hover:text-primary transition-colors" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useRouter, usePathname } from "next/navigation"
|
import { useRouter, usePathname } from "next/navigation"
|
||||||
import { Home, FolderOpen, FileText, LifeBuoy, User, Activity, Receipt, LogOut, Menu, X } from "lucide-react"
|
import { Home, FolderOpen, FileText, LifeBuoy, User, Activity, Receipt, Settings, LogOut, Menu, X, Sun, Moon } from "lucide-react"
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ href: "/client-portal/dashboard", label: "Dashboard", icon: Home },
|
{ href: "/client-portal/dashboard", label: "Dashboard", icon: Home },
|
||||||
@@ -12,6 +14,7 @@ const navItems = [
|
|||||||
{ href: "/client-portal/support", label: "Support", icon: LifeBuoy },
|
{ href: "/client-portal/support", label: "Support", icon: LifeBuoy },
|
||||||
{ href: "/client-portal/activity", label: "Activity", icon: Activity },
|
{ href: "/client-portal/activity", label: "Activity", icon: Activity },
|
||||||
{ href: "/client-portal/profile", label: "Profile", icon: User },
|
{ href: "/client-portal/profile", label: "Profile", icon: User },
|
||||||
|
{ href: "/client-portal/settings", label: "Settings", icon: Settings },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function ClientPortalLayout({ children }: { children: React.ReactNode }) {
|
export default function ClientPortalLayout({ children }: { children: React.ReactNode }) {
|
||||||
@@ -19,6 +22,11 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const [auth, setAuth] = useState<boolean | null>(null)
|
const [auth, setAuth] = useState<boolean | null>(null)
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||||||
|
const { theme, setTheme } = useTheme()
|
||||||
|
const { websiteTheme, setWebsiteTheme } = useWebsiteTheme()
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => { setMounted(true) }, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/api/auth/me", { credentials: "include" })
|
fetch("/api/auth/me", { credentials: "include" })
|
||||||
@@ -39,8 +47,8 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
|
|
||||||
if (auth === null) {
|
if (auth === null) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-[#0a0a0f] items-center justify-center">
|
<div className="flex min-h-screen bg-background items-center justify-center">
|
||||||
<div className="w-8 h-8 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -48,18 +56,18 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
if (!auth) return null
|
if (!auth) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-[#0a0a0f]">
|
<div className="flex min-h-screen bg-background">
|
||||||
{/* Mobile overlay */}
|
{/* Mobile overlay */}
|
||||||
{sidebarOpen && (
|
{sidebarOpen && (
|
||||||
<div className="fixed inset-0 bg-black/50 z-40 lg:hidden" onClick={() => setSidebarOpen(false)} />
|
<div className="fixed inset-0 bg-black/50 z-40 lg:hidden" onClick={() => setSidebarOpen(false)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className={`fixed lg:static inset-y-0 left-0 z-50 w-64 bg-[#0d1117] border-r border-[#1a1a24] transform transition-transform duration-200 ${sidebarOpen ? "translate-x-0" : "-translate-x-full"} lg:translate-x-0 flex flex-col`}>
|
<aside className={`fixed lg:static inset-y-0 left-0 z-50 w-64 bg-card border-r border-border transform transition-transform duration-200 ${sidebarOpen ? "translate-x-0" : "-translate-x-full"} lg:translate-x-0 flex flex-col`}>
|
||||||
<div className="p-5 border-b border-[#1a1a24]">
|
<div className="p-5 border-b border-border">
|
||||||
<div className="text-lg font-bold">
|
<div className="text-lg font-bold">
|
||||||
<span className="text-[#1BB0CE]">Client</span>
|
<span className="text-primary">Client</span>
|
||||||
<span className="text-white"> Portal</span>
|
<span className="text-foreground"> Portal</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -73,8 +81,8 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
onClick={() => { router.push(item.href); setSidebarOpen(false) }}
|
onClick={() => { router.push(item.href); setSidebarOpen(false) }}
|
||||||
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-all ${
|
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-all ${
|
||||||
active
|
active
|
||||||
? "bg-[#1BB0CE]/10 text-[#1BB0CE] border border-[#1BB0CE]/20"
|
? "bg-primary/10 text-primary border border-primary/20"
|
||||||
: "text-[#8a8a95] hover:text-white hover:bg-[#1a1a24]"
|
: "text-muted-foreground hover:text-accent-foreground hover:bg-accent"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Icon className="h-4 w-4" />
|
<Icon className="h-4 w-4" />
|
||||||
@@ -84,10 +92,10 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="p-3 border-t border-[#1a1a24]">
|
<div className="p-3 border-t border-border">
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm text-[#8a8a95] hover:text-red-400 hover:bg-red-400/5 transition-all"
|
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm text-muted-foreground hover:text-destructive hover:bg-destructive/5 transition-all"
|
||||||
>
|
>
|
||||||
<LogOut className="h-4 w-4" />
|
<LogOut className="h-4 w-4" />
|
||||||
Logout
|
Logout
|
||||||
@@ -97,11 +105,31 @@ export default function ClientPortalLayout({ children }: { children: React.React
|
|||||||
|
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<div className="flex-1 flex flex-col min-h-screen">
|
<div className="flex-1 flex flex-col min-h-screen">
|
||||||
<header className="sticky top-0 z-30 bg-[#0d1117]/80 backdrop-blur-md border-b border-[#1a1a24] px-4 lg:px-6 h-14 flex items-center">
|
<header className="sticky top-0 z-30 bg-card/80 backdrop-blur-md border-b border-border px-4 lg:px-6 h-14 flex items-center justify-between">
|
||||||
<button className="lg:hidden mr-3 text-[#8a8a95] hover:text-white" onClick={() => setSidebarOpen(true)}>
|
<div className="flex items-center">
|
||||||
|
<button className="lg:hidden mr-3 text-muted-foreground hover:text-accent-foreground" onClick={() => setSidebarOpen(true)}>
|
||||||
<Menu className="h-5 w-5" />
|
<Menu className="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
<span className="text-sm text-[#6a6a75]">Client Portal</span>
|
<span className="text-sm text-muted-foreground">Client Portal</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{mounted && (
|
||||||
|
<button
|
||||||
|
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
||||||
|
className="p-2 rounded-lg text-muted-foreground hover:text-accent-foreground hover:bg-accent transition-all"
|
||||||
|
title={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
|
||||||
|
>
|
||||||
|
{theme === "dark" ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => router.push("/client-portal/settings")}
|
||||||
|
className="p-2 rounded-lg text-muted-foreground hover:text-accent-foreground hover:bg-accent transition-all"
|
||||||
|
title="Theme settings"
|
||||||
|
>
|
||||||
|
<Settings className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="flex-1 p-4 lg:p-6">
|
<main className="flex-1 p-4 lg:p-6">
|
||||||
|
|||||||
@@ -66,51 +66,51 @@ export default function ClientLoginPage() {
|
|||||||
|
|
||||||
if (checking) {
|
if (checking) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-[#0a0a0f] items-center justify-center">
|
<div className="flex min-h-screen bg-background items-center justify-center">
|
||||||
<div className="w-8 h-8 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-[#0a0a0f] items-center justify-center p-4">
|
<div className="flex min-h-screen bg-background items-center justify-center p-4">
|
||||||
<div className="w-full max-w-sm">
|
<div className="w-full max-w-sm">
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="text-2xl font-bold mb-1">
|
<div className="text-2xl font-bold mb-1">
|
||||||
<span className="text-[#1BB0CE]">Client</span>
|
<span className="text-primary">Client</span>
|
||||||
<span className="text-white"> Portal</span>
|
<span className="text-foreground"> Portal</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-[#6a6a75]">Sign in to view your projects and invoices</p>
|
<p className="text-sm text-muted-foreground">Sign in to view your projects and invoices</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1.5">Email</label>
|
<label className="block text-sm text-muted-foreground mb-1.5">Email</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
className="w-full bg-[#1a1a24] border border-[#2a2a35] rounded-lg px-3 py-2.5 text-sm text-white placeholder-[#6a6a75] outline-none focus:border-[#1BB0CE]/50"
|
className="w-full bg-muted border border-border rounded-lg px-3 py-2.5 text-sm text-foreground placeholder-muted-foreground outline-none focus:border-primary/50"
|
||||||
placeholder="your@email.com"
|
placeholder="your@email.com"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1.5">Password</label>
|
<label className="block text-sm text-muted-foreground mb-1.5">Password</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full bg-[#1a1a24] border border-[#2a2a35] rounded-lg px-3 py-2.5 pr-10 text-sm text-white placeholder-[#6a6a75] outline-none focus:border-[#1BB0CE]/50"
|
className="w-full bg-muted border border-border rounded-lg px-3 py-2.5 pr-10 text-sm text-foreground placeholder-muted-foreground outline-none focus:border-primary/50"
|
||||||
placeholder="Enter password"
|
placeholder="Enter password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-[#6a6a75] hover:text-white"
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-accent-foreground"
|
||||||
>
|
>
|
||||||
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -119,8 +119,8 @@ export default function ClientLoginPage() {
|
|||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className={"text-sm bg-opacity-10 border rounded-lg px-3 py-2 " + (error === "Login successful! Redirecting..."
|
<div className={"text-sm bg-opacity-10 border rounded-lg px-3 py-2 " + (error === "Login successful! Redirecting..."
|
||||||
? "text-[#1BB0CE] bg-[#1BB0CE]/5 border-[#1BB0CE]/20"
|
? "text-primary bg-primary/5 border-primary/20"
|
||||||
: "text-red-400 bg-red-400/5 border-red-400/10")}>
|
: "text-destructive bg-destructive/5 border-destructive/10")}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{error !== "Login successful! Redirecting..." && <AlertCircle className="h-4 w-4 shrink-0" />}
|
{error !== "Login successful! Redirecting..." && <AlertCircle className="h-4 w-4 shrink-0" />}
|
||||||
<span className="font-medium">{error}</span>
|
<span className="font-medium">{error}</span>
|
||||||
@@ -132,7 +132,7 @@ export default function ClientLoginPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full bg-[#1BB0CE] hover:bg-[#1BB0CE]/80 text-white rounded-lg px-4 py-2.5 text-sm font-medium transition-all disabled:opacity-40"
|
className="w-full bg-primary hover:bg-primary/80 text-primary-foreground rounded-lg px-4 py-2.5 text-sm font-medium transition-all disabled:opacity-40"
|
||||||
>
|
>
|
||||||
{loading ? "Signing in..." : "Sign In"}
|
{loading ? "Signing in..." : "Sign In"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function ClientProfile() {
|
|||||||
if (!profile) {
|
if (!profile) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-32">
|
<div className="flex items-center justify-center h-32">
|
||||||
<div className="w-6 h-6 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -56,24 +56,24 @@ export default function ClientProfile() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Profile</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Profile</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">Your account and company information</p>
|
<p className="text-sm text-muted-foreground mb-6">Your account and company information</p>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
{/* Main info card */}
|
{/* Main info card */}
|
||||||
<div className="lg:col-span-2 space-y-4">
|
<div className="lg:col-span-2 space-y-4">
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-6">
|
<div className="bg-card border border-border rounded-xl p-6">
|
||||||
<div className="flex items-center gap-4 mb-5">
|
<div className="flex items-center gap-4 mb-5">
|
||||||
<div className="w-14 h-14 rounded-xl bg-[#1BB0CE]/10 flex items-center justify-center">
|
<div className="w-14 h-14 rounded-xl bg-primary/10 flex items-center justify-center">
|
||||||
<Building2 className="h-7 w-7 text-[#1BB0CE]" />
|
<Building2 className="h-7 w-7 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold text-white">{name || "Unnamed"}</h2>
|
<h2 className="text-lg font-semibold text-foreground">{name || "Unnamed"}</h2>
|
||||||
<div className="flex items-center gap-2 mt-1">
|
<div className="flex items-center gap-2 mt-1">
|
||||||
<span className="text-[11px] px-2 py-0.5 rounded-full" style={{ backgroundColor: `${profile.status_color}15`, color: profile.status_color }}>
|
<span className="text-[11px] px-2 py-0.5 rounded-full" style={{ backgroundColor: `${profile.status_color}15`, color: profile.status_color }}>
|
||||||
{profile.status_name}
|
{profile.status_name}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[11px] text-[#6a6a75]">{isCompany ? "Company" : "Individual"}</span>
|
<span className="text-[11px] text-muted-foreground">{isCompany ? "Company" : "Individual"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,64 +81,64 @@ export default function ClientProfile() {
|
|||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
{isCompany && profile.industry && (
|
{isCompany && profile.industry && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Hash className="h-4 w-4 text-[#6a6a75]" />
|
<Hash className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Industry</p>
|
<p className="text-muted-foreground text-[11px]">Industry</p>
|
||||||
<p className="text-white">{profile.industry}</p>
|
<p className="text-foreground">{profile.industry}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isCompany && profile.registration_number && (
|
{isCompany && profile.registration_number && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<FileText className="h-4 w-4 text-[#6a6a75]" />
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Registration</p>
|
<p className="text-muted-foreground text-[11px]">Registration</p>
|
||||||
<p className="text-white">{profile.registration_number}</p>
|
<p className="text-foreground">{profile.registration_number}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{profile.tax_id && (
|
{profile.tax_id && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<FileText className="h-4 w-4 text-[#6a6a75]" />
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Tax / VAT</p>
|
<p className="text-muted-foreground text-[11px]">Tax / VAT</p>
|
||||||
<p className="text-white">{profile.tax_id}</p>
|
<p className="text-foreground">{profile.tax_id}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isCompany && profile.company_size && (
|
{isCompany && profile.company_size && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Users className="h-4 w-4 text-[#6a6a75]" />
|
<Users className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Company Size</p>
|
<p className="text-muted-foreground text-[11px]">Company Size</p>
|
||||||
<p className="text-white">{profile.company_size}</p>
|
<p className="text-foreground">{profile.company_size}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isCompany && profile.annual_revenue && (
|
{isCompany && profile.annual_revenue && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<DollarSign className="h-4 w-4 text-[#6a6a75]" />
|
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Annual Revenue</p>
|
<p className="text-muted-foreground text-[11px]">Annual Revenue</p>
|
||||||
<p className="text-white">{Number(profile.annual_revenue).toLocaleString()}</p>
|
<p className="text-foreground">{Number(profile.annual_revenue).toLocaleString()}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{profile.website && (
|
{profile.website && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Globe className="h-4 w-4 text-[#6a6a75]" />
|
<Globe className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Website</p>
|
<p className="text-muted-foreground text-[11px]">Website</p>
|
||||||
<p className="text-[#1BB0CE]">{profile.website}</p>
|
<p className="text-primary">{profile.website}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isCompany && profile.job_title && (
|
{!isCompany && profile.job_title && (
|
||||||
<div className="flex items-center gap-3 text-sm">
|
<div className="flex items-center gap-3 text-sm">
|
||||||
<Hash className="h-4 w-4 text-[#6a6a75]" />
|
<Hash className="h-4 w-4 text-muted-foreground" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[#6a6a75] text-[11px]">Job Title</p>
|
<p className="text-muted-foreground text-[11px]">Job Title</p>
|
||||||
<p className="text-white">{profile.job_title}</p>
|
<p className="text-foreground">{profile.job_title}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -146,9 +146,9 @@ export default function ClientProfile() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{profile.customer_notes && (
|
{profile.customer_notes && (
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5">
|
<div className="bg-card border border-border rounded-xl p-5">
|
||||||
<h3 className="text-sm font-medium text-white mb-2">Notes</h3>
|
<h3 className="text-sm font-medium text-foreground mb-2">Notes</h3>
|
||||||
<p className="text-sm text-[#8a8a95]">{profile.customer_notes}</p>
|
<p className="text-sm text-muted-foreground">{profile.customer_notes}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -156,24 +156,24 @@ export default function ClientProfile() {
|
|||||||
{/* Contacts sidebar */}
|
{/* Contacts sidebar */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{(contacts || []).map((c, i) => (
|
{(contacts || []).map((c, i) => (
|
||||||
<div key={i} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-4">
|
<div key={i} className="bg-card border border-border rounded-xl p-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{c.type === "email" ? <Mail className="h-4 w-4 text-[#1BB0CE]" /> :
|
{c.type === "email" ? <Mail className="h-4 w-4 text-primary" /> :
|
||||||
c.type === "phone" || c.type === "mobile" ? <Phone className="h-4 w-4 text-[#1BB0CE]" /> :
|
c.type === "phone" || c.type === "mobile" ? <Phone className="h-4 w-4 text-primary" /> :
|
||||||
c.type === "website" ? <Globe className="h-4 w-4 text-[#1BB0CE]" /> :
|
c.type === "website" ? <Globe className="h-4 w-4 text-primary" /> :
|
||||||
<Hash className="h-4 w-4 text-[#1BB0CE]" />}
|
<Hash className="h-4 w-4 text-primary" />}
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[11px] text-[#6a6a75]">{c.label || c.type}</p>
|
<p className="text-[11px] text-muted-foreground">{c.label || c.type}</p>
|
||||||
<p className="text-sm text-white">{c.value}</p>
|
<p className="text-sm text-foreground">{c.value}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{c.is_primary && (
|
{c.is_primary && (
|
||||||
<span className="text-[10px] text-[#1BB0CE] mt-1.5 block">Primary</span>
|
<span className="text-[10px] text-primary mt-1.5 block">Primary</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{contacts.length === 0 && (
|
{contacts.length === 0 && (
|
||||||
<div className="text-center py-8 text-sm text-[#6a6a75] bg-[#0d1117] border border-[#1a1a24] rounded-xl">
|
<div className="text-center py-8 text-sm text-muted-foreground bg-card border border-border rounded-xl">
|
||||||
No contact info
|
No contact info
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default function ProjectDetail() {
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="w-8 h-8 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -97,18 +97,18 @@ export default function ProjectDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">{project.name}</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">{project.name}</h1>
|
||||||
{project.description && (
|
{project.description && (
|
||||||
<p className="text-sm text-[#8a8a95] mb-4">{project.description}</p>
|
<p className="text-sm text-muted-foreground mb-4">{project.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Overall progress bar */}
|
{/* Overall progress bar */}
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 mb-6">
|
<div className="bg-card border border-border rounded-xl p-5 mb-6">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<span className="text-sm text-[#8a8a95]">Overall Progress</span>
|
<span className="text-sm text-muted-foreground">Overall Progress</span>
|
||||||
<span className="text-lg font-bold text-white">{overallProgress}%</span>
|
<span className="text-lg font-bold text-foreground">{overallProgress}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-2 bg-[#1a1a24] rounded-full overflow-hidden">
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full bg-gradient-to-r from-[#1BB0CE] to-emerald-400 rounded-full transition-all duration-500"
|
className="h-full bg-gradient-to-r from-[#1BB0CE] to-emerald-400 rounded-full transition-all duration-500"
|
||||||
style={{ width: `${overallProgress}%` }}
|
style={{ width: `${overallProgress}%` }}
|
||||||
@@ -125,8 +125,8 @@ export default function ProjectDetail() {
|
|||||||
const today = new Date()
|
const today = new Date()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 mb-6">
|
<div className="bg-card border border-border rounded-xl p-5 mb-6">
|
||||||
<h2 className="text-base font-semibold text-white mb-4">Timeline</h2>
|
<h2 className="text-base font-semibold text-foreground mb-4">Timeline</h2>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{sorted.map((m, i) => {
|
{sorted.map((m, i) => {
|
||||||
const mDate = new Date(m.due_date)
|
const mDate = new Date(m.due_date)
|
||||||
@@ -138,9 +138,9 @@ export default function ProjectDetail() {
|
|||||||
return (
|
return (
|
||||||
<div key={m.id} className="flex items-center gap-3 mb-3 last:mb-0">
|
<div key={m.id} className="flex items-center gap-3 mb-3 last:mb-0">
|
||||||
<div className="w-24 flex-shrink-0 text-right">
|
<div className="w-24 flex-shrink-0 text-right">
|
||||||
<span className="text-[10px] text-[#6a6a75]">{mDate.toLocaleDateString()}</span>
|
<span className="text-[10px] text-muted-foreground">{mDate.toLocaleDateString()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 h-7 bg-[#1a1a24] rounded-md overflow-hidden relative">
|
<div className="flex-1 h-7 bg-muted rounded-md overflow-hidden relative">
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-md transition-all flex items-center px-2"
|
className="h-full rounded-md transition-all flex items-center px-2"
|
||||||
style={{
|
style={{
|
||||||
@@ -153,7 +153,7 @@ export default function ProjectDetail() {
|
|||||||
{m.name}
|
{m.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-[9px] text-[#6a6a75]">
|
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-[9px] text-muted-foreground">
|
||||||
{m.progress}%
|
{m.progress}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -166,10 +166,10 @@ export default function ProjectDetail() {
|
|||||||
})()}
|
})()}
|
||||||
|
|
||||||
{/* Milestones */}
|
{/* Milestones */}
|
||||||
<h2 className="text-base font-semibold text-white mb-3">Milestones</h2>
|
<h2 className="text-base font-semibold text-foreground mb-3">Milestones</h2>
|
||||||
<div className="space-y-2 mb-6">
|
<div className="space-y-2 mb-6">
|
||||||
{milestones.length === 0 ? (
|
{milestones.length === 0 ? (
|
||||||
<p className="text-sm text-[#6a6a75]">No milestones yet</p>
|
<p className="text-sm text-muted-foreground">No milestones yet</p>
|
||||||
) : (
|
) : (
|
||||||
(milestones || []).map((m) => {
|
(milestones || []).map((m) => {
|
||||||
const si = statusIcon[m.status] || { icon: Clock, color: "#6a6a75" }
|
const si = statusIcon[m.status] || { icon: Clock, color: "#6a6a75" }
|
||||||
@@ -177,48 +177,48 @@ export default function ProjectDetail() {
|
|||||||
const milestoneDeliverables = deliverables.filter((d) => d.milestone_id === m.id)
|
const milestoneDeliverables = deliverables.filter((d) => d.milestone_id === m.id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={m.id} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl overflow-hidden">
|
<div key={m.id} className="bg-card border border-border rounded-xl overflow-hidden">
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleExpanded(m.id)}
|
onClick={() => toggleExpanded(m.id)}
|
||||||
className="w-full flex items-center justify-between p-4 hover:bg-[#1a1a24]/50 transition-colors"
|
className="w-full flex items-center justify-between p-4 hover:bg-accent/50 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Icon className="h-5 w-5" style={{ color: si.color }} />
|
<Icon className="h-5 w-5" style={{ color: si.color }} />
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<span className="text-white text-sm font-medium">{m.name}</span>
|
<span className="text-foreground text-sm font-medium">{m.name}</span>
|
||||||
<div className="flex items-center gap-2 mt-0.5">
|
<div className="flex items-center gap-2 mt-0.5">
|
||||||
<div className="h-1.5 w-20 bg-[#1a1a24] rounded-full overflow-hidden">
|
<div className="h-1.5 w-20 bg-muted rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-full transition-all"
|
className="h-full rounded-full transition-all"
|
||||||
style={{ width: `${m.progress}%`, backgroundColor: si.color }}
|
style={{ width: `${m.progress}%`, backgroundColor: si.color }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] text-[#6a6a75]">{m.progress}%</span>
|
<span className="text-[10px] text-muted-foreground">{m.progress}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{expanded.includes(m.id) ? (
|
{expanded.includes(m.id) ? (
|
||||||
<ChevronUp className="h-4 w-4 text-[#6a6a75]" />
|
<ChevronUp className="h-4 w-4 text-muted-foreground" />
|
||||||
) : (
|
) : (
|
||||||
<ChevronDown className="h-4 w-4 text-[#6a6a75]" />
|
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{expanded.includes(m.id) && (
|
{expanded.includes(m.id) && (
|
||||||
<div className="px-4 pb-4 border-t border-[#1a1a24] pt-3">
|
<div className="px-4 pb-4 border-t border-border pt-3">
|
||||||
{m.description && (
|
{m.description && (
|
||||||
<p className="text-sm text-[#8a8a95] mb-3">{m.description}</p>
|
<p className="text-sm text-muted-foreground mb-3">{m.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Deliverables */}
|
{/* Deliverables */}
|
||||||
{milestoneDeliverables.length > 0 && (
|
{milestoneDeliverables.length > 0 && (
|
||||||
<div className="space-y-2 mb-3">
|
<div className="space-y-2 mb-3">
|
||||||
<span className="text-[11px] text-[#6a6a75] uppercase tracking-wider">Deliverables</span>
|
<span className="text-[11px] text-muted-foreground uppercase tracking-wider">Deliverables</span>
|
||||||
{(milestoneDeliverables || []).map((d) => (
|
{(milestoneDeliverables || []).map((d) => (
|
||||||
<div key={d.id} className="flex items-center justify-between bg-[#1a1a24] rounded-lg px-3 py-2">
|
<div key={d.id} className="flex items-center justify-between bg-muted rounded-lg px-3 py-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FileText className="h-3.5 w-3.5 text-[#6a6a75]" />
|
<FileText className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
<span className="text-sm text-white">{d.name}</span>
|
<span className="text-sm text-foreground">{d.name}</span>
|
||||||
<span className={`text-[10px] px-1.5 py-0.5 rounded ${
|
<span className={`text-[10px] px-1.5 py-0.5 rounded ${
|
||||||
d.status === "approved" ? "bg-emerald-500/10 text-emerald-400" :
|
d.status === "approved" ? "bg-emerald-500/10 text-emerald-400" :
|
||||||
d.status === "rejected" ? "bg-red-500/10 text-red-400" :
|
d.status === "rejected" ? "bg-red-500/10 text-red-400" :
|
||||||
@@ -228,7 +228,7 @@ export default function ProjectDetail() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{d.file_url && (
|
{d.file_url && (
|
||||||
<a href={d.file_url} target="_blank" className="text-[#1BB0CE] hover:underline text-xs flex items-center gap-1">
|
<a href={d.file_url} target="_blank" className="text-primary hover:underline text-xs flex items-center gap-1">
|
||||||
<ExternalLink className="h-3 w-3" /> View
|
<ExternalLink className="h-3 w-3" /> View
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ export default function ClientProjects() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Projects</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Projects</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">View your project milestones and progress</p>
|
<p className="text-sm text-muted-foreground mb-6">View your project milestones and progress</p>
|
||||||
|
|
||||||
{projects.length === 0 ? (
|
{projects.length === 0 ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<FolderOpen className="h-10 w-10 text-[#2a2a35] mx-auto mb-3" />
|
<FolderOpen className="h-10 w-10 text-muted/50 mx-auto mb-3" />
|
||||||
<p className="text-sm text-[#6a6a75]">No projects yet</p>
|
<p className="text-sm text-muted-foreground">No projects yet</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -42,25 +42,25 @@ export default function ClientProjects() {
|
|||||||
<button
|
<button
|
||||||
key={p.id}
|
key={p.id}
|
||||||
onClick={() => router.push(`/client-portal/projects/${p.id}`)}
|
onClick={() => router.push(`/client-portal/projects/${p.id}`)}
|
||||||
className="w-full bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 text-left hover:border-[#1BB0CE]/30 transition-all group"
|
className="w-full bg-card border border-border rounded-xl p-5 text-left hover:border-primary/30 transition-all group"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
<h3 className="text-white font-semibold">{p.name}</h3>
|
<h3 className="text-foreground font-semibold">{p.name}</h3>
|
||||||
<span className="text-[10px] font-medium px-2 py-0.5 rounded-full" style={{ backgroundColor: `${p.status_color}15`, color: p.status_color }}>
|
<span className="text-[10px] font-medium px-2 py-0.5 rounded-full" style={{ backgroundColor: `${p.status_color}15`, color: p.status_color }}>
|
||||||
{(p.status_name || "unknown").replace("_", " ")}
|
{(p.status_name || "unknown").replace("_", " ")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{p.description && (
|
{p.description && (
|
||||||
<p className="text-sm text-[#8a8a95] line-clamp-1">{p.description}</p>
|
<p className="text-sm text-muted-foreground line-clamp-1">{p.description}</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-4 mt-2 text-[11px] text-[#6a6a75]">
|
<div className="flex gap-4 mt-2 text-[11px] text-muted-foreground">
|
||||||
{p.start_date && <span>Start: {new Date(p.start_date).toLocaleDateString()}</span>}
|
{p.start_date && <span>Start: {new Date(p.start_date).toLocaleDateString()}</span>}
|
||||||
{p.end_date && <span>Due: {new Date(p.end_date).toLocaleDateString()}</span>}
|
{p.end_date && <span>Due: {new Date(p.end_date).toLocaleDateString()}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="h-5 w-5 text-[#2a2a35] group-hover:text-[#1BB0CE] transition-colors" />
|
<ChevronRight className="h-5 w-5 text-muted/50 group-hover:text-primary transition-colors" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -25,32 +25,32 @@ export default function ClientReceipts() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Receipts</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Receipts</h1>
|
||||||
<p className="text-sm text-[#6a6a75] mb-6">View all your payment receipts in one place</p>
|
<p className="text-sm text-muted-foreground mb-6">View all your payment receipts in one place</p>
|
||||||
|
|
||||||
{receipts.length === 0 ? (
|
{receipts.length === 0 ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<FileText className="h-10 w-10 text-[#2a2a35] mx-auto mb-3" />
|
<FileText className="h-10 w-10 text-muted-foreground mx-auto mb-3" />
|
||||||
<p className="text-sm text-[#6a6a75]">No receipts yet</p>
|
<p className="text-sm text-muted-foreground">No receipts yet</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{(receipts || []).map((r) => (
|
{(receipts || []).map((r) => (
|
||||||
<div key={r.id} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 flex items-center justify-between hover:border-[#1BB0CE]/30 transition-all">
|
<div key={r.id} className="bg-card border border-border rounded-xl p-5 flex items-center justify-between hover:border-primary/30 transition-all">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="w-10 h-10 rounded-lg bg-[#1BB0CE]/10 flex items-center justify-center">
|
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
||||||
<FileText className="h-5 w-5 text-[#1BB0CE]" />
|
<FileText className="h-5 w-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-white font-medium">{r.receipt_number}</p>
|
<p className="text-foreground font-medium">{r.receipt_number}</p>
|
||||||
<div className="flex items-center gap-3 mt-0.5">
|
<div className="flex items-center gap-3 mt-0.5">
|
||||||
<span className="text-xs text-[#6a6a75]">{r.invoice_number}</span>
|
<span className="text-xs text-muted-foreground">{r.invoice_number}</span>
|
||||||
<span className="text-xs text-[#2a2a35]">·</span>
|
<span className="text-xs text-muted-foreground">·</span>
|
||||||
<span className="text-xs text-[#8a8a95]">
|
<span className="text-xs text-muted-foreground">
|
||||||
{r.currency} {Number(r.total_amount).toLocaleString()}
|
{r.currency} {Number(r.total_amount).toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-[#2a2a35]">·</span>
|
<span className="text-xs text-muted-foreground">·</span>
|
||||||
<span className="text-xs text-[#6a6a75]">
|
<span className="text-xs text-muted-foreground">
|
||||||
{new Date(r.issued_at).toLocaleDateString()}
|
{new Date(r.issued_at).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,7 +60,7 @@ export default function ClientReceipts() {
|
|||||||
<a
|
<a
|
||||||
href={r.file_url}
|
href={r.file_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex items-center gap-1.5 text-xs text-[#1BB0CE] hover:underline px-3 py-1.5 border border-[#1BB0CE]/20 rounded-lg hover:bg-[#1BB0CE]/5 transition-all"
|
className="flex items-center gap-1.5 text-xs text-primary hover:underline px-3 py-1.5 border border-primary/20 rounded-lg hover:bg-primary/5 transition-all"
|
||||||
>
|
>
|
||||||
<Download className="h-3.5 w-3.5" />
|
<Download className="h-3.5 w-3.5" />
|
||||||
Download
|
Download
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
||||||
|
import { Sun, Moon, Monitor, Shield, Palette } from "lucide-react"
|
||||||
|
|
||||||
|
const modeOptions = [
|
||||||
|
{ value: "light", icon: Sun, label: "Light" },
|
||||||
|
{ value: "dark", icon: Moon, label: "Dark" },
|
||||||
|
{ value: "system", icon: Monitor, label: "System" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const COLOR_THEME_KEY = "crm-color-theme"
|
||||||
|
|
||||||
|
const colorOptions = [
|
||||||
|
{ value: "default", label: "Default", color: "bg-blue-600", ring: "ring-blue-600" },
|
||||||
|
{ value: "ocean", label: "Ocean", color: "bg-teal-500", ring: "ring-teal-500" },
|
||||||
|
{ value: "forest", label: "Forest", color: "bg-green-600", ring: "ring-green-600" },
|
||||||
|
{ value: "sunset", label: "Sunset", color: "bg-orange-500", ring: "ring-orange-500" },
|
||||||
|
{ value: "midnight", label: "Midnight", color: "bg-indigo-600", ring: "ring-indigo-600" },
|
||||||
|
{ value: "rose", label: "Rose", color: "bg-pink-600", ring: "ring-pink-600" },
|
||||||
|
{ value: "amber", label: "Amber", color: "bg-amber-500", ring: "ring-amber-500" },
|
||||||
|
{ value: "violet", label: "Violet", color: "bg-violet-600", ring: "ring-violet-600" },
|
||||||
|
{ value: "slate", label: "Slate", color: "bg-slate-500", ring: "ring-slate-500" },
|
||||||
|
{ value: "ruby", label: "Ruby", color: "bg-red-600", ring: "ring-red-600" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const bgOptions = [
|
||||||
|
{ value: "default", label: "Default", color: "bg-zinc-500", ring: "ring-zinc-500", desc: "Standard CRM appearance" },
|
||||||
|
{ value: "spidey", label: "Spidey", color: "bg-red-600", ring: "ring-red-600", desc: "Dark theme with red accents" },
|
||||||
|
{ value: "cosmic", label: "Cosmic", color: "bg-gradient-to-r from-purple-500 to-green-500", ring: "ring-purple-500", desc: "Deep space with purple nebula" },
|
||||||
|
{ value: "cyberpunk", label: "Cyberpunk", color: "bg-gradient-to-r from-pink-500 to-cyan-400", ring: "ring-pink-500", desc: "Synthwave aesthetic" },
|
||||||
|
{ value: "cyber2", label: "Cyber 2.0", color: "bg-gradient-to-r from-cyan-400 to-fuchsia-500", ring: "ring-cyan-400", desc: "Neon grid with glow" },
|
||||||
|
{ value: "pumpkin", label: "Pumpkin Spice", color: "bg-gradient-to-r from-orange-600 to-amber-500", ring: "ring-orange-600", desc: "Warm autumn orange" },
|
||||||
|
{ value: "bw", label: "Black & White", color: "bg-gradient-to-r from-gray-900 to-gray-400", ring: "ring-gray-500", desc: "Clean monochrome" },
|
||||||
|
]
|
||||||
|
|
||||||
|
function getStoredColorTheme(): string {
|
||||||
|
if (typeof window === "undefined") return "default"
|
||||||
|
return localStorage.getItem(COLOR_THEME_KEY) || "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyColorTheme(theme: string) {
|
||||||
|
const colorThemes = ["default", "ocean", "forest", "sunset", "midnight", "rose", "amber", "violet", "slate", "ruby"]
|
||||||
|
const classes = document.documentElement.className.split(" ").filter((c) => !colorThemes.includes(c))
|
||||||
|
if (theme !== "default") classes.push(theme)
|
||||||
|
document.documentElement.className = classes.join(" ").trim()
|
||||||
|
localStorage.setItem(COLOR_THEME_KEY, theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ClientSettings() {
|
||||||
|
const { theme, setTheme } = useTheme()
|
||||||
|
const { websiteTheme, setWebsiteTheme } = useWebsiteTheme()
|
||||||
|
const [colorTheme, setColorTheme] = useState("default")
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
setColorTheme(getStoredColorTheme())
|
||||||
|
applyColorTheme(getStoredColorTheme())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
function handleColorChange(value: string) {
|
||||||
|
setColorTheme(value)
|
||||||
|
applyColorTheme(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mounted) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className="text-xl font-bold text-foreground mb-1">Settings</h1>
|
||||||
|
<p className="text-sm text-muted-foreground mb-6">Customize your portal appearance</p>
|
||||||
|
|
||||||
|
<div className="space-y-6 max-w-2xl">
|
||||||
|
{/* Theme Mode */}
|
||||||
|
<div className="bg-card border border-border rounded-xl p-6">
|
||||||
|
<h2 className="text-sm font-semibold text-foreground mb-1">Theme Mode</h2>
|
||||||
|
<p className="text-xs text-muted-foreground mb-4">Light, dark, or system default</p>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
{modeOptions.map(({ value, icon: Icon, label }) => {
|
||||||
|
const active = theme === value
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={value}
|
||||||
|
onClick={() => setTheme(value)}
|
||||||
|
className={`flex flex-col items-center gap-2 rounded-lg border-2 p-4 transition-all ${
|
||||||
|
active
|
||||||
|
? "border-primary bg-primary/5"
|
||||||
|
: "border-border hover:border-border/80 text-muted-foreground hover:text-accent-foreground"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Icon className={`h-5 w-5 ${active ? "text-primary" : ""}`} />
|
||||||
|
<span className={`text-sm font-medium ${active ? "text-primary" : ""}`}>{label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Color Accent */}
|
||||||
|
<div className="bg-card border border-border rounded-xl p-6">
|
||||||
|
<h2 className="text-sm font-semibold text-foreground mb-1">Color Accent</h2>
|
||||||
|
<p className="text-xs text-muted-foreground mb-4">Dashboard accent color</p>
|
||||||
|
<div className="grid grid-cols-5 gap-3">
|
||||||
|
{colorOptions.map(({ value, label, color, ring }) => {
|
||||||
|
const active = colorTheme === value
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={value}
|
||||||
|
onClick={() => handleColorChange(value)}
|
||||||
|
title={label}
|
||||||
|
className={`flex flex-col items-center gap-2 rounded-lg border-2 p-3 transition-all ${
|
||||||
|
active ? "border-primary bg-primary/5" : "border-border hover:border-border/80"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`h-6 w-6 rounded-full ${color} ${ring} ring-2 ring-offset-2 ring-offset-card`} />
|
||||||
|
<span className={`text-[11px] ${active ? "text-primary" : "text-muted-foreground"}`}>{label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Background Theme */}
|
||||||
|
<div className="bg-card border border-border rounded-xl p-6">
|
||||||
|
<h2 className="text-sm font-semibold text-foreground mb-1">Background Theme</h2>
|
||||||
|
<p className="text-xs text-muted-foreground mb-4">Choose a background style</p>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{bgOptions.map(({ value, label, color, ring, desc }) => {
|
||||||
|
const active = websiteTheme === value
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={value}
|
||||||
|
onClick={() => setWebsiteTheme(value)}
|
||||||
|
className={`flex items-center gap-3 rounded-lg border-2 p-4 transition-all text-left ${
|
||||||
|
active
|
||||||
|
? "border-primary bg-primary/5"
|
||||||
|
: "border-border hover:border-border/80"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`h-8 w-8 shrink-0 rounded-full ${color} ${ring} ring-2 ring-offset-2 ring-offset-card`} />
|
||||||
|
<div>
|
||||||
|
<p className={`text-sm font-medium ${active ? "text-primary" : "text-foreground"}`}>{label}</p>
|
||||||
|
<p className={`text-[11px] ${active ? "text-primary/60" : "text-muted-foreground"}`}>{desc}</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -75,12 +75,12 @@ export default function ClientSupport() {
|
|||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-white mb-1">Support</h1>
|
<h1 className="text-xl font-bold text-foreground mb-1">Support</h1>
|
||||||
<p className="text-sm text-[#6a6a75]">Submit and track support requests</p>
|
<p className="text-sm text-muted-foreground">Submit and track support requests</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowForm(!showForm)}
|
onClick={() => setShowForm(!showForm)}
|
||||||
className="bg-[#1BB0CE] hover:bg-[#1BB0CE]/80 text-white text-sm px-4 py-2 rounded-lg transition-all"
|
className="bg-primary hover:bg-primary/80 text-foreground text-sm px-4 py-2 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
{showForm ? "Cancel" : "New Ticket"}
|
{showForm ? "Cancel" : "New Ticket"}
|
||||||
</button>
|
</button>
|
||||||
@@ -88,34 +88,34 @@ export default function ClientSupport() {
|
|||||||
|
|
||||||
{/* Submit form */}
|
{/* Submit form */}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<form onSubmit={handleSubmit} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-5 mb-6 space-y-4">
|
<form onSubmit={handleSubmit} className="bg-card border border-border rounded-xl p-5 mb-6 space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1">Title</label>
|
<label className="block text-sm text-muted-foreground mb-1">Title</label>
|
||||||
<input
|
<input
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
className="w-full bg-[#1a1a24] border border-[#2a2a35] rounded-lg px-3 py-2 text-sm text-white placeholder-[#6a6a75] outline-none focus:border-[#1BB0CE]/50"
|
className="w-full bg-muted border border-border rounded-lg px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground outline-none focus:border-primary/50"
|
||||||
placeholder="Brief summary of the issue"
|
placeholder="Brief summary of the issue"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1">Description</label>
|
<label className="block text-sm text-muted-foreground mb-1">Description</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full bg-[#1a1a24] border border-[#2a2a35] rounded-lg px-3 py-2 text-sm text-white placeholder-[#6a6a75] outline-none focus:border-[#1BB0CE]/50 resize-none"
|
className="w-full bg-muted border border-border rounded-lg px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground outline-none focus:border-primary/50 resize-none"
|
||||||
placeholder="Detailed description of your issue"
|
placeholder="Detailed description of your issue"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1">Severity</label>
|
<label className="block text-sm text-muted-foreground mb-1">Severity</label>
|
||||||
<Select value={severity} onValueChange={setSeverity}>
|
<Select value={severity} onValueChange={setSeverity}>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full bg-muted">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -128,7 +128,7 @@ export default function ClientSupport() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{submitError && (
|
{submitError && (
|
||||||
<div className="text-xs text-red-400 bg-red-400/5 border border-red-400/10 rounded-lg px-3 py-2">
|
<div className="text-xs text-destructive bg-destructive/5 border border-destructive/10 rounded-lg px-3 py-2">
|
||||||
{submitError}
|
{submitError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -136,7 +136,7 @@ export default function ClientSupport() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="flex items-center gap-2 bg-[#1BB0CE] hover:bg-[#1BB0CE]/80 text-white text-sm px-4 py-2 rounded-lg transition-all disabled:opacity-40"
|
className="flex items-center gap-2 bg-primary hover:bg-primary/80 text-foreground text-sm px-4 py-2 rounded-lg transition-all disabled:opacity-40"
|
||||||
>
|
>
|
||||||
<Send className="h-3.5 w-3.5" />
|
<Send className="h-3.5 w-3.5" />
|
||||||
{submitting ? "Submitting..." : "Submit Ticket"}
|
{submitting ? "Submitting..." : "Submit Ticket"}
|
||||||
@@ -147,19 +147,19 @@ export default function ClientSupport() {
|
|||||||
{/* Tickets list */}
|
{/* Tickets list */}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center h-32">
|
<div className="flex items-center justify-center h-32">
|
||||||
<div className="w-6 h-6 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
|
<div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : tickets.length === 0 ? (
|
) : tickets.length === 0 ? (
|
||||||
<div className="text-center py-16">
|
<div className="text-center py-16">
|
||||||
<LifeBuoy className="h-10 w-10 text-[#2a2a35] mx-auto mb-3" />
|
<LifeBuoy className="h-10 w-10 text-muted-foreground mx-auto mb-3" />
|
||||||
<p className="text-sm text-[#6a6a75]">No support tickets yet</p>
|
<p className="text-sm text-muted-foreground">No support tickets yet</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{(tickets || []).map((t) => (
|
{(tickets || []).map((t) => (
|
||||||
<div key={t.id} className="bg-[#0d1117] border border-[#1a1a24] rounded-xl p-4">
|
<div key={t.id} className="bg-card border border-border rounded-xl p-4">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<h3 className="text-white text-sm font-medium">{t.title}</h3>
|
<h3 className="text-foreground text-sm font-medium">{t.title}</h3>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<span className={`text-[10px] px-1.5 py-0.5 rounded ${
|
<span className={`text-[10px] px-1.5 py-0.5 rounded ${
|
||||||
t.severity === "critical" ? "bg-red-500/10 text-red-400" :
|
t.severity === "critical" ? "bg-red-500/10 text-red-400" :
|
||||||
@@ -179,15 +179,15 @@ export default function ClientSupport() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-[#8a8a95] line-clamp-2">{t.description}</p>
|
<p className="text-sm text-muted-foreground line-clamp-2">{t.description}</p>
|
||||||
<div className="flex items-center justify-between mt-2">
|
<div className="flex items-center justify-between mt-2">
|
||||||
<span className="text-[10px] text-[#6a6a75]">
|
<span className="text-[10px] text-muted-foreground">
|
||||||
{new Date(t.created_at).toLocaleDateString()}
|
{new Date(t.created_at).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{t.resolution_notes && (
|
{t.resolution_notes && (
|
||||||
<div className="mt-2 text-xs text-[#6a6a75] bg-[#1a1a24] rounded-lg px-3 py-2">
|
<div className="mt-2 text-xs text-muted-foreground bg-muted rounded-lg px-3 py-2">
|
||||||
<span className="text-[#8a8a95]">Resolution: </span>{t.resolution_notes}
|
<span className="text-muted-foreground">Resolution: </span>{t.resolution_notes}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ import { Toaster } from "@/components/ui/sonner"
|
|||||||
import "./globals.css"
|
import "./globals.css"
|
||||||
import "../../Web_Backgrounds/default-theme.css"
|
import "../../Web_Backgrounds/default-theme.css"
|
||||||
import "../../Web_Backgrounds/spidey-theme.css"
|
import "../../Web_Backgrounds/spidey-theme.css"
|
||||||
|
import "../../Web_Backgrounds/cosmic-theme.css"
|
||||||
|
import "../../Web_Backgrounds/cyberpunk-theme.css"
|
||||||
|
import "../../Web_Backgrounds/cyber2-theme.css"
|
||||||
|
import "../../Web_Backgrounds/pumpkin-theme.css"
|
||||||
|
import "../../Web_Backgrounds/bw-theme.css"
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
|
|||||||
Reference in New Issue
Block a user