Implement comprehensive dark/light mode overhaul for admin panel
- Refactor color palette in `ui-design-spec.md` to utilize CSS variables exclusively, eliminating fixed hex values and Tailwind utility classes. - Complete dark mode implementation in `uiStore.ts`, ensuring proper theme application via `applyTheme()` and `applyBrand()`. - Create `admin-theme-dark-light-audit.md` to document the transition process, outlining issues with inline styles and fixed colors. - Introduce `theme-tokens.test.ts` to enforce rules against fixed hex colors and ensure compliance with the design system. - Update various components and styles to replace inline styles and fixed colors with CSS variables, ensuring consistent theming across light and dark modes. - Ensure all changes maintain visual integrity in both light and dark modes, with a focus on accessibility and contrast standards.
This commit is contained in:
@@ -16,23 +16,23 @@ import type { Secretary, SecretaryPermissions } from "../types";
|
||||
|
||||
const PlusIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M5 10H15" stroke="#EFEFEF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 15V5" stroke="#EFEFEF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5 10H15" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--on-primary)' }}/>
|
||||
<path d="M10 15V5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--on-primary)' }}/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const EyeIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M12.9833 9.99993C12.9833 11.6499 11.6499 12.9833 9.99993 12.9833C8.34993 12.9833 7.0166 11.6499 7.0166 9.99993C7.0166 8.34993 8.34993 7.0166 9.99993 7.0166C11.6499 7.0166 12.9833 8.34993 12.9833 9.99993Z" stroke="#616161" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9.99987 16.8918C12.9415 16.8918 15.6832 15.1584 17.5915 12.1584C18.3415 10.9834 18.3415 9.00843 17.5915 7.83343C15.6832 4.83343 12.9415 3.1001 9.99987 3.1001C7.0582 3.1001 4.31654 4.83343 2.4082 7.83343C1.6582 9.00843 1.6582 10.9834 2.4082 12.1584C4.31654 15.1584 7.0582 16.8918 9.99987 16.8918Z" stroke="#616161" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M12.9833 9.99993C12.9833 11.6499 11.6499 12.9833 9.99993 12.9833C8.34993 12.9833 7.0166 11.6499 7.0166 9.99993C7.0166 8.34993 8.34993 7.0166 9.99993 7.0166C11.6499 7.0166 12.9833 8.34993 12.9833 9.99993Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--text-2)' }}/>
|
||||
<path d="M9.99987 16.8918C12.9415 16.8918 15.6832 15.1584 17.5915 12.1584C18.3415 10.9834 18.3415 9.00843 17.5915 7.83343C15.6832 4.83343 12.9415 3.1001 9.99987 3.1001C7.0582 3.1001 4.31654 4.83343 2.4082 7.83343C1.6582 9.00843 1.6582 10.9834 2.4082 12.1584C4.31654 15.1584 7.0582 16.8918 9.99987 16.8918Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--text-2)' }}/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const EditIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M11.0504 3.00002L4.20878 10.2417C3.95045 10.5167 3.70045 11.0584 3.65045 11.4334L3.34211 14.1334C3.23378 15.1084 3.93378 15.775 4.90045 15.6084L7.58378 15.15C7.95878 15.0834 8.48378 14.8084 8.74211 14.525L15.5838 7.28335C16.7671 6.03335 17.3004 4.60835 15.4588 2.86668C13.6254 1.14168 12.2338 1.75002 11.0504 3.00002Z" stroke="#616161" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9.9082 4.2085C10.2665 6.5085 12.1332 8.26683 14.4499 8.50016" stroke="#616161" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M2.5 18.3335H17.5" stroke="#616161" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M11.0504 3.00002L4.20878 10.2417C3.95045 10.5167 3.70045 11.0584 3.65045 11.4334L3.34211 14.1334C3.23378 15.1084 3.93378 15.775 4.90045 15.6084L7.58378 15.15C7.95878 15.0834 8.48378 14.8084 8.74211 14.525L15.5838 7.28335C16.7671 6.03335 17.3004 4.60835 15.4588 2.86668C13.6254 1.14168 12.2338 1.75002 11.0504 3.00002Z" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--text-2)' }}/>
|
||||
<path d="M9.9082 4.2085C10.2665 6.5085 12.1332 8.26683 14.4499 8.50016" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--text-2)' }}/>
|
||||
<path d="M2.5 18.3335H17.5" strokeWidth="1.5" strokeMiterlimit="10" strokeLinecap="round" strokeLinejoin="round" style={{ stroke: 'var(--text-2)' }}/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ const ChevronDown = ({ open }: { open: boolean }) => (
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="text-[#616161] dark:text-[#A1A1A1] transition-transform"
|
||||
className="text-[var(--text-2)] transition-transform"
|
||||
style={{ transform: open ? "rotate(180deg)" : "rotate(0deg)" }}
|
||||
>
|
||||
<path d="M6 9L12 15L18 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
@@ -56,7 +56,7 @@ function Avatar({ name, size = 32 }: { name?: string; size?: number }) {
|
||||
const firstLetter = (name?.trim().charAt(0) || "?").toUpperCase();
|
||||
return (
|
||||
<div
|
||||
className="rounded-full flex items-center justify-center bg-gradient-to-br from-[#5559CE] to-[#7B7FE8] text-white font-bold flex-shrink-0"
|
||||
className="rounded-full flex items-center justify-center bg-gradient-to-br from-[var(--primary)] to-[var(--primary)] text-[var(--on-primary)] font-bold flex-shrink-0"
|
||||
style={{ width: size, height: size, fontSize: size / 2 }}
|
||||
>
|
||||
{firstLetter}
|
||||
@@ -268,7 +268,7 @@ function PermissionAccordions({
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<p className="text-[#525252] dark:text-[#D7D8ED] text-[16px] font-bold mb-[16px]">
|
||||
<p className="text-[var(--text)] text-[16px] font-bold mb-[16px]">
|
||||
مجوزهای دسترسی
|
||||
</p>
|
||||
<div className="flex flex-col gap-[8px] w-full items-stretch">
|
||||
@@ -278,14 +278,14 @@ function PermissionAccordions({
|
||||
return (
|
||||
<div
|
||||
key={section.key}
|
||||
className="w-full border border-[#DBDBDB] dark:border-[#343645] rounded-[8px] overflow-hidden"
|
||||
className="w-full border border-[var(--border-2)] rounded-[8px] overflow-hidden"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleOpen(section.key)}
|
||||
className="w-full flex items-center justify-between px-[16px] min-h-[64px] cursor-pointer"
|
||||
>
|
||||
<p className="text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">
|
||||
<p className="text-[var(--text)] text-[14px] font-medium">
|
||||
{section.title}
|
||||
</p>
|
||||
<ChevronDown open={open} />
|
||||
@@ -306,9 +306,9 @@ function PermissionAccordions({
|
||||
onChange(section.key, item.key, e.target.checked)
|
||||
}
|
||||
className="w-[20px] h-[20px] shrink-0"
|
||||
style={{ accentColor: "#5559CE", cursor: "pointer" }}
|
||||
style={{ accentColor: "var(--primary)", cursor: "pointer" }}
|
||||
/>
|
||||
<span className="text-[#616161] dark:text-[#A1A1A1] text-[14px]">
|
||||
<span className="text-[var(--text-2)] text-[14px]">
|
||||
{item.label}
|
||||
</span>
|
||||
</label>
|
||||
@@ -347,9 +347,9 @@ function DefaultTextField({
|
||||
maxDigits?: number;
|
||||
}) {
|
||||
const cls =
|
||||
"w-full bg-[#FAFAFA] dark:bg-[#222433] rounded-[8px] border border-[#D7D7D7] dark:border-[#343645] " +
|
||||
"text-[#7E7E7E] dark:text-[#D7D8ED] text-[16px] font-normal px-[12px] py-[12.5px] outline-none " +
|
||||
"focus:border-[#5559CE] disabled:opacity-70";
|
||||
"w-full bg-[var(--bg)] dark:bg-[var(--surface)] rounded-[8px] border border-[var(--border-2)] dark:border-[var(--border)] " +
|
||||
"text-[var(--text-2)] dark:text-[var(--text)] text-[16px] font-normal px-[12px] py-[12.5px] outline-none " +
|
||||
"focus:border-[var(--primary)] disabled:opacity-70";
|
||||
if (multiline) {
|
||||
return (
|
||||
<textarea
|
||||
@@ -493,7 +493,7 @@ function SecretaryModal({
|
||||
<>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="border border-[#5559CE] dark:border-transparent dark:bg-[#C7CEF4] text-[#5559CE] dark:text-[#222433]
|
||||
className="border border-[var(--primary)] dark:border-transparent dark:bg-[var(--primary-soft2)] text-[var(--primary)] dark:text-[var(--surface)]
|
||||
h-[40px] md:h-[44px] lg:h-[48px] py-[11px] px-[48px] rounded-[4px] cursor-pointer"
|
||||
>
|
||||
{mode === "view" ? "بستن" : "انصراف"}
|
||||
@@ -502,7 +502,7 @@ function SecretaryModal({
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={saving}
|
||||
className="bg-[#5559CE] text-[#EFEFEF] rounded-[4px] h-[40px] md:h-[44px] lg:h-[48px] py-[11px] px-[48px] cursor-pointer disabled:opacity-60"
|
||||
className="bg-[var(--primary)] text-[var(--on-primary)] rounded-[4px] h-[40px] md:h-[44px] lg:h-[48px] py-[11px] px-[48px] cursor-pointer disabled:opacity-60"
|
||||
>
|
||||
{saving ? "در حال ذخیره..." : "ذخیره"}
|
||||
</button>
|
||||
@@ -515,12 +515,12 @@ function SecretaryModal({
|
||||
{showDoctorPicker && (
|
||||
<div className="w-full">
|
||||
<div className="flex items-center justify-between mb-[16px]">
|
||||
<p className="text-[#525252] dark:text-[#D7D8ED] text-[16px] font-bold">
|
||||
<p className="text-[var(--text)] text-[16px] font-bold">
|
||||
پزشکانِ این منشی
|
||||
</p>
|
||||
<span className="text-[13px] text-[#7E7E7E]">{doctorUuids.length} انتخابشده</span>
|
||||
<span className="text-[13px] text-[var(--text-2)]">{doctorUuids.length} انتخابشده</span>
|
||||
</div>
|
||||
<p className="text-[13px] text-[#7E7E7E] mb-[12px]">
|
||||
<p className="text-[13px] text-[var(--text-2)] mb-[12px]">
|
||||
منشی فقط به نوبتها و اطلاعاتِ پزشکانِ انتخابشده دسترسی خواهد داشت.
|
||||
</p>
|
||||
<DoctorMultiSelect doctors={clinicDoctors} selected={doctorUuids} onChange={setDoctorUuids} />
|
||||
@@ -529,16 +529,16 @@ function SecretaryModal({
|
||||
|
||||
{/* اطلاعات پایه */}
|
||||
<div className="w-full">
|
||||
<p className="text-[#525252] dark:text-[#D7D8ED] text-[16px] font-bold mb-[16px]">
|
||||
<p className="text-[var(--text)] text-[16px] font-bold mb-[16px]">
|
||||
اطلاعات پایه
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[16px]">
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[12px]">
|
||||
<p className="w-full text-right text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">نام</p>
|
||||
<p className="w-full text-right text-[var(--text)] text-[14px] font-medium">نام</p>
|
||||
<DefaultTextField placeholder="نام" value={form.name} onChange={(v) => setField("name", v)} disabled={disabled} />
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[12px]">
|
||||
<p className="w-full text-right text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">نام خانوادگی</p>
|
||||
<p className="w-full text-right text-[var(--text)] text-[14px] font-medium">نام خانوادگی</p>
|
||||
<DefaultTextField placeholder="نام خانوادگی" value={form.family} onChange={(v) => setField("family", v)} disabled={disabled} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -546,17 +546,17 @@ function SecretaryModal({
|
||||
|
||||
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-[16px]">
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[12px]">
|
||||
<p className="w-full text-right text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">شماره موبایل</p>
|
||||
<p className="w-full text-right text-[var(--text)] text-[14px] font-medium">شماره موبایل</p>
|
||||
<DefaultTextField numeric maxDigits={11} placeholder="09121234567" value={form.telephone} onChange={(v) => setField("telephone", v)} disabled={disabled || mode === "edit"} />
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[12px]">
|
||||
<p className="w-full text-right text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">کد ملی</p>
|
||||
<p className="w-full text-right text-[var(--text)] text-[14px] font-medium">کد ملی</p>
|
||||
<DefaultTextField numeric maxDigits={10} placeholder="کد ملی" value={form.national_code} onChange={(v) => setField("national_code", v)} disabled={disabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[12px]">
|
||||
<p className="w-full text-right text-[#525252] dark:text-[#D7D8ED] text-[14px] font-medium">آدرس</p>
|
||||
<p className="w-full text-right text-[var(--text)] text-[14px] font-medium">آدرس</p>
|
||||
<DefaultTextField placeholder="آدرس" value={form.address} onChange={(v) => setField("address", v)} disabled={disabled} multiline rows={2} />
|
||||
</div>
|
||||
|
||||
@@ -636,15 +636,15 @@ function SecretaryTable({
|
||||
showDoctor: boolean;
|
||||
}) {
|
||||
const headCls =
|
||||
"text-[#616161] dark:text-[#D7D8ED] text-[14px] font-normal py-[10px] px-[18px] text-start";
|
||||
"text-[var(--text-2)] dark:text-[var(--text)] text-[14px] font-normal py-[10px] px-[18px] text-start";
|
||||
const cellCls =
|
||||
"text-[#616161] dark:text-[#A1A1A1] text-[16px] font-medium px-[18px] py-[10px] whitespace-nowrap";
|
||||
"text-[var(--text-2)] text-[16px] font-medium px-[18px] py-[10px] whitespace-nowrap";
|
||||
return (
|
||||
<div className="hidden lg:block mt-[24px]">
|
||||
<div className="rounded-[8px] overflow-hidden border border-solid border-[#E7E7E7] dark:border-[#35343D]">
|
||||
<div className="rounded-[8px] overflow-hidden border border-solid border-[var(--border)]">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-[#EFEFEF] dark:bg-[#35343D]">
|
||||
<tr className="bg-[var(--surface-2)]">
|
||||
<th className={headCls}>ردیف</th>
|
||||
<th className={headCls}>نام منشی</th>
|
||||
{showDoctor && <th className={headCls}>پزشک</th>}
|
||||
@@ -659,7 +659,7 @@ function SecretaryTable({
|
||||
{data.map((group, idx) => {
|
||||
const row = group.primary;
|
||||
return (
|
||||
<tr key={group.key} className="border-b border-[#DBDBDB] dark:border-[#343645]">
|
||||
<tr key={group.key} className="border-b border-[var(--border-2)]">
|
||||
<td className={cellCls}>{idx + 1}</td>
|
||||
<td className={cellCls}>
|
||||
<div className="flex items-center justify-start gap-[8px]">
|
||||
@@ -682,14 +682,14 @@ function SecretaryTable({
|
||||
{row.is_active ? (
|
||||
<button
|
||||
onClick={() => onDeactivate(group)}
|
||||
className="border border-[#E53935] text-[#E53935] hover:bg-[#FFEBEE] text-[12px] font-medium py-[6px] px-[16px] rounded-[4px] cursor-pointer"
|
||||
className="border border-[var(--danger)] text-[var(--danger)] hover:bg-[var(--danger-bg)] text-[12px] font-medium py-[6px] px-[16px] rounded-[4px] cursor-pointer"
|
||||
>
|
||||
لغو همکاری
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => onDeactivate(group)}
|
||||
className="border border-[#2E7D32] text-[#2E7D32] hover:bg-[#E8F5E9] text-[12px] font-medium py-[6px] px-[16px] rounded-[4px] cursor-pointer"
|
||||
className="border border-[var(--success)] text-[var(--success)] hover:bg-[var(--success-bg)] text-[12px] font-medium py-[6px] px-[16px] rounded-[4px] cursor-pointer"
|
||||
>
|
||||
فعالسازی
|
||||
</button>
|
||||
@@ -727,49 +727,49 @@ function SecretaryCards({
|
||||
return (
|
||||
<div
|
||||
key={group.key}
|
||||
className="bg-[#FFF] dark:bg-[#222433] dark:shadow-transparent rounded-[8px] shadow-[0px_1px_24.8px_0px_rgba(204,204,204,0.18)] p-[12px]"
|
||||
className="bg-[var(--surface)] dark:shadow-transparent rounded-[8px] shadow-[0px_1px_24.8px_0px_rgba(204,204,204,0.18)] p-[12px]"
|
||||
>
|
||||
<div className="flex items-center justify-start gap-[8px]">
|
||||
<Avatar name={item.user_name} size={32} />
|
||||
<p className="text-[#616161] dark:text-[#D7D8ED] text-[14px] font-medium">{item.user_name}</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-medium">{item.user_name}</p>
|
||||
</div>
|
||||
<div className="w-full mt-[16px]">
|
||||
{showDoctor && (
|
||||
<>
|
||||
<div className="flex items-start justify-between gap-[8px]">
|
||||
<p className="text-[#7E7E7E] dark:text-[#A1A1A1] text-[14px] font-normal shrink-0">پزشک:</p>
|
||||
<p className="text-[#616161] dark:text-[#A1A1A1] text-[14px] font-normal text-left">
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal shrink-0">پزشک:</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal text-left">
|
||||
{group.doctorNames.join("، ")}
|
||||
</p>
|
||||
</div>
|
||||
<span className="block w-full h-px bg-[#EFEFEF] dark:bg-[#343645] my-[8px]" />
|
||||
<span className="block w-full h-px bg-[var(--surface-2)] my-[8px]" />
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[#7E7E7E] dark:text-[#A1A1A1] text-[14px] font-normal">کدملی:</p>
|
||||
<p className="text-[#616161] dark:text-[#A1A1A1] text-[14px] font-normal">{item.national_code || "-"}</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal">کدملی:</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal">{item.national_code || "-"}</p>
|
||||
</div>
|
||||
<span className="block w-full h-px bg-[#EFEFEF] dark:bg-[#343645] my-[8px]" />
|
||||
<span className="block w-full h-px bg-[var(--surface-2)] my-[8px]" />
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[#7E7E7E] dark:text-[#A1A1A1] text-[14px] font-normal">تاریخ همکاری:</p>
|
||||
<p className="text-[#616161] dark:text-[#A1A1A1] text-[14px] font-normal">{formatDate(Number(item.created_at))}</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal">تاریخ همکاری:</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal">{formatDate(Number(item.created_at))}</p>
|
||||
</div>
|
||||
<span className="block w-full h-px bg-[#EFEFEF] dark:bg-[#343645] my-[8px]" />
|
||||
<span className="block w-full h-px bg-[var(--surface-2)] my-[8px]" />
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[#7E7E7E] dark:text-[#A1A1A1] text-[14px] font-normal">شماره تماس:</p>
|
||||
<p className="text-[#616161] dark:text-[#A1A1A1] text-[14px] font-normal" dir="ltr">{item.mobile_number}</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal">شماره تماس:</p>
|
||||
<p className="text-[var(--text-2)] text-[14px] font-normal" dir="ltr">{item.mobile_number}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-[16px]">
|
||||
<button
|
||||
onClick={() => onView(group)}
|
||||
className="text-[#5559CE] dark:bg-[#C7CEF4] gap-[5px] text-[14px] font-medium border border-[#5559CE] py-[8px] px-[12px] rounded-[4px] cursor-pointer flex items-center"
|
||||
className="text-[var(--primary)] dark:bg-[var(--primary-soft2)] gap-[5px] text-[14px] font-medium border border-[var(--primary)] py-[8px] px-[12px] rounded-[4px] cursor-pointer flex items-center"
|
||||
>
|
||||
<EyeIcon />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onEdit(group)}
|
||||
className="border border-[#EFEFEF] dark:border-[#343645] rounded-[4px] p-[7px] cursor-pointer"
|
||||
className="border border-[var(--border)] rounded-[4px] p-[7px] cursor-pointer"
|
||||
>
|
||||
<EditIcon />
|
||||
</button>
|
||||
@@ -778,8 +778,8 @@ function SecretaryCards({
|
||||
onClick={() => onDeactivate(group)}
|
||||
className={
|
||||
item.is_active
|
||||
? "w-full border border-[#E53935] text-[#E53935] hover:bg-[#FFEBEE] text-[14px] font-medium py-[8px] mt-[12px] rounded-[4px] cursor-pointer"
|
||||
: "w-full border border-[#2E7D32] text-[#2E7D32] hover:bg-[#E8F5E9] text-[14px] font-medium py-[8px] mt-[12px] rounded-[4px] cursor-pointer"
|
||||
? "w-full border border-[var(--danger)] text-[var(--danger)] hover:bg-[var(--danger-bg)] text-[14px] font-medium py-[8px] mt-[12px] rounded-[4px] cursor-pointer"
|
||||
: "w-full border border-[var(--success)] text-[var(--success)] hover:bg-[var(--success-bg)] text-[14px] font-medium py-[8px] mt-[12px] rounded-[4px] cursor-pointer"
|
||||
}
|
||||
>
|
||||
{item.is_active ? "لغو همکاری" : "فعالسازی"}
|
||||
@@ -816,22 +816,22 @@ function DoctorMultiSelect({
|
||||
const toggleAll = () => onChange(allSelected ? [] : doctors.map((d) => d.uuid));
|
||||
|
||||
return (
|
||||
<div className="w-full border border-[#EFEFEF] dark:border-[#343645] rounded-[8px] overflow-hidden">
|
||||
<div className="w-full border border-[var(--border)] rounded-[8px] overflow-hidden">
|
||||
{/* هدر: جستجو + انتخاب همه */}
|
||||
<div className="flex items-center gap-[8px] p-[10px] border-b border-[#EFEFEF] dark:border-[#343645] bg-[#FAFAFC] dark:bg-[#222433]">
|
||||
<div className="flex items-center gap-[8px] p-[10px] border-b border-[var(--border)] bg-[var(--surface)]">
|
||||
<svg width="16" height="16" viewBox="0 0 20 20" fill="none" className="flex-shrink-0">
|
||||
<path d="M9 16A7 7 0 109 2a7 7 0 000 14zM18 18l-3.5-3.5" stroke="#9A9AB0" strokeWidth="1.5" strokeLinecap="round" />
|
||||
<path d="M9 16A7 7 0 109 2a7 7 0 000 14zM18 18l-3.5-3.5" style={{ stroke: 'var(--text-3)' }} strokeWidth="1.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
<input
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
placeholder="جستجوی پزشک..."
|
||||
className="flex-1 text-[13px] bg-transparent outline-none text-[#525252] dark:text-[#D7D8ED]"
|
||||
className="flex-1 text-[13px] bg-transparent outline-none text-[var(--text)]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleAll}
|
||||
className="text-[12px] text-[#5559CE] font-medium whitespace-nowrap cursor-pointer"
|
||||
className="text-[12px] text-[var(--primary)] font-medium whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
{allSelected ? "لغو همه" : "انتخاب همه"}
|
||||
</button>
|
||||
@@ -839,7 +839,7 @@ function DoctorMultiSelect({
|
||||
|
||||
{/* چیپهای انتخابشده */}
|
||||
{selected.length > 0 && (
|
||||
<div className="flex flex-wrap gap-[6px] p-[10px] border-b border-[#EFEFEF] dark:border-[#343645]">
|
||||
<div className="flex flex-wrap gap-[6px] p-[10px] border-b border-[var(--border)]">
|
||||
{selected.map((uuid) => {
|
||||
const d = doctors.find((x) => x.uuid === uuid);
|
||||
if (!d) return null;
|
||||
@@ -847,7 +847,7 @@ function DoctorMultiSelect({
|
||||
<span
|
||||
key={uuid}
|
||||
onClick={() => toggle(uuid)}
|
||||
className="inline-flex items-center gap-[4px] bg-[#EEF0FF] dark:bg-[#33365A] text-[#5559CE] dark:text-[#C7CEF4] text-[12px] px-[8px] py-[3px] rounded-full cursor-pointer"
|
||||
className="inline-flex items-center gap-[4px] bg-[var(--primary-soft)] text-[var(--primary)] text-[12px] px-[8px] py-[3px] rounded-full cursor-pointer"
|
||||
>
|
||||
{d.name}
|
||||
<span className="text-[14px] leading-none">×</span>
|
||||
@@ -860,7 +860,7 @@ function DoctorMultiSelect({
|
||||
{/* لیست پزشکان */}
|
||||
<div className="max-h-[220px] overflow-y-auto">
|
||||
{filtered.length === 0 ? (
|
||||
<p className="text-center text-[12px] text-[#7E7E7E] py-[14px]">نتیجهای یافت نشد</p>
|
||||
<p className="text-center text-[12px] text-[var(--text-2)] py-[14px]">نتیجهای یافت نشد</p>
|
||||
) : (
|
||||
filtered.map((d) => {
|
||||
const checked = selected.includes(d.uuid);
|
||||
@@ -868,18 +868,18 @@ function DoctorMultiSelect({
|
||||
<label
|
||||
key={d.uuid}
|
||||
className={
|
||||
"flex items-center gap-[10px] px-[12px] py-[9px] cursor-pointer border-b border-[#F2F2F6] dark:border-[#2A2C3A] last:border-b-0 " +
|
||||
(checked ? "bg-[#F5F6FF] dark:bg-[#2A2D45]" : "hover:bg-[#FAFAFC] dark:hover:bg-[#2A2C3A]")
|
||||
"flex items-center gap-[10px] px-[12px] py-[9px] cursor-pointer border-b border-[var(--border)] last:border-b-0 " +
|
||||
(checked ? "bg-[var(--primary-soft)] dark:bg-[var(--surface-3)]" : "hover:bg-[var(--surface)] dark:hover:bg-[var(--surface-2)]")
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="accent-[#5559CE] w-[16px] h-[16px]"
|
||||
className="accent-[var(--primary)] w-[16px] h-[16px]"
|
||||
checked={checked}
|
||||
onChange={() => toggle(d.uuid)}
|
||||
/>
|
||||
<Avatar name={d.name} size={26} />
|
||||
<span className="text-[13px] text-[#525252] dark:text-[#D7D8ED]">{d.name}</span>
|
||||
<span className="text-[13px] text-[var(--text)]">{d.name}</span>
|
||||
</label>
|
||||
);
|
||||
})
|
||||
@@ -1040,12 +1040,12 @@ function MySecretariesPageContent() {
|
||||
return (
|
||||
<div dir="rtl">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[#525252] dark:text-[#D7D8ED] text-[20px] font-bold">لیست منشی ها</p>
|
||||
<p className="text-[var(--text)] text-[20px] font-bold">لیست منشی ها</p>
|
||||
</div>
|
||||
|
||||
{/* تبها + دکمه افزودن */}
|
||||
<div className="w-full flex items-end justify-between mt-[20px]">
|
||||
<div className="flex items-center gap-[8px] border-b border-[#EFEFEF] dark:border-[#343645]">
|
||||
<div className="flex items-center gap-[8px] border-b border-[var(--border)]">
|
||||
{["منشی های فعلی", "منشی های قبلی"].map((label, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
@@ -1053,8 +1053,8 @@ function MySecretariesPageContent() {
|
||||
className={
|
||||
"px-[12px] py-[10px] text-[14px] md:text-[16px] font-bold cursor-pointer border-b-2 -mb-px " +
|
||||
(tab === idx
|
||||
? "text-[#5559CE] border-[#5559CE]"
|
||||
: "text-[#495057] dark:text-[#A1A1A1] border-transparent")
|
||||
? "text-[var(--primary)] border-[var(--primary)]"
|
||||
: "text-[var(--text)] dark:text-[var(--text-2)] border-transparent")
|
||||
}
|
||||
>
|
||||
{label}
|
||||
@@ -1063,7 +1063,7 @@ function MySecretariesPageContent() {
|
||||
</div>
|
||||
<button
|
||||
onClick={handleAddClick}
|
||||
className="shadow-none gap-[8px] bg-[#5559CE] text-[#EFEFEF] text-[14px] md:text-[15px] lg:text-[16px]
|
||||
className="shadow-none gap-[8px] bg-[var(--primary)] text-[var(--on-primary)] text-[14px] md:text-[15px] lg:text-[16px]
|
||||
font-medium py-[10px] px-[16px] h-[43px] md:h-[45px] lg:h-[48px] rounded-[4px] cursor-pointer
|
||||
flex items-center disabled:opacity-60"
|
||||
>
|
||||
@@ -1073,11 +1073,11 @@ function MySecretariesPageContent() {
|
||||
</div>
|
||||
|
||||
{!isClinic && !doctorUuid ? (
|
||||
<div className="mt-[24px] text-center text-[#7E7E7E] py-[40px]">پروفایل پزشک یافت نشد</div>
|
||||
<div className="mt-[24px] text-center text-[var(--text-2)] py-[40px]">پروفایل پزشک یافت نشد</div>
|
||||
) : isLoading ? (
|
||||
<div className="mt-[24px] text-center text-[#7E7E7E] py-[40px]">در حال بارگذاری...</div>
|
||||
<div className="mt-[24px] text-center text-[var(--text-2)] py-[40px]">در حال بارگذاری...</div>
|
||||
) : secretaries.length === 0 ? (
|
||||
<div className="mt-[24px] text-center text-[#7E7E7E] py-[60px]">
|
||||
<div className="mt-[24px] text-center text-[var(--text-2)] py-[60px]">
|
||||
{tab === 0 ? "هنوز منشی فعالی اضافه نشده است" : "منشی قبلیای وجود ندارد"}
|
||||
</div>
|
||||
) : (
|
||||
@@ -1101,7 +1101,7 @@ function MySecretariesPageContent() {
|
||||
|
||||
{atLimit && (
|
||||
<div className="mt-[16px]">
|
||||
<Link to="/admin/subscription" className="text-[#5559CE] text-[13px] underline">
|
||||
<Link to="/admin/subscription" className="text-[var(--primary)] text-[13px] underline">
|
||||
برای افزودن منشی بیشتر، پنل خود را ارتقا دهید
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user