The booking modal presented one flat scroll of fields whose order did not
match the order of the decisions behind them, and gave no reason when the
submit button stayed grey.
- Group the form into numbered steps (service+time, patient) so the order of
decisions is visible. The optional visit-price collapse stays unnumbered —
numbering an optional step reads as required.
- Show the first blocking condition above the footer instead of leaving a
disabled button unexplained.
- Label the header chip's facts ("device:", "supervising doctor:") and add the
appointment's Jalali date, which the modal never displayed at all.
- Replace the hand-rolled primary/ghost button pair with the design system's
`.seg` + `.on`, and announce state via aria-pressed.
- Move autoFocus off the patient search in picker mode; the first decision is
the section select above it.
- Give every input an id and its label an htmlFor.
- Surface a distinct error state for the slot query. A failed request used to
fall through to "not enough free time", which sent users to another day for
no reason.
- Raise the service remove button (18px), the duration pill and the time chips
to at least the 32px hit target; mark service rows role=checkbox.
- Modal close button gets an accessible name; `.field` controls stretch to the
full 40px box so the whole frame is clickable.
Runtime probe on the open modal goes from 4 unnamed icon controls, 1 unlabelled
field and 2 sub-32px controls to clean, across light/dark/compact/mobile.
The redesign-page probe now names the offending elements instead of only
counting them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1081 lines
59 KiB
CSS
1081 lines
59 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); }
|
|
.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); }
|