admin user and ui panel

This commit is contained in:
s.esmaeili
2026-09-21 08:32:31 +03:30
parent 79226c9f13
commit ef1d4638cc
341 changed files with 2270 additions and 657 deletions
+47 -4
View File
@@ -4,7 +4,7 @@
// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.
generator client {
provider = "prisma-client-js"
provider = "prisma-client-js"
// native = local Windows/macOS; debian-openssl-3.0.x = node:20-bookworm-slim (Arvan)
binaryTargets = ["native", "debian-openssl-3.0.x"]
}
@@ -28,12 +28,27 @@ enum AdminRole {
/// Legacy Customer.type — what kind of wholesale customer this is. Values and
/// labels taken verbatim from bulkCustomer.vue's userTypes list.
///
/// SALES_AGENT is a LEGACY value only: legacy stored a sales agent as a
/// Customer row with type=1, but the redesigned system models a sales agent
/// as an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts
/// and admin-customers.service.ts's listReagents()/create(). No Customer row
/// should be created with kind SALES_AGENT going forward; the value is kept
/// only so legacyTypeToKind()/kindToLegacyType() stay total functions and any
/// already-migrated data keeps a readable label.
///
/// RETAIL marks a "تک" (single/retail) customer — legacy kept these in an
/// entirely separate table (SingleCustomer/`login`) that could never contain
/// a wholesale/agent/shopkeeper/banker row. The redesigned schema folded both
/// into one Customer model, so this value is what listSingle() now filters on
/// to reproduce that same separation.
enum CustomerKind {
SALES_AGENT // 1 کارشناس فروش
SALES_AGENT // legacy 1 کارشناس فروش — superseded by AdminRole.SALES_EXPERT, do not use for new rows
APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن
WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری
BANKER // 4 بنکدار
WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه
RETAIL // مشتری تک — legacy SingleCustomer
}
enum VerificationStep {
@@ -127,6 +142,18 @@ model AdminUser {
isActive Boolean @default(true)
createdAt DateTime @default(now())
/// Legacy stored a sales agent as a `customers` row with type=1, so
/// ReagentCustomer.vue's shared form collected these alongside the
/// wholesale-customer fields. A sales agent is now an AdminUser
/// (role=SALES_EXPERT), so these live here instead — only ever set for that
/// role; every other AdminUser leaves them null.
avatarFileId String?
avatarFile UploadedFile? @relation("AdminUserAvatar", fields: [avatarFileId], references: [id])
address String?
storeName String? // legacy store_name
pasazhName String? // legacy pasazh_name
memberCode String? // legacy mcode
referredCustomers Customer[] @relation("ReferralAgent")
supervisedWorkshops Workshop[] @relation("WorkshopSupervisor")
branchAllocations BranchAllocation[] @relation("AllocationAuthor")
@@ -139,6 +166,7 @@ model AdminUser {
@@index([role])
@@index([userTypeId])
@@index([avatarFileId])
}
model Customer {
@@ -329,6 +357,11 @@ model ProductImage {
/// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so
/// existing rows and any generic "just attach an image" caller keep working.
role ProductImageRole @default(GALLERY)
/// Variants that display this image. A gallery image may back several
/// variants (one photo, several weights), which is why this is a plain
/// one-to-many and not a join table.
variants ProductVariant[]
}
enum ProductImageRole {
@@ -356,7 +389,16 @@ model ProductVariant {
isPreOrder Boolean @default(false)
isActive Boolean @default(true)
/// The gallery image shown for this variant, picked from its own product's
/// images — never a re-uploaded copy. onDelete: SetNull because dropping an
/// image must never cascade into the variant: OrderItem rows reference
/// variants, so losing one would break order history.
imageId String?
image ProductImage? @relation(fields: [imageId], references: [id], onDelete: SetNull)
orderItems OrderItem[]
@@index([imageId])
}
// ─────────────────────────────────────────────────────────────────────────
@@ -861,8 +903,9 @@ model UploadedFile {
uploadedByAdminUserId String?
uploadedByAdminUser AdminUser? @relation("FileUploader", fields: [uploadedByAdminUserId], references: [id])
createdAt DateTime @default(now())
customerAvatars Customer[] @relation("CustomerAvatar")
createdAt DateTime @default(now())
customerAvatars Customer[] @relation("CustomerAvatar")
adminUserAvatars AdminUser[] @relation("AdminUserAvatar")
@@index([purpose])
}