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:
Alireza Hassani
2026-08-31 13:40:40 +03:30
co-authored by Cursor
parent 65012717d0
commit e9400c2281
83 changed files with 586 additions and 193 deletions
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { AlertTriangle, X } from 'lucide-react'
import { lockBodyScroll, unlockBodyScroll } from '../utils/lockBodyScroll'
import styles from './ConfirmDeleteModal.module.css'
interface ConfirmDeleteModalProps {
@@ -22,6 +23,8 @@ export function ConfirmDeleteModal({
}: ConfirmDeleteModalProps) {
const [mounted, setMounted] = useState(open)
const [closing, setClosing] = useState(false)
const onCancelRef = useRef(onCancel)
onCancelRef.current = onCancel
useEffect(() => {
if (open) {
@@ -40,16 +43,15 @@ export function ConfirmDeleteModal({
useEffect(() => {
if (!mounted || closing) return
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') onCancel()
if (e.key === 'Escape') onCancelRef.current()
}
const prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
lockBodyScroll()
document.addEventListener('keydown', onKey)
return () => {
document.body.style.overflow = prevOverflow
unlockBodyScroll()
document.removeEventListener('keydown', onKey)
}
}, [mounted, closing, onCancel])
}, [mounted, closing])
if (!mounted) return null
+9 -6
View File
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { createPortal } from 'react-dom'
import { X } from 'lucide-react'
import { lockBodyScroll, unlockBodyScroll } from '../utils/lockBodyScroll'
import styles from './Modal.module.css'
interface ModalProps {
@@ -14,19 +15,21 @@ interface ModalProps {
}
export function Modal({ open, title, children, onClose, wide, xl }: ModalProps) {
const onCloseRef = useRef(onClose)
onCloseRef.current = onClose
useEffect(() => {
if (!open) return
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose()
if (e.key === 'Escape') onCloseRef.current()
}
const prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
lockBodyScroll()
document.addEventListener('keydown', onKey)
return () => {
document.body.style.overflow = prevOverflow
unlockBodyScroll()
document.removeEventListener('keydown', onKey)
}
}, [open, onClose])
}, [open])
if (!open) return null
@@ -4,12 +4,42 @@
height: 22px;
border-radius: 999px;
background: rgba(148, 163, 184, 0.45);
overflow: hidden;
transition: background 0.2s;
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 {
background: #16a34a;
background: var(--primary);
}
.switch.on::before,
.switch.on::after {
color: #fff;
}
.thumb {
@@ -19,9 +49,10 @@
width: 18px;
height: 18px;
border-radius: 50%;
background: white;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
transition: transform 0.2s;
z-index: 1;
}
.switch.on .thumb {