First commit of the Hedena Gold product (separate from gold-bank): branding, package names, and deploy defaults aligned to the new brand; legacy Henza trees stay local-only via .gitignore.
117 KiB
Inventory — apps/admin-panel (target admin)
All paths relative to apps/admin-panel/. Read from source; node_modules, dist, branchDist ignored.
Root = E:\gold bank\gold-banking-monorepo\apps\admin-panel.
1. Stack & versions
Source: package.json:1-37.
| Concern | Package | Version | Line |
|---|---|---|---|
| Framework | vue |
^3.5.0 |
package.json:23 |
| UI library | ant-design-vue |
^4.2.6 |
package.json:17 |
| UI icons | @ant-design/icons-vue |
^7.0.1 |
package.json:14 |
| Router | vue-router |
^4.4.0 |
package.json:24 |
| Store | pinia |
^2.2.0 |
package.json:22 |
| HTTP client | axios |
^1.7.7 |
package.json:19 |
| Date library | moment-jalaali |
^0.10.0 |
package.json:21 |
| Charts | apexcharts ^7.4.0 + vue3-apexcharts ^1.11.1 |
— | package.json:18,25 |
| Utility | lodash ^4.17.21, @vueuse/core ^9.13.0 |
— | package.json:16,20 |
| Shared types | @gold-bank/shared-types (workspace:*) |
— | package.json:15 |
| Build | vite ^5.4.0, @vitejs/plugin-vue ^5.1.0, vue-tsc ^2.1.0, typescript ^5.5.0, less ^4.1.2 |
— | package.json:28-35 |
Scripts (package.json:5-12): dev (vite), dev:branch (vite --mode branch), build (vue-tsc -b && vite build), build:branch (--mode branch), preview, lint.
Bootstrap src/main.ts:15-22: createApp(App) → createPinia() → Antd → router → VueApexCharts → mount #app. Styles ./assets/styles/main.less (src/main.ts:13). Vue-2 legacy plugins (Vuesax, VeeValidate, vue2-hammer, vue-clipboard2, ACL, i18n) intentionally dropped (src/main.ts:1-5).
index.html:2 — <html lang="fa" dir="rtl">, title منتزر — پنل مدیریت (index.html:7), preloader #loading-bg removed by router.afterEach (index.html:12, src/router/index.ts:63-68).
src/env.d.ts:12-17 declares the global apexchart component for vue-tsc.
2. HTTP layer
2.1 Client — src/api/client.ts
| Aspect | Value | Line |
|---|---|---|
| Base URL | import.meta.env.VITE_API_BASE_URL ?? '/api/' |
src/api/client.ts:11 |
| Default headers | Content-Type: application/json, Accept: application/json |
src/api/client.ts:13-16 |
| Boot token | reads localStorage[tokenName] and seeds Authorization: Bearer <t> |
src/api/client.ts:18-21 |
| Instance | axios.create({ baseURL, headers }) |
src/api/client.ts:23-26 |
Request interceptor (src/api/client.ts:34-58):
- Rewrites
config.urlthroughmapEndpoint()(:36-38). - Re-reads token each request; sets
Authorization: Bearer <token>when present, otherwise deletes the header (legacy sent literal"Bearer null"— fixed) (:39-44). - If
config.data instanceof FormData, deletesContent-Typeso the browser sets the multipart boundary (:52-54) — required forfile/createuploads.
Response interceptor (src/api/client.ts:68-82): NOT in legacy. On HTTP 401, if not already on /auth/login and not already redirecting, removes the token and does window.location.assign(${import.meta.env.BASE_URL}auth/login). Guarded by a module redirecting flag so it fires once. There is no refresh-token flow — no /refresh call, no retry queue.
Error handling / toasts: none centrally. Every view does its own try/catch + message.error(e?.response?.data?.message ?? '<farsi fallback>'). Shared delete errors go through Modal.error in src/composables/useDeleteHandler.ts:43-46.
Token storage key is mode-dependent: tokenName = isBranch() ? 'branch_token' : 'token', userName = isBranch() ? 'branch_user' : 'user' (src/utils/index.ts:18-19).
2.2 Endpoint mapping layer — src/api/endpoints.ts
Legacy path → new REST path rewrite, applied inside the request interceptor. normalize() strips leading slashes (:20-22); mapEndpoint() checks EXACT first, then PATTERNS in order, else passes through; absolute http(s):// URLs bypass entirely (:251-270). isMapped() is a dev diagnostic (:273-276).
EXACT map (src/api/endpoints.ts:29-168)
| Legacy path | New path | Line |
|---|---|---|
file/create |
admin/files/create |
:33 |
admin-login |
admin/auth/login |
:36 |
users/show |
admin/auth/me |
:37 |
single-order |
admin/orders/list |
:41 |
single/order-sts/edit |
admin/orders/status |
:42 |
major/order/table |
admin/orders/wholesale/list |
:43 |
order-tamin |
admin/orders/tamin/list |
:44 |
order-sts/edit |
admin/orders/status |
:48 |
delete-order |
admin/orders/delete |
:49 |
get-custom-orders |
admin/pre-orders/list |
:52 |
customer |
admin/customers-omde/list |
:56 |
banker |
admin/customers-omde/banker/list |
:57 |
shopkeeper |
admin/customers-omde/shopkeeper/list |
:58 |
major/verify/table |
admin/customers-omde/non-verified/list |
:59 |
major/archive/table |
admin/customers-omde/archived/list |
:60 |
reagent |
admin/customers-omde/reagent/list |
:61 |
customer/toggle-active |
admin/customers-omde/toggle-active |
:62 |
customer/toggle-sts |
admin/customers-omde/toggle-sts |
:63 |
major/verify |
admin/customers-omde/verify |
:64 |
single-customer |
admin/customers-omde/single/list |
:67 |
single |
admin/customers-omde/single (sets kind=RETAIL) |
:75 |
customer/create |
admin/customers-omde/create |
:76 |
query/reagent |
admin/customers-omde/query/reagent |
:77 |
wallet/charge |
admin/customers-omde/wallet/charge |
:78 |
comment/table |
admin/comments/list |
:79 |
comment/sts |
admin/comments/sts |
:80 |
comment |
admin/comments |
:81 |
order |
admin/orders/create |
:84 |
order/type |
admin/orders/type-options |
:85 |
add/variant |
admin/products/variants/create |
:86 |
product/variant |
admin/products/by-ids |
:87 |
products |
admin/products/list |
:90 |
product/create |
admin/products/create |
:91 |
get/variants |
admin/products/variants/list |
:92 |
change/percent/count |
admin/products/change-percent/count |
:93 |
change/all |
admin/products/change-percent/apply |
:94 |
cash/table |
admin/documents/list |
:97 |
cash |
admin/documents |
:98 |
query/customer |
admin/documents/query/customer |
:99 |
wallet/table |
admin/documents/wallet/list |
:100 |
wallet/config |
admin/documents/wallet/config |
:101 |
wallet/birthday/config |
admin/documents/wallet/birthday/config |
:102 |
product/variant/promotion |
admin/promotions |
:105 |
major/promotion |
admin/promotions/list |
:106 |
autopromotion |
admin/promotions/auto |
:107 |
ignore |
admin/promotions/exclusions |
:108 |
users |
admin/users |
:113 |
users/list |
admin/users/list |
:114 |
const/__list__ut |
admin/users/types |
:115 |
const |
admin/users/types |
:116 |
roles/create |
admin/users/roles |
:117 |
branch/product |
admin/invoices |
:120 |
branch/product/table |
admin/invoices/list |
:121 |
branch/list |
admin/invoices/branch-list |
:122 |
branch |
admin/branches |
:125 |
branch/table |
admin/branches/list |
:126 |
workshop |
admin/workshops |
:127 |
user/list |
admin/branches/user-list |
:128 |
cities |
admin/misc/cities |
:131 |
sms |
admin/misc/sms |
:132 |
sms/table |
admin/misc/sms/list |
:133 |
notify |
admin/misc/notify |
:134 |
notify/table |
admin/misc/notify/list |
:135 |
notify/customer |
admin/misc/notify/customer |
:136 |
ddf |
admin/misc/newsletter/list |
:138 |
fashion/table |
admin/misc/newsletter/list |
:139 |
log/list/customer |
admin/misc/log/customers |
:140 |
dashboard/link |
admin/misc/dashboard-links |
:141 |
get-analytics |
admin/dashboard/omde |
:144 |
get-analytics-tak |
admin/dashboard/tak |
:145 |
dashboard/overview |
admin/dashboard/overview |
:146 |
brand/create |
admin/misc/brands |
:147 |
ciny/create |
admin/categories/collections |
:149 |
cities/list |
admin/cities/list |
:152 |
report/order-item |
admin/dashboard/report/order-item |
:155 |
report/product |
admin/dashboard/report/product |
:156 |
workshop/table |
admin/workshops/list |
:157 |
brand |
admin/products/brands |
:158 |
category |
admin/categories/list |
:165 |
category/ |
admin/categories |
:166 |
ciny |
admin/categories/collections/list |
:167 |
PATTERN map (src/api/endpoints.ts:173-248) — first match wins
| Pattern | Rewrite | Line |
|---|---|---|
cities/get-percent-aux/(.+) |
admin/misc/cities/percent-aux/$1 |
:185 |
cities/get-percent/(.+) |
admin/misc/cities/percent/$1 |
:186 |
cities/(.+) |
admin/misc/cities/$1 |
:187 |
const/(.+) |
admin/misc/const/$1 |
:190 |
brand/(.+) |
admin/misc/brands/$1 |
:193 |
log/ago/(.+) |
admin/misc/log/customer/$1 |
:196 |
notify/read/(.+) |
admin/misc/notify/read/$1 |
:199 |
notify/(.+) |
admin/misc/notify/$1 |
:200 |
single/(.+) |
admin/customers-omde/$1 |
:203 |
custom-order/(.+) |
admin/pre-orders/$1 |
:206 |
get-custom-order/(.+) |
admin/pre-orders/$1 |
:207 |
major/archive/(.+) |
admin/customers-omde/archive/$1 |
:209 |
major/dashboard/(.+) |
admin/customers-omde/dashboard/$1 |
:210 |
comment/(.+) |
admin/comments/$1 |
:213 |
customer/(.+) |
admin/customers-omde/$1 |
:215 |
delete/product/(.+) |
admin/products/delete/$1 |
:218 |
product/show/(.+) |
admin/products/$1 |
:219 |
product/update/(.+) |
admin/products/$1 |
:220 |
product/(.+)/variant |
admin/products/$1/variants |
:221 |
category/(.+) |
admin/categories/$1 |
:223 |
ciny/(.+) |
admin/categories/collections/$1 |
:224 |
cash/confirem/(.+) |
admin/documents/$1/confirm (client keeps legacy misspelling) |
:227 |
cash/(.+) |
admin/documents/$1 |
:228 |
product/variant/promotion/(.+) |
admin/promotions/$1 |
:231 |
major/promotion/(.+) |
admin/promotions/$1 |
:232 |
ignore/(.+) |
admin/promotions/exclusions/$1 |
:233 |
roles/(\d+)/(.+) |
admin/users/types/$1/roles/$2 |
:236 |
roles/(.+) |
admin/users/types/$1/roles |
:237 |
users/(.+) |
admin/users/$1 |
:238 |
branch/product/(.+) |
admin/invoices/$1 |
:243 |
branch/active/(.+) |
admin/branches/active/$1 |
:244 |
workshop/active/(.+) |
admin/workshops/active/$1 |
:245 |
branch/(.+) |
admin/branches/$1 |
:246 |
workshop/(.+) |
admin/workshops/$1 |
:247 |
2.3 Every API call site (method + URL as written; resolve via §2.2)
| File:line | Method | URL (as written) |
|---|---|---|
src/components/upload/ImageUploadInput.vue:97 |
POST | file/create (FormData) |
src/components/x-table/XTable.vue:288 |
POST | props.model.url (per table model, §7/§6) |
src/composables/useDeleteHandler.ts:34,36 |
POST / DELETE | caller-supplied url |
src/router/index.ts:87 |
GET | users/show |
src/stores/auth.ts:58 |
POST | admin-login |
src/stores/cart.ts:102 |
POST | product/variant {products_id} |
src/stores/users.ts:94 |
POST | cities/list |
src/views/Dashboard.vue:301 |
GET | dashboard/overview |
src/views/apps/category/Add.vue:215 |
PUT | category/{id} |
src/views/apps/category/Add.vue:217 |
POST | category/ |
src/views/apps/category/Add.vue:235 |
GET | admin/categories/tree |
src/views/apps/category/Add.vue:243 |
GET | admin/categories/{id} |
src/views/apps/customer/ReagentCustomer.vue:154 |
PUT | users/{id} |
src/views/apps/customer/ReagentCustomer.vue:156 |
POST | users |
src/views/apps/customer/ReagentCustomer.vue:171 |
GET | admin/users/{id} |
src/views/apps/customer/bulkCustomer.vue:400 |
PUT | customer/{id} |
src/views/apps/customer/bulkCustomer.vue:402 |
POST | customer/create |
src/views/apps/customer/bulkCustomer.vue:422 |
GET | query/reagent |
src/views/apps/customer/bulkCustomer.vue:428 |
POST | cities/list |
src/views/apps/customer/bulkCustomer.vue:435 |
GET | customer/{id} |
src/views/apps/customer/singleCustomer.vue:97 |
PUT | single/{id} |
src/views/apps/customer/singleCustomer.vue:99 |
POST | single |
src/views/apps/customer/singleCustomer.vue:112 |
POST | cities/list |
src/views/apps/customer/singleCustomer.vue:120 |
GET | customer/{id} |
src/views/apps/product/AddProduct.vue:437 |
PUT | product/update/{id} |
src/views/apps/product/AddProduct.vue:439 |
POST | product/create |
src/views/apps/product/AddProduct.vue:453 |
GET | brand |
src/views/apps/product/AddProduct.vue:454 |
POST | category |
src/views/apps/product/AddProduct.vue:455 |
GET | admin/products/collections |
src/views/apps/product/AddProduct.vue:472 |
GET | product/show/{id} |
src/views/apps/product/ChangePercent.vue:87 |
POST | change/percent/count |
src/views/apps/product/ChangePercent.vue:104 |
POST | change/all |
src/views/apps/product/ChangePercent.vue:121 |
GET/POST | brand, category |
src/views/apps/product/ProductList.vue:131 |
GET | admin/products/{id} |
src/views/apps/product/ProductList.vue:146 |
POST | delete/product/{id} (via delete handler) |
src/views/branch/AddBranches.vue:139 |
PUT | branch/{id} |
src/views/branch/AddBranches.vue:141 |
POST | branch |
src/views/branch/AddBranches.vue:162 |
GET | user/list |
src/views/branch/AddBranches.vue:169 |
GET | branch/{id} |
src/views/branch/FacilityList.vue:104 |
GET | {activePath}/{id} (branch/active | workshop/active) |
src/views/branch/buyList.vue:69 |
GET | branch/product/{id} |
src/views/branch/dashboard.vue:75 |
GET | get-analytics-tak |
src/views/branch/dashboard.vue:76 |
POST | products {page:1,limit:5} |
src/views/branch/order/create.vue:213 |
POST | order |
src/views/branch/order/create.vue:233 |
POST | single-customer {page:1,limit:500} |
src/views/city/cityList.vue:92 |
GET | cities/get-percent/{id} |
src/views/city/cityList.vue:93 |
GET | cities/get-percent-aux/{id} |
src/views/city/cityList.vue:106 |
PUT | cities/{id} |
src/views/city/createCity.vue:85 |
POST | cities |
src/views/customer/Comments/Comments.vue:198 |
POST | comment/sts {id, sts} |
src/views/customer/Comments/Comments.vue:215 |
DELETE | comment/{id} |
src/views/customer/Comments/Comments.vue:225 |
POST | comment |
src/views/customer/CustomerDetail.vue:174 |
GET | customer/{id} |
src/views/customer/CustomerDetail.vue:175 |
GET | major/dashboard/{id} |
src/views/customer/CustomerDetail.vue:176 |
GET | admin/customers-omde/{id}/orders |
src/views/customer/CustomerDetail.vue:177 |
GET | admin/customers-omde/{id}/documents |
src/views/customer/CustomerDetail.vue:191 |
GET | log/ago/{id} |
src/views/customer/omde/OmdeCustomerList.vue:352 |
POST | major/verify |
src/views/customer/omde/OmdeCustomerList.vue:391 |
POST | customer/toggle-active |
src/views/customer/omde/OmdeCustomerList.vue:403 |
POST | customer/toggle-sts |
src/views/customer/omde/OmdeCustomerList.vue:413 |
GET | major/archive/{id} |
src/views/customer/reagentList/AgentDetail.vue:108 |
GET | admin/customers-omde/reagent/{id} |
src/views/customer/reagentList/ReagentList.vue:109 |
PUT | users/{id} {is_active} |
src/views/customer/single-customer/SingleCustomer.vue:99 |
POST | wallet/charge |
src/views/finical/addFinDoc.vue:305 |
PUT | cash/{id} |
src/views/finical/addFinDoc.vue:307 |
POST | cash |
src/views/finical/addFinDoc.vue:325 |
GET | query/customer |
src/views/finical/addFinDoc.vue:333 |
GET | cash/{id} |
src/views/finical/list.vue:111 |
POST | cash/confirem/{id} |
src/views/invoice/AddInvoice.vue:231 |
PUT | branch/product/{id} |
src/views/invoice/AddInvoice.vue:233 |
POST | branch/product |
src/views/invoice/AddInvoice.vue:253 |
GET | branch/list |
src/views/invoice/AddInvoice.vue:261 |
GET | branch/product/{id} |
src/views/invoice/ListInvoice.vue:113 |
GET | branch/product/{id} |
src/views/manage/Const.vue:217 |
GET | brand |
src/views/manage/Const.vue:224 |
POST | brand/create |
src/views/manage/Const.vue:241 |
PUT | brand/{id} |
src/views/manage/Const.vue:275 |
PUT | const/__list__tags {val} |
src/views/manage/Const.vue:290 |
PUT | const/slider {val} |
src/views/manage/Const.vue:299 |
POST | ciny {page:1,limit:200} |
src/views/manage/Const.vue:307 |
PUT | ciny/{id} |
src/views/manage/Const.vue:309 |
POST | ciny/create |
src/views/manage/Const.vue:333 |
DELETE | ciny/{id} |
src/views/manage/Const.vue:346-347 |
GET | const/__list__tags, const/slider |
src/views/manage/report.vue:24 |
GET | dashboard/link |
src/views/manage/wallet.vue:101 |
POST | wallet/config |
src/views/manage/wallet.vue:122 |
POST | wallet/birthday/config |
src/views/manage/wallet.vue:137-138 |
GET | wallet/config, wallet/birthday/config |
src/views/notify/create.vue:47 |
POST | notify |
src/views/notify/user.vue:73 |
GET | notify/{id} |
src/views/notify/user.vue:81 |
GET | notify/read/{id} |
src/views/offersIgnore/offersIgnore-add-edit.vue:166 |
POST | ignore |
src/views/offersIgnore/offersIgnore-add-edit.vue:184 |
DELETE | ignore/{type} |
src/views/offersIgnore/offersIgnore-add-edit.vue:198 |
GET | ignore |
src/views/order/List.vue:197 |
GET | admin/orders/{id} |
src/views/order/List.vue:203,215 |
POST | single/order-sts/edit |
src/views/order/MakeOrder.vue:168 |
POST | workshop/table {page:1,limit:100} |
src/views/order/MakeOrder.vue:177 |
GET | admin/pre-orders/{id} |
src/views/order/MakeOrder.vue:193 |
PUT | custom-order/{id} |
src/views/order/OmdeList.vue:171 |
GET | admin/orders/{id} |
src/views/order/OmdeList.vue:187,198 |
POST | order-sts/edit |
src/views/order/TaminList.vue:153 |
GET | admin/orders/{id} |
src/views/order/TaminList.vue:169,184 |
POST | order-sts/edit |
src/views/order/create.vue:327 |
POST | add/variant |
src/views/order/create.vue:373 |
POST | order |
src/views/order/create.vue:394 |
GET | order/type |
src/views/order/create.vue:395 |
POST | single-customer {page:1,limit:500} |
src/views/promotion/PromotionForm.vue:335 |
PUT | product/variant/promotion/{id} |
src/views/promotion/PromotionForm.vue:337 |
POST | product/variant/promotion |
src/views/promotion/PromotionForm.vue:356-358 |
POST/GET | products, category, brand (limit 500) |
src/views/promotion/PromotionForm.vue:369 |
GET | query/customer |
src/views/promotion/PromotionForm.vue:378 |
GET | admin/promotions/{id} |
src/views/promotion/autopromotion.vue:55 |
POST | autopromotion |
src/views/promotion/autopromotion.vue:69 |
GET | autopromotion |
src/views/promotion/promotion_list.vue:129 |
DELETE | major/promotion/{id} |
src/views/reg.vue:92 |
POST | customer/create |
src/views/report/order_item.vue:76 |
GET | report/order-item {params: filters} |
src/views/report/product.vue:60 |
GET | report/product {params: filters} |
src/views/sms/create.vue:94 |
POST | sms |
src/views/statics/index.vue:71 |
GET | log/ago/{customer} |
src/views/statics/index.vue:82 |
GET | log/list/customer |
src/views/user/ListUser.vue:90 |
GET | const/__list__ut |
src/views/user/Permission.vue:118 |
GET | const/__list__ut |
src/views/user/Permission.vue:136 |
GET | roles/{id} |
src/views/user/Permission.vue:160 |
POST | const (create user type) |
src/views/user/Permission.vue:167 |
POST | roles/create {ut, roles} |
src/views/user/UserCreate.vue:220 |
PUT | users/{id} |
src/views/user/UserCreate.vue:222 |
POST | users |
src/views/user/UserCreate.vue:241-242 |
GET | const/__list__ut, admin/users/role-options |
src/views/user/UserCreate.vue:252 |
GET | admin/users/{id} |
src/views/user/myData.vue:37 |
GET | admin/users/me |
src/views/workshop/AddWorkshop.vue:151 |
PUT | workshop/{id} |
src/views/workshop/AddWorkshop.vue:153 |
POST | workshop |
src/views/workshop/AddWorkshop.vue:172 |
GET | workshop/{id} |
3. Router — every route
3.1 Structure — src/router/index.ts
createWebHistory(import.meta.env.BASE_URL) (:10); scrollBehavior returns {left:0,top:0} (:12-14).
Two top-level shells (:17-53):
path: ''→layouts/main/Main.vue, children =isBranch() ? branchRoute : adminRoute(:18-21)path: ''→layouts/full-page/FullPage.vue, children =fullPage*Route+ 4 error pages (:23-53)- catch-all
/:pathMatch(.*)*→ redirect/pages/error-404(:55-58)
3.2 Admin routes (src/router/admin.ts) — main layout (layouts/main/Main.vue)
meta.layout does not exist as a key; layout is determined by the parent shell. Meta keys actually used: rule, role, parent, no_scroll, pageTitle, breadcrumb.
| path | name | component file | meta (rule / role / parent / no_scroll / pageTitle) | line |
|---|---|---|---|---|
/ |
dashboard |
— (redirect /dashboard/ecommerce) |
— | :27-30 |
/dashboard/analytics |
dashboard-analytics |
views/Dashboard.vue |
editor / dashboard.view |
:36-43 |
/my-data |
my-data |
views/user/myData.vue |
editor / (role commented out) | :45-52 |
/dashboard/ecommerce |
dashboard-ecommerce |
views/Dashboard.vue |
admin / dashboard.view |
:54-61 |
/config/manage/const |
— | views/manage/Const.vue |
editor / config.statics / manage / true / مدیریت ثابت ها |
:63-82 |
/config/manage/wallet |
— | views/manage/wallet.vue |
editor / config.wallet / manage / true / مدیریت کیف پول |
:84-103 |
/config/manage/report |
— | views/manage/report.vue |
editor / config.report / manage / true / گزارش |
:105-124 |
/apps/product/ |
product |
— (redirect /apps/product/list) |
— | :127-130 |
/product/change/percent |
— | views/apps/product/ChangePercent.vue |
editor / product.percent / product / true |
:132-140 |
/apps/product/add |
— | views/apps/product/AddProduct.vue |
editor / product.create / product / true |
:142-150 |
/apps/product/edit/:id/:variant_id? |
product-edit |
views/apps/product/EditProduct.vue |
editor / product.list.edit / product / true |
:152-161 |
/apps/product/list |
product-list |
views/apps/product/ProductList.vue |
editor / product.list / product / true |
:163-172 |
/category |
category |
— (redirect /category/list) |
— | :175-178 |
/category/add/:id? |
category-add |
views/apps/category/Add.vue |
editor / collection.create / category / true |
:180-189 |
/category/edit/:id |
category-edit |
views/apps/category/Edit.vue |
editor / collection.list.edit / category / true |
:191-200 |
/category/list |
category-list |
views/apps/category/List.vue |
editor / collection.list / category / true |
:202-211 |
/add/findoc |
— | views/finical/addFinDoc.vue |
editor / document.create |
:213-221 |
/edit/findoc/:id |
— | views/finical/addFinDoc.vue |
editor / document.list.edit |
:222-230 |
/order |
order |
— (redirect /order/list) |
— | :232-236 |
/order/list |
— | views/order/List.vue |
editor / order.list / order / true |
:238-246 |
/order/omde-list |
— | views/order/OmdeList.vue |
editor / order.omde / order / true |
:248-256 |
/order/make |
— | views/order/MakeOrder.vue |
editor / order.makeOrder / order / true |
:258-266 |
/order/create |
— | views/order/create.vue |
editor / order.create / order / true |
:268-276 |
/order/tamin-list |
— | views/order/TaminList.vue |
editor / order.tamin / order / true |
:278-286 |
/major/promotion/list |
— | views/promotion/promotion_list.vue |
editor / offers.list / true |
:288-296 |
/major/promotion/add |
— | views/promotion/promotion_add_edit.vue |
editor / offers.create / true |
:298-306 |
/major/promotion/edit/:id |
— | views/promotion/promotion_add_edit.vue |
editor / offers.list.edit / true |
:308-316 |
/major/promotion/tak/add |
— | views/promotion/promotion_tak_add_edit.vue |
editor / offers.create.tak / true |
:318-326 |
/major/promotion/tak/edit/:id |
— | views/promotion/promotion_tak_add_edit.vue |
editor / offers.list.tak.edit / true |
:328-336 |
/major/promotion/autopromotion |
— | views/promotion/autopromotion.vue |
editor / offers.autopromotion / true |
:338-345 |
/major/offersIgnore |
— | views/offersIgnore/offersIgnore-add-edit.vue |
editor / offersIgnore |
:347-354 |
/comments |
comments |
views/customer/Comments/Comments.vue |
editor / comment / true / دیدگاه ها |
:356-377 |
/sms/create |
create_sms |
views/sms/create.vue |
editor / sms.create / true / ارسال پیامک |
:379-398 |
/notify |
notify |
views/notify/user.vue |
editor / notify.me / true / لیست نوتیفیکیشن های ذریافتی |
:400-419 |
/notify/create |
create_notify |
views/notify/create.vue |
editor / notify.create / true / ارسال نوتیفیکیشن |
:421-440 |
/wallet |
wallet |
views/wallet/wallet.vue |
editor / wallet / true / لیست کیف پول های شارژ شده |
:442-461 |
/reagent/edit/:id |
reagent-edit |
views/apps/customer/EditReagent.vue |
editor / persons.list.sellers.edit / product / true |
:463-473 |
/reagent/add |
reagent-add |
views/apps/customer/EditReagent.vue |
editor / persons.create / product / true |
:475-485 |
/reagent/view/:id |
reagent-view |
views/customer/reagentList/AgentDetail.vue |
editor / persons.list.sellers.view / product / true |
:487-496 |
/bulkCustomer/edit/:id |
bulkCustomer-edit |
views/apps/customer/EditBulkCustomer.vue |
editor / persons.list.client.omde.edit / product / true |
:498-507 |
/bulkCustomer/view/:id |
bulkCustomer-view |
views/customer/omde/ViewOmdeCustomer.vue |
editor / persons.list.client.omde.view / product / true |
:509-518 |
/singleCustomer/edit/:id |
singleCustomer-edit |
views/apps/customer/EditSingleCustomer.vue |
editor / persons.list.client.single.edit / product / true |
:520-529 |
/singleCustomer/view/:id |
singleCustomer-view |
views/customer/single-customer/ViewSingleCustomer.vue |
editor / persons.list.client.single.view / product / true |
:531-540 |
/customer/omde-list |
omde-list-customer |
views/customer/omde/OmdeList.vue |
editor / persons.list.client.omde / مشتریان عمده |
:542-560 |
/customer/omde-non-verified-list |
omde-non-verified-list |
views/customer/omde/OmdeNonVerifiedList.vue |
editor / persons.list.client.omde / مشتریان در انتظار |
:562-580 |
/customer/banker-list |
banker-list |
views/customer/omde/BankerList.vue |
editor / persons.list.client.omde / لیست بنکداران |
:582-600 |
/customer/shopkeeper-list |
shopkeeper-list |
views/customer/omde/ShopkeeperList.vue |
editor / persons.list.client.omde / لیست مغازه داران حضوری |
:602-620 |
/customer/omde-archived-list |
omde-archived-list |
views/customer/omde/OmdeArchivedList.vue |
editor / persons.list.client.omde / مشتریان بایگانی |
:622-640 |
/customer/reagent-list |
raegentList |
views/customer/reagentList/ReagentList.vue |
editor / persons.list.sellers / کارشناسان فروش |
:642-660 |
/customer/add |
add-customer |
views/apps/customer/AddCustomer.vue |
editor / persons.create / افزودن مشتری |
:662-681 |
/customer/list |
list-customer |
views/customer/single-customer/SingleCustomer.vue |
editor / persons.list.client.single / لیست مشتریان |
:683-698 |
/report/order_item |
report_order_item |
views/report/order_item.vue |
editor / report.order.item |
:700-712 |
/report/product |
report_product |
views/report/product.vue |
editor / report.product |
:714-721 |
/finical/list |
finical_list |
views/finical/list.vue |
editor / document.list |
:723-736 |
/user/list |
— | views/user/ListUser.vue |
editor / user.list / user / true / لیست مدیران |
:738-757 |
/user/create |
— | views/user/UserCreate.vue |
editor / user.create / user / true / افزودن کاربر |
:759-778 |
/user/edit/:id |
— | views/user/UserEdit.vue |
editor / user.list.edit / user / true / ویرایش نوع کاربر |
:780-799 |
/permission |
— | views/user/Permission.vue |
editor / user.permission / permission / true / دسترسی ها |
:802-821 |
/city/create |
— | views/city/createCity.vue |
editor / cities.create / city / true / افزودن شهر |
:823-842 |
/city/list |
— | views/city/cityList.vue |
editor / cities.list / city / true / لیست شهر ها |
:844-863 |
/statics |
statics |
views/statics/index.vue |
editor / statics / true / آمار |
:866-885 |
/newsletters |
newsletters |
views/newsletters/newsletters.vue |
editor / role commented out / true / خبرنامه |
:887-906 |
/branch/add |
add-branch |
views/branch/AddBranches.vue |
editor / branch.create / افزودن شعب |
:908-927 |
/branch/edit/:id |
edit-branch |
views/branch/EditBranches.vue |
editor / branch.create / ویرایش شعب |
:929-937 |
/branch/list |
list-branch |
views/branch/ListBranches.vue |
editor / branch.list / لیست شعب |
:939-958 |
/workshop/add |
add-workshop |
views/workshop/AddWorkshop.vue |
editor / workshop.create / افزودن کارگاه |
:961-980 |
/workshop/edit/:id |
edit-workshop |
views/workshop/EditWorkshop.vue |
editor / workshop.create / ویرایش کارگاه |
:982-990 |
/workshop/list |
list-workshop |
views/workshop/ListWorkshop.vue |
editor / workshop.list / لیست کارگاه ها |
:992-1011 |
/invoice/add |
add-invoice |
views/invoice/AddInvoice.vue |
editor / invoice.create / افزودن فاکتور |
:1014-1033 |
/invoice/edit/:id |
edit-invoice |
views/invoice/EditInvoice.vue |
editor / invoice.create / ویرایش فاکتورها |
:1035-1043 |
/invoice/list |
list-invoice |
views/invoice/ListInvoice.vue |
editor / invoice.list / لیست فاکتورها |
:1045-1064 |
Admin full-page (src/router/admin.ts:1068-1085, shell layouts/full-page/FullPage.vue):
| path | name | component | meta | line |
|---|---|---|---|---|
/auth/login |
page-login |
views/auth/Login.vue |
rule editor | :1070-1076 |
/reg |
reg |
views/reg.vue |
rule editor | :1078-1084 |
Shared full-page pages (src/router/index.ts:28-51):
| path | name | component | meta | line |
|---|---|---|---|---|
/pages/error-404 |
page-error-404 |
views/pages/Error404.vue |
rule editor | :28-33 |
/pages/error-500 |
page-error-500 |
views/pages/Error500.vue |
rule editor | :34-39 |
/pages/not-authorized |
page-not-authorized |
views/pages/NotAuthorized.vue |
rule editor | :40-45 |
/pages/maintenance |
page-maintenance |
views/pages/Maintenance.vue |
rule editor | :46-51 |
3.3 Branch routes (src/router/branch.ts, active only when MODE === 'branch')
| path | name | component file | meta | line |
|---|---|---|---|---|
/ |
main |
— (redirect /dashboard) |
— | :15-18 |
/dashboard |
dashboard |
views/branch/dashboard.vue |
admin / dashboard.view |
:20-29 |
/products |
product-list |
views/apps/product/ProductList.vue |
editor / product.list / product / true |
:31-40 |
/order |
order |
— (redirect /order/list) |
— | :42-45 |
/order/list |
— | views/order/List.vue |
editor / order.list / order / true |
:47-55 |
/buy/order/list |
— | views/branch/buyList.vue |
editor / order.list / order / true |
:57-65 |
/buy/order/list (duplicate path) |
— | views/order/List.vue |
editor / buy.order.list / true |
:67-74 |
/order/create |
— | views/branch/order/create.vue |
editor / order.create / order / true |
:76-84 |
/singleCustomer/edit/:id |
singleCustomer-edit |
views/apps/customer/EditSingleCustomer.vue |
editor / persons.list.client.single.edit / product / true |
:86-95 |
/singleCustomer/view/:id |
singleCustomer-view |
views/customer/single-customer/ViewSingleCustomer.vue |
editor / persons.list.client.single.view / product / true |
:97-106 |
/singleCustomer/edit/:id (duplicate) |
singleCustomer-edit |
views/apps/customer/EditSingleCustomer.vue |
editor / persons.list.client.single.edit / product / true |
:108-117 |
/customer/add |
add-customer |
views/branch/addCustomer.vue |
editor / persons.create |
:119-126 |
/customer/list |
list-customer |
views/customer/single-customer/SingleCustomer.vue |
editor / persons.list.client.single |
:128-135 |
/report/order_item |
report_order_item |
views/report/order_item.vue |
editor / report.order.item |
:137-149 |
/report/product |
report_product |
views/report/product.vue |
editor / report.product |
:151-158 |
/finical/list (commented out) |
finical_list |
views/finical/list.vue |
editor / document.list |
:159-172 (inactive) |
Branch full-page (src/router/branch.ts:175-184): /auth/login → page-login → views/branch/login.vue (the views/auth/Login.vue import is commented out at :178), meta rule editor.
Counts: admin main-layout routes 73 (incl. 4 redirect-only), admin full-page 2, shared error pages 4, catch-all 1 → 80 admin-mode route records. Branch mode: 15 active main-layout records + 1 full-page.
3.4 Guards
router.afterEach (src/router/index.ts:63-68) hides #loading-bg.
getPermission() (:82-100): if usersStore.roles already set, returns it; else GET users/show → users.getRoles(data.permission), strips permission and ut, persists api_token under tokenName and the user blob under userName. Errors are swallowed with console.log.
router.beforeEach (:111-129):
noneAuthUrl = ['/auth/login','/auth/forgot-password','/auth/register','/reg'](:71) → allow.- No token in
localStorage[tokenName]→ redirect{path:'/auth/login'}(:128). - With a token: load roles; if
to.meta.roleis set and!roles.includes(role)→ if the route name isdashboard-ecommerceordashboard-analytics, redirect to{name:'my-data'}, else{name:'page-not-authorized'}(:119-124).
meta.rule (editor/admin) is inert — never read by any guard (legacy ACL plugin was not ported).
4. Menu / navigation
Defined in src/layouts/navMenuItems.ts (641 lines). Exports adminMenu (:44), branchMenu (:551), getMenu() selector (:637-639, isBranch() ? branchMenu : adminMenu). Node interface NavMenuNode { url, name, slug, icon, i18n, tag, tagColor, permissionId, header, items, submenu } (:30-42).
Rendered by src/layouts/components/VerticalNavMenu.vue (uses a-menu-item-group for header nodes, :39) and recursively by src/layouts/components/NavMenuNode.vue (a-sub-menu when node.submenu, :8). Icons resolved from legacy Feather names via src/components/icons.ts resolveIcon().
Visibility rule (VerticalNavMenu.vue:81-82, NavMenuNode.vue:36-37): a node with no permissionId is always visible; otherwise it requires users.hasPermission(permissionId).
4.1 Admin menu items
| Label (i18n) | url | icon | permissionId | line |
|---|---|---|---|---|
| پیشخوان | /dashboard/ecommerce |
HomeIcon | dashboard.view |
:49-55 |
[header] کاربری (Apps) |
— | PackageIcon | — | :58-60 |
| اشخاص | null | UserIcon | persons |
:63-67 |
| ├ اضافه کردن شخص | /customer/add |
— | persons.create |
:73-77 |
| ├ لیست کارشناسان فروش | /customer/reagent-list |
— | persons.list.sellers |
:80-84 |
| ├ لیست مشتریان عمده | /customer/omde-list |
— | persons.list.client.omde |
:87-91 |
| ├ لیست مشتریان در انتظار | /customer/omde-non-verified-list |
— | persons.list.client.omde |
:94-98 |
| ├ لیست مشتریان بایگانی | /customer/omde-archived-list |
— | persons.list.client.omde |
:101-105 |
| ├ لیست مشتریان تک | /customer/list |
— | persons.list.client.single |
:108-112 |
| ├ لیست بنکداران | /customer/banker-list |
— | persons.list.client.omde (was commented out in legacy; filled in) |
:115-124 |
| └ لیست مغازه داران حضوری | /customer/shopkeeper-list |
— | persons.list.client.omde (same fix) |
:130-136 |
| شعب و کارگاه | null | GridIcon | — | :144-148 |
| ├ شعب | null | GridIcon | branch |
:150-154 |
| │ └ لیست شعب | /branch/list |
— | branch.list |
:159-163 |
| ├ کارگاه | null | GridIcon | workshop |
:168-172 |
| │ └ لیست کارگاه ها | /workshop/list |
— | workshop.list |
:177-181 |
| محصولات | null | ShoppingBagIcon | product |
:189-193 |
| ├ افزودن محصول | /apps/product/add |
— | product.create |
:196-200 |
| ├ محصولات | /apps/product/list |
— | product.list |
:203-207 |
| └ تغییر درصد محصولات | /product/change/percent |
— | product.percent |
:210-214 |
| سفارش ها | null | BoxIcon | order |
:219-223 |
| ├ ثبت سفارش تک | /order/create |
— | order.create |
:226-230 |
| ├ لیست سفارش های تک | /order/list |
— | order.list |
:233-237 |
| ├ سفارش های ساختنی | /order/make |
— | order.makeOrder |
:240-244 |
| ├ لیست سفارش های عمده | /order/omde-list |
— | order.omde |
:247-251 |
| └ لیست تامین | /order/tamin-list |
— | order.tamin |
:254-258 |
| مدیران | null | UserIcon | user |
:263-267 |
| ├ دسترسی ها | /permission |
— | user.permission |
:270-274 |
| └ لیست مدیران | /user/list |
— | user.list |
:279-283 |
| تخفیفات | null | GiftIcon | offers |
:288-292 |
| ├ تخفیف اتوماتیک | /major/promotion/autopromotion |
— | offers.autopromotion |
:298-302 |
| ├ لیست تخفیفات | /major/promotion/list |
— | offers.list |
:307-311 |
| └ استثناعات تخفیف | /major/offersIgnore |
SlashIcon | offersIgnore |
:315-320 |
| گزارشات و اسناد مالی | null | FileTextIcon | — | :329-333 |
| ├ اسناد مالی | null | DollarSignIcon | document |
:335-339 |
| │ └ لیست اسناد | /finical/list |
— | document.list |
:342-346 |
| ├ فاکتور انتقال | null | BookIcon | invoice |
:354-358 |
| │ └ لیست فاکتورها | /invoice/list |
— | invoice.list |
:363-367 |
| ├ گزارشات | null | FileTextIcon | report |
:372-376 |
| │ ├ گزارش تجمیعی سفارش و اسناد | /report/order_item |
— | report.order.item |
:379-383 |
| │ └ گزارش محصولات | /report/product |
— | report.product |
:386-390 |
| پیامک | null | MailIcon | sms |
:397-401 |
| └ ارسال پیامک | /sms/create |
— | sms.create |
:404-408 |
| آمار | /statics |
BarChartIcon | statics |
:413-417 |
| خبرنامه | /newsletters |
FileTextIcon | newsletters (corrected from legacy's dead newsletters.newsletters) |
:420-429 |
[header] مدیریت (UI) |
— | LayersIcon | — | :434-436 |
| پیکر بندی | null | SettingsIcon | config |
:439-443 |
| ├ مدیریت ثابت ها | /config/manage/const |
— | config.statics |
:446-450 |
| ├ گزارش | /config/manage/report |
— | config.report |
:453-457 |
| └ مدیریت کیف پول | /config/manage/wallet |
— | config.wallet |
:460-464 |
| دسته بندی ها | null | ListIcon | collection |
:469-473 |
| └ لیست دسته بندی ها | /category/list |
— | collection.list |
:478-482 |
| کیف پول | null | CreditCardIcon | wallet |
:487-491 |
| └ لیست کیف پول های شارژ شده | /wallet |
— | wallet.list |
:494-498 |
| شهر ها | null | PercentIcon | cities |
:503-508 |
| └ لیست شهر ها | /city/list |
— | cities.list |
:513-517 |
| دیدگاه ها | /comments |
MessageSquareIcon | comment |
:522-527 |
| نوتیفیکیشن | null | BellIcon | notify |
:530-534 |
| └ ارسال نوتیفیکیشن | /notify/create |
— | notify.create |
:537-541 |
4.2 Branch menu (src/layouts/navMenuItems.ts:551-634)
| Label | url | icon | permissionId | line |
|---|---|---|---|---|
| پیشخوان | /dashboard |
HomeIcon | dashboard |
:552-559 |
| اشخاص | null | UserIcon | persons |
:560-566 |
| ├ اضافه کردن مشتری | /customer/add |
UserPlusIcon | persons.create |
:567-573 |
| └ لیست مشتریان | /customer/list |
UsersIcon | persons.list.client.single |
:575-581 |
| محصولات | /products |
ShoppingBagIcon | product.list |
:584-591 |
| فاکتور خرید | /buy/order/list |
ShoppingBagIcon | product.list |
:592-599 |
| سفارش ها | null | BoxIcon | order |
:609-614 |
| ├ ثبت سفارش | /order/create |
InboxIcon | order.create |
:616-622 |
| └ لیست سفارش | /order/list |
ListIcon | order.list |
:624-630 |
Note: /wallet menu item is gated on wallet.list while the route /wallet is gated on wallet (src/router/admin.ts:458) — a rid mismatch preserved from legacy.
5. Permissions
5.1 Mechanism
- Store
src/stores/users.ts. State:roles: string[] | undefined(undefined = "not fetched"),cityFilters,userInfo(:249-256). - Parent expansion
checkParent()(:220-229) usinggetParents()(:196-204): grantingpersons.list.client.omdeimplicitly grantspersons.list.client,persons.list,persons. It only walks up, never down — a user withwalletstill fails awallet.listcheck (documented legacy behaviour,:216-218). - Getter
hasPermission(permissionId)(:263-269): returnsroles.includes(id)orundefinedwhen roles unloaded (falsy inv-if). This is the port of the legacy globalVue.prototype.$hasPermission. - Action
getRoles(roles)commitscheckParent(roles)(:293-295);deleteRoles()setsundefined(:296-298). - Sources of roles: login response
data.roles.map(x => x.rid)(src/stores/auth.ts:152), orGET users/show→data.permission(src/router/index.ts:88). - Route guard:
to.meta.rolechecked inrouter.beforeEach(src/router/index.ts:119-124). - Menu guard:
node.permissionId(VerticalNavMenu.vue:81-82,NavMenuNode.vue:36-37). - Template guard: each view defines
const hasPermission = (rid: string) => Boolean(users.hasPermission(rid))and usesv-if. E.g.src/views/apps/product/AddProduct.vue:286,src/views/apps/product/ProductList.vue:113. - Table-model guard: some columns are added only if
users.roles.includes(...)—src/views/order/orderListModel.ts:24,src/views/order/OmdeListModel.ts:32,src/views/order/TaminListModel.ts:21,src/views/order/MakeOrderModel.ts:29,src/views/customer/Comments/CommentListModel.ts:21. - Component guard:
src/components/common/ListPageHeader.vue:146renders the add button only when!rid || hasPermission(rid). - No directives (no
v-permission) and no composable — only the store getter.
5.2 Permission vocabulary — src/views/user/permissionConst.ts (treeData, :23)
179 key entries, 174 unique (5 duplicated: persons.list.client.single.view at :131/:139, product.list.view at :191/:199, order.list.view at :252/:275, order.tamin.view at :386/:409, cities.list.view at :640/:648).
| key | label | line |
|---|---|---|
dashboard.view |
پیشخوان | :29 |
persons |
اشخاص | :33 |
persons.create |
اضافه کردن شخص | :37 |
persons.create.seller |
افزودن کارشناس فروش | :41 |
persons.create.client |
افزودن مشتری عمده | :45 |
persons.create.single |
افزودن مشتری تک | :49 |
persons.list.sellers |
لیست کارشناسان فروش | :55 |
persons.list.sellers.edit |
ویرایش اطلاعات کارشناس فروش | :59 |
persons.list.sellers.view.orders |
مشاهده سفارشات کارشناس فروش | :63 |
persons.list.sellers.view.documents |
مشاهده اسناد مالی کارشناس فروش | :67 |
persons.list.sellers.unable.order |
توقف سفارش کارشناس فروش | :71 |
persons.list.sellers.able.order |
فعال سازی سفارش کارشناس فروش | :75 |
persons.list.sellers.able |
فعال سازی حساب کاربری کارشناس فروش | :79 |
persons.list.sellers.unable |
غیر فعال سازی حساب کاربری کارشناس فروش | :83 |
persons.list.client.omde |
لیست مشتریان عمده | :89 |
persons.list.client.omde.view |
مشاهده اطلاعات مشتریان عمده | :93 |
persons.list.client.omde.edit |
ویرایش اطلاعات مشتری عمده | :97 |
persons.list.client.omde.view.orders |
مشاهده سفارشات مشتری عمده | :101 |
persons.list.client.omde.view.documents |
مشاهده اسناد مالی مشتری عمده | :105 |
persons.list.client.omde.unable.order |
توقف سفارش مشتری عمده | :109 |
persons.list.client.omde.able.order |
فعال سازی سفارش مشتری عمده | :113 |
persons.list.client.omde.able |
فعال سازی مشتری عمده | :117 |
persons.list.client.omde.unable |
غیر فعال سازی مشتری عمده | :121 |
persons.list.client.single |
لیست مشتریان تک | :127 |
persons.list.client.single.view |
مشاهده اطلاعات مشتریان تک | :131, :139 |
persons.list.client.single.edit |
ویرایش اطلاعات مشتری تک | :135 |
branch |
شعب | :147 |
branch.create |
افزودن شعب | :152 |
branch.list |
لیست شعب | :156 |
workshop |
کارگاه | :162 |
workshop.create |
افزودن کارگاه | :167 |
workshop.list |
لیست کارگاه ها | :171 |
product |
محصولات | :177 |
product.create |
افزودن محصول | :182 |
product.list |
لیست محصولات | :186 |
product.list.view |
مشاهده لیست محصولات / مشاهده اطلاعات جامع محصول | :191, :199 |
product.list.edit |
ویرایش اطلاعات محصول | :195 |
product.list.variants |
تنوع های محصول | :203 |
product.list.variants.view |
مشاهده تنوع های محصول | :208 |
product.list.variants.edit |
ویرایش تنوع محصول | :212 |
product.list.variants.remove |
حذف تنوع محصول | :216 |
product.list.variants.add |
افزودن تنوع محصول | :220 |
product.list.remove |
حذف محصول | :226 |
product.list.group.edit |
تغییر دسته ای | :230 |
product.percent |
تغییر درصد محصولات | :236 |
order |
سفارشات | :242 |
order.list |
لیست سفارشات | :247 |
order.list.view |
مشاهده لیست سفارشات / مشاهده سفارش | :252, :275 |
order.list.sts |
وضعیت سفارش | :256 |
order.list.sts.view |
مشاهده وضعیت سفارش | :261 |
order.list.sts.remove |
حذف وضعیت | :265 |
order.list.sts.submit |
ثبت وضعیت | :269 |
order.list.view.edit |
ویرایش سفارش | :279 |
order.list.pdf |
دریافت pdf | :283 |
order.omde |
سفارشات عمده | :289 |
order.omde.state |
وضعیت سفارش | :294 |
order.omde.state.view |
مشاهده وضعیت سفارش | :299 |
order.omde.state.submit |
ثبت وضعیت سفارش | :303 |
order.omde.state.remove |
حذف وضعیت سفارش | :307 |
order.omde.view |
مشاهده سفارش | :313 |
order.omde.view.edit |
ویرایش سفارش | :317 |
order.omde.view.remove |
حذف آیتم از سفارش | :321 |
order.omde.remove |
حذف سفارش | :325 |
order.omde.get |
دریافت pdf سفارش | :329 |
order.makeOrder |
سفارشات ساختنی | :335 |
order.makeOrder.state |
وضعیت سفارش | :340 |
order.makeOrder.state.view |
مشاهده وضعیت سفارش | :345 |
order.makeOrder.state.submit |
ثبت وضعیت سفارش | :349 |
order.makeOrder.state.remove |
حذف وضعیت سفارش | :353 |
order.makeOrder.view |
مشاهده سفارش | :359 |
order.makeOrder.view.edit |
ویرایش سفارش | :363 |
order.makeOrder.view.remove |
حذف آیتم از سفارش | :367 |
order.makeOrder.remove |
حذف سفارش | :371 |
order.makeOrder.get |
دریافت pdf سفارش | :375 |
order.tamin |
لیست تامین | :381 |
order.tamin.view |
مشاهده لیست تامین / مشاهده سفارش | :386, :409 |
order.tamin.state |
وضعیت سفارش | :390 |
order.tamin.state.view |
مشاهده وضعیت سفارش | :395 |
order.tamin.state.submit |
ثبت وضعیت سفارش | :399 |
order.tamin.state.remove |
حذف وضعیت سفارش | :403 |
order.tamin.view.edit |
ویرایش سفارش | :413 |
order.create |
ثبت سفارش تک | :419 |
user |
مدیران | :425 |
user.create |
افزودن مدیر | :430 |
user.list |
لیست مدیران | :434 |
user.list.view |
مشاهده لیست مدیران | :439 |
user.list.edit |
ویرایش مدیر | :443 |
user.list.remove |
حذف مدیر | :447 |
user.list.create |
افزودن مدیر | :451 |
user.permission |
دسترسی ها | :457 |
user.permission.edit |
ویرایش دسترسی | :462 |
user.permission.create |
افزودن دسترسی | :466 |
collection |
دسته بندی ها | :474 |
collection.create |
افزودن دسته بندی | :479 |
collection.list |
لیست دسته بندی ها | :483 |
collection.list.view |
مشاهده لیست دسته بندی ها | :488 |
collection.list.edit |
ویرایش دسته | :492 |
collection.list.remove |
حذف دسته | :496 |
document |
اسناد مالی | :504 |
document.list |
لیست اسناد مالی | :509 |
document.list.view |
مشاهده لیست اسناد مالی | :514 |
document.list.edit |
ویرایش سند مالی | :518 |
document.list.remove |
حذف سند مالی | :522 |
document.list.deny |
رد کردن سند مالی | :526 |
document.list.confirm |
تأیید کردن سند مالی | :530 |
document.list.get |
دریافت pdf سند مالی | :534 |
document.create |
وارد کردن سند مالی مشتری | :540 |
offersIgnore |
استثناعات تخفیف | :546 |
offers |
تخفیفات | :550 |
offers.list |
لیست تخفیفات | :555 |
offers.list.view |
مشاهده لیست تخفیفات | :560 |
offers.list.edit |
ویرایش تخفیف عمده | :564 |
offers.list.tak.edit |
ویرایش تخفیف تک | :568 |
offers.list.remove |
حذف تخفیف عمده | :572 |
offers.list.tak.remove |
حذف تخفیف تک | :576 |
offers.create |
تعریف تخفیف عمده | :582 |
offers.create.tak |
تعریف تخفیف تک | :586 |
offers.autopromotion |
تخفیف اتوماتیک | :590 |
invoice |
فاکتور انتقال | :596 |
invoice.create |
افزودن فاکتور | :601 |
invoice.list |
لیست فاکتورها | :605 |
report |
گزارشات | :611 |
report.order.item |
گزارش تجمیعی سفارشات و اسناد مالی | :616 |
report.product |
گزارش محصولات | :620 |
cities |
شهرها | :626 |
cities.create |
افزودن شهر | :631 |
cities.list |
لیست شهرها | :635 |
cities.list.view |
مشاهده لیست شهرها / مشاهده شهر | :640, :648 |
cities.list.remove |
حذف شهر | :644 |
cities.list.view.edit |
ویرایش شهر | :652 |
comment |
دیدگاه ها | :660 |
comment.view.all |
مشاهده لیست دیدگاه ها | :664 |
comment.state |
تغییر وضعیت دیدگاه | :668 |
comment.view |
مشاهده دیدگاه | :672 |
comment.view.edit |
ویرایش دیدگاه | :676 |
comment.remove |
حذف دیدگاه | :680 |
sms |
پیامک | :686 |
sms.create |
ارسال پیامک | :691 |
sms.create.single |
ارسال برای یک شماره تلفن | :696 |
sms.create.all |
ارسال برای همه کاربران | :700 |
sms.create.reagent |
ارسال برای کارشناسان فروش | :704 |
sms.create.omde |
ارسال برای مشتریان عمده | :708 |
sms.create.multiple |
ارسال برای چند شماره تلفن | :712 |
sms.list |
لیست پیامک های ارسال شده | :718 |
wallet |
کیف پول | :725 |
wallet.list |
لیست کیف پول های شارژ شده | :730 |
wallet.charge |
شارژ کیف پول | :734 |
wallet.percent |
تغییر درصد شارژ کیف پول | :738 |
notify |
نوتیفیکیشن | :744 |
notify.create |
ارسال نوتیفیکیشن | :749 |
notify.create.multiple.reagent |
ارسال برای کارشناسان فروش | :754 |
notify.create.single.customer |
ارسال برای یک مشتری | :758 |
notify.create.multiple.omde |
ارسال برای مشتریان عمده | :762 |
notify.create.multiple.customer |
ارسال برای همه مشتریان | :766 |
notify.create.single.admin |
ارسال برای یک مدیر | :770 |
notify.create.multiple.admin |
ارسال برای همه مدیران | :774 |
notify.me |
لیست نوتیفیکیشن های دریافتی | :780 |
config |
پیکربندی | :786 |
config.statics |
مدیریت ثابت ها | :790 |
config.statics.brand |
برند ها | :795 |
config.statics.brand.create |
افزودن برند | :800 |
config.statics.brand.edit |
ویرایش برند | :804 |
config.statics.brand.remove |
حذف برند | :808 |
config.statics.tags |
برچسب ها | :814 |
config.statics.tags.create |
افزودن برچسب | :819 |
config.statics.tags.edit |
ویرایش برچسب | :823 |
config.statics.tags.remove |
حذف برچسب | :827 |
config.wallet |
مدیریت کیف پول | :835 |
config.report |
گزارش | :839 |
statics |
آمار | :845 |
newsletters.newsletters |
خبرنامه | :849 |
custom |
سفارشی | :853 |
custom.config.statics.slider |
اسلایدر موبایل | :857 |
custom.config.statics.tags.banner.ids |
برچسب بنر اول سایت | :861 |
Known gaps (src/views/user/permissionConst.ts:6-15): 4 rids are enforced but absent from the tree and cannot be granted from the UI — buy.order.list, newsletters, persons.create.customer.tak, persons.list.sellers.view. ~36 tree keys are never checked anywhere.
5.3 rids checked via hasPermission() in templates (73 distinct)
cities.list.remove, cities.list.view.edit, collection.list.edit, collection.list.remove, comment.remove, comment.state, comment.view, comment.view.edit, config.statics, config.statics.brand.create, config.statics.brand.edit, config.statics.brand.remove, config.statics.tags.create, config.statics.tags.remove, config.wallet, custom.config.statics.slider, document.list.confirm, document.list.deny, document.list.edit, document.list.remove, offers.list.edit, offers.list.remove, offers.list.tak.edit, offers.list.tak.remove, order.create, order.list.pdf, order.list.sts, order.list.sts.remove, order.list.sts.submit, order.list.view, order.list.view.edit, order.makeOrder.state, order.makeOrder.view, order.makeOrder.view.edit, order.omde.remove, order.omde.state, order.omde.state.remove, order.omde.state.submit, order.omde.view, order.tamin.state, order.tamin.state.remove, order.tamin.state.submit, order.tamin.view, persons.create, persons.create.client, persons.create.seller, persons.list.client.omde.able, persons.list.client.omde.able.order, persons.list.client.omde.edit, persons.list.client.omde.view, persons.list.client.omde.view.documents, persons.list.client.omde.view.orders, persons.list.sellers.able, persons.list.sellers.edit, persons.list.sellers.unable, persons.list.sellers.view, product.list.edit, product.list.remove, product.list.variants, product.list.variants.add, product.list.variants.edit, product.list.variants.remove, product.list.view, sms.create.all, sms.create.multiple, sms.create.omde, sms.create.reagent, sms.create.single, sms.list, user.list.edit, user.list.remove, user.permission, user.permission.create, user.permission.edit, wallet.charge, wallet.percent.
6. Views — full recursive inventory of src/views
6.1 Every file (110 files; 91 .vue, 19 .ts)
| file | LOC | purpose |
|---|---|---|
src/views/Dashboard.vue |
327 | PROTECTED. Merged wholesale+retail dashboard; see §10a. |
src/views/apps/category/Add.vue |
261 | Category create/edit form (shared by Edit.vue). |
src/views/apps/category/CategoryTbl.ts |
21 | XTbl model for the category grid (admin/categories/list). |
src/views/apps/category/Edit.vue |
13 | Thin wrapper over Add.vue for edit mode. |
src/views/apps/category/List.vue |
87 | Category grid + row actions + delete. |
src/views/apps/customer/AddCustomer.vue |
47 | Tabbed shell picking retail/wholesale/reagent customer forms. |
src/views/apps/customer/EditBulkCustomer.vue |
13 | Wrapper → bulkCustomer.vue in edit mode. |
src/views/apps/customer/EditReagent.vue |
12 | Wrapper → ReagentCustomer.vue. |
src/views/apps/customer/EditSingleCustomer.vue |
13 | Wrapper → singleCustomer.vue in edit mode. |
src/views/apps/customer/ReagentCustomer.vue |
205 | Sales-agent (کارشناس فروش) create/edit form. |
src/views/apps/customer/bulkCustomer.vue |
472 | Wholesale customer create/edit form. |
src/views/apps/customer/singleCustomer.vue |
136 | Retail customer create/edit form. |
src/views/apps/product/AddProduct.vue |
579 | PROTECTED (variants). Product create/edit; see §10b. |
src/views/apps/product/ChangePercent.vue |
136 | Bulk percentage-wage change across brand/category. |
src/views/apps/product/EditProduct.vue |
17 | Wrapper → AddProduct.vue with route :id. |
src/views/apps/product/ProductList.vue |
168 | Product grid, row actions, detail modal, delete. |
src/views/apps/product/productListModel.ts |
31 | XTbl model for admin/products/list. |
src/views/auth/Login.vue |
162 | Admin login (mobile + password). |
src/views/branch/AddBranches.vue |
184 | Branch create/edit form. |
src/views/branch/EditBranches.vue |
10 | Wrapper → AddBranches.vue. |
src/views/branch/FacilityList.vue |
112 | Shared branch/workshop list implementation with active toggle. |
src/views/branch/ListBranch.ts |
20 | XTbl model admin/branches/list. |
src/views/branch/ListBranches.vue |
25 | Wrapper → FacilityList.vue for branches. |
src/views/branch/addCustomer.vue |
14 | Branch-mode customer add wrapper. |
src/views/branch/buyList.vue |
80 | Branch "فاکتور خرید" allocations list. |
src/views/branch/dashboard.vue |
93 | Branch-mode dashboard (retail analytics + top 5 products). |
src/views/branch/login.vue |
105 | Branch-mode login. |
src/views/branch/order/create.vue |
253 | Branch order builder. |
src/views/branch/orderListModel.ts |
20 | XTbl model admin/invoices/list (branch buy list). |
src/views/city/CityListModel.ts |
18 | XTbl model admin/misc/cities/list. |
src/views/city/cityList.vue |
127 | City list with inline percent editing + delete. |
src/views/city/createCity.vue |
102 | City create form. |
src/views/customer/Comments/CommentListModel.ts |
30 | XTbl model admin/comments/list. |
src/views/customer/Comments/Comments.vue |
274 | Product-comment moderation (approve/reject/reply/delete). |
src/views/customer/CustomerDetail.vue |
211 | Shared customer detail (omde + single) with orders/docs/log tabs. |
src/views/customer/omde/BankerList.vue |
13 | Wrapper → OmdeCustomerList source banker. |
src/views/customer/omde/OmdeArchivedList.vue |
13 | Wrapper → source archived. |
src/views/customer/omde/OmdeCustomerList.vue |
448 | Shared implementation of the 5 wholesale-customer lists. |
src/views/customer/omde/OmdeList.vue |
18 | Wrapper → source omde. |
src/views/customer/omde/OmdeNonVerifiedList.vue |
13 | Wrapper → source nonVerified. |
src/views/customer/omde/ShopkeeperList.vue |
13 | Wrapper → source shopkeeper. |
src/views/customer/omde/ViewOmdeCustomer.vue |
18 | Wrapper → CustomerDetail.vue (omde mode). |
src/views/customer/omde/omdeListModel.ts |
71 | Factory building all 5 omde list models. |
src/views/customer/reagentList/AgentDetail.vue |
127 | Sales-agent detail view. |
src/views/customer/reagentList/ReagentList.vue |
128 | Sales-agent list + active toggle. |
src/views/customer/reagentList/ReagentListModel.ts |
48 | XTbl model admin/customers-omde/reagent/list. |
src/views/customer/single-customer/SingleCustomer.vue |
131 | Retail customer list + wallet charge modal. |
src/views/customer/single-customer/SingleCustomerListModel.ts |
17 | XTbl model admin/customers-omde/single/list. |
src/views/customer/single-customer/ViewSingleCustomer.vue |
19 | Wrapper → CustomerDetail.vue (single mode). |
src/views/finical/addFinDoc.vue |
358 | Financial-document create/edit (5 document types). |
src/views/finical/list.vue |
135 | Financial-document list + confirm/deny + delete. |
src/views/finical/list_model.ts |
39 | XTbl model admin/documents/list. |
src/views/invoice/AddInvoice.ts |
20 | Product-picker table model for the invoice builder. |
src/views/invoice/AddInvoice.vue |
288 | Branch-allocation (فاکتور) builder. |
src/views/invoice/EditInvoice.vue |
15 | Wrapper → AddInvoice.vue. |
src/views/invoice/ListInvoice.ts |
19 | XTbl model admin/invoices/list. |
src/views/invoice/ListInvoice.vue |
130 | Invoice list + detail modal + delete. |
src/views/manage/Const.vue |
384 | Settings: brands / tags / mobile slider / collections tabs. |
src/views/manage/report.vue |
43 | Dashboard-link shortcut board. |
src/views/manage/wallet.vue |
157 | Wallet config (expiry/percent) + birthday config. |
src/views/newsletters/newsletter.ts |
17 | XTbl model admin/misc/newsletter/list. |
src/views/newsletters/newsletters.vue |
27 | Newsletter subscriber list. |
src/views/notify/create.vue |
60 | Send-notification form. |
src/views/notify/notifyTbl.ts |
16 | XTbl model admin/misc/notify/list (noFilters()). |
src/views/notify/user.vue |
94 | Received notifications + mark-read. |
src/views/offersIgnore/offersIgnore-add-edit.vue |
225 | Promotion exclusion (استثناعات) manager. |
src/views/order/List.vue |
263 | Retail order list + status edit + PDF (stubbed). |
src/views/order/MakeOrder.vue |
224 | Made-to-order (ساختنی) pre-order list + workshop assign. |
src/views/order/MakeOrderModel.ts |
38 | XTbl model admin/pre-orders/list. |
src/views/order/OmdeList.vue |
238 | Wholesale order list + status edit + delete. |
src/views/order/OmdeListModel.ts |
57 | XTbl model admin/orders/wholesale/list. |
src/views/order/TaminList.vue |
209 | Supply (تامین) order list + status edit. |
src/views/order/TaminListModel.ts |
38 | XTbl model admin/orders/tamin/list. |
src/views/order/create.vue |
425 | Admin order builder (cart + custom variant weight). |
src/views/order/orderListModel.ts |
34 | XTbl model admin/orders/list. |
src/views/pages/Error404.vue |
20 | 404 page. |
src/views/pages/Error500.vue |
20 | 500 page. |
src/views/pages/Maintenance.vue |
20 | Maintenance page. |
src/views/pages/NotAuthorized.vue |
20 | 403 page. |
src/views/promotion/PromotionForm.vue |
408 | Shared omde/tak promotion create-edit form. |
src/views/promotion/autopromotion.vue |
79 | Automatic-discount percent config. |
src/views/promotion/promotionListModel.ts |
18 | XTbl model admin/promotions/list. |
src/views/promotion/promotion_add_edit.vue |
26 | Wrapper → PromotionForm (omde mode). |
src/views/promotion/promotion_list.vue |
145 | Promotion list + edit routing + delete. |
src/views/promotion/promotion_tak_add_edit.vue |
26 | Wrapper → PromotionForm (tak mode). |
src/views/reg.vue |
121 | Public self-registration form (no auth guard). |
src/views/report/order_item.vue |
100 | Aggregated order+document report with date filters. |
src/views/report/product.vue |
84 | Per-product sales report. |
src/views/sms/create.vue |
115 | SMS composer + sent log. |
src/views/sms/smsTbl.ts |
17 | XTbl model admin/misc/sms/list. |
src/views/statics/index.vue |
91 | Customer activity log viewer. |
src/views/user/ListUser.vue |
98 | Admin user list + delete. |
src/views/user/Permission.vue |
215 | Permission tree editor per user type. |
src/views/user/UserCreate.vue |
279 | Admin user create/edit. |
src/views/user/UserEdit.vue |
13 | Wrapper → UserCreate.vue. |
src/views/user/myData.vue |
45 | Current-user profile ("my data"). |
src/views/user/permissionConst.ts |
866 | The 179-entry permission tree (treeData). |
src/views/user/userTbl.ts |
18 | XTbl model admin/users/list. |
src/views/wallet/wallet.vue |
44 | Charged-wallet list. |
src/views/wallet/walletTbl.ts |
16 | XTbl model admin/documents/wallet/list (noFilters()). |
src/views/workshop/AddWorkshop.vue |
189 | Workshop create/edit. |
src/views/workshop/EditWorkshop.vue |
10 | Wrapper → AddWorkshop.vue. |
src/views/workshop/ListWorkshop.ts |
14 | XTbl model admin/workshops/list. |
src/views/workshop/ListWorkshop.vue |
25 | Wrapper → FacilityList.vue for workshops. |
6.2 Significant views — detail
src/views/auth/Login.vue
Fields: موبایل (a-input, :23-28), رمز عبور (a-input-password, :36-41, submits on Enter).
Validation (ported VeeValidate required|min:8 / required|min:5, on blur — :79-92): empty → وارد کردن موبایل الزامی است / وارد کردن رمز عبور الزامی است; too short → مقدار ورودی صحیح نیست. Submit disabled until validateForm (:95-97).
Action: auth.login({phone, password}) → POST admin-login; on success router.push('/dashboard/ecommerce') (:105-108); on failure message.error('خطا در ورود') (:111). Legacy's token_mobile (Firebase push) intentionally omitted (:103-104).
src/views/apps/product/ProductList.vue
Table model buildProductListModel() → POST admin/products/list. Columns (productListModel.ts:10-26): id "کد" (filterNumber), ProductImage "تصویر محصول" (slot image, noSort), title "نام محصول" (filterText), brand.title "برند" (filterText), category.collection.title "دسته بندی" (filterText), product_code "کد محصول" (filterText), show_type "نوع نمایش" (filterSelect {1:'سایت',2:'گوشی',3:'سایت و گوشی'}, slot show_type), action "عملیات" (slot op, noSort).
Selection enabled (has-selection, :15) with a count banner تعداد N محصول انتخاب شده است. (:9).
Row actions (:31-56, all hidden in branch mode via v-if="!is_branch"): ویرایش (product.list.edit → /apps/product/edit/{id}), اطلاعات جامع (product.list.view → GET admin/products/{id} → modal), حذف (product.list.remove, a-popconfirm بله/خیر, title آیا مطمئن به حذف محصول مورد نظر هستید ؟).
Delete: POST delete/product/{id} via useDeleteHandler (:138-147), success محصول حذف شد (message.info), fallback error خطا در حذف محصول.
Detail modal (:60-90, width 70%): descriptions نام محصول / کد محصول / برند / دسته بندی / نوع نمایش / تعداد تنوع, plus a variant sub-table (عنوان، وزن، اجرت درصدی، تعداد — :123-128).
No export.
src/views/apps/product/ChangePercent.vue
Bulk percentage-wage change. Loads GET brand + POST category (:121). POST change/percent/count previews affected count (:87); POST change/all applies (:104). Validation: empty percent → message.error('لطفا اجرت درصدی را وارد کنید') (:99).
src/views/apps/category/Add.vue (+ Edit.vue)
Loads the category tree GET admin/categories/tree (:235) and, in edit mode, GET admin/categories/{id} (:243).
Validation (:205-206): !form.type → لطفا نوع نمایش دسته بندی را انتخاب کنید; !form.title → لطفا مقدار را وارد کنید.
Submit: PUT category/{id} (edit, :215) / POST category/ (create, :217); success اطلاعات با موفقیت به ثبت رسید. (:219), error message.error(server ?? 'خطا در ثبت اطلاعات.') (:227).
src/views/apps/category/List.vue
Table admin/categories/list (CategoryTbl.ts:9). Columns: id شماره (filterNumber), image تصویر (slot, noSort), type_str نوع نمایش, title عنوان دسته (filterNumber — legacy quirk preserved, :14), parent.title پدر دسته (filterNumber), opt عملیات (slot). Row actions gated on collection.list.edit / collection.list.remove. Delete via useDeleteHandler, success دسته حذف شد (:69-71).
src/views/apps/customer/bulkCustomer.vue (wholesale customer form)
Loads GET query/reagent (agent list, :422) and POST cities/list (:428); edit mode GET customer/{id} (:435).
Validation (:377-379): fname, lname, mobile each → لطفا مقدار را وارد کنید.
Submit PUT customer/{id} / POST customer/create (:400-402); success اطلاعات با موفقیت به ثبت رسید. (:404); error prefers a mobile-specific server message (:414).
src/views/apps/customer/singleCustomer.vue (retail customer form)
POST cities/list (:112), edit GET customer/{id} (:120). Submit PUT single/{id} / POST single (:97-99) — the single endpoint sets kind=RETAIL server-side (see endpoints.ts:68-75).
src/views/apps/customer/ReagentCustomer.vue (sales agent form)
Edit GET admin/users/{id} (:171); submit PUT users/{id} / POST users (:154-156).
src/views/order/List.vue (retail orders)
Table admin/orders/list (orderListModel.ts:12). Columns: id شماره (filterNumber); customer.fname نام مشتری (filterKey customer.fullName, filterText); customer.lname نام خانوادگی (same filterKey); total_price جمع کل سفارش (slot, filterText); items تعداد آیتم ها (slot items_len, noSort); caj تاریخ ثبت (filterKey createdAt, filterDate, noSort); uaj تاریخ ویرایش (hidden, filterKey updatedAt, filterDate); user ثبت کننده (slot, noSort); conditional on order.tamin.state (:24) → sts_str وضعیت (slot sts) + branch.name نام شعبه (filterText); opt عملیات (slot).
Detail: GET admin/orders/{id} (:197). Status edits POST single/order-sts/edit (:203, :215) — success ویرایش موفق آمیز, error خطا در ویرایش. PDF action is stubbed: message.warning('خروجی PDF هنوز در بکاند جدید پیادهسازی نشده است') (:245), gated on order.list.pdf.
src/views/order/OmdeList.vue (wholesale orders)
Table admin/orders/wholesale/list. Columns (OmdeListModel.ts:19-52): id شماره; customer.name مشتری (filterKey customer.fullName); customer.mobile موبایل (hidden); input جمع کل سفارش (filterKey totalGoldWeightGrams); discount جمع کل تخفیف (درصد); customer.city_id شهر (hidden, slot city — the filterSelect(cityFilters) call stays commented out, legacy quirk :26); items تعداد آیتم ها; caj/uaj dates; conditional on order.omde.state → sts وضعیت (slot sts_str, filterKey status, filterSelect {0:'معلق',1:'ثبت سفارش',2:'تایید مدیر سیستم',3:'در حال تامین',4:'در حال آماده سازی ',5:'تحویل مسئول فروش',6:'تحویل مشتری '}); confirmed وضعیت تایید مشتری (filterSelect {1:'تایید شده',2:'در انتظار تایید'}); opt عملیات.
Detail GET admin/orders/{id} (:171); status POST order-sts/edit (:187, :198); delete → سفارش با موفقیت حذف گردید (:217), gated order.omde.remove.
src/views/order/TaminList.vue
Table admin/orders/tamin/list, title لیست سفارش های عمده (same as OmdeList — legacy quirk, TaminListModel.ts:3-4,11). Columns add wage اجرت (filterKey wageWeightGrams). The status column is gated on order.tamin.state and calls filterSelect() with no argument → an empty dropdown (legacy quirk, :22-24).
src/views/order/MakeOrder.vue
Table admin/pre-orders/list (MakeOrderModel.ts:13). Columns: customer.id شماره (filterKey id), customer.fname/customer.lname (filterKey customer.fullName), color رنگ محصول (filterKey colorName), size سایز محصول (sizeSpec), color_mina رنگ مینا (minaColor), color_stone رنگ سنگ (stoneColor), sum تعداد, time_creat_fa تاریخ ثبت (createdAt, date), time_update_fa (hidden), customer.name ثبت کننده (slot), conditional on order.tamin.state (NOT order.makeOrder.state — legacy quirk :28-29) → sts_str_fa وضعیت; opt عملیات.
Workshop list POST workshop/table {page:1,limit:100} (:168); detail GET admin/pre-orders/{id} (:177); update PUT custom-order/{id} (:193).
src/views/order/create.vue (admin order builder, 425 LOC)
Loads GET order/type and POST single-customer {page:1,limit:500} (:394-395).
Form fields (:126-161): نوع فروش (select, validated), نام دریافت کننده, نام خانوادگی دریافت کننده, استان, شهر, کد پستی, تلفن همراه, تلفن ثابت, توضیحات, آدرس.
Validation (:350-352): customer_id, sts, type each empty → مقدار را وارد نمایید.
Cart table has a حذف column (:251, width 60, centered). Adding an item → message.info('اضافه شد') (:308). Custom weight → POST add/variant (:327), error خطا در ثبت وزن (:336). Submit POST order (:373); success عملیات با موفقیت انجام شد (:380). Cart state from src/stores/cart.ts.
src/views/finical/addFinDoc.vue (358 LOC)
Type-dependent form (:30 نوع سند select). Per-type fields (:60-118): assay — وزن (گرم), عیار, نام آزمایشگاه, انگ, وزن نهایی اعمال شده; transfer — وزن (گرم), حواله به; cheque — مبلغ چک, وزن (گرم), نام صاحب حساب, نام بانک; cash — میزان پول, مظنه; always توضیحات (:116).
Customer lookup GET query/customer (:325); edit GET cash/{id} (:333).
Validation (:296): !form.customer_id → مقدار را وارد نمایید.
Submit PUT cash/{id} / POST cash (:305-307); success اطلاعات با موفقیت به ثبت رسید.
src/views/finical/list.vue
Table admin/documents/list (list_model.ts:9). Columns: id شماره سفارش (filterNumber); type_str نوع سند (filterText); price پول (filterDate — legacy quirk, :13); value وزن نهایی (گرم) (filterDate); to به (hidden); customer.name نام و نام خانوادگی مشتری; note توضیحات; ayar عیار (filterNumber); lab آزمایشگاه; recive_date تاریخ دریافت (filterDate); account_name نام حساب (hidden); bank_name بانک (hidden); caj تاریخ ثبت; uaj بروزرسانی (hidden); confirmed وضعیت تایید سند (slot, filterSelect {0:'تایید نشده',1:'تایید شده'}); price_per_gram مظنه (hidden); original_value وزن طلای داده شده (hidden); ang انگ (hidden); opt عملیات.
Confirm POST cash/confirem/{id} (:111) → تغییرات با موفقیت ثبت شد. Actions gated on document.list.confirm, document.list.deny, document.list.edit, document.list.remove. Delete → سند حذف شد (:124-126).
src/views/customer/omde/OmdeCustomerList.vue (448 LOC, backs 5 routes)
Table model via buildOmdeListModel(source); source ∈ omde|banker|shopkeeper|nonVerified|archived → urls in omdeListModel.ts:12-18.
Columns (omdeListModel.ts:34-65): id کد (filterNumber); fname/lname (filterText, hidden); name نام و نام خانوادگی (filterText); mobile موبایل (hidden, noSort); city_id شهر (hidden, slot city, filterSelect from users.cityFilters); reagent.name کارشناس فروش; gram_level سطح اعتبار; percent درصد; remain بدهی گرمی; rial_remain بدهی ریالی; caj تاریخ ثبت (filterDate); uaj بروزرسانی (hidden); sts_str وضعیت ثبت سفارش (slot sts); active وضعیت حساب کاربری (slot, filterSelect {'1':'فعال','-1':'غیر فعال'}); phone, address, store_name, zone, pasazh_name (all hidden); opt عملیات.
Actions: verify POST major/verify (:352); toggle account POST customer/toggle-active (:391); toggle order permission POST customer/toggle-sts (:403); archive GET major/archive/{id} (:413). All success toasts تغییرات ثبت شد (with trailing space). Errors: خطا در تغییر وضعیت, خطا در بایگانی.
OmdeNonVerifiedList.vue:5 notes a preserved legacy quirk: the step-one confirm rejects instead of accepting.
src/views/customer/single-customer/SingleCustomer.vue
Table admin/customers-omde/single/list. Columns (SingleCustomerListModel.ts:6-12): id کد (filterNumber); fname نام (filterKey fullName); lname نام خانوادگی (filterKey fullName, noSort); phone تلفن تماس (filterKey mobile, noSort); email ایمیل (noSort); branch.name نام شعبه (noSort); op عملیات.
Wallet-charge modal → POST wallet/charge (:99), gated wallet.charge; empty value → لطفا مقدار را وارد کنید (:94); success عملیات با موفقیت انجام شد (:103).
src/views/customer/reagentList/ReagentList.vue
Table admin/customers-omde/reagent/list. Columns per ReagentListModel.ts:16-43, incl. the legacy quirk that name is added twice (visible نام و نام خانوادگی :18, hidden نام :20), plus grams_sell میزان فروش. Toggle active → PUT users/{id} {is_active} (:109).
src/views/customer/CustomerDetail.vue
Parallel loads (:174-177): GET customer/{id}, GET major/dashboard/{id} (catch→undefined), GET admin/customers-omde/{id}/orders (catch→[]), GET admin/customers-omde/{id}/documents (catch→[]), plus lazily GET log/ago/{id} (:191). Orders/documents tabs gated on persons.list.client.omde.view.orders / .view.documents.
src/views/customer/Comments/Comments.vue
Table admin/comments/list; changeState column added only with comment.state (CommentListModel.ts:21-23).
Status change POST comment/sts {id, sts} (:198) → تغییرات ثبت شد ; guards حالت فعلی تایید می باشد / حالت فعلی عدم تایید می باشد when already in that state (:50, :61). Delete DELETE comment/{id} (:215) → دیدگاه حذف شد / خطا در حذف. Reply POST comment (:225).
src/views/invoice/AddInvoice.vue / ListInvoice.vue
Builder: branch list GET branch/list (:253); edit GET branch/product/{id} (:261); submit PUT branch/product/{id} / POST branch/product with {...form, products} (:231-233). Validation !form.branch_id → مقدار را وارد نمایید (:224). Cart from src/stores/invoiceCart.ts. Item table has a حذف column (:202); add → اضافه شد (:214); success عملیات با موفقیت انجام شد (:235).
List columns (ListInvoice.ts:7-14): id ردیف; name نام مشتری (filterKey branch.name); sum_weight جمع کل سفارش; count_item تعداد آیتم ها; caj تاریخ ثبت (filterKey createdAt, filterDate); allocator_user.name ثبت کننده (slot user); note توضیحات; op عملیات (slot actions). Detail GET branch/product/{id} (:113); delete → فاکتور حذف شد (:118-120).
src/views/user/Permission.vue
Loads user types GET const/__list__ut (:118), then the type's rids GET roles/{id} (:136), rendering treeData as a checkable a-tree.
Validation (:147, :151): !ut → لطفاسطح دسترسی را انتخاب کنید; !user → لطفا نام سطح دسترسی را وارد کنید.
Create user type POST const (:160); save rids POST roles/create {ut, roles} (:167) → عملیات با موفقیت انجام شد. Gated on user.permission, user.permission.create, user.permission.edit.
src/views/user/UserCreate.vue / ListUser.vue
Create: parallel GET const/__list__ut + GET admin/users/role-options (:241-242); edit GET admin/users/{id} (:252). Validation: password mismatch → رمز عبور و تأیید آن یکسان نیستند (:200). Submit PUT users/{id} / POST users (:220-222).
List columns (userTbl.ts:6-13): id کد مدیر; fname نام (filterKey fullName); lname نام خانوادگی (filterKey fullName); mobile موبایل (filterText); ut نوع کاربر (filterNumber, slot ut); op عملیات. Delete → کاربر حذف شد (:82-84), gated user.list.remove / user.list.edit.
src/views/manage/Const.vue (384 LOC) — 4 tabs
برند ها (:19): GET brand (:217), POST brand/create (:224) → برند اضافه شد, PUT brand/{id} (:241) → تغییرات ثبت شد, delete via handler → برند حذف شد. Gated config.statics.brand.create/.edit/.remove.
برچسب ها (:65): GET const/__list__tags (:346), PUT const/__list__tags {val} (:275) → برچسب ها ذخیره شد. Gated config.statics.tags.create/.remove.
اسلایدر موبایل (:99-102, only when custom.config.statics.slider, :347): GET const/slider, PUT const/slider {val} (:290) → اسلایدر ذخیره شد; uses ImageUploadInput.
سینی ها (collections, :120): POST ciny {page:1,limit:200} (:299), PUT ciny/{id} / POST ciny/create (:307-309) → عملیات انجام شد, DELETE ciny/{id} (:333) → حذف شد.
src/views/manage/wallet.vue
GET wallet/config + GET wallet/birthday/config (:137-138); POST wallet/config {expire, percent} (:101); POST wallet/birthday/config (:122). Both → تغییرات ثبت شد. Gated config.wallet, wallet.percent.
src/views/promotion/PromotionForm.vue (408 LOC, backs omde + tak)
Loads in parallel (:356-358): POST products {page:1,limit:500}, POST category {page:1,limit:500}, GET brand; plus GET query/customer (:369) and, in edit mode, GET admin/promotions/{id} (:378).
Validation (:323-325): !form.title → لطفا مقدار را وارد کنید; missing discount percent → لطفا مقدار را وارد کنید.
Submit PUT product/variant/promotion/{id} / POST product/variant/promotion (:335-337) → اطلاعات با موفقیت به ثبت رسید.
src/views/promotion/promotion_list.vue
Table admin/promotions/list (promotionListModel.ts:5). Columns: id کد (filterNumber); title نام پروموشن (filterText); promotion_type_str نوع پروموشن; platform پلتفرم نمایش ; status وضعیت (slot sts); start تاریخ شروع (slot startDate); end تاریخ پایان (slot endDate); action عملیات (slot op). Delete DELETE major/promotion/{id} (:129) → تخفیف حذف شد / خطا در حذف تخفیف. Edit/remove gated on offers.list.edit / offers.list.tak.edit / offers.list.remove / offers.list.tak.remove.
src/views/promotion/autopromotion.vue
GET autopromotion (:69), POST autopromotion (:55). Validation: err.discount_percent = 'وارد کردن درصد تخفیف اتوماتیک الزامی است' (:50); success اطلاعات با موفقیت به ثبت رسید. (:59).
src/views/offersIgnore/offersIgnore-add-edit.vue
GET ignore (:198, error خطا در دریافت اطلاعات), POST ignore (:166) → عملیات با موفقیت انجام شد, DELETE ignore/{type} (:184) → حذف شد.
src/views/city/cityList.vue / createCity.vue
List table admin/misc/cities/list (CityListModel.ts:8). Columns: title نام شهر (filterKey name, filterText); level سطح (filterNumber, noSort — no such field in the payload, always blank; legacy quirk :3-4); percent درصد مشتری عمده (slot); percent_aux درصد کارشناس فروش (slot); opt عملیات.
Editing a row loads GET cities/get-percent/{id} + GET cities/get-percent-aux/{id} (:92-93) then PUT cities/{id} (:106) → تغییرات ثبت شد. Gated cities.list.view.edit / cities.list.remove; delete → شهر حذف شد (:120-122).
Create: validation err.title = 'لطفا مقدار را وارد کنید' (:80); POST cities (:85).
src/views/branch/AddBranches.vue / FacilityList.vue / workshop/AddWorkshop.vue
Branch form: GET user/list for the manager select (:162); edit GET branch/{id} (:169); submit PUT branch/{id} / POST branch (:139-141). Validation err.name = 'لطفا مقدار را وارد کنید' (:133).
Workshop form: validation !form.workshop_name → لطفا مقدار را وارد کنید (:141); !user_phone → موبایل سرپرست کارگاه الزامی است (:144); submit PUT workshop/{id} / POST workshop (:151-153).
FacilityList.vue is shared by ListBranches.vue and ListWorkshop.vue; the active toggle calls GET {activePath}/{id} (:104) → تغییرات ثبت شد / خطا در تغییر وضعیت.
Branch columns (ListBranch.ts:10-15): name نام, address آدرس, phone شماره تلفن (filterNumber), user.name مدیر شعبه (filterNumber, noSort), op عملیات. Workshop columns (ListWorkshop.ts:6-9): name, address, phone (filterNumber), op.
src/views/sms/create.vue
Recipient mode gated per rid: sms.create.single, sms.create.multiple, sms.create.all, sms.create.reagent, sms.create.omde; log tab gated sms.list.
Validation (:86-88): empty text → متن پیامک الزامی است; empty mobile → شماره موبایل الزامی است.
Submit POST sms (:94); success پیامک برای ${data.sentCount} شماره ثبت شد (:99); error خطا در ارسال پیامک.
Log table admin/misc/sms/list (smsTbl.ts:6-12): id شماره; text متن پیامک (filterKey body, filterText); mobile گیرندگان (noSort); sent_count تعداد; sender ارسال کننده; caj تاریخ (filterKey createdAt, filterDate).
src/views/notify/create.vue / user.vue
Create validation (:41-42): !form.title → عنوان الزامی است; !form.text → متن پیام الزامی است. POST notify (:47) → نوتیفیکیشن ارسال شد.
Received list admin/misc/notify/list with noFilters() (notifyTbl.ts:11); columns id شماره, title عنوان, text متن (noSort, key differs from legacy's note — :8-9), opt عملیات. Detail GET notify/{id} (:73); mark read GET notify/read/{id} (:81) → علامتگذاری شد.
src/views/report/order_item.vue / product.vue
GET report/order-item and GET report/product, both with { params: filters } (:76, :60) — query-string filters, not the XTable POST envelope. Date filters use DateInput.vue (Jalali → ISO).
src/views/statics/index.vue
GET log/list/customer (:82) then per-customer GET log/ago/{customer} (:71).
src/views/wallet/wallet.vue
Table admin/documents/wallet/list with noFilters() (walletTbl.ts:11). Columns: id شماره; customer.name نام مشتری (filterKey customer.fullName); order.id شماره سفارش (filterText); value میزان شارژ (slot); expire_at تاریخ انقضا (noSort, slot time).
src/views/newsletters/newsletters.vue
Table admin/misc/newsletter/list (newsletter.ts:9). Columns: name نام (filterKey email, filterText); mobile شماره موبایل (filterNumber); op عملیات (slot actions).
src/views/reg.vue (PUBLIC — in noneAuthUrl)
Validation (:85-87): !fname → لطفا نام را وارد کنید; !lname → لطفا نام خانوادگی را وارد کنید; !mobile → لطفا شماره تلفن همراه را وارد کنید. Submit POST customer/create (:92) → اطلاعات با موفقیت ثبت شد.
src/views/branch/dashboard.vue (branch mode)
Parallel GET get-analytics-tak + POST products {page:1,limit:5} (:75-76).
src/views/user/myData.vue
GET admin/users/me (:37). Fallback target for the dashboard guard redirect (src/router/index.ts:121).
6.3 Export
XTable's export buttons (excel/json/print) and the fullscreen toggle were dropped — the legacy endpoint returning a file URL is not implemented in the new backend (src/components/x-table/XTable.vue:8-12). A decorative چاپ (print) button exists with no handler (XTable.vue:32-36). requestData() still sends select, titles and report_title (XTable.vue:263-280), i.e. the server-side export contract is preserved even though no client triggers it.
7. Store (Pinia)
| Store | File | State | Getters | Actions | Endpoints |
|---|---|---|---|---|---|
auth |
src/stores/auth.ts |
userPermission: string[] (:120) |
token (localStorage tokenName, :123), user (JSON from userName, :124-132), isLoggedIn (:133-135) |
login(payload) (:148-162), inject(payload) (:165-170), logout() (:177-184) |
POST admin-login (:149) |
users |
src/stores/users.ts |
cityFilters: Record<id,title> (:251), userInfo (:252), roles?: string[] (:255) |
hasPermission(id) (:263-269) |
setCities (:273), getCities() (:278-285), setUserInfo (:286), deleteUserInfo (:289), getRoles(roles) → checkParent (:293-295), deleteRoles (:296) |
POST cities/list (:280) |
cart |
src/stores/cart.ts |
items: CartItem[] (:45) |
— | addToCart (:49-61), setItems (persist, :64-67), add (:69), remove (:74), updateItemQuantity (:80), clear (:85), init() (:94-123) |
POST product/variant {products_id} (:102) |
invoiceCart |
src/stores/invoiceCart.ts |
items: InvoiceCartItem[] (:28) |
— | setItems (:32), add (:38), + remove/clear |
none (no rehydrate fetch, :12-13) |
layout |
src/stores/layout.ts |
windowWidth, scrollY, theme, isSidebarActive, sidebarCollapsed (:323-329) |
isSmallScreen (<992, :333) |
updateWindowWidth, updateWindowScrollY, toggleSidebar, setSidebarCollapsed (persists admin-panel:sidebarCollapsed) (:336-356) |
— |
auth.login (src/stores/auth.ts:148-162): POST admin-login → users.getRoles(data.roles.map(x => x.rid)), strips roles + ut, then inject() writes tokenName, login='true' (consumed once by App.vue for the welcome toast) and userName to localStorage.
auth.logout (:177-184) — not in legacy: clears token, user, login, and both users.deleteRoles() / deleteUserInfo() (fixes the cross-session permission leak).
cart persistence key: isBranch() ? 'branch_cart_items' : 'admin_cart_items' (:16). cart.init() refetches products and drops out-of-stock, non-pre-order variants (:108).
invoiceCart key: isBranch() ? 'branch_invoice_items' : 'admin_invoice_items' (:17) — deliberately different from legacy, which reused admin_cart_items for both and clobbered data (:6-10).
8. Shared utils
8.1 src/utils/index.ts (19 lines)
| Export | Value | Line |
|---|---|---|
isBranch() |
import.meta.env.MODE === 'branch' |
:12-14 |
tokenName |
isBranch() ? 'branch_token' : 'token' |
:18 |
userName |
isBranch() ? 'branch_user' : 'user' |
:19 |
8.2 src/utils/filters.ts (72 lines) — formatters (Vue 3 has no Vue.filter, so these are plain functions)
| Function | Behaviour | Line |
|---|---|---|
capitalize(value) |
Capitalizes each space-separated word; '' when falsy |
:29-39 |
title(value, replacer='_') |
Capitalizes each replacer-separated word |
:41-51 |
truncate(value, limit) |
value.substring(0, limit) |
:53-55 |
priceUnit(val) |
Thousands separators + ' تومان ' (trailing space); returns input unchanged when falsy, so 0 renders "0" not "0 تومان" |
:58-63 |
price(val) |
Thousands separators only, regex /\B(?=(\d{3})+(?!\d))/g → ','; returns input unchanged when falsy |
:66-71 |
jdate(val) |
moment(val).format('jYYYY/jMM/jDD') |
:74-79 |
jdateTime(val) |
moment(val).format('jYYYY/jMM/jDD - hh:mm:ss') — 12-hour hh, legacy quirk preserved |
:82-87 |
none(val) |
Identity | :89-91 |
Gram/weight: there is no dedicated gram formatter. Weights are rendered raw with a literal گرم suffix at each call site (e.g. Dashboard.vue:23,50,112,116) or via Number(x).toFixed(2) (Dashboard.vue:217,222).
Persian↔English digit conversion: only one implementation, inline in src/components/input/DateInput.vue:121:
raw.replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d))). There is no shared digit-normalisation util and no price-input mask.
8.3 Validators
No validator module exists. Every form hand-rolls a validate() that writes into a reactive err object; the recurring message is لطفا مقدار را وارد کنید / مقدار را وارد نمایید (see §6.2 per view).
8.4 Constants / enums with exact values
| Constant | Values | Source |
|---|---|---|
show_type (product display) |
1: سایت, 2: گوشی, 3: سایت و گوشی |
src/views/apps/product/productListModel.ts:20-22; same in AddProduct.vue:100-102 |
| Wholesale order status | 0: معلق, 1: ثبت سفارش, 2: تایید مدیر سیستم, 3: در حال تامین, 4: در حال آماده سازی (trailing space), 5: تحویل مسئول فروش, 6: تحویل مشتری (trailing space) |
src/views/order/OmdeListModel.ts:35-41 |
| Customer confirm status | 1: تایید شده, 2: در انتظار تایید |
src/views/order/OmdeListModel.ts:48-49, TaminListModel.ts:29-30 |
| Account active status | '1': فعال, '-1': غیر فعال |
src/views/customer/omde/omdeListModel.ts:56-57, ReagentListModel.ts:39-40 |
| Document confirm status | 0: تایید نشده, 1: تایید شده |
src/views/finical/list_model.ts:27-28 |
| Variant active tag | فعال / غیر فعال (green/red) |
src/views/apps/product/AddProduct.vue:186-188 |
| Variant pre-order tag | بله / خیر (blue/none) |
src/views/apps/product/AddProduct.vue:191-193 |
| Dashboard tag colours | success→green, warning→orange, danger→red, primary→blue |
src/views/Dashboard.vue:209-211 |
| Dashboard chart palette | COLOR_PRIMARY '#1c2d56', COLOR_SUCCESS '#28c76f', COLOR_WARNING '#ff9f43', COLOR_DANGER '#ea5455' |
src/views/Dashboard.vue:190-193 |
BaseFormModal widths |
sm:520, md:720, lg:900, xl:1140 |
src/components/common/BaseFormModal.vue:230-235 |
| OMDE list sources | 5 url/title pairs | src/views/customer/omde/omdeListModel.ts:11-19 |
FilterType |
'default' | 'switch' | 'query' | 'date' | 'number' | 'select' |
src/components/x-table/model.ts:15 |
| Sidebar persist key | 'admin-panel:sidebarCollapsed' |
src/stores/layout.ts:308 |
| Small-screen breakpoint | 992 |
src/stores/layout.ts:333 |
9. Components
| Component | File | Role / API |
|---|---|---|
| XTable | src/components/x-table/XTable.vue |
The one surviving table wrapper (legacy had 4 forks, model.ts:9-12). Props model: XTbl, has-selection, v-model = selected rows. Contract: POST model.url with {search, filters, total, limit, page, order, select, titles, report_title} (:263-280), response {rows, query:{total,page,limit}} (:289-294). Exposes fetch(). Toolbar: search (:17-25), filter bar with badge count (:30-37), column show/hide dropdown (:41-53), reload (:55-60), decorative print button with no handler (:32-36). Pagination {current, pageSize, total, showSizeChanger:true} unless _hidePagination (:251-259). Sorting via onTableChange → sort + sortDirection (asc/desc, :331-337). addFilterRow() keys rows with a random float (:315-317). |
| XTbl / Xtc | src/components/x-table/model.ts |
Fluent column API. Xtc(key,title) with .filterText() .filterSwitch() .filterQuery(action) .filterDate() .filterNumber() .filterSelect(itemsMap?) .filterKey(k) .renderSlot(name) .noSort() .hidden() .setWidth(w) (:55-129). XTbl(url,title) with .add(col) .addDefaultFilter() .hidePagination() .hideSearch() .noFilters() .getSortFields() (:139-188). filterSelect accepts an object map value→label and is legitimately callable with no argument (:98-112). Legacy's hideSearch() bug (set _hidePageination) is fixed here (:169-178). |
| ImageUploadInput | src/components/upload/ImageUploadInput.vue |
Single-image uploader (a-upload, list-type="picture-card", max-count=1, accept="image/*"). POST file/create as FormData with file + optional purpose (:88-99). v-model is the stored URL; also emits ok with the whole response. Watches modelValue to keep the thumbnail in sync (:74-84). Preview modal (:32-34). Error بارگذاری فایل ناموفق بود (:103). Replaces legacy's four near-identical uploaders (:6-9). |
| DateInput | src/components/input/DateInput.vue |
Jalali date input. Displays jYYYY/jMM/jDD, emits ISO YYYY-MM-DD (:126). Normalises Persian digits before parsing (:121). Invalid input → inline hint تاریخ معتبر نیست (:128), styled #ea5455 (:135). Replaces vue-persian-datetime-picker (no Vue 3 build, :56-60). Placeholder default مثال: ۱۴۰۳/۰۵/۱۲ (:84). |
| BaseFormModal | src/components/common/BaseFormModal.vue |
Standard add/edit modal shell. Props open, id (presence switches title to editTitle), addTitle, editTitle, loading, submitting, width (sm|md|lg|xl), okText (default ثبت اطلاعات). Cancel text انصراف, mask-closable: false. Emits update:open, submit, cancel. Replaced 16 hand-rolled modals with inconsistent widths (:174-176). |
| ListPageHeader | src/components/common/ListPageHeader.vue |
Permission-gated "add" button for list pages: `v-if="!rid |
| TooltipBtn | src/components/tooltip-btn/TooltipBtn.vue |
Row-action icon button. Props title, popTitle, icon, iconClass (default w-5 h-5). With popTitle wraps in a-popconfirm (بله/خیر, :259-266); without, fires immediately. Icon resolved from a legacy Feather name via resolveIcon (:297), falling back to UnorderedListOutlined. |
| StatisticsCardLine | src/components/statistics-cards/StatisticsCardLine.vue |
KPI tile: icon, color, statistic, statistic-title. The legacy sparkline branch is not ported — every live caller passed hideChart (:307-312). Used 6× on the Dashboard. |
| icons.ts | src/components/icons.ts |
Legacy Feather-name → ant-design icon map, shared by TooltipBtn and the sidebar (:1-6). Exports ICONS and resolveIcon. |
| useDeleteHandler | src/composables/useDeleteHandler.ts |
Shared delete composable (not a component). remove(url, {method?: 'delete'|'post'}); success → message[successType](successMessage) then onSuccess(); failure → Modal.error({ title: server message ?? fallback, okText:'بستن' }) (:43-46). Defaults: با موفقیت حذف شد, success, خطا در حذف. Used by 6 list views. |
| Layout | src/layouts/main/Main.vue (91), components/TheNavbar.vue (223), components/VerticalNavMenu.vue (192), components/NavMenuNode.vue (45), full-page/FullPage.vue (6) |
Main shell = navbar + vertical nav + <router-view>; FullPage is a bare <router-view> for login/error pages. |
Absent: there is no price-input component with a mask or digit conversion. Price/number fields use plain a-input-number (e.g. AddProduct.vue:212,215,218). Digit conversion exists only inside DateInput.vue.
10. PROTECTED zones — precise description
(a) Dashboard — src/views/Dashboard.vue (327 lines)
Route: two paths both mount this one file — /dashboard/ecommerce (dashboard-ecommerce, meta rule admin, role dashboard.view, src/router/admin.ts:54-61) and /dashboard/analytics (dashboard-analytics, rule editor, same role, :36-43). / redirects to /dashboard/ecommerce (:27-30). This merges legacy's two dashboards (آمار عمده فروشی + آمار تک فروشی, two rids) into one screen behind one rid (Dashboard.vue:2-10). A user lacking dashboard.view is redirected to {name:'my-data'} rather than the 403 page (src/router/index.ts:120-122).
Data: a single GET dashboard/overview in onMounted (:301) → admin/dashboard/overview (endpoints.ts:146). Object.assign(data, res), then two client-side sorts preserved from legacy: data.reagent desc by grams_sell (:304-306) and data.lastOrder desc by w (:307-309). Wrapped in a-spin :spinning="loading" (:11); errors only console.error (:311).
Reactive shape (:172-186): customerCount, allgram, ordercount, reagent[], lastOrder[], sumLastOrder, sumLastOrderMonth, ordersTrend[], statusBreakdown[], channelBreakdown[], topProducts[], goldPricePerGram, totalCustomerDebtGrams. Interfaces: OrdersTrendPoint {date,total,retail,wholesale} (:149-154), StatusBreakdownRow {status,label,count} (:155-159), ChannelBreakdownRow {channel,label,count,grams} (:160-165), TopProductRow {title,weight,qty} (:166-170).
Layout, top to bottom:
- KPI row —
.grid-4, fourStatisticsCardLine(:13-38):icon color statistic title line UsersIcon warning ${data.customerCount ?? 0} نفرمشتریان جدید (۳۰ روز گذشته) :14-19ShoppingBagIcon primary ${data.allgram ?? 0} گرمطلای فروش رفته (۳۰ روز گذشته) :20-25ShoppingBagIcon success ${data.ordercount ?? 0} سفارشتعداد سفارشات (۳۰ روز گذشته) :26-31DollarSignIcon danger goldPriceLabelنرخ هر گرم طلای ۱۸ عیار :32-37 - Second KPI row —
.grid-2.mt-4, two tiles (:40-53): BarChartIcon/primary${data.sumLastOrder ?? 0} سفارش"تعداد سفارشات تحویلشده (۳۰ روز گذشته)"; CreditCardIcon/warning${totalDebtLabel} گرم"مجموع بدهی طلای مشتریان". روند سفارشها (۳۰ روز گذشته)—a-card+ apexchart typearea, height 300 (:56-63). Two series:تک فروشی=ordersTrend.map(d => d.retail),عمده فروشی=d.wholesale(:226-232). Options (:233-252): colors[COLOR_PRIMARY, COLOR_SUCCESS], toolbar hidden,dataLabelsoff,stroke {curve:'smooth', width:2}, gradient fillopacityFrom .35 / opacityTo .05, legend top-right, x-axis categoriesjdate(d.date)withrotate:-45, rotateAlways:true, hideOverlappingLabels:true, fontSize 12px, fontWeight 500(added deliberately because 30 daily labels overlapped,:242-249),tooltip {shared:true}..grid-2.mt-4— two donut charts, height 300 (:65-73):وضعیت سفارشات (۳۰ روز گذشته): seriesstatusBreakdown.map(s => s.count), labelss.label, an 8-entry colour array[WARNING, PRIMARY, PRIMARY, WARNING, WARNING, SUCCESS, SUCCESS, DANGER](:259), legend bottom, dataLabels on.تفکیک عمده / تک (۳۰ روز گذشته): serieschannelBreakdown.map(c => c.count), labelsc.label, colors[PRIMARY, SUCCESS], legend bottom, dataLabels on (:265-272).
.grid-2.mt-4— two cards (:75-104):عملکرد کارشناسان فروش: horizontal bar (plotOptions.bar {horizontal:true, borderRadius:4},:280), height 280, rendered onlyv-if="agentChartSeries[0]?.data.length"(:78); seriesگرم فروختهشدهfromreagent[].grams_sell, x categoriesreagent[].name(:275-284). Always followed by ana-table(size small,pagination:false,row-key="id") with columns کارشناس فروش (name) / میزان فروش (گرم) (grams_sell) / تعداد سفارش (order_count) (:84-91,:195-199).محصولات پرفروش (۳۰ روز گذشته): vertical bar, height 280,columnWidth '55%',borderRadius 4, color[COLOR_SUCCESS], dataLabels off, categoriestopProducts[].title, seriesوزن فروختهشده (گرم)fromtopProducts[].weight(:287-296); falls back to<a-empty description="دادهای برای نمایش وجود ندارد" />(:102).
میزان فروش—a-descriptions bordered size=small :column=3(:106-118): تعداد سفارش (۳۰ روز) =sumLastOrder; مجموع وزن (۳۰ روز) =sumLastOrderMonth+گرم; میانگین هر سفارش =avgWeightPerOrder+گرم.سفارشات هفته جاری—a-tableoverdata.lastOrder,size small,pagination {pageSize:10},row-key="id"(:120-137). Columns (:201-207): شماره سفارش (name), مشتری (customer), وزن (w), وضعیت (sts_str), تاریخ (caj).bodyCelloverrides:sts_str→<a-tag :color="tagColor(record.statusColor)">(:129-131);caj→jdate(record.caj)(:132-134).
Computed (:213-223): goldPriceLabel = price(Math.round(Number(goldPricePerGram))) + ' تومان', or '—' when falsy; totalDebtLabel = price(Number(totalCustomerDebtGrams ?? 0).toFixed(2)); avgWeightPerOrder = sumLastOrderMonth / sumLastOrder to 2 dp, '0' when count is 0.
tagColor(c) maps {success:'green', warning:'orange', danger:'red', primary:'blue'}, default '' (:209-211).
Scoped styles only define .mt-4 (1rem) and .mt-3 (.75rem); .grid-2/.grid-3/.grid-4 come from the global utilities.less (:318-326).
No filters, no date-range picker, no export, no drill-down links, no refresh button on this screen — one fetch on mount.
(b) Variants section inside Create/Edit Product — src/views/apps/product/AddProduct.vue
AddProduct.vue (579 lines) backs both create (/apps/product/add, role product.create) and edit (/apps/product/edit/:id/:variant_id?, role product.list.edit) — EditProduct.vue (17 lines) is only a wrapper.
Card gate: the whole تنوع ها card renders only with product.list.variants (:151). The header افزودن تنوع button needs product.list.variants.add (:155-161).
Variant table (:165-196, row-key="__key", :pagination="false", size="small"). Columns (:363-372):
| title | dataIndex | rendering |
|---|---|---|
| تصویر | image |
<img :src="record.image_url" class="variant-thumb"> (40×40, cover, radius 4) or a dashed — placeholder (:173-176, :520-533) |
| عنوان | title |
plain |
| وزن | grams |
plain |
| اجرت درصدی | percentage_pay |
plain |
| تعداد | qty |
plain |
| فعال | is_active |
<a-tag :color="green|red"> فعال / غیر فعال (:185-189) |
پیش سفارش (leading space) |
is_pre_order |
<a-tag :color="blue|''"> بله / خیر (:190-194) |
| عملیات | op |
ویرایش (product.list.variants.edit, EditOutlined) and حذف (product.list.variants.remove, DeleteOutlined → variants.splice(index,1), no confirm dialog) (:177-184) |
Variant modal (:206-270), title افزودن تنوع when editingIndex === null, otherwise ویرایش تنوع:
| Field | Control | Line |
|---|---|---|
| عنوان | a-input |
:208-210 |
| وزن | a-input-number :step="0.001", full width |
:211-213 |
| اجرت درصدی | a-input-number :step="0.1", full width |
:214-216 |
| تعداد | a-input-number, full width |
:217-219 |
| تصویر تنوع | gallery picker — the extra option | :230-257 |
| فعال | a-switch |
:259-261 |
پیش سفارش (leading space) |
a-switch |
:262-264 |
Footer: انصراف (closes) and ثبت (saveVariant) (:266-269).
⭐ THE EXTRA / NEW OPTION — تصویر تنوع (variant image picker), :221-257 + :315-361 + :519-568:
- It is not an uploader. It is a picker over the product's already-saved gallery: the variant points at the same
ProductImagerow the gallery shows, so one photo can back several weights (:222-224). - Source data is
gallery = ref<GalleryImage[]>([])whereGalleryImage {id, url, role, sortOrder}(:318-324), populated only in edit mode fromdata.galleryonGET product/show/{id}(:487). On create it is empty, because uploaded files have noProductImagerows (hence no ids) until the first save (:226-228,:315-317). - UI when
gallery.length(:231-250): a.variant-pickerflex-wrap row of 64×64 buttons. The first button is aبدون تصویر("no image") tile callingpickVariantImage(null), highlighted when!variantForm.image_id(:232-239). Then one<img>button per gallery image, highlighted (.is-on, blue#1890ff2px border,:556-558) whenvariantForm.image_id === img.id. - UI when the gallery is empty (
:251-256): ana-alert type="info" show-iconwhose message is context-dependent —برای این محصول تصویری ثبت نشده است.in edit mode (idtruthy) vsابتدا محصول را ذخیره کنید تا بتوانید تصویر تنوع را انتخاب کنید.on create. pickVariantImage(img)sets bothvariantForm.image_id = img?.id ?? nullandvariantForm.image_url = img?.url ?? null(:358-361).image_idis what gets posted;image_urlis carried purely so the table can draw the thumbnail without a lookup (:336-340).
Variant row type VariantRow (:327-341): __key (client key), id?, title, grams?, percentage_pay?, qty?, is_active, is_pre_order, image_id?, image_url?.
variantForm defaults (:346-356): title:'', grams:undefined, percentage_pay:undefined, qty:0, is_active:true, is_pre_order:false, image_id:null, image_url:null.
showModal(record?, index?) (:374-394): editing copies the record via Object.assign; adding resets and mints __key = 'v-' + Date.now() + '-' + random base36 slice (:381).
saveVariant() (:396-408): the only variant validation is if (!variantForm.title) message.error('عنوان تنوع الزامی است') (:397-400); otherwise pushes or replaces at editingIndex and closes.
Persistence — variants are edited in memory and saved with the product, never through a per-variant endpoint. handleSubmit() (:416-448) builds payload = {...form, cats: form.category_id ? [form.category_id] : [], images: gallerySite, images_mobile: galleryMobile, variants: variants.map(v => ({id, title, grams, percentage_pay, qty, is_active, is_pre_order, image_id: v.image_id ?? null}))} (:420-435) — note image_url and __key are stripped before posting. Then PUT product/update/{id} (edit) or POST product/create (:436-440), success اطلاعات با موفقیت به ثبت رسید. and router.push('/apps/product/list').
On load in edit mode, variants come from data.variants_all mapped with __key: v.id ?? 'v-'+i, grams: Number(v.grams), percentage_pay: Number(v.percentage_pay), image_id: v.image_id ?? null, image_url: v.image?.url ?? null (:488-499).
Rest of the product form (context, also in the same file): image card with 4 slots — تصویر شاخص (indicator_id, purpose product.indicator), تصویر شاخص موبایل (mobile_pic, product.indicator_mobile), گالری تصاویر سایت (gallerySite[], product.gallery), گالری تصاویر موبایل (galleryMobile[], product.gallery_mobile) (:35-82). Basic-info card: کد محصول, نام محصول (the only validated field → لطفا مقدار را وارد کنید, :412), نوع نمایش (1/2/3), دسته بندی, برند, سینی ها (multiple), شرح محصول (سایت) textarea, نمایش محصول switch (:84-148). Lookups on mount: GET brand, POST category, GET admin/products/collections (:452-456); category titles get legacy's (${type_str}) suffix (:460-463). Fields with no schema column are deliberately not rendered: delivery_type, message_long_delivery, offer_day, has_warranty, is_exist, tags, and the variant colour/rial-wage fields (:10-13); legacy's per-gallery-image imageBody caption and the 5th "پادکست" audio slot are also dropped (:27-30).
11. Env / config
11.1 import.meta.env usages (only 4 runtime reads)
| Key | Use | Line |
|---|---|---|
VITE_API_BASE_URL |
axios baseURL, falls back to '/api/' |
src/api/client.ts:11 |
BASE_URL |
401 redirect target ${BASE_URL}auth/login |
src/api/client.ts:77 |
BASE_URL |
createWebHistory(...) |
src/router/index.ts:10 |
MODE |
isBranch() → MODE === 'branch' |
src/utils/index.ts:13 |
No other VITE_* variable is read anywhere in src.
11.2 vite.config.ts (70 lines)
defineConfig(({ mode }) => ...); const isBranch = mode === 'branch' (:10).
| Setting | Default mode | branch mode |
Line |
|---|---|---|---|
base |
'/' |
'/branch/' |
:25 |
build.outDir |
'dist' |
'branchDist' |
:51 |
build.emptyOutDir |
true |
true |
:52 |
build.sourcemap |
false |
false |
:53 |
envDirpoints at the repo root (../..), so the app has no.envof its own (:19,:12-13).loadEnv(mode, <repo root>, '')readsADMIN_PANEL_PORT(default 5180) andAPI_PORT(default 3000) (:14-16). Confirmed in the root.env:API_PORT=3000(line 24),ADMIN_PANEL_PORT=5180(line 25).- Alias
@→./src(:27-31). css.preprocessorOptions.less.javascriptEnabled: true(:32-42); ant-design-vue 4 dropped LESSmodifyVars, so theming now lives inApp.vue's<a-config-provider :theme>(:35-38) — LESS only compiles the app's ownfarsi.less/utilities.less.server:host '0.0.0.0',port,strictPort: true(fail loudly rather than drift to another port,:58-60), proxy/api→http://localhost:${apiPort}and/uploads→ same (needed in dev because uploadedProductImage.urlfiles are served by the API's static handler; production nginx already proxies them) (:61-67).
branch mode summary — it is a different application variant, not a theme:
--mode branch → isBranch() true → different routes (branchRoute/fullPageBranchRoute instead of adminRoute, src/router/index.ts:20,27), different menu (branchMenu, navMenuItems.ts:637-639), different localStorage keys (branch_token/branch_user, src/utils/index.ts:18-19; branch_cart_items, src/stores/cart.ts:16; branch_invoice_items, src/stores/invoiceCart.ts:17), a different login page (views/branch/login.vue), different base path and output dir. ProductList.vue:32 additionally hides all row actions in branch mode.
Other config files: tsconfig.json, Dockerfile (COPY dist /usr/share/nginx/html), nginx.conf (proxies /api and /uploads), .dockerignore, .gitignore, public/.