Add business dashboard dark mode from Super Admin theme setting.
Login and in-app surfaces follow tenant themeMode, matching customer dark tokens, with themed switches and tighter card/table chrome. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
65012717d0
commit
e9400c2281
@@ -79,11 +79,11 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<BusinessDomainGuard>
|
<BusinessDomainGuard>
|
||||||
<LocaleProvider>
|
<LocaleProvider>
|
||||||
|
<BusinessThemeProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<TenantBrandingProvider>
|
<TenantBrandingProvider>
|
||||||
<DashboardDocumentTitle />
|
<DashboardDocumentTitle />
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<BusinessThemeProvider>
|
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<GuestRoute />}>
|
<Route element={<GuestRoute />}>
|
||||||
@@ -170,10 +170,10 @@ function App() {
|
|||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</BusinessThemeProvider>
|
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</TenantBrandingProvider>
|
</TenantBrandingProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
</BusinessThemeProvider>
|
||||||
</LocaleProvider>
|
</LocaleProvider>
|
||||||
</BusinessDomainGuard>
|
</BusinessDomainGuard>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -132,3 +132,7 @@
|
|||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme='dark']) .bgOrbs {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
background: rgba(255, 255, 255, 0.82);
|
background: var(--surface);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
|
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 520px;
|
max-width: 520px;
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: var(--surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
.modal {
|
.modal {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right var(--select-arrow-offset) center;
|
background-position: right var(--select-arrow-offset) center;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
min-height: var(--field-height);
|
min-height: var(--field-height);
|
||||||
padding: 0 var(--field-padding-x);
|
padding: 0 var(--field-padding-x);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
box-shadow: 0 10px 28px rgba(31, 38, 135, 0.14);
|
box-shadow: 0 10px 28px rgba(31, 38, 135, 0.14);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid rgba(15, 23, 42, 0.1);
|
border: 1px solid rgba(15, 23, 42, 0.1);
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: var(--surface);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
max-width: 440px;
|
max-width: 440px;
|
||||||
padding: 32px 28px 24px;
|
padding: 32px 28px 24px;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
background: rgba(255, 255, 255, 0.94);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid rgba(245, 158, 11, 0.28);
|
border: 1px solid rgba(245, 158, 11, 0.28);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
padding: 32px 28px 24px;
|
padding: 32px 28px 24px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid rgba(239, 68, 68, 0.25);
|
border: 1px solid rgba(239, 68, 68, 0.25);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -235,7 +235,7 @@
|
|||||||
|
|
||||||
.discountRow {
|
.discountRow {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.12s ease, visibility 0.12s;
|
transition: opacity 0.12s ease, visibility 0.12s;
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
-webkit-backdrop-filter: blur(10px);
|
-webkit-backdrop-filter: blur(10px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 14px;
|
padding: 8px 14px;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
background: rgba(255, 255, 255, 0.5);
|
background: var(--surface);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: box-shadow 0.2s, border-color 0.2s;
|
transition: box-shadow 0.2s, border-color 0.2s;
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
-webkit-backdrop-filter: blur(20px);
|
-webkit-backdrop-filter: blur(20px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
border: 2px dashed rgba(148, 163, 184, 0.4);
|
border: 2px dashed rgba(148, 163, 184, 0.4);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.4);
|
background: var(--surface);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.5);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cropArea {
|
.cropArea {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
max-height: calc(100vh - 180px);
|
max-height: calc(100vh - 180px);
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
opacity: 0.72;
|
opacity: 0.72;
|
||||||
transition: opacity 0.2s, border-color 0.2s, transform 0.2s;
|
transition: opacity 0.2s, border-color 0.2s, transform 0.2s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
border: 2px dashed rgba(148, 163, 184, 0.4);
|
border: 2px dashed rgba(148, 163, 184, 0.4);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.4);
|
background: var(--surface);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemCardHeader {
|
.itemCardHeader {
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
padding: var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--field-padding-x);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: var(--surface);
|
||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
.calendar :global(.rmdp-wrapper) {
|
.calendar :global(.rmdp-wrapper) {
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(16px);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
padding: var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--field-padding-x);
|
||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.65);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
padding: var(--field-padding-y) var(--select-padding-end) var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--select-padding-end) var(--field-padding-y) var(--field-padding-x);
|
||||||
appearance: none;
|
appearance: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
background-color: rgba(255, 255, 255, 0.65);
|
background-color: var(--surface);
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right var(--select-arrow-offset) center;
|
background-position: right var(--select-arrow-offset) center;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
max-height: calc(100vh - 40px);
|
max-height: calc(100vh - 40px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(28px);
|
backdrop-filter: blur(28px);
|
||||||
-webkit-backdrop-filter: blur(28px);
|
-webkit-backdrop-filter: blur(28px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: rgba(255, 255, 255, 0.65);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(16px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
border: 1.5px solid rgba(148, 163, 184, 0.55);
|
border: 1.5px solid rgba(148, 163, 184, 0.55);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: white;
|
color: white;
|
||||||
background: white;
|
background: var(--card-media-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkSelected {
|
.checkSelected {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemThumb {
|
.itemThumb {
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: var(--surface);
|
||||||
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.txHeader {
|
.txHeader {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
background: rgba(255, 255, 255, 0.65);
|
background: var(--surface);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition: border-color 0.2s, background 0.2s, transform 0.15s;
|
transition: border-color 0.2s, background 0.2s, transform 0.15s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.12s ease, visibility 0.12s;
|
transition: opacity 0.12s ease, visibility 0.12s;
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
-webkit-backdrop-filter: blur(10px);
|
-webkit-backdrop-filter: blur(10px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
box-shadow: var(--glass-shadow);
|
box-shadow: var(--glass-shadow);
|
||||||
|
overflow: hidden;
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,16 +35,15 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: cover;
|
||||||
padding: 10px;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imagePlaceholder {
|
.imagePlaceholder {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
.tabActive {
|
.tabActive {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: var(--surface);
|
||||||
box-shadow: inset 0 -2px 0 var(--primary);
|
box-shadow: inset 0 -2px 0 var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
.comment,
|
.comment,
|
||||||
.review {
|
.review {
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
background: rgba(255, 255, 255, 0.65);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
min-height: var(--field-height);
|
min-height: var(--field-height);
|
||||||
padding: 0 var(--field-padding-x);
|
padding: 0 var(--field-padding-x);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(16px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: linear-gradient(135deg, var(--primary-light) 0%, rgba(219, 234, 254, 0.5) 100%);
|
background: linear-gradient(135deg, var(--icon-bg) 0%, var(--icon-bg-end) 100%);
|
||||||
|
border: 1px solid rgba(var(--primary-rgb) / 0.22);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.qtyBtn {
|
.qtyBtn {
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: var(--surface);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: var(--surface);
|
||||||
max-height: 180px;
|
max-height: 180px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -356,7 +356,7 @@
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
@@ -450,7 +450,7 @@
|
|||||||
padding: var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--field-padding-x);
|
||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.75);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
.brandLogoUploaded {
|
.brandLogoUploaded {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: var(--card-media-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.brandEditBtn {
|
.brandEditBtn {
|
||||||
|
|||||||
@@ -91,10 +91,9 @@ export function Sidebar({ open = false }: SidebarProps) {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { user, logout } = useAuth()
|
const { user, logout } = useAuth()
|
||||||
const { locale } = useLocale()
|
const { locale } = useLocale()
|
||||||
const { enabledModules } = useTenantBranding()
|
const { enabledModules, logoUrl: brandLogoUrl } = useTenantBranding()
|
||||||
const t = useT()
|
const t = useT()
|
||||||
const [openGroups, setOpenGroups] = useState<Record<string, boolean>>({})
|
const [openGroups, setOpenGroups] = useState<Record<string, boolean>>({})
|
||||||
const [brandLogoUrl, setBrandLogoUrl] = useState<string | null>(null)
|
|
||||||
const [nameEn, setNameEn] = useState('')
|
const [nameEn, setNameEn] = useState('')
|
||||||
const [nameFa, setNameFa] = useState('')
|
const [nameFa, setNameFa] = useState('')
|
||||||
|
|
||||||
@@ -242,12 +241,10 @@ export function Sidebar({ open = false }: SidebarProps) {
|
|||||||
async function loadBranding() {
|
async function loadBranding() {
|
||||||
try {
|
try {
|
||||||
const data = await getBusinessProfile(controller.signal)
|
const data = await getBusinessProfile(controller.signal)
|
||||||
setBrandLogoUrl(data.profile.logoUrl)
|
|
||||||
setNameEn(data.profile.nameEn.trim())
|
setNameEn(data.profile.nameEn.trim())
|
||||||
setNameFa(data.profile.nameFa.trim())
|
setNameFa(data.profile.nameFa.trim())
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isAbortError(err)) return
|
if (isAbortError(err)) return
|
||||||
setBrandLogoUrl(null)
|
|
||||||
setNameEn('')
|
setNameEn('')
|
||||||
setNameFa('')
|
setNameFa('')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.03em;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
background: rgba(255, 255, 255, 0.82);
|
background: var(--surface);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
|
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.optionSelected .checkbox {
|
.optionSelected .checkbox {
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.removeSelectedBtn {
|
.removeSelectedBtn {
|
||||||
|
|||||||
@@ -5,13 +5,43 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
background: rgba(148, 163, 184, 0.45);
|
background: rgba(148, 163, 184, 0.45);
|
||||||
|
overflow: hidden;
|
||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch::before,
|
||||||
|
.switch::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-family: var(--font-en), var(--font-ui);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::before {
|
||||||
|
content: '1';
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::after {
|
||||||
|
content: '0';
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.on {
|
.switch.on {
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch.on::before,
|
||||||
|
.switch.on::after {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.switch:disabled {
|
.switch:disabled {
|
||||||
opacity: 0.55;
|
opacity: 0.55;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@@ -27,6 +57,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.2);
|
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.2);
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch.on .thumb {
|
.switch.on .thumb {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
min-height: var(--field-height);
|
min-height: var(--field-height);
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
.fieldCard {
|
.fieldCard {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: var(--toast-bg, rgba(255, 255, 255, 0.94));
|
background: var(--toast-bg, var(--elevated-surface));
|
||||||
backdrop-filter: blur(var(--blur-glass));
|
backdrop-filter: blur(var(--blur-glass));
|
||||||
-webkit-backdrop-filter: blur(var(--blur-glass));
|
-webkit-backdrop-filter: blur(var(--blur-glass));
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@@ -4,12 +4,42 @@
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(148, 163, 184, 0.45);
|
background: rgba(148, 163, 184, 0.45);
|
||||||
|
overflow: hidden;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch::before,
|
||||||
|
.switch::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-family: var(--font-en), var(--font-ui);
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::before {
|
||||||
|
content: '1';
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::after {
|
||||||
|
content: '0';
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.on {
|
.switch.on {
|
||||||
background: #16a34a;
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch.on::before,
|
||||||
|
.switch.on::after {
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
@@ -19,9 +49,10 @@
|
|||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: white;
|
background: #fff;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch.on .thumb {
|
.switch.on .thumb {
|
||||||
@@ -33,6 +64,19 @@
|
|||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compact::before,
|
||||||
|
.compact::after {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact::before {
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact::after {
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.compact .thumb {
|
.compact .thumb {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.15s ease, visibility 0.15s;
|
transition: opacity 0.15s ease, visibility 0.15s;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -44,10 +44,10 @@
|
|||||||
|
|
||||||
.tipTop::after {
|
.tipTop::after {
|
||||||
top: 100%;
|
top: 100%;
|
||||||
border-top-color: rgba(255, 255, 255, 0.95);
|
border-top-color: var(--elevated-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipBottom::after {
|
.tipBottom::after {
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
border-bottom-color: rgba(255, 255, 255, 0.95);
|
border-bottom-color: var(--elevated-surface);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
padding: var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--field-padding-x);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -262,7 +262,7 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
transition: all 0.15s;
|
transition: all 0.15s;
|
||||||
@@ -300,7 +300,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 9 / 4;
|
aspect-ratio: 9 / 4;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { useEffect, type ReactNode } from 'react'
|
|||||||
import { isAbortError } from '../lib/api'
|
import { isAbortError } from '../lib/api'
|
||||||
import { getBusinessDomain } from '../lib/config'
|
import { getBusinessDomain } from '../lib/config'
|
||||||
import { resolveTenantByDomain } from '../services/tenantService'
|
import { resolveTenantByDomain } from '../services/tenantService'
|
||||||
import { applyBusinessPrimaryColor } from '../utils/applyBusinessTheme'
|
import { applyBusinessPrimaryColor, applyThemeMode } from '../utils/applyBusinessTheme'
|
||||||
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the tenant primary color. Does not reset to blue on unmount —
|
* Applies the tenant primary color and dashboard theme mode.
|
||||||
* Strict Mode / HMR remounts were wiping the brand color back to the default.
|
* Does not reset to blue on unmount — Strict Mode / HMR remounts were
|
||||||
|
* wiping the brand color back to the default.
|
||||||
*/
|
*/
|
||||||
export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -21,9 +22,11 @@ export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
|||||||
applyBusinessPrimaryColor(
|
applyBusinessPrimaryColor(
|
||||||
normalizeBusinessPrimaryColorId(tenant.primaryColor),
|
normalizeBusinessPrimaryColorId(tenant.primaryColor),
|
||||||
)
|
)
|
||||||
|
applyThemeMode(tenant.themeMode)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isAbortError(err) || controller.signal.aborted) return
|
if (isAbortError(err) || controller.signal.aborted) return
|
||||||
applyBusinessPrimaryColor(undefined)
|
applyBusinessPrimaryColor(undefined)
|
||||||
|
applyThemeMode('light')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import { getBusinessDomain } from '../lib/config'
|
|||||||
import { BUSINESS_PROFILE_UPDATED_EVENT } from '../lib/businessContext'
|
import { BUSINESS_PROFILE_UPDATED_EVENT } from '../lib/businessContext'
|
||||||
import { getBusinessProfile } from '../services/businessProfileService'
|
import { getBusinessProfile } from '../services/businessProfileService'
|
||||||
import { resolveTenantByDomain } from '../services/tenantService'
|
import { resolveTenantByDomain } from '../services/tenantService'
|
||||||
import { applyBusinessPrimaryColor } from '../utils/applyBusinessTheme'
|
import {
|
||||||
|
applyBusinessPrimaryColor,
|
||||||
|
applyThemeMode,
|
||||||
|
pickThemedLogoUrl,
|
||||||
|
type DashboardThemeMode,
|
||||||
|
} from '../utils/applyBusinessTheme'
|
||||||
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||||
import {
|
import {
|
||||||
DEFAULT_ENABLED_BUSINESS_MODULES,
|
DEFAULT_ENABLED_BUSINESS_MODULES,
|
||||||
@@ -31,6 +36,7 @@ interface TenantBrandingContextValue {
|
|||||||
businessNameEn: string
|
businessNameEn: string
|
||||||
logoUrl: string | null
|
logoUrl: string | null
|
||||||
faviconUrl: string | null
|
faviconUrl: string | null
|
||||||
|
themeMode: DashboardThemeMode
|
||||||
enabledModules: BusinessModuleId[]
|
enabledModules: BusinessModuleId[]
|
||||||
homeCharts: [HomeChartId, HomeChartId]
|
homeCharts: [HomeChartId, HomeChartId]
|
||||||
refreshBranding: () => void
|
refreshBranding: () => void
|
||||||
@@ -54,6 +60,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
const [nameFa, setNameFa] = useState('')
|
const [nameFa, setNameFa] = useState('')
|
||||||
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
||||||
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
|
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
|
||||||
|
const [themeMode, setThemeMode] = useState<DashboardThemeMode>('light')
|
||||||
const [enabledModules, setEnabledModules] = useState<BusinessModuleId[]>(
|
const [enabledModules, setEnabledModules] = useState<BusinessModuleId[]>(
|
||||||
DEFAULT_ENABLED_BUSINESS_MODULES,
|
DEFAULT_ENABLED_BUSINESS_MODULES,
|
||||||
)
|
)
|
||||||
@@ -78,7 +85,13 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
let nextNameEn = pickBusinessName(tenant.name, domain)
|
let nextNameEn = pickBusinessName(tenant.name, domain)
|
||||||
let nextNameFa = pickBusinessName(tenant.nameFa, tenant.name, domain)
|
let nextNameFa = pickBusinessName(tenant.nameFa, tenant.name, domain)
|
||||||
let nextLogo = tenant.logoUrl?.trim() || null
|
const nextThemeMode: DashboardThemeMode =
|
||||||
|
tenant.themeMode === 'dark' ? 'dark' : 'light'
|
||||||
|
let nextLogo = pickThemedLogoUrl(
|
||||||
|
tenant.logoUrl,
|
||||||
|
tenant.logoDarkUrl,
|
||||||
|
nextThemeMode,
|
||||||
|
)
|
||||||
let nextFavicon =
|
let nextFavicon =
|
||||||
tenant.faviconUrl?.trim() || tenant.logoUrl?.trim() || null
|
tenant.faviconUrl?.trim() || tenant.logoUrl?.trim() || null
|
||||||
|
|
||||||
@@ -96,7 +109,12 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
nextNameFa,
|
nextNameFa,
|
||||||
domain,
|
domain,
|
||||||
)
|
)
|
||||||
nextLogo = profile.profile.logoUrl?.trim() || nextLogo
|
nextLogo =
|
||||||
|
pickThemedLogoUrl(
|
||||||
|
profile.profile.logoUrl,
|
||||||
|
profile.profile.logoDarkUrl,
|
||||||
|
nextThemeMode,
|
||||||
|
) || nextLogo
|
||||||
nextFavicon =
|
nextFavicon =
|
||||||
profile.profile.faviconUrl?.trim() ||
|
profile.profile.faviconUrl?.trim() ||
|
||||||
profile.profile.logoUrl?.trim() ||
|
profile.profile.logoUrl?.trim() ||
|
||||||
@@ -113,12 +131,14 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
setNameFa(nextNameFa || nextNameEn || domain)
|
setNameFa(nextNameFa || nextNameEn || domain)
|
||||||
setLogoUrl(nextLogo)
|
setLogoUrl(nextLogo)
|
||||||
setFaviconUrl(nextFavicon)
|
setFaviconUrl(nextFavicon)
|
||||||
|
setThemeMode(nextThemeMode)
|
||||||
setEnabledModules(normalizeEnabledBusinessModules(tenant.enabledModules))
|
setEnabledModules(normalizeEnabledBusinessModules(tenant.enabledModules))
|
||||||
setHomeCharts(normalizeHomeCharts(tenant.homeCharts))
|
setHomeCharts(normalizeHomeCharts(tenant.homeCharts))
|
||||||
applyDocumentFavicon(nextFavicon)
|
applyDocumentFavicon(nextFavicon)
|
||||||
applyBusinessPrimaryColor(
|
applyBusinessPrimaryColor(
|
||||||
normalizeBusinessPrimaryColorId(tenant.primaryColor),
|
normalizeBusinessPrimaryColorId(tenant.primaryColor),
|
||||||
)
|
)
|
||||||
|
applyThemeMode(nextThemeMode)
|
||||||
if (!defaultLocaleAppliedRef.current) {
|
if (!defaultLocaleAppliedRef.current) {
|
||||||
defaultLocaleAppliedRef.current = true
|
defaultLocaleAppliedRef.current = true
|
||||||
if (tenant.defaultLocale === 'en' || tenant.defaultLocale === 'fa') {
|
if (tenant.defaultLocale === 'en' || tenant.defaultLocale === 'fa') {
|
||||||
@@ -135,10 +155,12 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
setNameFa(domain)
|
setNameFa(domain)
|
||||||
setLogoUrl(null)
|
setLogoUrl(null)
|
||||||
setFaviconUrl(null)
|
setFaviconUrl(null)
|
||||||
|
setThemeMode('light')
|
||||||
setEnabledModules([...DEFAULT_ENABLED_BUSINESS_MODULES])
|
setEnabledModules([...DEFAULT_ENABLED_BUSINESS_MODULES])
|
||||||
setHomeCharts([...DEFAULT_HOME_CHARTS])
|
setHomeCharts([...DEFAULT_HOME_CHARTS])
|
||||||
applyDocumentFavicon(null)
|
applyDocumentFavicon(null)
|
||||||
applyBusinessPrimaryColor(undefined)
|
applyBusinessPrimaryColor(undefined)
|
||||||
|
applyThemeMode('light')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +195,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
businessNameEn,
|
businessNameEn,
|
||||||
logoUrl,
|
logoUrl,
|
||||||
faviconUrl,
|
faviconUrl,
|
||||||
|
themeMode,
|
||||||
enabledModules,
|
enabledModules,
|
||||||
homeCharts,
|
homeCharts,
|
||||||
refreshBranding,
|
refreshBranding,
|
||||||
@@ -182,6 +205,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
businessNameEn,
|
businessNameEn,
|
||||||
logoUrl,
|
logoUrl,
|
||||||
faviconUrl,
|
faviconUrl,
|
||||||
|
themeMode,
|
||||||
enabledModules,
|
enabledModules,
|
||||||
homeCharts,
|
homeCharts,
|
||||||
refreshBranding,
|
refreshBranding,
|
||||||
|
|||||||
@@ -44,6 +44,16 @@
|
|||||||
--text-primary: #1e293b;
|
--text-primary: #1e293b;
|
||||||
--text-secondary: #64748b;
|
--text-secondary: #64748b;
|
||||||
--text-muted: #94a3b8;
|
--text-muted: #94a3b8;
|
||||||
|
--border-color: rgba(148, 163, 184, 0.35);
|
||||||
|
--surface: rgba(255, 255, 255, 0.82);
|
||||||
|
--card-media-bg: #ffffff;
|
||||||
|
--elevated-surface: rgba(255, 255, 255, 0.96);
|
||||||
|
--icon-bg: color-mix(in srgb, var(--primary) 14%, #ffffff);
|
||||||
|
--icon-bg-end: color-mix(in srgb, var(--primary) 8%, #f1f5f9);
|
||||||
|
--card-hover-lift: -4px;
|
||||||
|
--card-hover-shadow: 0 16px 48px rgba(var(--primary-rgb) / 0.14);
|
||||||
|
--card-hover-transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||||
|
--table-header-bg: rgba(255, 255, 255, 0.35);
|
||||||
--sidebar-width: 260px;
|
--sidebar-width: 260px;
|
||||||
--radius: 16px;
|
--radius: 16px;
|
||||||
--radius-sm: 12px;
|
--radius-sm: 12px;
|
||||||
@@ -61,6 +71,57 @@
|
|||||||
sans-serif;
|
sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dark mode: neutral dark gray backgrounds.
|
||||||
|
* Brand/theme color (--primary) is for accents only — not page/card backgrounds.
|
||||||
|
*/
|
||||||
|
html[data-theme='dark'] {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
--bg-gradient-start: #2a2d34;
|
||||||
|
--bg-gradient-mid: #22252b;
|
||||||
|
--bg-gradient-end: #1a1d23;
|
||||||
|
|
||||||
|
--glass-bg: rgba(45, 49, 57, 0.9);
|
||||||
|
--header-bg: color-mix(in srgb, #2a2d34 92%, transparent);
|
||||||
|
--glass-border: rgba(255, 255, 255, 0.1);
|
||||||
|
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
|
||||||
|
--modal-overlay-bg: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--text-primary: #f1f5f9;
|
||||||
|
--text-secondary: #cbd5e1;
|
||||||
|
--text-muted: #94a3b8;
|
||||||
|
--border-color: rgba(255, 255, 255, 0.12);
|
||||||
|
--surface: rgba(55, 59, 68, 0.95);
|
||||||
|
--card-media-bg: #32363f;
|
||||||
|
--elevated-surface: #3a3e48;
|
||||||
|
--toast-bg: rgba(58, 62, 72, 0.96);
|
||||||
|
--toast-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
--card-hover-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
/* Table header: one step above the row, still dark */
|
||||||
|
--table-header-bg: rgba(255, 255, 255, 0.07);
|
||||||
|
|
||||||
|
/* Icon chip: dark gray tile, slight primary tint — not a light wash */
|
||||||
|
--icon-bg: color-mix(in srgb, var(--primary) 22%, #2f333c);
|
||||||
|
--icon-bg-end: color-mix(in srgb, var(--primary) 12%, #25282f);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] select {
|
||||||
|
background-color: var(--surface);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark']
|
||||||
|
input:not([type='checkbox']):not([type='radio']):not([type='file']):not([type='range']):not([type='hidden']),
|
||||||
|
html[data-theme='dark'] textarea {
|
||||||
|
background-color: var(--surface);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
@@ -91,6 +152,29 @@ body {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] body {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--bg-gradient-start) 0%,
|
||||||
|
var(--bg-gradient-mid) 45%,
|
||||||
|
var(--bg-gradient-end) 100%
|
||||||
|
);
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Soft neutral aura — no theme-color wash on the page */
|
||||||
|
html[data-theme='dark'] body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: -30%;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
display: block;
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse 520px 520px at 72% 18%, rgba(255, 255, 255, 0.04), transparent 72%),
|
||||||
|
radial-gradient(ellipse 420px 420px at 22% 82%, rgba(255, 255, 255, 0.03), transparent 72%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Portaled dialogs sit on body; blur the app behind them. */
|
/* Portaled dialogs sit on body; blur the app behind them. */
|
||||||
body:has([aria-modal='true']) #root {
|
body:has([aria-modal='true']) #root {
|
||||||
filter: blur(var(--modal-overlay-blur, 16px));
|
filter: blur(var(--modal-overlay-blur, 16px));
|
||||||
@@ -117,8 +201,8 @@ select {
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
min-height: var(--field-height);
|
min-height: var(--field-height);
|
||||||
padding: var(--field-padding-y) var(--select-padding-end) var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--select-padding-end) var(--field-padding-y) var(--field-padding-x);
|
||||||
|
|||||||
@@ -158,7 +158,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
||||||
@@ -318,7 +318,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -363,7 +363,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectField {
|
.selectField {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.galleryThumbs {
|
.galleryThumbs {
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.galleryThumb img {
|
.galleryThumb img {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.35);
|
background: var(--table-header-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.35);
|
background: var(--table-header-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
@@ -433,7 +433,7 @@
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.4);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailItemTop {
|
.detailItemTop {
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
padding: 24px 32px 28px;
|
padding: 24px 32px 28px;
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: var(--glass-bg);
|
||||||
backdrop-filter: blur(24px);
|
backdrop-filter: blur(24px);
|
||||||
-webkit-backdrop-filter: blur(24px);
|
-webkit-backdrop-filter: blur(24px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
box-shadow: 0 20px 60px rgba(31, 38, 135, 0.12);
|
box-shadow: var(--glass-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand {
|
.brand {
|
||||||
@@ -176,8 +176,8 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.8);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
@@ -325,13 +325,20 @@ button.submitBtn {
|
|||||||
|
|
||||||
.error {
|
.error {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #b91c1c;
|
line-height: 1.55;
|
||||||
background: rgba(239, 68, 68, 0.08);
|
color: #fecaca;
|
||||||
border: 1px solid rgba(239, 68, 68, 0.25);
|
background: rgba(239, 68, 68, 0.16);
|
||||||
|
border: 1px solid rgba(248, 113, 113, 0.45);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html:not([data-theme='dark'])) .error {
|
||||||
|
color: #b91c1c;
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border-color: rgba(239, 68, 68, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.35);
|
background: var(--table-header-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
|
|||||||
@@ -163,7 +163,7 @@
|
|||||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
background: #ffffff;
|
background: var(--card-media-bg);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.15);
|
border: 1px solid rgba(148, 163, 184, 0.15);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
.thumb {
|
.thumb {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
background: #fff;
|
background: var(--card-media-bg);
|
||||||
border: 2px solid rgba(148, 163, 184, 0.2);
|
border: 2px solid rgba(148, 163, 184, 0.2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.8);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.gatewayCard,
|
.gatewayCard,
|
||||||
.comingSoonCard {
|
.comingSoonCard {
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: var(--glass-bg);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
padding: 0 36px;
|
padding: 0 36px;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
border: 1px solid rgba(var(--primary-rgb) / 0.3);
|
border: 1px solid rgba(var(--primary-rgb) / 0.3);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
backdrop-filter: blur(18px);
|
backdrop-filter: blur(18px);
|
||||||
-webkit-backdrop-filter: blur(18px);
|
-webkit-backdrop-filter: blur(18px);
|
||||||
box-shadow: 0 8px 28px rgba(31, 38, 135, 0.12);
|
box-shadow: 0 8px 28px rgba(31, 38, 135, 0.12);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.35);
|
background: var(--table-header-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right var(--select-arrow-offset) center;
|
background-position: right var(--select-arrow-offset) center;
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
border: 1px solid rgba(var(--primary-rgb) / 0.35);
|
border: 1px solid rgba(var(--primary-rgb) / 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
appearance: none;
|
appearance: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
padding-right: var(--select-padding-end);
|
padding-right: var(--select-padding-end);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right var(--select-arrow-offset) center;
|
background-position: right var(--select-arrow-offset) center;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { apiRequest } from '../lib/api'
|
import { apiRequest } from '../lib/api'
|
||||||
import type { DashboardLocale } from '@meshkee/dashboard-core'
|
import type { DashboardLocale } from '@meshkee/dashboard-core'
|
||||||
import type { BusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
import type { BusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||||
|
import type { DashboardThemeMode } from '../utils/applyBusinessTheme'
|
||||||
import type { BusinessModuleId, HomeChartId } from '../utils/businessModules'
|
import type { BusinessModuleId, HomeChartId } from '../utils/businessModules'
|
||||||
|
|
||||||
export interface ResolvedTenant {
|
export interface ResolvedTenant {
|
||||||
@@ -11,10 +12,12 @@ export interface ResolvedTenant {
|
|||||||
domain: string
|
domain: string
|
||||||
primaryColor: BusinessPrimaryColorId
|
primaryColor: BusinessPrimaryColorId
|
||||||
defaultLocale?: DashboardLocale
|
defaultLocale?: DashboardLocale
|
||||||
|
themeMode?: DashboardThemeMode
|
||||||
enabledModules?: BusinessModuleId[]
|
enabledModules?: BusinessModuleId[]
|
||||||
homeCharts?: [HomeChartId, HomeChartId]
|
homeCharts?: [HomeChartId, HomeChartId]
|
||||||
specialProductsSource?: 'product' | 'store_item'
|
specialProductsSource?: 'product' | 'store_item'
|
||||||
logoUrl?: string | null
|
logoUrl?: string | null
|
||||||
|
logoDarkUrl?: string | null
|
||||||
faviconUrl?: string | null
|
faviconUrl?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
textarea.aiPromptTextarea:focus {
|
textarea.aiPromptTextarea:focus {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.94)),
|
linear-gradient(var(--elevated-surface), var(--elevated-surface)),
|
||||||
linear-gradient(
|
linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
#ff375f 0%,
|
#ff375f 0%,
|
||||||
|
|||||||
@@ -4,6 +4,25 @@ import {
|
|||||||
type BusinessPrimaryColorId,
|
type BusinessPrimaryColorId,
|
||||||
} from './businessPrimaryColors'
|
} from './businessPrimaryColors'
|
||||||
|
|
||||||
|
export type DashboardThemeMode = 'light' | 'dark'
|
||||||
|
|
||||||
|
export function applyThemeMode(mode: 'light' | 'dark' | undefined) {
|
||||||
|
document.documentElement.setAttribute(
|
||||||
|
'data-theme',
|
||||||
|
mode === 'dark' ? 'dark' : 'light',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pickThemedLogoUrl(
|
||||||
|
lightUrl: string | null | undefined,
|
||||||
|
darkUrl: string | null | undefined,
|
||||||
|
mode: DashboardThemeMode,
|
||||||
|
): string | null {
|
||||||
|
const light = lightUrl?.trim() || null
|
||||||
|
const dark = darkUrl?.trim() || null
|
||||||
|
return mode === 'dark' ? dark || light : light
|
||||||
|
}
|
||||||
|
|
||||||
const CSS_VAR_DEFAULTS: Record<string, string> = {
|
const CSS_VAR_DEFAULTS: Record<string, string> = {
|
||||||
'--primary': '#3b82f6',
|
'--primary': '#3b82f6',
|
||||||
'--primary-glow': '#3b82f6',
|
'--primary-glow': '#3b82f6',
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ html[data-theme='dark'] {
|
|||||||
|
|
||||||
--card-hover-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
--card-hover-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
/* Icon chip: clear primary tint on dark gray (not muddy page wash) */
|
/* Icon chip: dark gray tile, slight primary tint — not a light wash */
|
||||||
--icon-bg: rgba(var(--primary-rgb) / 0.22);
|
--icon-bg: color-mix(in srgb, var(--primary) 22%, #2f333c);
|
||||||
--icon-bg-end: rgba(var(--primary-rgb) / 0.12);
|
--icon-bg-end: color-mix(in srgb, var(--primary) 12%, #25282f);
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme='dark'] select {
|
html[data-theme='dark'] select {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { AlertTriangle, X } from 'lucide-react'
|
import { AlertTriangle, X } from 'lucide-react'
|
||||||
|
import { lockBodyScroll, unlockBodyScroll } from '../utils/lockBodyScroll'
|
||||||
import styles from './ConfirmDeleteModal.module.css'
|
import styles from './ConfirmDeleteModal.module.css'
|
||||||
|
|
||||||
interface ConfirmDeleteModalProps {
|
interface ConfirmDeleteModalProps {
|
||||||
@@ -22,6 +23,8 @@ export function ConfirmDeleteModal({
|
|||||||
}: ConfirmDeleteModalProps) {
|
}: ConfirmDeleteModalProps) {
|
||||||
const [mounted, setMounted] = useState(open)
|
const [mounted, setMounted] = useState(open)
|
||||||
const [closing, setClosing] = useState(false)
|
const [closing, setClosing] = useState(false)
|
||||||
|
const onCancelRef = useRef(onCancel)
|
||||||
|
onCancelRef.current = onCancel
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
@@ -40,16 +43,15 @@ export function ConfirmDeleteModal({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mounted || closing) return
|
if (!mounted || closing) return
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') onCancel()
|
if (e.key === 'Escape') onCancelRef.current()
|
||||||
}
|
}
|
||||||
const prevOverflow = document.body.style.overflow
|
lockBodyScroll()
|
||||||
document.body.style.overflow = 'hidden'
|
|
||||||
document.addEventListener('keydown', onKey)
|
document.addEventListener('keydown', onKey)
|
||||||
return () => {
|
return () => {
|
||||||
document.body.style.overflow = prevOverflow
|
unlockBodyScroll()
|
||||||
document.removeEventListener('keydown', onKey)
|
document.removeEventListener('keydown', onKey)
|
||||||
}
|
}
|
||||||
}, [mounted, closing, onCancel])
|
}, [mounted, closing])
|
||||||
|
|
||||||
if (!mounted) return null
|
if (!mounted) return null
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { X } from 'lucide-react'
|
import { X } from 'lucide-react'
|
||||||
|
import { lockBodyScroll, unlockBodyScroll } from '../utils/lockBodyScroll'
|
||||||
import styles from './Modal.module.css'
|
import styles from './Modal.module.css'
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
@@ -14,19 +15,21 @@ interface ModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Modal({ open, title, children, onClose, wide, xl }: ModalProps) {
|
export function Modal({ open, title, children, onClose, wide, xl }: ModalProps) {
|
||||||
|
const onCloseRef = useRef(onClose)
|
||||||
|
onCloseRef.current = onClose
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return
|
if (!open) return
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') onClose()
|
if (e.key === 'Escape') onCloseRef.current()
|
||||||
}
|
}
|
||||||
const prevOverflow = document.body.style.overflow
|
lockBodyScroll()
|
||||||
document.body.style.overflow = 'hidden'
|
|
||||||
document.addEventListener('keydown', onKey)
|
document.addEventListener('keydown', onKey)
|
||||||
return () => {
|
return () => {
|
||||||
document.body.style.overflow = prevOverflow
|
unlockBodyScroll()
|
||||||
document.removeEventListener('keydown', onKey)
|
document.removeEventListener('keydown', onKey)
|
||||||
}
|
}
|
||||||
}, [open, onClose])
|
}, [open])
|
||||||
|
|
||||||
if (!open) return null
|
if (!open) return null
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,42 @@
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(148, 163, 184, 0.45);
|
background: rgba(148, 163, 184, 0.45);
|
||||||
|
overflow: hidden;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch::before,
|
||||||
|
.switch::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-family: var(--font-en), var(--font-ui);
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::before {
|
||||||
|
content: '1';
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch::after {
|
||||||
|
content: '0';
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.on {
|
.switch.on {
|
||||||
background: #16a34a;
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch.on::before,
|
||||||
|
.switch.on::after {
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumb {
|
.thumb {
|
||||||
@@ -19,9 +49,10 @@
|
|||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: white;
|
background: #fff;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch.on .thumb {
|
.switch.on .thumb {
|
||||||
|
|||||||
@@ -576,6 +576,44 @@
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.renewalDaysRow {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.renewalRefreshBtn {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.renewalRefreshBtn:hover:not(:disabled) {
|
||||||
|
background: rgba(var(--primary-rgb) / 0.1);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.renewalRefreshBtn:disabled {
|
||||||
|
opacity: 0.55;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.renewalRefreshSpin {
|
||||||
|
animation: renewalRefreshSpin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes renewalRefreshSpin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.renewalOk {
|
.renewalOk {
|
||||||
color: #15803d;
|
color: #15803d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Lock,
|
Lock,
|
||||||
Pencil,
|
Pencil,
|
||||||
Plus,
|
Plus,
|
||||||
|
RefreshCw,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Search,
|
Search,
|
||||||
Trash2,
|
Trash2,
|
||||||
@@ -112,28 +113,43 @@ function emptyModuleSelection(
|
|||||||
) as Record<BusinessModuleId, boolean>
|
) as Record<BusinessModuleId, boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Next anniversary of the contract date (renews every year). */
|
function startOfLocalDay(d: Date) {
|
||||||
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Anniversary of the contract date. Does not roll a missed date into next year. */
|
||||||
function nextAnnualRenewal(contractAt: string, now = new Date()) {
|
function nextAnnualRenewal(contractAt: string, now = new Date()) {
|
||||||
const contract = new Date(contractAt)
|
const contract = new Date(contractAt)
|
||||||
if (Number.isNaN(contract.getTime())) return null
|
if (Number.isNaN(contract.getTime())) return null
|
||||||
|
|
||||||
const startToday = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
const startToday = startOfLocalDay(now)
|
||||||
let next = new Date(
|
const contractDay = startOfLocalDay(contract)
|
||||||
startToday.getFullYear(),
|
|
||||||
contract.getMonth(),
|
const due =
|
||||||
contract.getDate(),
|
contractDay.getTime() > startToday.getTime()
|
||||||
)
|
? contractDay
|
||||||
if (next.getTime() < startToday.getTime()) {
|
: new Date(
|
||||||
next = new Date(
|
startToday.getFullYear(),
|
||||||
startToday.getFullYear() + 1,
|
contract.getMonth(),
|
||||||
contract.getMonth(),
|
contract.getDate(),
|
||||||
contract.getDate(),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
const daysRemaining = Math.round(
|
const daysRemaining = Math.round(
|
||||||
(next.getTime() - startToday.getTime()) / (24 * 60 * 60 * 1000),
|
(due.getTime() - startToday.getTime()) / (24 * 60 * 60 * 1000),
|
||||||
)
|
)
|
||||||
return { next, year: next.getFullYear(), daysRemaining }
|
return { next: due, year: due.getFullYear(), daysRemaining }
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneYearFromTodayIso(from = new Date()) {
|
||||||
|
const next = new Date(
|
||||||
|
from.getFullYear() + 1,
|
||||||
|
from.getMonth(),
|
||||||
|
from.getDate(),
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
return next.toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDateInputValue(iso: string | null | undefined): string {
|
function toDateInputValue(iso: string | null | undefined): string {
|
||||||
@@ -165,12 +181,18 @@ function formatRenewalCell(
|
|||||||
if (!next) {
|
if (!next) {
|
||||||
return { dateLabel: '—', daysLabel: null, tone: null }
|
return { dateLabel: '—', daysLabel: null, tone: null }
|
||||||
}
|
}
|
||||||
|
const overdue = next.daysRemaining < 0
|
||||||
|
const absDays = Math.abs(next.daysRemaining)
|
||||||
const daysLabel =
|
const daysLabel =
|
||||||
next.daysRemaining === 0
|
next.daysRemaining === 0
|
||||||
? 'Renews today'
|
? 'Renews today'
|
||||||
: next.daysRemaining === 1
|
: overdue
|
||||||
? '1 day left'
|
? absDays === 1
|
||||||
: `${next.daysRemaining} days left`
|
? '1 day past'
|
||||||
|
: `${absDays} days past`
|
||||||
|
: next.daysRemaining === 1
|
||||||
|
? '1 day left'
|
||||||
|
: `${next.daysRemaining} days left`
|
||||||
return {
|
return {
|
||||||
dateLabel: next.next.toLocaleDateString('en-US', {
|
dateLabel: next.next.toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
@@ -178,7 +200,7 @@ function formatRenewalCell(
|
|||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
}),
|
}),
|
||||||
daysLabel,
|
daysLabel,
|
||||||
tone: renewalTone(next.daysRemaining),
|
tone: overdue ? ('danger' as const) : renewalTone(next.daysRemaining),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +382,7 @@ export function BusinessesPage() {
|
|||||||
const [removeTarget, setRemoveTarget] = useState<BusinessListItem | null>(null)
|
const [removeTarget, setRemoveTarget] = useState<BusinessListItem | null>(null)
|
||||||
const [togglingId, setTogglingId] = useState<string | null>(null)
|
const [togglingId, setTogglingId] = useState<string | null>(null)
|
||||||
const [savingColorId, setSavingColorId] = useState<string | null>(null)
|
const [savingColorId, setSavingColorId] = useState<string | null>(null)
|
||||||
|
const [savingRenewalId, setSavingRenewalId] = useState<string | null>(null)
|
||||||
const [savingLocaleId, setSavingLocaleId] = useState<string | null>(null)
|
const [savingLocaleId, setSavingLocaleId] = useState<string | null>(null)
|
||||||
|
|
||||||
const [modulesOpen, setModulesOpen] = useState(false)
|
const [modulesOpen, setModulesOpen] = useState(false)
|
||||||
@@ -861,14 +884,42 @@ export function BusinessesPage() {
|
|||||||
setEditOpen(false)
|
setEditOpen(false)
|
||||||
setEditBusiness(null)
|
setEditBusiness(null)
|
||||||
showToast('Business updated.', 'success')
|
showToast('Business updated.', 'success')
|
||||||
await fetchList()
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setEditError(err instanceof ApiError ? err.message : 'Unable to update business.')
|
setEditError(
|
||||||
|
err instanceof ApiError ? err.message : 'Unable to update business.',
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
setEditSubmitting(false)
|
setEditSubmitting(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function restartRenewalCountdown(b: BusinessListItem) {
|
||||||
|
const annualRenewalAt = oneYearFromTodayIso()
|
||||||
|
setSavingRenewalId(b.id)
|
||||||
|
try {
|
||||||
|
await updateBusiness(b.id, { annualRenewalAt })
|
||||||
|
setData((prev) => {
|
||||||
|
if (!prev) return prev
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
items: prev.items.map((item) =>
|
||||||
|
item.id === b.id ? { ...item, annualRenewalAt } : item,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
showToast(`Renewal countdown restarted for "${b.name}".`, 'success')
|
||||||
|
} catch (err) {
|
||||||
|
showToast(
|
||||||
|
err instanceof ApiError
|
||||||
|
? err.message
|
||||||
|
: 'Unable to restart renewal countdown.',
|
||||||
|
'error',
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
setSavingRenewalId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openDomainEdit(b: BusinessListItem) {
|
function openDomainEdit(b: BusinessListItem) {
|
||||||
setDomainBusiness(b)
|
setDomainBusiness(b)
|
||||||
setDomainId(b.domainId)
|
setDomainId(b.domainId)
|
||||||
@@ -1457,17 +1508,37 @@ export function BusinessesPage() {
|
|||||||
<div className={styles.renewalCell}>
|
<div className={styles.renewalCell}>
|
||||||
<span className={styles.renewalDate}>{renewal.dateLabel}</span>
|
<span className={styles.renewalDate}>{renewal.dateLabel}</span>
|
||||||
{renewal.daysLabel ? (
|
{renewal.daysLabel ? (
|
||||||
<span
|
<div className={styles.renewalDaysRow}>
|
||||||
className={`${styles.renewalDays} ${
|
<span
|
||||||
renewal.tone === 'ok'
|
className={`${styles.renewalDays} ${
|
||||||
? styles.renewalOk
|
renewal.tone === 'ok'
|
||||||
: renewal.tone === 'warn'
|
? styles.renewalOk
|
||||||
? styles.renewalWarn
|
: renewal.tone === 'warn'
|
||||||
: styles.renewalDanger
|
? styles.renewalWarn
|
||||||
}`}
|
: styles.renewalDanger
|
||||||
>
|
}`}
|
||||||
{renewal.daysLabel}
|
>
|
||||||
</span>
|
{renewal.daysLabel}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.renewalRefreshBtn}
|
||||||
|
onClick={() => void restartRenewalCountdown(b)}
|
||||||
|
disabled={savingRenewalId === b.id}
|
||||||
|
aria-label={`Restart renewal countdown for ${b.name}`}
|
||||||
|
title="Restart countdown"
|
||||||
|
>
|
||||||
|
<RefreshCw
|
||||||
|
size={12}
|
||||||
|
aria-hidden="true"
|
||||||
|
className={
|
||||||
|
savingRenewalId === b.id
|
||||||
|
? styles.renewalRefreshSpin
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/** Nested-modal-safe body scroll lock. Restoring a captured overflow value
|
||||||
|
* leaves `overflow: hidden` on body after overlapping dialogs close. */
|
||||||
|
let lockCount = 0
|
||||||
|
|
||||||
|
export function lockBodyScroll() {
|
||||||
|
lockCount += 1
|
||||||
|
if (lockCount === 1) {
|
||||||
|
document.documentElement.style.overflow = 'hidden'
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unlockBodyScroll() {
|
||||||
|
lockCount = Math.max(0, lockCount - 1)
|
||||||
|
if (lockCount === 0) {
|
||||||
|
document.documentElement.style.overflow = ''
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
> **For AI agents:** Read this file at the start of a new chat before making changes.
|
> **For AI agents:** Read this file at the start of a new chat before making changes.
|
||||||
> Update this document when a major feature is completed or architecture changes.
|
> Update this document when a major feature is completed or architecture changes.
|
||||||
|
|
||||||
Last updated: August 21, 2026
|
Last updated: August 31, 2026
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ Match **Super Admin** compact field sizing across all dashboards — see `.curso
|
|||||||
Global input sizing lives in `packages/dashboard-core/src/styles/tokens.css` (customer) and each app’s `index.css` (business / super-admin). Change password → shared `PasswordResetModal` from `@meshkee/dashboard-ui`.
|
Global input sizing lives in `packages/dashboard-core/src/styles/tokens.css` (customer) and each app’s `index.css` (business / super-admin). Change password → shared `PasswordResetModal` from `@meshkee/dashboard-ui`.
|
||||||
|
|
||||||
- **Farsi text:** Customer app → Yekan Bakh (`src/fonts/yekanbakh.css`); business/super-admin → IranYekan (`iranyekan.css`). `--font-en` / `--font-fa` are named faces only; `--font-ui` appends system fallbacks. Self-host files in `public/fonts/` — see `.cursor/rules/ui-farsi-fonts.mdc`
|
- **Farsi text:** Customer app → Yekan Bakh (`src/fonts/yekanbakh.css`); business/super-admin → IranYekan (`iranyekan.css`). `--font-en` / `--font-fa` are named faces only; `--font-ui` appends system fallbacks. Self-host files in `public/fonts/` — see `.cursor/rules/ui-farsi-fonts.mdc`
|
||||||
- **Per-business theme:** Super Admin sets `branding.primaryColor`; business app applies via `BusinessThemeProvider`
|
- **Per-business theme:** Super Admin sets `branding.primaryColor` and `branding.themeMode` (`light` \| `dark`); business + customer dashboards apply both via their theme providers (`data-theme` on `<html>`). Dark mode uses neutral gray surfaces; `--primary` stays for accents only.
|
||||||
- **Per-business modules:** Super Admin **Modules** column on Businesses list; stored in `settings.modules.enabled` (business + customer sections); business Home + Sidebar hide disabled business modules; `customer_products` gates customer My Products (`/my-products`, `/my-products/new`) which call `businesses/:id/my-user-products`
|
- **Per-business modules:** Super Admin **Modules** column on Businesses list; stored in `settings.modules.enabled` (business + customer sections); business Home + Sidebar hide disabled business modules; `customer_products` gates customer My Products (`/my-products`, `/my-products/new`) which call `businesses/:id/my-user-products`
|
||||||
- **RTL:** Farsi inputs use `dir="rtl"` and the app’s `--font-fa`
|
- **RTL:** Farsi inputs use `dir="rtl"` and the app’s `--font-fa`
|
||||||
- **IRT prices:** comma-separated thousands + `IRT` suffix — see `.cursor/rules/ui-irt-price.mdc`
|
- **IRT prices:** comma-separated thousands + `IRT` suffix — see `.cursor/rules/ui-irt-price.mdc`
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectField {
|
.selectField {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 440px;
|
max-width: 440px;
|
||||||
padding: 28px 28px 24px;
|
padding: 28px 28px 24px;
|
||||||
background: rgba(255, 255, 255, 0.88);
|
background: var(--elevated-surface);
|
||||||
backdrop-filter: blur(28px);
|
backdrop-filter: blur(28px);
|
||||||
-webkit-backdrop-filter: blur(28px);
|
-webkit-backdrop-filter: blur(28px);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: var(--surface);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -101,12 +101,24 @@
|
|||||||
color: #b91c1c;
|
color: #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme='dark']) .error {
|
||||||
|
color: #fecaca;
|
||||||
|
background: rgba(239, 68, 68, 0.16);
|
||||||
|
border-color: rgba(248, 113, 113, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
.success {
|
.success {
|
||||||
background: rgba(34, 197, 94, 0.08);
|
background: rgba(34, 197, 94, 0.08);
|
||||||
border: 1px solid rgba(34, 197, 94, 0.25);
|
border: 1px solid rgba(34, 197, 94, 0.25);
|
||||||
color: #15803d;
|
color: #15803d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme='dark']) .success {
|
||||||
|
color: #bbf7d0;
|
||||||
|
background: rgba(34, 197, 94, 0.16);
|
||||||
|
border-color: rgba(74, 222, 128, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: var(--surface);
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
gap: 2px;
|
gap: 2px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
border-bottom: 1px solid rgba(148, 163, 184, 0.2);
|
border-bottom: 1px solid rgba(148, 163, 184, 0.2);
|
||||||
background: rgba(248, 250, 252, 0.8);
|
background: var(--elevated-surface);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.98);
|
background: var(--elevated-surface);
|
||||||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
padding: var(--field-padding-y) var(--field-padding-x);
|
padding: var(--field-padding-y) var(--field-padding-x);
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
border: 1px solid rgba(148, 163, 184, 0.35);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: var(--elevated-surface);
|
||||||
font-size: var(--field-font-size);
|
font-size: var(--field-font-size);
|
||||||
font-family: var(--font-ui);
|
font-family: var(--font-ui);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: var(--toast-bg, rgba(255, 255, 255, 0.94));
|
background: var(--toast-bg, var(--elevated-surface));
|
||||||
backdrop-filter: blur(var(--blur-glass));
|
backdrop-filter: blur(var(--blur-glass));
|
||||||
-webkit-backdrop-filter: blur(var(--blur-glass));
|
-webkit-backdrop-filter: blur(var(--blur-glass));
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
Reference in New Issue
Block a user