diff --git a/.env.example b/.env.example index a4cf02d..baffbb3 100644 --- a/.env.example +++ b/.env.example @@ -19,7 +19,6 @@ DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGR # --------------------------------------------------------------------------- API_PORT=3000 ADMIN_PANEL_PORT=5180 -USER_APP_PORT=5181 USER_APP_V2_PORT=5182 # API Configuration (kept for any tooling that still reads bare PORT) diff --git a/DELIVERY.md b/DELIVERY.md index 0e0d043..34816d5 100644 --- a/DELIVERY.md +++ b/DELIVERY.md @@ -240,7 +240,7 @@ pnpm dev # all apps via turbo ``` Ports come from the repo-root `.env` (single source of truth): -API 3000, admin-panel 5180, user-app 5181, user-app-v2 5182. +API 3000, admin-panel 5180, user-app-v2 5182. ### Build @@ -263,8 +263,8 @@ docker compose up -d --build > success. During this delivery's compose run that bundle was four days old and > the container was missing every route added since. Tracked as **DEPLOY-03**. -Services: `db` (Postgres 15), `api` (port 3000), `admin-panel`, `user-app`, -`user-app-v2` (each nginx on port 80). The apps expose no host ports — +Services: `db` (Postgres 15), `api` (port 3000), `admin-panel`, +`user-app-v2` (nginx on port 80). The apps expose no host ports — Dokploy/Traefik routes to them by domain. Uploads persist in the `api_uploads` volume; without it every previously uploaded image 404s after a redeploy. diff --git a/DOCKER.md b/DOCKER.md index 2515837..3aac272 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -8,7 +8,7 @@ Dokploy only builds thin images that copy those files (no `pnpm install` on the ```bash pnpm install pnpm build:release -git add -f apps/api/release apps/admin-panel/dist apps/user-app/dist apps/user-app-v2/dist +git add -f apps/api/release apps/admin-panel/dist apps/user-app-v2/dist git commit -m "chore: refresh release artifacts" git push ``` @@ -19,8 +19,7 @@ What this produces: |---|---|---| | API | `apps/api/release/app.js` (+ Prisma under `release/vendor`) | `docker.arvancloud.ir/node:20-bookworm` | | Admin | `apps/admin-panel/dist/` | `docker.arvancloud.ir/nginx:latest` | -| User | `apps/user-app/dist/` | `docker.arvancloud.ir/nginx:latest` | -| User v2 | `apps/user-app-v2/dist/` | `docker.arvancloud.ir/nginx:latest` | +| User | `apps/user-app-v2/dist/` | `docker.arvancloud.ir/nginx:latest` | ## Dokploy Environment @@ -45,8 +44,7 @@ different Compose project name for each application. | Service | Domain | Port | |---|---|---| | `admin-panel` | admin.example.com | **80** | -| `user-app` | app.example.com | **80** | -| `user-app-v2` | app-v2.example.com | **80** | +| `user-app-v2` | app.example.com | **80** | | `api` | (optional) | **3000** | Frontends call `/api` on their own domain; nginx proxies to `api:3000`. diff --git a/apps/admin-panel/src/layouts/components/TheNavbar.vue b/apps/admin-panel/src/layouts/components/TheNavbar.vue index 9e115ad..a0d80d2 100644 --- a/apps/admin-panel/src/layouts/components/TheNavbar.vue +++ b/apps/admin-panel/src/layouts/components/TheNavbar.vue @@ -12,6 +12,9 @@ + + + {{ pageTitle }} @@ -75,6 +78,7 @@ import { computed } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { + ArrowRightOutlined, LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, @@ -104,6 +108,22 @@ const displayName = computed(() => auth.user?.name ?? auth.user?.fullName ?? 'ک const initial = computed(() => displayName.value.trim().charAt(0) || 'ک'); const canSeePermissions = computed(() => Boolean(users.hasPermission('user.permission'))); +function goBack() { + const parent = breadcrumb.value + ?.slice() + .reverse() + .find((c) => c.url && !c.active); + if (parent?.url) { + router.push(parent.url); + return; + } + if (window.history.length > 1) { + router.back(); + return; + } + router.push('/'); +} + async function onLogout() { const left = await auth.logout(); if (!left) router.push('/auth/login'); @@ -133,6 +153,10 @@ async function onLogout() { .nav-right { flex-shrink: 0; } +.nav-back { + color: #1c2d56; + font-size: 16px; +} .page-title { font-size: 16px; color: #1c2d56; diff --git a/apps/admin-panel/src/stores/cart.ts b/apps/admin-panel/src/stores/cart.ts index 6e51a8f..95827ea 100644 --- a/apps/admin-panel/src/stores/cart.ts +++ b/apps/admin-panel/src/stores/cart.ts @@ -23,6 +23,9 @@ export interface CartVariant { price_with_discount?: number; grams?: number; title?: string; + /** Fixed rial making charge on the variant (legacy rial_pay). */ + rial_pay?: number | null; + percentage_pay?: number | null; } export interface CartProduct { @@ -38,6 +41,11 @@ export interface CartItem { product: CartProduct; qty: number; promotion_id?: string | null; + /** + * Operator's per-unit rial charge for this line. Unset means "use the + * variant's rial_pay"; 0 means the operator cleared it. + */ + rial_input?: number | null; } export const useCartStore = defineStore('cart', { @@ -82,6 +90,15 @@ export const useCartStore = defineStore('cart', { this.setItems(this.items); }, + /** Keeps an edited rial charge on the line across quantity changes and reloads. */ + updateItemRial(payload: { index: number; rial: number | null }) { + const item = this.items[payload.index]; + if (!item) return; + const n = payload.rial == null ? 0 : Number(payload.rial); + item.rial_input = Number.isFinite(n) ? n : 0; + this.setItems(this.items); + }, + clear() { this.items = []; this.setItems(this.items); @@ -112,6 +129,7 @@ export const useCartStore = defineStore('cart', { product, qty: cartItem.qty, promotion_id: product.promotion && product.promotion.id ? product.promotion.id : null, + ...(cartItem.rial_input != null ? { rial_input: Number(cartItem.rial_input) } : {}), }); } } diff --git a/apps/admin-panel/src/views/branch/order/create.vue b/apps/admin-panel/src/views/branch/order/create.vue index d232211..f41d23b 100644 --- a/apps/admin-panel/src/views/branch/order/create.vue +++ b/apps/admin-panel/src/views/branch/order/create.vue @@ -82,6 +82,17 @@ {{ record.product.title }} + + +