feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login

This commit is contained in:
ali_mohaghegh
2026-08-25 19:08:15 +03:30
parent 170aed66ea
commit 4e105c5cd5
103 changed files with 16352 additions and 0 deletions
+679
View File
@@ -0,0 +1,679 @@
@import "tailwindcss";
/* ============================================================
طراحی بر اساس ui-ux-pro-max: Flat Design — وب‌اپ سازمانی
اصول: خطوط تمیز، بدون سایه‌های سنگین، تایپوگرافی منظم،
ترنزیشن‌های کوتاه، آیکون‌های SVG (Lucide)
============================================================ */
@theme {
--color-bg: #f3f5f9;
--color-panel: #ffffff;
--color-ink: #0f172a;
--color-muted: #64748b;
--color-line: #e5e9f0;
--color-accent: #4f46e5;
--color-accent-dark: #4338ca;
--color-accent-bg: #eef2ff;
--color-green: #16a34a;
--color-green-bg: #dcfce7;
--color-amber: #d97706;
--color-amber-bg: #fef3c7;
--color-red: #dc2626;
--color-red-bg: #fee2e2;
--color-blue-bg: #dbeafe;
--color-slate-bg: #f1f5f9;
--font-sans: "Vazirmatn", system-ui, -apple-system, sans-serif;
}
html {
direction: rtl;
}
body {
@apply bg-bg text-ink antialiased;
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.6;
}
/* نشانگر دست روی همه‌ی عناصر قابل کلیک */
button:not(:disabled),
[role="button"]:not(:disabled),
select:not(:disabled),
a[href],
label[for],
input[type="checkbox"]:not(:disabled),
input[type="radio"]:not(:disabled) {
cursor: pointer;
}
button:disabled {
cursor: not-allowed;
}
/* احترام به prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
/* ---------- تایپوگرافی ---------- */
.num-words {
@apply text-muted;
font-size: 0.82em;
font-weight: 600;
}
/* ---------- نمودار جریان (Flow) ---------- */
.flow {
@apply relative bg-slate-bg/70 px-4 py-3;
}
.flow-cols {
@apply grid grid-cols-2 gap-10;
}
.flow-col {
@apply flex flex-col gap-2;
}
.flow-col-h {
@apply mb-0.5 flex items-center gap-1 text-[11px] font-bold text-muted;
}
.flow-box {
@apply relative rounded-lg border border-line bg-panel px-3 py-2 text-sm;
}
.flow-box.rejected {
@apply border-dashed opacity-55;
}
.flow-box.deal {
@apply border-green bg-green-bg/40;
}
/* کارت ترم‌شیت: پذیرفته = آبی، رسیده = سبز */
.flow-ts.ts-accepted {
@apply border-[#bfdbfe] bg-[#eff6ff];
}
.flow-ts.ts-arrived {
@apply border-green/40 bg-green-bg/40;
}
.fb-name {
@apply flex items-center gap-1 text-[12.5px] font-semibold leading-5;
}
.fb-meta {
@apply mt-0.5 flex items-center gap-1 text-[11px] text-muted;
}
.fb-nums {
@apply mt-1 flex flex-wrap gap-x-3 gap-y-0.5 text-[12px] tabular-nums;
}
.fb-total {
@apply font-semibold;
}
.fb-conv {
@apply mt-0.5 flex items-center gap-1 text-[10.5px] text-muted;
}
.fb-inv {
@apply mt-0.5 inline-flex items-center gap-1 text-[11px] text-accent underline underline-offset-2;
}
.fb-badge {
@apply ms-1 rounded-full px-1.5 py-px text-[9.5px] font-bold leading-4;
}
/* پیشنهاد = نارنجی، پذیرفته = آبی، رسیده = سبز، رد شده = قرمز، منقضی = خاکستری */
.fb-badge.offered {
@apply bg-amber-bg text-amber;
}
.fb-badge.accepted {
@apply bg-blue-bg text-[#1d4ed8];
}
.fb-badge.arrived {
@apply bg-green text-white;
}
.fb-badge.rejected {
@apply bg-red-bg text-red;
}
.fb-badge.expired {
@apply bg-slate-bg text-muted;
}
.fb-ms-list {
@apply mt-1 flex flex-col gap-0.5;
}
.fb-ms {
@apply flex items-center gap-1 text-[11px] text-muted;
}
.flow-lines {
@apply pointer-events-none absolute inset-0 h-full w-full;
}
.flow-line {
fill: none;
stroke: #cbd5e1;
stroke-width: 1.5;
}
.flow-line.accepted {
stroke: #2563eb;
stroke-width: 2;
}
.flow-rejected-h {
@apply mt-3 flex items-center gap-1 text-[11px] font-bold text-muted;
}
.flow-rejected-hint {
@apply text-[10.5px] text-muted mt-1;
}
.flow-attach {
@apply mt-3 flex flex-wrap gap-2;
}
.detail-item {
@apply text-[11.5px];
}
.detail-link {
@apply inline-flex items-center gap-1 text-accent underline underline-offset-2;
}
.detail-chip {
@apply text-muted;
}
/* هشدار «واحد جدید» در جدول دفتر */
.unit-warn {
@apply ms-1 inline-flex items-center gap-0.5 rounded bg-amber-bg px-1.5 py-px text-[10px] font-medium text-amber no-underline;
}
/* ردیف منقضی در دفتر */
.stale-row td {
@apply bg-amber-bg/25;
}
/* چیپ اقدامات در جدول دفتر */
.chip {
@apply inline-flex cursor-pointer items-center gap-0.5 rounded-full border border-line bg-panel px-2.5 py-1 text-[11px] font-medium text-ink transition-colors hover:bg-slate-bg;
}
.chip-act {
@apply border-accent/30 bg-accent-bg text-accent-dark hover:bg-accent-bg/70;
}
.chip-arrive {
@apply border-green/30 bg-green-bg text-green hover:bg-green-bg/70;
}
.chip-expand {
@apply border-transparent bg-transparent px-1.5 text-muted hover:bg-slate-bg hover:text-ink;
}
/* برگه‌های quick-chip ویزارد */
.quick-chip {
@apply inline-flex cursor-pointer items-center gap-1 rounded-full border border-line bg-panel px-3 py-1 text-xs transition-colors hover:border-accent/40 hover:bg-accent-bg/60;
}
.quick-chip.selected {
@apply border-accent bg-accent-bg text-accent-dark;
}
.qc-sub {
@apply text-[10px] text-muted;
}
.chip-divider {
@apply text-xs text-muted;
}
/* spec chips */
.spec-chip {
@apply inline-flex items-center gap-1 rounded-full bg-slate-bg px-2 py-0.5 text-[11px] font-medium;
}
.spec-chip-x {
@apply cursor-pointer text-muted hover:text-red;
}
/* کارت‌های termsheet در ویزارد/مودال‌ها */
.variant-card {
@apply rounded-lg border border-line bg-panel p-3;
}
.variant-head {
@apply mb-2 flex items-center justify-between;
}
.variant-num {
@apply text-sm font-bold;
}
.variant-x {
@apply cursor-pointer rounded p-0.5 text-muted hover:text-red;
}
.milestone-row {
@apply flex items-center gap-2;
}
.ms-amount-wrap {
@apply flex-1;
}
.ms-x {
@apply cursor-pointer text-muted hover:text-red;
}
.calc-pill {
@apply mt-0.5 text-[10.5px] text-muted;
}
.calc-pill.empty {
@apply hidden;
}
.ts-items-box {
@apply mt-2 rounded-md bg-slate-bg/60 p-2;
}
.ts-items-title {
@apply mb-1.5 text-[11.5px] font-bold text-muted;
}
.ts-items-empty {
@apply text-[11.5px] text-muted;
}
.ts-item-label {
@apply flex cursor-pointer items-center gap-1.5 py-0.5 text-[12px];
}
.ts-cb-label {
@apply flex cursor-pointer items-center gap-1.5 py-0.5 text-[12px];
}
.e-restore {
@apply font-medium text-green;
}
/* درگ‌اندروپ پروژه‌ها (مرحله ۴ ویزارد) */
.dd-panels {
@apply grid grid-cols-2 gap-4;
}
.dd-col {
@apply rounded-lg border border-line bg-panel p-3;
}
.dd-item {
@apply mb-2 flex cursor-grab items-center justify-between gap-2 rounded-md border border-line bg-panel px-3 py-2 text-[12.5px] transition-colors hover:border-accent/40;
}
.dd-item.dd-unassigned {
@apply border-red/40;
}
.dd-item.picked {
@apply border-accent bg-accent-bg/60;
}
.dd-item.dragging {
@apply opacity-50;
}
.dd-count {
@apply text-[10.5px] text-muted;
}
.dd-project {
@apply mb-2 rounded-md border border-dashed border-line p-2 transition-colors;
}
.dd-project.drop-hover {
@apply border-accent bg-accent-bg/40;
}
.dp-name {
@apply mb-1 flex items-center gap-1 text-[12.5px] font-bold;
}
.dp-tags {
@apply flex flex-wrap gap-1;
}
.dd-tag {
@apply inline-flex items-center gap-1 rounded-full bg-slate-bg px-2 py-0.5 text-[10.5px];
}
.dd-tag-x {
@apply cursor-pointer text-muted hover:text-red;
}
.dd-proj-empty {
@apply text-[11px] text-muted;
}
.dd-empty {
@apply rounded-lg border border-dashed border-line p-6 text-center text-sm text-muted;
}
/* لیست فاکتورها */
.inv-empty {
@apply mb-1 text-[12px] text-muted;
}
.inv-row {
@apply mb-1 flex items-center gap-2 rounded-md border border-line bg-panel px-2 py-1.5 text-[12px];
}
.inv-name {
@apply flex flex-1 items-center gap-1 truncate;
}
.inv-open {
@apply text-accent no-underline;
}
.inv-x {
@apply cursor-pointer text-muted hover:text-red;
}
/* فایل‌ها در ویزارد */
.file-warn {
@apply mt-0.5 text-[11px] text-amber;
}
.file-x {
@apply cursor-pointer text-muted hover:text-red;
}
/* آیتم‌های ورودی عددی */
.num-input {
text-align: left;
direction: ltr;
}
.num-pill {
@apply mt-0.5 block text-[11.5px] font-semibold text-ink/80;
}
.num-pill.empty {
@apply hidden;
}
/* ردیف‌های اقلام در ویزارد/ویرایش */
.item-row {
@apply grid items-start gap-2;
}
.wizard-item-row {
grid-template-columns: 1.1fr 1.1fr 0.7fr 0.8fr 0.8fr 0.9fr 0.9fr 28px;
}
.e-item-row {
grid-template-columns: 1.1fr 1.1fr 0.7fr 0.8fr 0.8fr 0.9fr 0.9fr auto 28px;
}
.item-row-head {
@apply text-[11px] font-bold text-muted;
}
.e-item-rejected td {
opacity: 0.55;
}
.spec-cell {
@apply relative;
}
.spec-trigger {
@apply flex h-9 w-full cursor-pointer items-center justify-between rounded-md border border-line bg-panel px-3 text-[12px] text-ink;
}
.spec-trigger-label {
@apply text-[12px] font-medium;
}
.spec-trigger-label.muted {
@apply text-muted;
}
.spec-drawer {
@apply absolute right-0 left-0 z-20 hidden rounded-md border border-line bg-panel p-2 shadow-md;
}
.spec-drawer.open {
@apply block;
}
.spec-add-row {
@apply mt-1.5 flex items-center gap-1;
}
.spec-add-btn {
@apply cursor-pointer rounded bg-accent px-2 py-1 text-[12px] font-bold text-white;
}
.spec-chips {
@apply flex flex-wrap gap-1;
}
/* بخش واحدها */
.units-layout {
@apply grid grid-cols-[220px_1fr] gap-4;
}
.units-tree {
@apply rounded-lg border border-line bg-panel p-3;
}
.tree-head {
@apply mb-2 flex items-center justify-between;
}
.ut-cat {
@apply flex cursor-pointer items-center justify-between rounded-md px-2.5 py-2 text-[12.5px] transition-colors hover:bg-slate-bg;
}
.ut-cat.selected {
@apply bg-accent-bg font-bold text-accent-dark;
}
.ut-ref {
@apply rounded bg-slate-bg px-1.5 py-px text-[10.5px] text-muted;
}
.units-panel {
@apply rounded-lg border border-line bg-panel p-4;
}
.ref-unit-box {
@apply rounded-md bg-slate-bg/60 p-3;
}
.ref-title {
@apply flex items-center gap-1 text-[13px] font-bold;
}
.ref-hint {
@apply mt-0.5 text-[11px] text-muted;
}
.ref-controls {
@apply mt-2 flex items-center gap-2;
}
.sec-title {
@apply mb-2 mt-4 flex items-center gap-1 text-[13px] font-bold;
}
.conv-empty {
@apply text-[12px] text-muted;
}
.cg-head {
@apply mt-3 mb-1 text-[11.5px] font-bold text-muted;
}
.conv-row {
@apply mb-1 flex items-center gap-2 rounded-md border border-line bg-panel px-2.5 py-1.5 text-[12px];
}
.cv-brand {
@apply w-28 shrink-0 text-[11.5px] text-muted;
}
.cv-factor {
@apply font-bold tabular-nums;
}
.cv-arrow {
@apply text-muted;
}
.cv-unit {
@apply font-medium;
}
.conv-actions {
@apply ms-auto flex items-center gap-1.5;
}
.conv-actions button {
@apply cursor-pointer text-muted transition-colors hover:text-ink;
}
.add-conv {
@apply mt-4 rounded-md border border-dashed border-line p-3;
}
.conv-form {
@apply grid grid-cols-[1.2fr_0.6fr_1fr_0.8fr_auto] items-end gap-2;
}
.conv-live {
@apply mt-2 text-[11.5px] text-muted;
}
.unknown-row {
@apply mb-1 flex items-center gap-2 rounded-md border border-amber/40 bg-amber-bg/40 px-2.5 py-1.5 text-[12px];
}
.uk-unit {
@apply font-bold;
}
.uk-count {
@apply text-[11px] text-muted;
}
.conv-hint {
@apply mt-2 text-[11px] text-muted;
}
/* کارت‌های تاپیک */
.topic-card {
@apply flex cursor-pointer flex-col items-center gap-1.5 rounded-lg border border-line bg-panel p-3 text-center transition-colors hover:border-accent/50;
}
.topic-card.selected {
@apply border-accent bg-accent-bg/50;
}
.topic-card img {
@apply h-11 w-11 object-contain;
}
.t-name {
@apply text-[12.5px] font-bold leading-5;
}
.t-count {
@apply text-[10.5px] text-muted;
}
/* کارت‌های تأمین‌کننده */
.sup-card {
@apply rounded-lg border border-line bg-panel p-3 transition-colors hover:border-accent/40;
}
.sc-head {
@apply flex items-center justify-between;
}
.sc-name-link {
@apply text-[13.5px] font-bold text-accent no-underline hover:underline;
}
.sup-del {
@apply cursor-pointer rounded px-1 text-muted transition-colors hover:text-red;
}
.sc-meta {
@apply mt-2 flex flex-col gap-0.5 text-[12px] text-muted;
}
.sc-meta span {
@apply flex items-center;
}
.sc-actions {
@apply mt-2;
}
/* پنل افزودن */
.add-panel {
@apply rounded-lg border border-line bg-panel p-4;
}
.add-panel h4 {
@apply mb-3 flex items-center gap-1 text-[13.5px] font-bold;
}
/* پروژه‌ها */
.proj-item {
@apply mb-1.5 flex cursor-pointer items-center gap-2 rounded-md border border-line bg-panel px-3 py-2.5 text-[13px] transition-colors hover:border-accent/50;
}
.proj-item.open {
@apply border-accent bg-accent-bg/50;
}
.proj-count {
@apply ms-auto text-[11px] text-muted;
}
.pj-col {
@apply rounded-lg border border-line bg-panel p-3;
}
.pj-col-h {
@apply mb-2 flex items-center gap-1 text-[12.5px] font-bold;
}
.pj-row {
@apply mb-2 rounded-md border border-line bg-panel px-2.5 py-2 text-[12px];
}
.pj-main {
@apply flex items-center gap-1 font-bold;
}
.pj-meta {
@apply mt-0.5 text-[11px] text-muted;
}
.pj-side {
@apply mt-1 text-[11px];
}
.pj-chip {
@apply ms-1 inline-block rounded-full px-1.5 py-px text-[10px] font-bold;
}
.pj-chip.red {
@apply bg-red-bg text-red;
}
.pj-chip.amber {
@apply bg-amber-bg text-amber;
}
.pj-chip.green {
@apply bg-green-bg text-green;
}
.pj-conv {
@apply text-[10px] text-muted;
}
.pj-sup-link {
@apply text-accent no-underline hover:underline;
}
.pj-empty {
@apply text-[12px] text-muted;
}
/* گزینه‌های مودال پذیرش */
.modal-variant {
@apply mb-2 rounded-lg border border-line bg-panel p-3;
}
.modal-variant-head {
@apply mb-2 flex items-center justify-between;
}
.accept-item-assign-row {
@apply mb-2 flex items-center gap-2;
}
.accept-item-assign-row.rejected {
@apply opacity-55;
}
.arrive-row {
@apply flex cursor-pointer items-center gap-2 rounded-md border border-line px-3 py-2 text-[13px];
}
.arrive-row.arrived {
@apply border-green bg-green-bg/40;
}
.arrive-meta {
@apply ms-auto text-[11.5px] text-muted;
}
/* ستون ورودی‌های فرم */
.field label {
@apply mb-1 block text-[12.5px] font-medium text-ink;
}
.field .hint {
@apply mt-1 text-[11px] text-muted;
}
.field.invalid input {
@apply border-red ring-1 ring-red/50;
}
/* کلاس‌های عمومی */
.empty {
@apply py-8 text-center text-sm text-muted;
}
.empty .big {
@apply mb-1 text-3xl;
}
.btn-line-file {
@apply inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-line bg-panel px-3 py-1.5 text-xs font-medium text-ink transition-colors hover:bg-slate-bg;
}
.review-row {
@apply flex items-center justify-between border-b border-line/60 py-1.5 text-[12.5px];
}
.review-row .r-label {
@apply font-bold text-muted;
}
.review-warn {
@apply text-amber;
}
/* ---------- سابقه خرید تأمین‌کننده (صفحه اطلاعات تماس) ---------- */
.ph-card {
@apply rounded-xl border border-line bg-panel p-5 shadow-sm;
}
.ph-head {
@apply mb-3 flex items-center gap-2;
}
.ph-title {
@apply text-[14px] font-bold;
}
.ph-count {
@apply rounded-full bg-accent-bg px-2 py-px text-[11px] font-bold text-accent-dark;
}
.ph-row {
@apply mb-2 rounded-lg border border-line bg-panel px-3 py-2.5;
}
.ph-row.stale {
@apply bg-amber-bg/25;
}
.ph-top {
@apply flex flex-wrap items-center gap-x-2 gap-y-1;
}
.ph-date {
@apply text-[12.5px] font-bold tabular-nums;
}
.ph-project {
@apply text-[11.5px] text-muted;
}
.ph-items {
@apply mt-1 text-[12px] text-ink;
}
.ph-total {
@apply mt-1 text-[12px] font-bold tabular-nums;
}
.ph-attach {
@apply mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 border-t border-line/60 pt-1.5 text-[11.5px];
}
.ph-attach-loading {
@apply text-[11px] text-muted;
}
.ph-empty {
@apply py-6 text-center;
}
.ph-empty-text {
@apply text-[13px] text-muted;
}