diff --git a/apps/admin-panel/dist/favicon.ico b/apps/admin-panel/dist/favicon.ico index 78ba2d0..ffa7c0a 100644 Binary files a/apps/admin-panel/dist/favicon.ico and b/apps/admin-panel/dist/favicon.ico differ diff --git a/apps/admin-panel/dist/favicon.png b/apps/admin-panel/dist/favicon.png index 7c1cb2d..8b8df04 100644 Binary files a/apps/admin-panel/dist/favicon.png and b/apps/admin-panel/dist/favicon.png differ diff --git a/apps/admin-panel/dist/index.html b/apps/admin-panel/dist/index.html index 9582ffa..1295f4b 100644 --- a/apps/admin-panel/dist/index.html +++ b/apps/admin-panel/dist/index.html @@ -1,17 +1,17 @@ - - - - - - - تهران تاج — پنل مدیریت + + + + + + + تهران تاج — پنل مدیریت - - -
- -
-
- - + + +
+ +
+
+ + diff --git a/apps/admin-panel/index.html b/apps/admin-panel/index.html index 04ce988..5972844 100644 --- a/apps/admin-panel/index.html +++ b/apps/admin-panel/index.html @@ -3,7 +3,8 @@ - + + %BRAND_TITLE% diff --git a/apps/admin-panel/public/favicon.ico b/apps/admin-panel/public/favicon.ico index 78ba2d0..ffa7c0a 100644 Binary files a/apps/admin-panel/public/favicon.ico and b/apps/admin-panel/public/favicon.ico differ diff --git a/apps/admin-panel/public/favicon.png b/apps/admin-panel/public/favicon.png index 7c1cb2d..8b8df04 100644 Binary files a/apps/admin-panel/public/favicon.png and b/apps/admin-panel/public/favicon.png differ diff --git a/apps/api/release/app.js b/apps/api/release/app.js index c427f2b..48ab618 100644 --- a/apps/api/release/app.js +++ b/apps/api/release/app.js @@ -164101,8 +164101,21 @@ var require_prisma_exception_filter = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.PrismaExceptionFilter = void 0; + exports2.uniqueViolationMessage = uniqueViolationMessage; var common_12 = require_common(); var client_1 = require("@prisma/client"); + function uniqueViolationMessage(target) { + const fields = (Array.isArray(target) ? target : [target]).map((t) => String(t ?? "")).join(",").toLowerCase(); + if (fields.includes("mobile")) + return "\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A"; + if (fields.includes("nationalcode")) + return "\u0627\u06CC\u0646 \u06A9\u062F \u0645\u0644\u06CC \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A"; + if (fields.includes("membercode")) + return "\u0627\u06CC\u0646 \u06A9\u062F \u0639\u0636\u0648\u06CC\u062A \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A"; + if (fields.includes("ordernumber")) + return "\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0633\u0641\u0627\u0631\u0634 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A"; + return "\u0627\u06CC\u0646 \u0645\u0642\u062F\u0627\u0631 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A \u0648 \u062A\u06A9\u0631\u0627\u0631\u06CC \u0627\u0633\u062A"; + } var PrismaExceptionFilter = class PrismaExceptionFilter { logger = new common_12.Logger("PrismaExceptionFilter"); catch(exception, host) { @@ -164133,7 +164146,7 @@ var require_prisma_exception_filter = __commonJS({ return response.status(common_12.HttpStatus.CONFLICT).json({ statusCode: common_12.HttpStatus.CONFLICT, error: "Conflict", - message: `Unique constraint failed on: ${exception.meta?.target?.join(", ")}` + message: uniqueViolationMessage(exception.meta?.target) }); case "P2003": return response.status(common_12.HttpStatus.BAD_REQUEST).json({ diff --git a/apps/api/src/prisma/prisma-exception.filter.spec.ts b/apps/api/src/prisma/prisma-exception.filter.spec.ts new file mode 100644 index 0000000..a8b6d86 --- /dev/null +++ b/apps/api/src/prisma/prisma-exception.filter.spec.ts @@ -0,0 +1,21 @@ +import { uniqueViolationMessage } from './prisma-exception.filter'; + +describe('uniqueViolationMessage', () => { + it('names a duplicate mobile in Persian, whether Prisma sends columns or an index name', () => { + expect(uniqueViolationMessage(['mobile'])).toBe('این شماره موبایل قبلاً ثبت شده است'); + expect(uniqueViolationMessage('Customer_mobile_key')).toBe('این شماره موبایل قبلاً ثبت شده است'); + expect(uniqueViolationMessage('Admin_mobile_key')).toBe('این شماره موبایل قبلاً ثبت شده است'); + }); + + it('names a duplicate national code', () => { + expect(uniqueViolationMessage(['nationalCode'])).toBe('این کد ملی قبلاً ثبت شده است'); + }); + + it('never leaks the English Prisma wording, even for an unknown column or no target', () => { + for (const t of [['somethingElse'], undefined, null, []]) { + const msg = uniqueViolationMessage(t); + expect(msg).toBe('این مقدار قبلاً ثبت شده است و تکراری است'); + expect(msg).not.toMatch(/[A-Za-z]/); + } + }); +}); diff --git a/apps/api/src/prisma/prisma-exception.filter.ts b/apps/api/src/prisma/prisma-exception.filter.ts index da93c8f..ca03381 100644 --- a/apps/api/src/prisma/prisma-exception.filter.ts +++ b/apps/api/src/prisma/prisma-exception.filter.ts @@ -2,6 +2,27 @@ import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus, Logger } from '@nest import { Response } from 'express'; import { Prisma } from '@prisma/client'; +/** + * Persian, user-facing text for a unique-index violation. The panel shows + * `message` as-is, and the raw Prisma wording ("Unique constraint failed on: + * mobile") is meaningless to the operator. + * + * `target` is the list of columns on Postgres, or the index name when the + * driver only reports that — so both are matched by substring. + */ +export function uniqueViolationMessage(target: unknown): string { + const fields = (Array.isArray(target) ? target : [target]) + .map((t) => String(t ?? '')) + .join(',') + .toLowerCase(); + + if (fields.includes('mobile')) return 'این شماره موبایل قبلاً ثبت شده است'; + if (fields.includes('nationalcode')) return 'این کد ملی قبلاً ثبت شده است'; + if (fields.includes('membercode')) return 'این کد عضویت قبلاً ثبت شده است'; + if (fields.includes('ordernumber')) return 'این شماره سفارش قبلاً ثبت شده است'; + return 'این مقدار قبلاً ثبت شده است و تکراری است'; +} + /** * Without this, an unmigrated table (P2021), a bad foreign key (P2003), etc. * all surfaced as an opaque "Internal Server Error" with no indication of @@ -44,7 +65,7 @@ export class PrismaExceptionFilter implements ExceptionFilter { return response.status(HttpStatus.CONFLICT).json({ statusCode: HttpStatus.CONFLICT, error: 'Conflict', - message: `Unique constraint failed on: ${(exception.meta?.target as string[] | undefined)?.join(', ')}`, + message: uniqueViolationMessage(exception.meta?.target), }); case 'P2003': // foreign key constraint violation return response.status(HttpStatus.BAD_REQUEST).json({