The page an operator opens to see the day's work did not say who any of it was for. TreatmentSession::toArray() carries no patient, so the list showed a service name, a time, and a repeated 40-char button — the same three lines for every row, with the finished work leading. The endpoint now sends patient_name and resource_name. They are added in the controller next to case_uuid/service_name rather than in toArray(), so patient identity does not leak into every other consumer of that method. The list is now a queue: unfinished work first, settled work (done, cancelled, no-show) below it, each group counted. Every row leads with its time, names the patient, and carries the service, device, session number and area progress on one meta line. The whole row is the link, so the repeated button is gone. Also fixes three things the redesign checklist calls out: Latin digits in the session and area counts (formatNumber), a date repeated on every row of a page whose title is "today", and a missing error state — a failed request rendered as "no sessions today", which reads as an empty day rather than a broken one. Adds the test file the page never had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1085 lines
60 KiB
CSS
1085 lines
60 KiB
CSS
@import "tailwindcss";
|
|
@import "@fontsource/vazirmatn/300.css";
|
|
@import "@fontsource/vazirmatn/400.css";
|
|
@import "@fontsource/vazirmatn/500.css";
|
|
@import "@fontsource/vazirmatn/600.css";
|
|
@import "@fontsource/vazirmatn/700.css";
|
|
@import "@fontsource/vazirmatn/800.css";
|
|
|
|
/* Class-based dark mode for Tailwind v4 */
|
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
|
|
@theme {
|
|
--font-sans: "Vazirmatn", ui-sans-serif, system-ui, sans-serif;
|
|
}
|
|
|
|
/* ── Template Design System Variables ─────────────────────────── */
|
|
:root {
|
|
--brand-h: 277;
|
|
--brand-c: 0.177;
|
|
--bg: #fafafa;
|
|
--bg-2: #f2f2f5;
|
|
--surface: #ffffff;
|
|
--surface-2: #f5f5f5;
|
|
--surface-3: #ececf1;
|
|
--border: #efefef;
|
|
--border-2: #e1e1e1;
|
|
--text: #3b3b3b;
|
|
--text-2: #7e7e7e;
|
|
--text-3: #a1a1a1;
|
|
/* پسزمینه آیتم فعال سایدبار — مطابق active-link-side در clinic-pro-tauri */
|
|
--nav-active-bg: #f8f8ff;
|
|
/* مقادیر sRGB پایه (fallback WebKit قدیمی iPhone 8)؛ نسخه oklch در @supports پایین فایل */
|
|
--primary: #5559CE;
|
|
--primary-600: #494CB3;
|
|
--primary-700: #3E41A0;
|
|
--primary-soft: #ecedfb;
|
|
--primary-soft2: #d9dbf6;
|
|
--on-primary: #ffffff;
|
|
--success: #15a35a;
|
|
--success-bg: #e6f6ed;
|
|
--warning: #d98a09;
|
|
--warning-bg: #fcf2df;
|
|
--danger: #e0394a;
|
|
--danger-bg: #fdebed;
|
|
--info: #2b86d8;
|
|
--info-bg: #e7f1fb;
|
|
--violet: #7c5cf0;
|
|
--violet-bg: #efeafe;
|
|
/* accent (نارنجی tauri) برای CTAهای ثانویه و آواتارها */
|
|
--accent: #f0682a;
|
|
--accent-600: #db5a1f;
|
|
--accent-bg: #fdeee4;
|
|
/* رنگهای پاستلی stat-cards داشبورد (دقیقاً مطابق clinic-pro-tauri) */
|
|
--stat-amber-bg: rgba(255,192,81,0.11); --stat-amber-fg: #FFC051;
|
|
--stat-violet-bg: rgba(85,89,206,0.08); --stat-violet-fg: #5559CE;
|
|
--stat-green-bg: rgba(0,157,121,0.10); --stat-green-fg: #009D79;
|
|
--stat-pink-bg: rgba(241,119,50,0.10); --stat-pink-fg: #F17732;
|
|
--ring: rgba(85,89,206,.32);
|
|
--shadow-sm: 0 1px 2px rgba(15,27,46,.06), 0 1px 3px rgba(15,27,46,.05);
|
|
--shadow: 0 2px 6px rgba(15,27,46,.06), 0 8px 24px rgba(15,27,46,.06);
|
|
--shadow-lg: 0 12px 32px rgba(15,27,46,.12), 0 4px 10px rgba(15,27,46,.06);
|
|
--r-xs: 7px; --r-sm: 8px; --r: 14px; --r-lg: 18px; --r-xl: 24px; --r-pill: 999px;
|
|
--gap: 20px;
|
|
--card-pad: 22px;
|
|
--row-h: 56px;
|
|
--sidebar-w: 243px;
|
|
--collapsed-w: 90px;
|
|
--topbar-h: 64px;
|
|
--ease: cubic-bezier(.22,.61,.36,1);
|
|
}
|
|
|
|
[data-density="compact"] {
|
|
--gap: 14px;
|
|
--card-pad: 16px;
|
|
--row-h: 46px;
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
--bg: #1f1d2b;
|
|
--bg-2: #1a1826;
|
|
--surface: #222433;
|
|
--surface-2: #2a2c3d;
|
|
--surface-3: #313349;
|
|
--border: #343645;
|
|
--border-2: #3d4152;
|
|
--text: #D7D8ED;
|
|
--text-2: #A1A1A1;
|
|
--text-3: #6d6e82;
|
|
--nav-active-bg: var(--primary-soft);
|
|
/* مقادیر sRGB پایه (fallback WebKit قدیمی)؛ نسخه oklch/color-mix در @supports پایین فایل */
|
|
--primary: #6B6FD6;
|
|
--primary-600: #5c60cf;
|
|
--primary-700: #494CB3;
|
|
--primary-soft: #2b2d47;
|
|
--primary-soft2: #363a63;
|
|
--on-primary: #EFEFEF;
|
|
--success-bg: #123332;
|
|
--warning-bg: #352e23;
|
|
--danger-bg: #36202f;
|
|
--info-bg: #162d48;
|
|
--violet-bg: #262751;
|
|
--accent: #f0763d;
|
|
--accent-600: #e0651f;
|
|
--accent-bg: #392b2d;
|
|
--stat-amber-bg: #312c24; --stat-amber-fg: var(--warning);
|
|
--stat-violet-bg: #24264d; --stat-violet-fg: var(--violet);
|
|
--stat-green-bg: #123031; --stat-green-fg: var(--success);
|
|
--stat-pink-bg: #35292c; --stat-pink-fg: var(--accent);
|
|
--shadow-sm: 0 1px 2px rgba(0,0,0,.4);
|
|
--shadow: 0 2px 8px rgba(0,0,0,.4), 0 12px 30px rgba(0,0,0,.35);
|
|
--shadow-lg: 0 18px 44px rgba(0,0,0,.55);
|
|
}
|
|
|
|
/* ── رنگهای مدرن (فقط مرورگرهای با پشتیبانی oklch/color-mix) ──────
|
|
iPhone 8 و WebKit قدیمی این بلوک را نادیده میگیرند و مقادیر sRGB بالا را
|
|
نگه میدارند؛ مرورگر مدرن این نسخههای oklch را استفاده میکند (بدون تغییر ظاهر). */
|
|
@supports (color: color-mix(in oklch, red, blue)) {
|
|
:root {
|
|
--primary: oklch(0.53 var(--brand-c) var(--brand-h));
|
|
--primary-600: oklch(0.47 var(--brand-c) var(--brand-h));
|
|
--primary-700: oklch(0.43 var(--brand-c) var(--brand-h));
|
|
--primary-soft: oklch(0.95 0.03 var(--brand-h));
|
|
--primary-soft2: oklch(0.90 0.05 var(--brand-h));
|
|
--ring: color-mix(in oklch, var(--primary) 32%, transparent);
|
|
}
|
|
[data-theme="dark"] {
|
|
--primary: oklch(0.59 var(--brand-c) var(--brand-h));
|
|
--primary-600: oklch(0.53 var(--brand-c) var(--brand-h));
|
|
--primary-700: oklch(0.47 var(--brand-c) var(--brand-h));
|
|
--primary-soft: color-mix(in oklch, var(--primary) 16%, var(--surface));
|
|
--primary-soft2: color-mix(in oklch, var(--primary) 26%, var(--surface));
|
|
--success-bg: color-mix(in oklch, var(--success) 18%, var(--surface));
|
|
--warning-bg: color-mix(in oklch, var(--warning) 18%, var(--surface));
|
|
--danger-bg: color-mix(in oklch, var(--danger) 18%, var(--surface));
|
|
--info-bg: color-mix(in oklch, var(--info) 18%, var(--surface));
|
|
--violet-bg: color-mix(in oklch, var(--violet) 20%, var(--surface));
|
|
--accent-bg: color-mix(in oklch, var(--accent) 18%, var(--surface));
|
|
--stat-amber-bg: color-mix(in oklch, var(--warning) 16%, var(--surface));
|
|
--stat-violet-bg: color-mix(in oklch, var(--violet) 18%, var(--surface));
|
|
--stat-green-bg: color-mix(in oklch, var(--success) 16%, var(--surface));
|
|
--stat-pink-bg: color-mix(in oklch, var(--accent) 16%, var(--surface));
|
|
}
|
|
}
|
|
|
|
/* ── Base ─────────────────────────────────────────────────────── */
|
|
*, *::before, *::after { font-family: var(--font-sans); box-sizing: border-box; }
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
direction: rtl;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-size: 15px;
|
|
line-height: 1.6;
|
|
font-feature-settings: 'ss02';
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
transition: background .3s var(--ease), color .3s var(--ease);
|
|
margin: 0;
|
|
}
|
|
|
|
/* ── Scrollbar ──────────────────────────────────────────────────*/
|
|
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 99px; }
|
|
::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
|
|
|
|
/* ── Animations ─────────────────────────────────────────────────*/
|
|
@keyframes scale-in { from { opacity: 0; transform: scale(0.96) translateY(-4px); } to { opacity: 1; transform: scale(1) translateY(0); } }
|
|
@keyframes slide-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
|
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
@keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
|
|
@keyframes fadein { from { opacity: 0; transform: translateY(10px); } }
|
|
@keyframes pop { from { opacity: 0; transform: translateY(14px) scale(.97); } }
|
|
@keyframes slidein { from { transform: translateX(30px); opacity: 0; } }
|
|
@keyframes slideup { from { opacity: 0; transform: translateY(16px); } }
|
|
@keyframes growbar { from { width: 0; } }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.animate-scale-in { animation: scale-in 200ms cubic-bezier(0.34, 1.2, 0.64, 1) both; }
|
|
.animate-slide-up { animation: slide-up 200ms ease both; }
|
|
.animate-fade-in { animation: fade-in 150ms ease both; }
|
|
.fade-in { animation: fadein .4s var(--ease); }
|
|
|
|
/* ── Skeleton shimmer ───────────────────────────────────────────*/
|
|
.skeleton {
|
|
background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.4s infinite;
|
|
}
|
|
|
|
/* ── Utility ────────────────────────────────────────────────────*/
|
|
.line-clamp-1 { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
|
|
.line-clamp-2 { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
|
.line-clamp-3 { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
|
|
|
|
/* ── Admin component tokens ─────────────────────────────────────*/
|
|
@layer components {
|
|
|
|
.cp-card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--r-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.cp-input {
|
|
width: 100%; height: 40px;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--r-sm);
|
|
padding: 0 14px;
|
|
font-size: 14px; color: var(--text);
|
|
outline: none;
|
|
transition: border-color .15s, box-shadow .15s;
|
|
}
|
|
.cp-input:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
.cp-input::placeholder { color: var(--text-3); }
|
|
|
|
.cp-textarea {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
|
|
padding: 10px 14px; font-size: 14px; color: var(--text); outline: none; width: 100%;
|
|
transition: border-color .15s, box-shadow .15s;
|
|
}
|
|
.cp-textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
.cp-textarea::placeholder { color: var(--text-3); }
|
|
|
|
.cp-select {
|
|
width: 100%; height: 40px;
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
|
|
padding: 0 14px; font-size: 14px; color: var(--text);
|
|
outline: none; transition: border-color .15s, box-shadow .15s;
|
|
}
|
|
.cp-select:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
|
|
.cp-label {
|
|
display: block; font-size: 13px; font-weight: 600; color: var(--text-2); margin-bottom: 6px;
|
|
}
|
|
|
|
.cp-btn-primary {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
height: 40px; padding: 0 18px; border-radius: var(--r-sm);
|
|
font-size: 14px; font-weight: 700; white-space: nowrap; border: none; cursor: pointer;
|
|
background: var(--primary); color: var(--on-primary); box-shadow: 0 4px 14px var(--ring);
|
|
transition: background .16s;
|
|
}
|
|
.cp-btn-primary:hover { background: var(--primary-600); }
|
|
.cp-btn-primary:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.cp-btn-secondary {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
height: 40px; padding: 0 18px; border-radius: var(--r-sm);
|
|
font-size: 14px; font-weight: 600; white-space: nowrap; cursor: pointer;
|
|
background: var(--surface); color: var(--text); border: 1px solid var(--border);
|
|
transition: .16s;
|
|
}
|
|
.cp-btn-secondary:hover { background: var(--surface-2); border-color: var(--border-2); }
|
|
.cp-btn-secondary:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.cp-btn-danger {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
height: 40px; padding: 0 18px; border-radius: var(--r-sm);
|
|
font-size: 14px; font-weight: 700; white-space: nowrap; border: none; cursor: pointer;
|
|
background: var(--danger); color: var(--on-primary); transition: .16s;
|
|
}
|
|
.cp-btn-danger:hover { filter: brightness(.92); }
|
|
.cp-btn-danger:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.cp-btn-ghost {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
height: 40px; padding: 0 18px; border-radius: var(--r-sm);
|
|
font-size: 14px; font-weight: 600; white-space: nowrap; border: none; cursor: pointer;
|
|
background: transparent; color: var(--text-2); transition: .16s;
|
|
}
|
|
.cp-btn-ghost:hover { background: var(--surface-2); color: var(--text); }
|
|
.cp-btn-ghost:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.cp-action-view { padding: 6px; border-radius: 8px; color: var(--text-3); transition: .14s; cursor: pointer; }
|
|
.cp-action-view:hover { background: var(--info-bg); color: var(--info); }
|
|
.cp-action-edit { padding: 6px; border-radius: 8px; color: var(--text-3); transition: .14s; cursor: pointer; }
|
|
.cp-action-edit:hover { background: var(--primary-soft); color: var(--primary-700); }
|
|
.cp-action-delete { padding: 6px; border-radius: 8px; color: var(--text-3); transition: .14s; cursor: pointer; }
|
|
.cp-action-delete:hover { background: var(--danger-bg); color: var(--danger); }
|
|
.cp-action-approve{ padding: 6px; border-radius: 8px; color: var(--text-3); transition: .14s; cursor: pointer; }
|
|
.cp-action-approve:hover{ background: var(--success-bg); color: var(--success); }
|
|
|
|
.cp-stat {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
|
|
padding: 18px; box-shadow: var(--shadow-sm);
|
|
display: flex; align-items: flex-start; gap: 16px;
|
|
transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s;
|
|
}
|
|
.cp-stat:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--border-2); }
|
|
.cp-stat-icon {
|
|
width: 40px; height: 40px; border-radius: 12px;
|
|
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
|
}
|
|
|
|
.cp-section-title { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 16px; }
|
|
|
|
.cp-info-row {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
padding: 10px 0; border-bottom: 1px solid var(--border);
|
|
}
|
|
.cp-info-row:last-child { border-bottom: none; }
|
|
.cp-info-label { font-size: 12px; color: var(--text-3); }
|
|
.cp-info-value { font-size: 13px; font-weight: 600; color: var(--text); }
|
|
}
|
|
|
|
/* ══ Template component classes ══════════════════════════════════*/
|
|
|
|
/* ── Layout ──────────────────────────────────────────────────── */
|
|
.app { display: flex; min-height: 100vh; }
|
|
.main {
|
|
flex: 1; min-width: 0; display: flex; flex-direction: column;
|
|
margin-right: var(--sidebar-w);
|
|
transition: margin .28s var(--ease);
|
|
}
|
|
.app[data-collapsed="true"] .main { margin-right: var(--collapsed-w); }
|
|
.content { padding: var(--gap); max-width: 1480px; width: 100%; margin: 0 auto; }
|
|
/* Settings pages go full-bleed: no max-width centering, and the settings menu
|
|
sits flush against the main nav (drop the inline-start / right padding). */
|
|
.content:has(> .settings-shell) { max-width: none; padding-inline-start: 0; }
|
|
|
|
/* ── Sidebar ─────────────────────────────────────────────────── */
|
|
.sidebar {
|
|
position: fixed; top: 0; bottom: 0; right: 0;
|
|
width: var(--sidebar-w);
|
|
background: var(--surface);
|
|
border-left: 1px solid var(--border);
|
|
display: flex; flex-direction: column;
|
|
z-index: 40;
|
|
transition: width .28s var(--ease);
|
|
}
|
|
.app[data-collapsed="true"] .sidebar { width: var(--collapsed-w); }
|
|
|
|
/* hover-expand سایدبار جمعشده (فقط دسکتاپ) — مثل clinic-pro-tauri */
|
|
@media (min-width: 1025px) {
|
|
.app[data-collapsed="true"] .sidebar:hover { width: var(--sidebar-w); box-shadow: var(--shadow-lg); }
|
|
.app[data-collapsed="true"] .sidebar:hover .brand-text,
|
|
.app[data-collapsed="true"] .sidebar:hover .user-meta { display: flex; }
|
|
.app[data-collapsed="true"] .sidebar:hover .nav-item { justify-content: flex-start; padding: 9px 12px; }
|
|
.app[data-collapsed="true"] .sidebar:hover .nav-item > span { display: revert; }
|
|
.app[data-collapsed="true"] .sidebar:hover .nav-item .badge-count { display: grid; }
|
|
.app[data-collapsed="true"] .sidebar:hover .nav-label { text-align: start; padding: 14px 12px 8px; font-size: 11px; }
|
|
.app[data-collapsed="true"] .sidebar:hover .nav-item.active::before { display: block; }
|
|
}
|
|
.sidebar-brand {
|
|
height: var(--topbar-h); flex: none;
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 0 20px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.brand-logo {
|
|
width: 38px; height: 38px; flex: none; border-radius: 11px;
|
|
display: grid; place-items: center; overflow: hidden;
|
|
flex-shrink: 0;
|
|
}
|
|
.brand-img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
|
|
.brand-text { display: flex; flex-direction: column; line-height: 1.25; overflow: hidden; white-space: nowrap; }
|
|
.brand-text b { font-size: 16px; letter-spacing: -.2px; color: var(--text); }
|
|
.brand-text span { font-size: 11.5px; color: var(--text-3); }
|
|
.app[data-collapsed="true"] .brand-text { display: none; }
|
|
|
|
.nav { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 14px 12px; }
|
|
.nav-group { margin-bottom: 6px; }
|
|
.nav-label {
|
|
font-size: 11px; font-weight: 700; color: var(--text-3);
|
|
padding: 14px 12px 8px; letter-spacing: .2px; display: block;
|
|
}
|
|
.app[data-collapsed="true"] .nav-label { text-align: center; padding: 12px 0 6px; font-size: 9px; }
|
|
.nav-item {
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 9px 12px; border-radius: var(--r-sm);
|
|
color: var(--text-2); font-size: 14px; font-weight: 500;
|
|
position: relative; width: 100%; text-align: start;
|
|
transition: background .15s, color .15s;
|
|
white-space: nowrap; text-decoration: none;
|
|
background: none; border: none; cursor: pointer;
|
|
}
|
|
.nav-item svg { flex-shrink: 0; }
|
|
.nav-item:hover { background: var(--surface-2); color: var(--text); }
|
|
.nav-item.active { background: var(--nav-active-bg); color: var(--primary-700); font-weight: 700; }
|
|
.nav-item.active::before {
|
|
content: ""; position: absolute; right: -12px; top: 50%; transform: translateY(-50%);
|
|
width: 4px; height: 22px; border-radius: 99px; background: var(--primary);
|
|
}
|
|
.nav-item .badge-count {
|
|
margin-inline-start: auto; background: var(--primary); color: var(--on-primary);
|
|
font-size: 11px; font-weight: 700; min-width: 20px; height: 20px; padding: 0 6px;
|
|
border-radius: 99px; display: grid; place-items: center;
|
|
}
|
|
.nav-subitem { font-size: 13px; padding: 8px 12px; gap: 10px; color: var(--text-3); }
|
|
.nav-subitem svg { color: var(--text-3); }
|
|
.app[data-collapsed="true"] .nav-item { justify-content: center; padding: 10px; }
|
|
.app[data-collapsed="true"] .nav-item > span,
|
|
.app[data-collapsed="true"] .nav-item .badge-count { display: none; }
|
|
.app[data-collapsed="true"] .nav-item.active::before { display: none; }
|
|
|
|
.sidebar-foot { flex: none; border-top: 1px solid var(--border); padding: 12px; }
|
|
.user-chip {
|
|
display: flex; align-items: center; gap: 11px; padding: 8px; border-radius: var(--r-sm);
|
|
transition: background .15s; cursor: pointer;
|
|
}
|
|
.user-chip:hover { background: var(--surface-2); }
|
|
.app[data-collapsed="true"] .user-chip .user-meta { display: none; }
|
|
.user-meta { display: flex; flex-direction: column; line-height: 1.3; overflow: hidden; flex: 1; min-width: 0; }
|
|
.user-meta b { font-size: 13.5px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.user-meta span { font-size: 11.5px; color: var(--text-3); }
|
|
.cp-avatar {
|
|
width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
|
|
display: grid; place-items: center; font-weight: 700; color: var(--on-primary); font-size: 13px;
|
|
background: linear-gradient(145deg, var(--primary), var(--primary-700));
|
|
overflow: hidden;
|
|
}
|
|
.cp-avatar.sm { width: 28px; height: 28px; font-size: 11px; }
|
|
.cp-avatar.lg { width: 96px; height: 96px; font-size: 32px; border-radius: 20px; }
|
|
|
|
/* ── Topbar ──────────────────────────────────────────────────── */
|
|
.topbar {
|
|
height: var(--topbar-h); flex: none; position: sticky; top: 0; z-index: 30;
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 0 var(--gap);
|
|
background: var(--bg); /* fallback WebKit قدیمی */
|
|
background: color-mix(in srgb, var(--bg) 85%, transparent);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.icon-btn {
|
|
width: 40px; height: 40px; border-radius: var(--r-sm); flex-shrink: 0;
|
|
display: grid; place-items: center; color: var(--text-2);
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
position: relative; transition: .15s; cursor: pointer;
|
|
}
|
|
.icon-btn:hover { color: var(--text); border-color: var(--border-2); background: var(--surface-2); }
|
|
.icon-btn .dot {
|
|
position: absolute; top: 8px; right: 9px;
|
|
width: 8px; height: 8px; border-radius: 50%;
|
|
background: var(--danger); border: 2px solid var(--surface);
|
|
}
|
|
.topbar-search {
|
|
display: flex; align-items: center; gap: 9px; flex: 1; max-width: 360px;
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: var(--r-pill); padding: 0 14px; height: 40px; color: var(--text-3);
|
|
transition: .18s;
|
|
}
|
|
.topbar-search:focus-within { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
.topbar-search input {
|
|
border: none; outline: none; background: none; flex: 1; font-size: 14px; color: var(--text);
|
|
font-family: inherit;
|
|
}
|
|
.topbar-search input::placeholder { color: var(--text-3); }
|
|
.topbar-search kbd {
|
|
font-size: 11px; color: var(--text-3); font-family: inherit;
|
|
background: var(--surface-3); border: 1px solid var(--border); border-radius: 6px; padding: 1px 7px;
|
|
}
|
|
.topbar-crumbs { display: flex; align-items: center; gap: 8px; color: var(--text-3); font-size: 13px; }
|
|
.topbar-crumbs b { color: var(--text); }
|
|
|
|
/* ── Profile menu (topbar) — معادلِ MenuProfile در clinic-pro-tauri ── */
|
|
.profile-trigger {
|
|
display: flex; align-items: center; gap: 8px; flex-shrink: 0;
|
|
padding: 4px 6px; border-radius: var(--r-sm);
|
|
background: none; border: 1px solid transparent; cursor: pointer;
|
|
transition: .15s; color: var(--text);
|
|
}
|
|
.profile-trigger:hover { background: var(--surface-2); border-color: var(--border); }
|
|
.profile-meta { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.35; overflow: hidden; }
|
|
.profile-meta b { font-size: 13px; font-weight: 600; color: var(--text); white-space: nowrap; }
|
|
.profile-meta span { font-size: 11.5px; color: var(--text-3); white-space: nowrap; }
|
|
.profile-caret { color: var(--text-3); }
|
|
/* موبایل: فقط آواتار (مثل md:hidden در tauri) */
|
|
@media (max-width: 640px) {
|
|
.profile-meta, .profile-caret { display: none; }
|
|
}
|
|
|
|
.profile-menu {
|
|
z-index: 9000; min-width: 190px; overflow: hidden;
|
|
padding: 8px; display: flex; flex-direction: column; gap: 2px;
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: var(--r); box-shadow: var(--shadow-lg);
|
|
}
|
|
.profile-item {
|
|
display: flex; align-items: center; gap: 8px;
|
|
width: 100%; padding: 8px 10px; border-radius: var(--r-sm);
|
|
font-size: 13px; font-weight: 500; color: var(--text-2);
|
|
background: transparent; border: none; cursor: pointer; text-align: right;
|
|
transition: .12s;
|
|
}
|
|
.profile-item:hover { background: var(--surface-2); color: var(--text); }
|
|
.profile-item.danger {
|
|
margin-top: 6px; justify-content: center;
|
|
color: var(--on-primary); background: var(--danger); font-weight: 600;
|
|
}
|
|
.profile-item.danger:hover { background: var(--danger); filter: brightness(0.94); color: var(--on-primary); }
|
|
|
|
/* در dark مرز سایدبار/هدر محو است — مثل clinic-pro-tauri */
|
|
[data-theme="dark"] .sidebar,
|
|
[data-theme="dark"] .sidebar-brand,
|
|
[data-theme="dark"] .topbar { border-color: transparent; }
|
|
|
|
/* ── Generic Card ────────────────────────────────────────────── */
|
|
.card {
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
|
|
}
|
|
.card-pad { padding: var(--card-pad); }
|
|
/* کارتی که خودش لینک است. بدون بازخورد hover، کاربر نمیفهمد کل ردیف قابل کلیک
|
|
است و دنبال دکمه میگردد. */
|
|
a.card { transition: .14s var(--ease); }
|
|
a.card:hover { border-color: var(--primary); box-shadow: var(--shadow); }
|
|
.card-head {
|
|
display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
padding: var(--card-pad) var(--card-pad) 0;
|
|
}
|
|
.card-head h3 { font-size: 16px; font-weight: 700; color: var(--text); }
|
|
.section-title { font-size: 19px; font-weight: 800; letter-spacing: -.3px; color: var(--text); }
|
|
.muted { color: var(--text-3); }
|
|
.link { color: var(--primary); font-weight: 600; font-size: 13px; text-decoration: none; }
|
|
.link:hover { color: var(--primary-700); }
|
|
/* همراستا با .mini-btn: لینکهای کوتاه («همه») روی موبایل باید ناحیهی لمسی
|
|
حداقلی داشته باشند (WCAG 2.5.5). چون داخل .card-title-row فلکساند، رشدِ
|
|
ارتفاع عنوان را جابهجا نمیکند. */
|
|
@media (max-width: 640px), (pointer: coarse) {
|
|
.link { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; min-width: 44px; }
|
|
}
|
|
.card-title-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
|
|
.flex { display: flex; align-items: center; }
|
|
.between { display: flex; align-items: center; justify-content: space-between; }
|
|
|
|
/* ── Stat cards ──────────────────────────────────────────────── */
|
|
.stat {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
|
|
padding: 18px; box-shadow: var(--shadow-sm); position: relative; overflow: hidden;
|
|
transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s;
|
|
}
|
|
.stat:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--border-2); }
|
|
.stat .ico { width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center; margin-bottom: 14px; }
|
|
.stat .lbl { color: var(--text-2); font-size: 13px; font-weight: 500; }
|
|
.stat .val { font-size: 26px; font-weight: 800; letter-spacing: -.5px; margin: 4px 0 2px; color: var(--text); }
|
|
.stat .hint { font-size: 11.5px; color: var(--text-3); }
|
|
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--gap); margin-bottom: var(--gap); }
|
|
.trend { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 99px; }
|
|
.trend.up { color: var(--success); background: var(--success-bg); }
|
|
.trend.down { color: var(--danger); background: var(--danger-bg); }
|
|
|
|
/* ── Badges ──────────────────────────────────────────────────── */
|
|
.badge {
|
|
display: inline-flex; align-items: center; gap: 5px;
|
|
font-size: 12px; font-weight: 700; padding: 3px 10px; border-radius: 99px; white-space: nowrap;
|
|
}
|
|
.badge .bdot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
|
|
.badge.green { color: var(--success); background: var(--success-bg); }
|
|
.badge.amber { color: var(--warning); background: var(--warning-bg); }
|
|
.badge.red { color: var(--danger); background: var(--danger-bg); }
|
|
.badge.blue { color: var(--info); background: var(--info-bg); }
|
|
.badge.violet { color: var(--violet); background: var(--violet-bg); }
|
|
.badge.gray { color: var(--text-2); background: var(--surface-3); }
|
|
|
|
/* ── Simple data table (rep dashboard/finance/settlement) ────── */
|
|
.tbl { border-collapse: collapse; font-size: 13px; }
|
|
.tbl thead tr { border-bottom: 1px solid var(--border); }
|
|
.tbl th { text-align: right; padding: 8px 12px; color: var(--text-3); font-weight: 500; white-space: nowrap; }
|
|
.tbl td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
|
|
.tbl tbody tr:last-child td { border-bottom: none; }
|
|
.tbl-zebra tbody tr:nth-child(even) td { background: var(--surface-2); }
|
|
.tbl-zebra tbody tr:hover td { background: var(--surface-3); }
|
|
|
|
/* ── Appointment status badges ───────────────────────────────── */
|
|
.appt-status {
|
|
display: inline-flex; align-items: center; gap: 6px;
|
|
font-size: 12px; font-weight: 700; padding: 4px 12px; border-radius: 99px;
|
|
white-space: nowrap; border: 1.5px solid transparent; letter-spacing: 0.01em;
|
|
}
|
|
.appt-status-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
|
|
.appt-status.status-green { color: var(--success); background: var(--success-bg); border-color: rgba(21,163,90,.25); border-color: color-mix(in srgb, var(--success) 25%, transparent); }
|
|
.appt-status.status-amber { color: var(--warning); background: var(--warning-bg); border-color: rgba(217,138,9,.25); border-color: color-mix(in srgb, var(--warning) 25%, transparent); }
|
|
.appt-status.status-red { color: var(--danger); background: var(--danger-bg); border-color: rgba(224,57,74,.25); border-color: color-mix(in srgb, var(--danger) 25%, transparent); }
|
|
.appt-status.status-blue { color: var(--info); background: var(--info-bg); border-color: rgba(43,134,216,.25); border-color: color-mix(in srgb, var(--info) 25%, transparent); }
|
|
.appt-status.status-violet { color: var(--violet); background: var(--violet-bg); border-color: rgba(124,92,240,.25); border-color: color-mix(in srgb, var(--violet) 25%, transparent); }
|
|
.appt-status.status-gray { color: var(--text-2); background: var(--surface-3); border-color: var(--border); }
|
|
|
|
/* field-label utility */
|
|
.field-label { display: block; font-size: 12.5px; font-weight: 600; color: var(--text-2); margin-bottom: 5px; }
|
|
|
|
/* پیام خطای اعتبارسنجی زیر ورودی — هر دو نام در صفحات موجود استفاده شدهاند */
|
|
.err-text, .field-error { display: block; font-size: 11.5px; font-weight: 600; color: var(--danger); }
|
|
|
|
/* ── Buttons ─────────────────────────────────────────────────── */
|
|
.btn {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
height: 40px; padding: 0 18px; border-radius: var(--r-sm);
|
|
font-size: 14px; font-weight: 700; transition: .16s var(--ease); white-space: nowrap;
|
|
border: 1px solid transparent; cursor: pointer; font-family: inherit;
|
|
}
|
|
.btn:active { transform: translateY(1px); }
|
|
.btn.primary { background: var(--primary); color: var(--on-primary); box-shadow: 0 4px 14px var(--ring); }
|
|
.btn.primary:hover { background: var(--primary-600); }
|
|
.btn.ghost { background: var(--surface); border-color: var(--border); color: var(--text); }
|
|
.btn.ghost:hover { background: var(--surface-2); border-color: var(--border-2); }
|
|
.btn.soft { background: var(--primary-soft); color: var(--primary-700); border-color: transparent; }
|
|
.btn.soft:hover { background: var(--primary-soft2); }
|
|
.btn.danger { background: var(--danger-bg); color: var(--danger); border-color: transparent; }
|
|
.btn.danger:hover { filter: brightness(.97); }
|
|
.btn.accent { background: var(--accent); color: var(--on-primary); box-shadow: 0 4px 14px rgba(240,104,42,.35); box-shadow: 0 4px 14px color-mix(in oklch, var(--accent) 35%, transparent); }
|
|
.btn.accent:hover { background: var(--accent-600); }
|
|
.btn.outline { background: transparent; border-color: var(--primary); color: var(--primary); }
|
|
.btn.outline:hover { background: var(--primary-soft); }
|
|
.btn.sm { height: 36px; padding: 0 14px; font-size: 13px; }
|
|
.btn.lg { height: 48px; padding: 0 24px; font-size: 15px; }
|
|
.btn.block { width: 100%; }
|
|
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
/* ── Tables ──────────────────────────────────────────────────── */
|
|
.table-wrap { overflow-x: auto; border-radius: var(--r-lg); border: 1px solid var(--border); }
|
|
table.t { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
|
table.t thead th {
|
|
text-align: start; font-size: 12px; font-weight: 700; color: var(--text-3);
|
|
padding: 14px 16px 12px; white-space: nowrap; border-bottom: 1px solid var(--border);
|
|
background: var(--surface-2);
|
|
}
|
|
table.t tbody td {
|
|
padding: 0 16px; height: var(--row-h); border-bottom: 1px solid var(--border);
|
|
vertical-align: middle; color: var(--text);
|
|
}
|
|
table.t tbody tr { transition: background .12s; }
|
|
table.t tbody tr:hover { background: var(--surface-2); }
|
|
table.t tbody tr:last-child td { border-bottom: none; }
|
|
.cell-user { display: flex; align-items: center; gap: 11px; }
|
|
.cell-user b { font-weight: 700; font-size: 13.5px; color: var(--text); }
|
|
.cell-user small { color: var(--text-3); font-size: 11.5px; }
|
|
.row-actions { display: flex; gap: 6px; opacity: 1; transition: opacity .15s; }
|
|
.mini-btn {
|
|
width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center;
|
|
color: var(--text-3); transition: .14s; cursor: pointer; border: none; background: none;
|
|
}
|
|
.mini-btn:hover { background: var(--surface-3); color: var(--text); }
|
|
.mini-btn.danger:hover { background: var(--danger-bg); color: var(--danger); }
|
|
/* دکمههای عملیات ردیف روی موبایل/لمسی باید حداقل ۴۴px باشند (WCAG 2.5.5).
|
|
هم بر اساس عرض (۶۴۰px، همان بریکپوینت موبایلِ پروژه) و هم pointer لمسی،
|
|
تا تبلت لمسیِ عریض هم پوشش داده شود. چگالی دسکتاپ دستنخورده میماند. */
|
|
@media (max-width: 640px), (pointer: coarse) {
|
|
.mini-btn { width: 44px; height: 44px; }
|
|
.row-actions { gap: 4px; }
|
|
}
|
|
|
|
/* ── Toolbar / Fields ────────────────────────────────────────── */
|
|
.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
|
|
.toolbar .spacer { flex: 1; }
|
|
.field {
|
|
display: flex; align-items: center; gap: 8px; height: 40px; padding: 0 14px;
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); color: var(--text-2);
|
|
transition: .15s;
|
|
}
|
|
.field:focus-within { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
/* کنترل باید کل ارتفاع ۴۰px قاب را بگیرد: هم هدف لمسی کامل میشود، هم کلیک روی
|
|
حاشیهٔ داخلیِ قاب فوکوس میدهد. بدون این، خودِ input حدود ۲۱px است. */
|
|
.field input, .field select, .field textarea { border: none; outline: none; background: none; flex: 1; min-width: 60px; align-self: stretch; color: var(--text); font-family: inherit; font-size: 14px; }
|
|
.field textarea { align-self: auto; }
|
|
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 3px; gap: 2px; }
|
|
/* `a` هم پذیرفته میشود: نوار تبهایی که بین صفحهها جابهجا میکنند باید لینک واقعی باشند
|
|
(باز کردن در تب جدید، کلیک وسط)، نه دکمهای که navigate صدا میزند. */
|
|
.seg button, .seg a { padding: 7px 14px; border-radius: 8px; font-size: 13px; font-weight: 600; color: var(--text-2); transition: .14s; cursor: pointer; font-family: inherit; text-decoration: none; display: inline-flex; align-items: center; }
|
|
/* `active` هممعنی `on` است — هر دو در کد استفاده شدهاند و نبودِ این alias یعنی تب فعال
|
|
در چند صفحه هیچ نشانهای نداشت. */
|
|
.seg button.on, .seg button.active, .seg a.on, .seg a.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
|
|
/* همراستا با .mini-btn: ارتفاع لمسیِ حداقلی روی موبایل (WCAG 2.5.5) */
|
|
@media (max-width: 640px), (pointer: coarse) {
|
|
.seg button, .seg a { min-height: 44px; padding: 7px 16px; }
|
|
}
|
|
|
|
/* ── Switch toggle ───────────────────────────────────────────── */
|
|
.switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; }
|
|
.switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; z-index: 2; }
|
|
.switch-track {
|
|
position: absolute; inset: 0; display: block;
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; transition: background .18s, border-color .18s;
|
|
}
|
|
.switch-thumb {
|
|
position: absolute; top: 50%; right: 3px; width: 16px; height: 16px; margin-top: -8px;
|
|
border-radius: 50%; background: var(--text-3); box-shadow: var(--shadow-sm);
|
|
transition: transform .18s, background .18s;
|
|
}
|
|
.switch input:checked ~ .switch-track { background: var(--primary); border-color: var(--primary); }
|
|
.switch input:checked ~ .switch-track .switch-thumb { background: var(--on-primary); transform: translateX(-20px); }
|
|
.switch input:focus-visible ~ .switch-track { box-shadow: 0 0 0 4px var(--ring); }
|
|
|
|
/* ── Pagination ──────────────────────────────────────────────── */
|
|
.pagination {
|
|
display: flex; align-items: center; gap: 6px; padding: 16px 0;
|
|
justify-content: space-between; flex-wrap: wrap; border-top: 1px solid var(--border); margin-top: 4px;
|
|
}
|
|
.page-btns { display: flex; gap: 4px; }
|
|
.page-btns button {
|
|
min-width: 36px; height: 36px; border-radius: 9px; font-weight: 600; font-size: 13px;
|
|
color: var(--text-2); border: 1px solid transparent; background: none;
|
|
cursor: pointer; transition: .14s; font-family: inherit;
|
|
}
|
|
.page-btns button:hover:not(.on):not(:disabled) { background: var(--surface-2); }
|
|
.page-btns button.on { background: var(--primary); color: var(--on-primary); }
|
|
.page-btns button:disabled { opacity: .3; cursor: not-allowed; }
|
|
|
|
/* ── Progress bar ────────────────────────────────────────────── */
|
|
.bar { height: 8px; border-radius: 99px; background: var(--surface-3); overflow: hidden; }
|
|
.bar > i { display: block; height: 100%; border-radius: 99px; background: linear-gradient(90deg, var(--primary), var(--primary-600)); }
|
|
|
|
/* ── Modal ───────────────────────────────────────────────────── */
|
|
.overlay {
|
|
position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 20px;
|
|
background: rgba(8,13,22,.5); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
|
|
animation: fade-in .2s var(--ease);
|
|
}
|
|
.modal {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl);
|
|
box-shadow: var(--shadow-lg); width: 100%; max-width: 540px; max-height: 90vh;
|
|
overflow-y: auto; overflow-x: hidden; animation: pop .26s var(--ease);
|
|
}
|
|
.modal-head {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
padding: 22px 24px; border-bottom: 1px solid var(--border);
|
|
}
|
|
.modal-head h2 { font-size: 17px; font-weight: 700; color: var(--text); margin: 0; }
|
|
.modal-body { padding: 24px; }
|
|
.modal-foot {
|
|
display: flex; gap: 10px; padding: 18px 24px; border-top: 1px solid var(--border); justify-content: flex-end;
|
|
}
|
|
.settings-panel {
|
|
width: 360px; height: 100%; overflow-y: auto;
|
|
animation: slidein .3s var(--ease);
|
|
border-radius: 0; max-height: 100%;
|
|
}
|
|
|
|
/* ── Form rows ───────────────────────────────────────────────── */
|
|
.form-row { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
|
|
.form-row label { font-size: 13px; font-weight: 600; color: var(--text-2); }
|
|
.input {
|
|
height: 44px; padding: 0 14px; border-radius: var(--r-sm);
|
|
background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
|
|
outline: none; width: 100%; transition: .15s; font-family: inherit; font-size: 14px;
|
|
}
|
|
.input:focus { border-color: var(--primary); background: var(--surface); box-shadow: 0 0 0 4px var(--ring); }
|
|
.input.err { border-color: var(--danger); }
|
|
.slot-select {
|
|
height: 40px; padding: 0 12px; border-radius: var(--r-sm);
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--text);
|
|
outline: none; transition: .15s; font-family: inherit; font-size: 14px;
|
|
}
|
|
.slot-select:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); }
|
|
|
|
/* ── Toast ───────────────────────────────────────────────────── */
|
|
.toasts {
|
|
position: fixed; bottom: 22px; left: 22px; z-index: 100;
|
|
display: flex; flex-direction: column; gap: 10px; pointer-events: none;
|
|
}
|
|
.toast {
|
|
display: flex; align-items: center; gap: 11px; padding: 13px 16px; min-width: 280px;
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
|
|
box-shadow: var(--shadow-lg); animation: slideup .3s var(--ease); pointer-events: auto;
|
|
}
|
|
.toast .tx { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; flex-shrink: 0; }
|
|
|
|
/* ── Dashboard misc ──────────────────────────────────────────── */
|
|
.timeline-item { display: flex; gap: 13px; padding: 11px 0; }
|
|
.timeline-item .tl-dot { width: 9px; height: 9px; border-radius: 50%; margin-top: 7px; flex-shrink: 0; box-shadow: 0 0 0 4px var(--surface); }
|
|
.timeline-item .tl-body { flex: 1; min-width: 0; }
|
|
.timeline-item .tl-body b { font-size: 13.5px; font-weight: 700; color: var(--text); display: block; }
|
|
.timeline-item .tl-body p { margin: 2px 0 0; font-size: 12.5px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.timeline-item .tl-body time { font-size: 11px; color: var(--text-3); }
|
|
.tl-connector { width: 1px; background: var(--border); flex-shrink: 0; margin: 0 4px; }
|
|
|
|
.quick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
|
|
.quick {
|
|
display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 20px 12px;
|
|
border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2);
|
|
transition: .18s var(--ease); text-align: center; text-decoration: none; cursor: pointer;
|
|
}
|
|
.quick:hover { transform: translateY(-3px); border-color: var(--primary); background: var(--surface); box-shadow: var(--shadow); }
|
|
.quick .qi { width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; }
|
|
.quick b { font-size: 13px; color: var(--text); }
|
|
|
|
/* ── Dashboard grid layouts ──────────────────────────────────── */
|
|
.dash-main { display: grid; grid-template-columns: 360px 1fr; gap: var(--gap); margin-bottom: var(--gap); }
|
|
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap); }
|
|
.dash-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
|
|
|
|
/* ── Colored avatar (hue-based gradient) ─────────────────────── */
|
|
.avatar {
|
|
width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
|
|
display: grid; place-items: center; font-weight: 700; color: var(--on-primary); font-size: 14px;
|
|
overflow: hidden;
|
|
}
|
|
.avatar.sm { width: 32px; height: 32px; font-size: 12px; }
|
|
.avatar.lg { width: 104px; height: 104px; font-size: 34px; border-radius: 22px; }
|
|
|
|
/* ── Responsive ──────────────────────────────────────────────── */
|
|
/* گارد ضد-overflow افقی سراسری */
|
|
html, body { max-width: 100%; overflow-x: hidden; }
|
|
|
|
/* لایههای ریسپانسیو مشترک */
|
|
.split-2 { display: grid; grid-template-columns: 1fr minmax(0, 320px); gap: var(--gap); align-items: start; }
|
|
|
|
.scrim { display: none; }
|
|
@media (max-width: 1080px) {
|
|
.dash-main { grid-template-columns: 1fr; }
|
|
.dash-3 { grid-template-columns: 1fr; }
|
|
}
|
|
@media (max-width: 1024px) {
|
|
.main { margin-right: 0 !important; }
|
|
.sidebar { transform: translateX(100%); box-shadow: var(--shadow-lg); }
|
|
.app[data-mobile-open="true"] .sidebar { transform: translateX(0); width: var(--sidebar-w); }
|
|
.app[data-mobile-open="true"] .scrim {
|
|
display: block; position: fixed; inset: 0; background: rgba(8,13,22,.5);
|
|
z-index: 35; backdrop-filter: blur(2px);
|
|
}
|
|
.topbar-search { display: none; }
|
|
.grid-2 { grid-template-columns: 1fr; }
|
|
}
|
|
@media (max-width: 860px) {
|
|
.split-2 { grid-template-columns: 1fr; }
|
|
}
|
|
@media (max-width: 560px) {
|
|
.page-head, .toolbar { flex-wrap: wrap; }
|
|
}
|
|
|
|
/* ارتفاع هدر ریسپانسیو دقیقاً مطابق clinic-pro-tauri (56 → 67 → 79 → 90) */
|
|
:root { --topbar-h: 56px; }
|
|
@media (min-width: 640px) { :root { --topbar-h: 67px; } }
|
|
@media (min-width: 768px) { :root { --topbar-h: 79px; } }
|
|
@media (min-width: 1024px) { :root { --topbar-h: 90px; } }
|
|
|
|
/* صفحه سرویسهای کلینیک */
|
|
.srv-section-row:hover { background: var(--surface-2); }
|
|
.srv-section-actions { opacity: 0; transition: opacity 0.15s; }
|
|
.srv-section-row:hover .srv-section-actions { opacity: 1; }
|
|
@media (hover: none) {
|
|
.srv-section-actions { opacity: 1; }
|
|
}
|
|
|
|
/* ═══ صفحه تنظیمات (Settings redesign) ═══════════════════════════ */
|
|
.settings-layout {
|
|
display: grid; grid-template-columns: 250px minmax(0, 1fr);
|
|
gap: var(--gap); align-items: start;
|
|
}
|
|
|
|
/* نوار ناوبری کناری (Sticky) */
|
|
.settings-nav { position: sticky; top: var(--gap); display: flex; flex-direction: column; gap: 12px; }
|
|
.settings-search {
|
|
display: flex; align-items: center; gap: 8px; height: 40px; padding: 0 12px;
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
|
|
color: var(--text-3); transition: .15s;
|
|
}
|
|
.settings-search:focus-within { border-color: var(--primary); box-shadow: 0 0 0 4px var(--ring); color: var(--primary); }
|
|
.settings-search input {
|
|
border: none; outline: none; background: none; flex: 1; min-width: 0;
|
|
color: var(--text); font-family: inherit; font-size: 13.5px;
|
|
}
|
|
.settings-nav-list { display: flex; flex-direction: column; gap: 3px; }
|
|
.settings-nav-item {
|
|
display: flex; align-items: center; gap: 10px; width: 100%; text-align: right;
|
|
padding: 9px 12px; border-radius: var(--r-sm); border: 1px solid transparent;
|
|
background: none; color: var(--text-2); font-family: inherit; font-size: 13.5px; font-weight: 600;
|
|
cursor: pointer; transition: .14s;
|
|
}
|
|
.settings-nav-item:hover { background: var(--surface-2); color: var(--text); }
|
|
.settings-nav-item.active { background: var(--primary-soft); color: var(--primary-700); border-color: transparent; }
|
|
.settings-nav-item svg { width: 17px; height: 17px; flex-shrink: 0; }
|
|
.settings-nav-item .dot { width: 7px; height: 7px; border-radius: 50%; margin-inline-start: auto; flex-shrink: 0; }
|
|
.settings-nav-empty { padding: 14px 12px; font-size: 12.5px; color: var(--text-3); text-align: center; }
|
|
|
|
/* هدر بخش */
|
|
.settings-sec-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 20px; }
|
|
.settings-sec-head .ico { width: 40px; height: 40px; border-radius: 11px; flex-shrink: 0; display: grid; place-items: center; }
|
|
.settings-sec-head h2 { font-size: 16.5px; font-weight: 800; color: var(--text); letter-spacing: -.2px; }
|
|
.settings-sec-head p { font-size: 12.5px; color: var(--text-3); margin-top: 3px; line-height: 1.6; }
|
|
|
|
/* گروه فیلدها */
|
|
.settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 18px; }
|
|
.settings-grid.one { grid-template-columns: 1fr; }
|
|
.field-block { display: flex; flex-direction: column; }
|
|
.field-block.span2 { grid-column: 1 / -1; }
|
|
.field-block > label { font-size: 13px; font-weight: 600; color: var(--text-2); margin-bottom: 6px; display: flex; align-items: center; gap: 5px; }
|
|
.field-block .req { color: var(--danger); font-weight: 700; }
|
|
.field-block .opt { color: var(--text-3); font-weight: 500; font-size: 11.5px; }
|
|
.field-hint { font-size: 11.5px; color: var(--text-3); margin-top: 6px; line-height: 1.65; }
|
|
.field-err { font-size: 12px; color: var(--danger); margin-top: 5px; }
|
|
.input-suffix { display: flex; align-items: center; gap: 8px; }
|
|
.input-suffix .suf { font-size: 13px; color: var(--text-3); white-space: nowrap; }
|
|
|
|
/* ردیف سوییچ (Toggle row) */
|
|
.toggle-row {
|
|
display: flex; align-items: center; justify-content: space-between; gap: 14px;
|
|
padding: 13px 16px; border-radius: var(--r-sm);
|
|
border: 1px solid var(--border); background: var(--surface-2); transition: .15s;
|
|
}
|
|
.toggle-row + .toggle-row { margin-top: 10px; }
|
|
.toggle-row.on { border-color: var(--border-2); border-color: color-mix(in oklch, var(--primary) 40%, var(--border)); background: var(--primary-soft); }
|
|
.toggle-row.warn.on { border-color: var(--warning); background: var(--warning-bg); }
|
|
.toggle-row .tr-title { font-size: 13.5px; font-weight: 600; color: var(--text); }
|
|
.toggle-row .tr-desc { font-size: 12px; color: var(--text-3); margin-top: 3px; line-height: 1.6; }
|
|
.toggle-sub { margin-top: 12px; padding-inline-start: 4px; max-width: 320px; }
|
|
|
|
/* کارت درگاه پرداخت */
|
|
.gw-card { border: 1px solid var(--border); border-radius: var(--r); padding: 16px; transition: .15s; }
|
|
.gw-card + .gw-card { margin-top: 14px; }
|
|
.gw-card.off { opacity: .6; }
|
|
.gw-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
|
|
.gw-name { display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 700; color: var(--text); }
|
|
.gw-name .dot { width: 9px; height: 9px; border-radius: 50%; }
|
|
.gw-state { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; }
|
|
|
|
/* نوار ذخیره چسبان (Sticky save bar) */
|
|
.save-bar {
|
|
position: sticky; bottom: 16px; z-index: 20; margin-top: 4px;
|
|
display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
padding: 12px 16px 12px 20px; border-radius: var(--r);
|
|
background: var(--surface); border: 1px solid var(--border-2);
|
|
box-shadow: var(--shadow-lg); animation: pop .22s var(--ease);
|
|
}
|
|
.save-bar .sb-msg { display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 600; color: var(--text-2); }
|
|
.save-bar .sb-actions { display: flex; gap: 10px; }
|
|
.save-bar.ok { border-color: var(--success); border-color: color-mix(in oklch, var(--success) 45%, var(--border)); }
|
|
|
|
@media (max-width: 900px) {
|
|
.settings-layout { grid-template-columns: minmax(0, 1fr); }
|
|
.settings-nav { position: static; min-width: 0; }
|
|
.settings-nav-list { flex-direction: row; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
|
|
.settings-nav-item { flex-shrink: 0; width: auto; }
|
|
.settings-nav-item .dot { display: none; }
|
|
}
|
|
@media (max-width: 560px) {
|
|
.settings-grid { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
/* ── Inventory (انبارداری) ─────────────────────────────────────────────────
|
|
Status pill colors mirror clinic-pro-tauri src/components/inventory/list/
|
|
InventoryStatus.jsx exactly, in both light and dark. */
|
|
.inv-badge {
|
|
display: inline-block; width: 100px; text-align: center;
|
|
padding: 4px 8px; border-radius: 4px; font-size: 14px; font-weight: 500;
|
|
}
|
|
.inv-badge.in-stock { background: var(--success-bg); color: var(--success); }
|
|
.inv-badge.low-stock { background: var(--warning-bg); color: var(--warning); }
|
|
.inv-badge.out-of-stock { background: var(--danger-bg); color: var(--danger); }
|
|
|
|
/* Tab row — mirrors inventory/TabsRow + TabItem. */
|
|
.inv-tabs {
|
|
display: flex; align-items: flex-end; gap: 24px;
|
|
border-bottom: 1px solid var(--border); margin: 4px 0 18px; width: 100%;
|
|
overflow-x: auto; scrollbar-width: none;
|
|
}
|
|
.inv-tabs::-webkit-scrollbar { display: none; }
|
|
.inv-tab {
|
|
display: flex; align-items: center; gap: 6px; padding: 0 8px 6px;
|
|
font-size: 16px; cursor: pointer; white-space: nowrap;
|
|
border-bottom: 2px solid transparent; color: var(--text-2); background: none;
|
|
}
|
|
.inv-tab.active { border-bottom: 3px solid var(--primary); color: var(--primary); }
|
|
|
|
/* Inventory responsive table/card switch (md breakpoint = 768px, like tauri). */
|
|
.inv-desktop { display: block; }
|
|
.inv-mobile { display: none; }
|
|
@media (max-width: 767px) {
|
|
.inv-desktop { display: none; }
|
|
.inv-mobile { display: grid; }
|
|
}
|
|
.inv-table { width: 100%; border-collapse: collapse; }
|
|
.inv-table thead tr { background: var(--surface-2); }
|
|
.inv-table th {
|
|
color: var(--text-2); font-size: 14px; font-weight: 400;
|
|
padding: 10px 18px; text-align: start; white-space: nowrap;
|
|
}
|
|
.inv-table td {
|
|
padding: 12px 18px; text-align: start; white-space: nowrap;
|
|
font-size: 16px; font-weight: 500; color: var(--text-2);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.inv-table tbody tr:hover { background: var(--surface-2); }
|
|
|
|
/* input[type=time] با آیکون سفارشی (گام «ایجاد سرویس»): آیکون native مرورگر حذف
|
|
میشود تا ساعت دوتایی نشود — مثل مبدأ tauri که فقط یک ClockField دارد. */
|
|
.time-input-plain::-webkit-calendar-picker-indicator { display: none; }
|
|
.time-input-plain { appearance: none; -webkit-appearance: none; }
|
|
|
|
/* ── شیفت هفتگی منبع ──────────────────────────────────────────────────────
|
|
چیدمان «یک ردیف = یک روز». کلاس است نه inline style، چون شکستِ ریسپانسیو و
|
|
حالت خطا با style درونخطی ممکن نیست، و رنگها که از توکن بیایند دارکمود و
|
|
تراکم فشرده خودبهخود درست میمانند. */
|
|
.wh-day {
|
|
display: grid; grid-template-columns: 88px minmax(0, 1fr) auto;
|
|
gap: 12px; align-items: start;
|
|
padding: 11px 14px; border: 1px solid var(--border);
|
|
border-radius: var(--r-sm); background: var(--surface-2); transition: .15s;
|
|
}
|
|
/* روزِ دارای شیفت باید از روزِ تعطیل جدا دیده شود، وگرنه هفته یک بلوک یکدست است. */
|
|
.wh-day.has-shift { background: var(--surface); border-color: var(--border-2); }
|
|
.wh-day.err { border-color: var(--danger); background: var(--danger-bg); }
|
|
.wh-day-name { font-weight: 700; font-size: 13.5px; color: var(--text); padding-top: 10px; }
|
|
.wh-day-off { font-size: 12.5px; color: var(--text-3); padding-top: 11px; }
|
|
.wh-rows { display: grid; gap: 8px; min-width: 0; }
|
|
.wh-actions { display: flex; align-items: center; gap: 4px; padding-top: 3px; }
|
|
|
|
.wh-shift { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
.wh-time { width: 138px; gap: 6px; }
|
|
.wh-time .lbl { font-size: 11.5px; color: var(--text-3); flex-shrink: 0; }
|
|
.wh-time input { font-size: 13.5px; }
|
|
/* چکباکس خام ~۱۳px است و زیر حداقلِ ارتفاعِ لمسی میافتد. */
|
|
/* «تا پایان روز» حالا سوییچ است، نه چکباکس. قاعدهٔ ابعادِ input برداشته شد چون
|
|
بعد از `.switch input` در فایل میآمد و با همان specificity آن را بازنویسی
|
|
میکرد — سوییچ به یک مربع ۱۷px تبدیل میشد. */
|
|
.wh-eod { display: flex; align-items: center; gap: 6px; min-height: 32px; font-size: 12.5px; color: var(--text-2); cursor: pointer; }
|
|
|
|
@media (max-width: 720px) {
|
|
.wh-day { grid-template-columns: minmax(0, 1fr); gap: 8px; }
|
|
.wh-day-name, .wh-day-off, .wh-actions { padding-top: 0; }
|
|
.wh-time { width: 100%; }
|
|
}
|
|
|
|
/* تعطیلات/استثنا در یک ستون و پیشنمایش در ستون دیگر — سه کارتِ نامرتبط در یک
|
|
auto-fit، ستون کناری را نیمهخالی رها میکرد. */
|
|
.wh-two-col { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--gap); align-items: start; }
|
|
@media (max-width: 1100px) { .wh-two-col { grid-template-columns: minmax(0, 1fr); } }
|
|
|
|
/* ── CKEditor 5 ────────────────────────────────────────────────────────────
|
|
ادیتور همهٔ رنگهایش را از متغیرهای --ck-color-* خودش میگیرد و پیشفرض آنها
|
|
روشن است؛ بدون این نگاشت، ادیتور در [data-theme="dark"] سفید میماند. */
|
|
.ck-rtl { direction: rtl; }
|
|
.ck-rtl .ck.ck-editor__editable { min-height: 320px; }
|
|
|
|
/* .ck-body-wrapper هم لازم است: بالنِ لینک و جدول به <body> منتقل میشوند و
|
|
بیرون از .ck-editor قرار میگیرند، پس متغیرها به آنها ارث نمیرسد. */
|
|
.ck.ck-editor,
|
|
.ck-body-wrapper {
|
|
--ck-color-base-background: var(--surface);
|
|
--ck-color-base-foreground: var(--surface-2);
|
|
--ck-color-base-text: var(--text);
|
|
--ck-color-base-border: var(--border);
|
|
--ck-color-base-active: var(--primary);
|
|
--ck-color-base-active-focus: var(--primary-600);
|
|
--ck-color-text: var(--text);
|
|
--ck-color-toolbar-background: var(--surface-2);
|
|
--ck-color-toolbar-border: var(--border);
|
|
--ck-color-button-default-background: transparent;
|
|
--ck-color-button-default-hover-background: var(--surface-3);
|
|
--ck-color-button-default-active-background: var(--surface-3);
|
|
--ck-color-button-on-background: var(--primary-soft);
|
|
--ck-color-button-on-hover-background: var(--primary-soft2);
|
|
--ck-color-button-on-color: var(--primary);
|
|
--ck-color-dropdown-panel-background: var(--surface);
|
|
--ck-color-dropdown-panel-border: var(--border);
|
|
--ck-color-list-background: var(--surface);
|
|
--ck-color-list-button-hover-background: var(--surface-3);
|
|
--ck-color-list-button-on-background: var(--primary-soft);
|
|
--ck-color-list-button-on-text: var(--primary);
|
|
--ck-color-panel-background: var(--surface);
|
|
--ck-color-panel-border: var(--border);
|
|
--ck-color-input-background: var(--surface);
|
|
--ck-color-input-border: var(--border);
|
|
--ck-color-input-text: var(--text);
|
|
--ck-color-input-disabled-background: var(--surface-2);
|
|
--ck-color-labeled-field-label-background: var(--surface);
|
|
--ck-color-split-button-hover-background: var(--surface-3);
|
|
--ck-color-split-button-hover-border: var(--border-2);
|
|
--ck-color-tooltip-background: var(--surface-3);
|
|
--ck-color-tooltip-text: var(--text);
|
|
--ck-color-editable-blur-selection: var(--surface-3);
|
|
--ck-border-radius: var(--r-sm);
|
|
}
|
|
.ck.ck-editor__editable_inline { background: var(--surface); color: var(--text); }
|
|
.ck.ck-editor__editable_inline.ck-focused {
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 4px var(--ring);
|
|
}
|
|
.ck.ck-content blockquote { border-inline-start: 3px solid var(--border-2); color: var(--text-2); }
|
|
.ck.ck-content a { color: var(--primary); }
|
|
.ck.ck-content :is(h2, h3, h4, h5, strong) { color: var(--text); }
|
|
.ck.ck-content :is(table td, table th) { border-color: var(--border); }
|
|
.ck.ck-content table th { background: var(--surface-2); }
|
|
|
|
/* آیکونهای SVG ادیتور fill ثابت دارند؛ بدون currentColor در دارکمود دیده نمیشوند. */
|
|
[data-theme="dark"] .ck.ck-button,
|
|
[data-theme="dark"] .ck.ck-button .ck-button__label { color: var(--text-2); }
|
|
[data-theme="dark"] .ck.ck-icon,
|
|
[data-theme="dark"] .ck.ck-icon :is(path, polygon, rect, circle, ellipse) { fill: currentColor; }
|
|
[data-theme="dark"] .ck.ck-button.ck-on .ck-icon :is(path, polygon, rect, circle, ellipse) { fill: var(--primary); }
|