From b4ffef9b6ff7de79d7109185a4fcd2e09045cef1 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Tue, 6 Oct 2026 20:51:50 +0330 Subject: [PATCH] =?UTF-8?q?rename:=20=DA=A9=D8=A7=D8=B1=DB=8C=D8=B2=20?= =?UTF-8?q?=D9=81=D8=B1=D9=88=D8=B4=20=E2=86=92=20=D9=85=D8=B3=DB=8C=D8=B1?= =?UTF-8?q?=20=D9=81=D8=B1=D9=88=D8=B4,=20=DA=A9=D8=A7=D8=B1=DB=8C=D8=B2?= =?UTF-8?q?=20=D9=BE=D8=B1=D9=88=DA=98=D9=87=20=E2=86=92=20=D8=AA=D8=A7?= =?UTF-8?q?=D8=A8=D9=84=D9=88=DB=8C=20=D9=BE=D8=B1=D9=88=DA=98=D9=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../migration.sql | 2 + apps/api/prisma/schema.prisma | 4 +- apps/api/prisma/seed.ts | 20 +++++----- apps/api/src/lead-forms/lead-forms.service.ts | 4 +- apps/api/src/pipelines/deals-excel.service.ts | 9 +++-- apps/api/src/pipelines/deals.service.ts | 6 +-- apps/api/src/pipelines/pipelines.service.ts | 4 +- .../project-cards-excel.service.ts | 7 +++- .../components/dashboard/HomeMiniPipeline.vue | 2 +- .../components/pipeline/PipelineToolbar.vue | 4 +- apps/web/src/lib/nav-hubs.ts | 12 +++--- apps/web/src/lib/pipeline-board.ts | 2 +- apps/web/src/views/DealFormView.vue | 6 +-- apps/web/src/views/LeadBoardView.vue | 8 ++-- apps/web/src/views/LeadFormBuilderView.vue | 2 +- apps/web/src/views/LeadStagesSettingsView.vue | 8 ++-- apps/web/src/views/LeadsView.vue | 4 +- apps/web/src/views/PersonProfileView.vue | 2 +- apps/web/src/views/PipelineBoardView.vue | 16 ++++---- apps/web/src/views/ProfileView.vue | 2 +- apps/web/src/views/ProjectBoardView.vue | 18 ++++----- apps/web/src/views/ReportsView.vue | 8 ++-- apps/web/src/views/SalesForecastView.vue | 4 +- docs/landing-data.md | 10 ++--- docs/user-guide/README.md | 6 +-- docs/راهنما-و-نقشه-راه.md | 6 +-- docs/راهنمای-کاربری.md | 38 +++++++++---------- docs/قابلیت‌های-منتکت.md | 12 +++--- packages/shared/src/index.ts | 25 +++++++----- 29 files changed, 131 insertions(+), 120 deletions(-) create mode 100644 apps/api/prisma/migrations/20261006200000_rename_default_pipeline/migration.sql diff --git a/apps/api/prisma/migrations/20261006200000_rename_default_pipeline/migration.sql b/apps/api/prisma/migrations/20261006200000_rename_default_pipeline/migration.sql new file mode 100644 index 0000000..b3c2256 --- /dev/null +++ b/apps/api/prisma/migrations/20261006200000_rename_default_pipeline/migration.sql @@ -0,0 +1,2 @@ +-- Rename the default sales pipeline: «کاریز فروش» → «مسیر فروش» +UPDATE "Pipeline" SET "name" = 'مسیر فروش' WHERE "name" = 'کاریز فروش'; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 5d83fc0..8d24e0b 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -1231,7 +1231,7 @@ model PortalOtp { @@index([expiresAt]) } -/// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از کاریز فروش) +/// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از مسیر فروش) model ProjectBoard { id String @id @default(cuid()) organizationId String @@ -1775,7 +1775,7 @@ model AuditLog { @@index([organizationId, entityType, createdAt]) } -/// فیلترهای ذخیره‌شده برای لیست‌ها و کاریزها +/// فیلترهای ذخیره‌شده برای لیست‌ها و مسیرهای فروش /// ستون‌های مخفی هر جدول برای هر کاربر (به‌ازای هر سازمان) model UserTablePreference { id String @id @default(cuid()) diff --git a/apps/api/prisma/seed.ts b/apps/api/prisma/seed.ts index 41e9b12..d7d583b 100644 --- a/apps/api/prisma/seed.ts +++ b/apps/api/prisma/seed.ts @@ -335,7 +335,7 @@ async function main() { pipeline = await prisma.pipeline.create({ data: { organizationId: org.id, - name: 'کاریز فروش', + name: 'مسیر فروش', isDefault: true, stages: { create: DEFAULT_STAGES.map((s, index) => ({ @@ -354,13 +354,13 @@ async function main() { } const servicesPipeline = await prisma.pipeline.findFirst({ - where: { organizationId: org.id, name: 'کاریز خدمات' }, + where: { organizationId: org.id, name: 'مسیر خدمات' }, }); if (!servicesPipeline) { await prisma.pipeline.create({ data: { organizationId: org.id, - name: 'کاریز خدمات', + name: 'مسیر خدمات', isDefault: false, stages: { create: DEFAULT_STAGES.map((s, index) => ({ @@ -377,7 +377,7 @@ async function main() { console.log('Services pipeline already seeded'); } - // کاریز منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه) + // مسیر منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه) const WAITING_STAGES = [ { name: 'پیگیری', isWon: false, isLost: false }, { name: 'مذاکرات اولیه', isWon: false, isLost: false }, @@ -483,7 +483,7 @@ async function main() { ]; let waitingPipeline = await prisma.pipeline.findFirst({ - where: { organizationId: org.id, name: 'کاریز منتظر' }, + where: { organizationId: org.id, name: 'مسیر منتظر' }, include: { stages: { orderBy: { position: 'asc' } } }, }); @@ -491,7 +491,7 @@ async function main() { waitingPipeline = await prisma.pipeline.create({ data: { organizationId: org.id, - name: 'کاریز منتظر', + name: 'مسیر منتظر', isDefault: false, stages: { create: WAITING_STAGES.map((s, index) => ({ @@ -504,7 +504,7 @@ async function main() { }, include: { stages: { orderBy: { position: 'asc' } } }, }); - console.log('Seeded waiting pipeline (کاریز منتظر)'); + console.log('Seeded waiting pipeline (مسیر منتظر)'); } else { console.log('Waiting pipeline already seeded'); } @@ -534,7 +534,7 @@ async function main() { expectedCloseAt: new Date(d.expectedCloseAt), ownerId: owner?.id, position: d.position, - tags: ['دمو-کاریز'], + tags: ['دمو-مسیر'], }; }), }); @@ -763,7 +763,7 @@ async function main() { { organizationId: org.id, authorId: owner?.id, - body: 'در دمو نوا روی گزارش کاریز خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.', + body: 'در دمو نوا روی گزارش مسیر فروش خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.', noteType: 'call_result', color: 'sky', tags: ['دمو'], @@ -971,7 +971,7 @@ async function main() { }; await ensure('deal_moved', { - title: 'جابه‌جایی معامله در کاریز', + title: 'جابه‌جایی معامله در مسیر فروش', body: sampleDeal ? `«${sampleDeal.title}» به مرحله جدید منتقل شد` : 'یک معامله جابه‌جا شد', diff --git a/apps/api/src/lead-forms/lead-forms.service.ts b/apps/api/src/lead-forms/lead-forms.service.ts index dfceced..f68c2d1 100644 --- a/apps/api/src/lead-forms/lead-forms.service.ts +++ b/apps/api/src/lead-forms/lead-forms.service.ts @@ -677,7 +677,7 @@ export class LeadFormsService { })); if (!pipeline?.stages.length) { - throw new BadRequestException('کاریز فعالی برای ثبت معامله وجود ندارد'); + throw new BadRequestException('مسیر فروش فعالی برای ثبت معامله وجود ندارد'); } const stage = @@ -744,7 +744,7 @@ export class LeadFormsService { where: { id, organizationId }, select: { id: true }, }); - if (!found) throw new BadRequestException('کاریز انتخاب‌شده معتبر نیست'); + if (!found) throw new BadRequestException('مسیر فروش انتخاب‌شده معتبر نیست'); } private async assertLeadStage( diff --git a/apps/api/src/pipelines/deals-excel.service.ts b/apps/api/src/pipelines/deals-excel.service.ts index a0abad5..9d941b4 100644 --- a/apps/api/src/pipelines/deals-excel.service.ts +++ b/apps/api/src/pipelines/deals-excel.service.ts @@ -502,7 +502,7 @@ export class DealsExcelService { } /** - * مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از کاریزهای واردشده حذف می‌شوند + * مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از مسیرهای فروش واردشده حذف می‌شوند * تا فقط مراحل خود فایل دیده شوند. */ private async removeUnusedDefaultStages(organizationId: string, pipelineIds: Set) { @@ -634,7 +634,8 @@ export class DealsExcelService { status: DealStatus, ctx: RowContext, ) { - const name = pipelineName?.trim() || 'کاریز فروش'; + const rawName = pipelineName?.trim(); + const name = !rawName || rawName === 'کاریز فروش' ? 'مسیر فروش' : rawName; const cacheKey = name.toLowerCase(); let pipeline = ctx.pipelines.get(cacheKey); if (!pipeline) { @@ -677,7 +678,7 @@ export class DealsExcelService { } const added = await this.pipelines.createStage(organizationId, pipeline.id, wanted); const stage = added.stages.find((s) => s.name === wanted) ?? added.stages.at(-1); - if (!stage) throw new Error('کاریز مرحله‌ای ندارد'); + if (!stage) throw new Error('مسیر فروش مرحله‌ای ندارد'); pipeline.stages = added.stages.map((s) => ({ id: s.id, name: s.name, @@ -688,7 +689,7 @@ export class DealsExcelService { } const stage = byStatusExisting ?? pipeline.stages[0]; - if (!stage) throw new Error('کاریز مرحله‌ای ندارد'); + if (!stage) throw new Error('مسیر فروش مرحله‌ای ندارد'); return { pipelineId: pipeline.id, stageId: stage.id, pipelineName: pipeline.name }; } diff --git a/apps/api/src/pipelines/deals.service.ts b/apps/api/src/pipelines/deals.service.ts index 0a42fe1..03f15ad 100644 --- a/apps/api/src/pipelines/deals.service.ts +++ b/apps/api/src/pipelines/deals.service.ts @@ -92,7 +92,7 @@ export class DealsService { resolvedPipeline.stages[0]; if (!stage) { - throw new BadRequestException('کاریز مرحله‌ای ندارد'); + throw new BadRequestException('مسیر فروش مرحله‌ای ندارد'); } await this.assertLinks(organizationId, actor, dto.personId, dto.companyId); @@ -337,7 +337,7 @@ export class DealsService { where: { id: dto.stageId, pipelineId: deal.pipelineId }, }); if (!stage) { - throw new BadRequestException('مرحله در این کاریز معتبر نیست'); + throw new BadRequestException('مرحله در این مسیر فروش معتبر نیست'); } const status = stage.isWon ? 'won' : stage.isLost ? 'lost' : 'open'; @@ -369,7 +369,7 @@ export class DealsService { organizationId, userId: deal.ownerId, type: 'deal_moved', - title: 'جابه‌جایی معامله در کاریز', + title: 'جابه‌جایی معامله در مسیر فروش', body: `«${deal.title}» به مرحله «${stage.name}» منتقل شد`, link: `/deals/${deal.id}`, entityType: 'deal', diff --git a/apps/api/src/pipelines/pipelines.service.ts b/apps/api/src/pipelines/pipelines.service.ts index 5aecd5a..b40039b 100644 --- a/apps/api/src/pipelines/pipelines.service.ts +++ b/apps/api/src/pipelines/pipelines.service.ts @@ -81,7 +81,7 @@ export class PipelinesService { const created = await this.prisma.pipeline.create({ data: { organizationId, - name: 'کاریز فروش', + name: 'مسیر فروش', isDefault: true, stages: { create: DEFAULT_STAGES.map((s, index) => ({ @@ -389,7 +389,7 @@ export class PipelinesService { where: { id: pipelineId, organizationId }, include: { stages: { orderBy: { position: 'asc' } } }, }); - if (!pipeline) throw new NotFoundException('کاریز پیدا نشد'); + if (!pipeline) throw new NotFoundException('مسیر فروش پیدا نشد'); return pipeline; } diff --git a/apps/api/src/project-boards/project-cards-excel.service.ts b/apps/api/src/project-boards/project-cards-excel.service.ts index fb832ae..75609df 100644 --- a/apps/api/src/project-boards/project-cards-excel.service.ts +++ b/apps/api/src/project-boards/project-cards-excel.service.ts @@ -33,8 +33,8 @@ const CASE_COLUMNS = [ { header: 'نام خانوادگی شخص کارت', key: 'personName', width: 22 }, { header: 'کد دیدار شخص', key: 'personCode', width: 14 }, { header: 'موبایل شخص کارت', key: 'personMobile', width: 16 }, - { header: 'کاریز کارت', key: 'boardName', width: 20 }, - { header: 'مرحله کاریز کارت', key: 'stageName', width: 18 }, + { header: 'تابلوی پروژه کارت', key: 'boardName', width: 20 }, + { header: 'مرحله تابلوی پروژه کارت', key: 'stageName', width: 18 }, { header: 'تاریخ انجام کارت', key: 'doneDate', width: 16 }, { header: 'برچسب', key: 'tag', width: 16 }, ]; @@ -53,6 +53,9 @@ const CASE_ALIASES: Record = { نامخانوادگیشخصکارت: 'personName', کددیدارشخص: 'personCode', موبایلشخصکارت: 'personMobile', + تابلویپروژهکارت: 'boardName', + مرحلهتابلویپروژهکارت: 'stageName', + // سرستون‌های قدیمی (قبل از تغییر نام کاریز → تابلوی پروژه) کاریزکارت: 'boardName', مرحلهکاریزکارت: 'stageName', تاریخانجامکارت: 'doneDate', diff --git a/apps/web/src/components/dashboard/HomeMiniPipeline.vue b/apps/web/src/components/dashboard/HomeMiniPipeline.vue index d2ef146..9253581 100644 --- a/apps/web/src/components/dashboard/HomeMiniPipeline.vue +++ b/apps/web/src/components/dashboard/HomeMiniPipeline.vue @@ -49,7 +49,7 @@ const cols = computed(() => { ('amountMin', { default: '' }); const amountMax = defineModel('amountMax', { default: '' }); const viewOptions = [ - { value: 'kanban' as const, label: 'نمای کاریز', icon: 'grid' as const }, + { value: 'kanban' as const, label: 'نمای ستونی', icon: 'grid' as const }, { value: 'list' as const, label: 'نمای لیست', icon: 'list' as const }, ]; diff --git a/apps/web/src/lib/nav-hubs.ts b/apps/web/src/lib/nav-hubs.ts index e081255..8e9297a 100644 --- a/apps/web/src/lib/nav-hubs.ts +++ b/apps/web/src/lib/nav-hubs.ts @@ -108,7 +108,7 @@ export const NAV_HUBS: NavHubDef[] = [ tint: 'sky', shortcuts: [ { to: '/leads', title: 'فهرست لیدها', icon: 'users', module: 'leads' }, - { to: '/leads/board', title: 'کاریز لیدها', icon: 'filter', module: 'leads' }, + { to: '/leads/board', title: 'مسیر لیدها', icon: 'filter', module: 'leads' }, ], cards: [ { @@ -121,8 +121,8 @@ export const NAV_HUBS: NavHubDef[] = [ }, { to: '/leads/board', - title: 'کاریز لیدها', - desc: 'نمای کانبان و حرکت بین مراحل', + title: 'مسیر لیدها', + desc: 'نمای ستونی و حرکت بین مراحل', icon: 'filter', tint: 'violet', module: 'leads', @@ -130,7 +130,7 @@ export const NAV_HUBS: NavHubDef[] = [ { to: '/settings/lead-stages', title: 'مراحل و وضعیت‌ها', - desc: 'تعریف وضعیت‌ها و ستون‌های کاریز لید', + desc: 'تعریف وضعیت‌ها و ستون‌های مسیر لید', icon: 'layers', tint: 'peach', admin: true, @@ -160,7 +160,7 @@ export const NAV_HUBS: NavHubDef[] = [ cards: [ { to: '/pipeline', - title: 'کاریز فروش', + title: 'مسیر فروش', desc: 'معاملات و مراحل فروش', icon: 'filter', tint: 'mint', @@ -329,7 +329,7 @@ export const NAV_HUBS: NavHubDef[] = [ cards: [ { to: '/projects', - title: 'کاریز پروژه', + title: 'تابلوی پروژه', desc: 'میز کار تیمی: صف، انجام، بازبینی و ارجاع کارت', icon: 'flag', tint: 'violet', diff --git a/apps/web/src/lib/pipeline-board.ts b/apps/web/src/lib/pipeline-board.ts index 5039647..d1d8232 100644 --- a/apps/web/src/lib/pipeline-board.ts +++ b/apps/web/src/lib/pipeline-board.ts @@ -125,7 +125,7 @@ export function currencyUnitLabel(currency: string): string { return currency; } -/** نگاشت برد API به مدل UI کاریز */ +/** نگاشت برد API به مدل UI مسیر فروش */ export function mapBoardToStages( board: { stages: Array<{ diff --git a/apps/web/src/views/DealFormView.vue b/apps/web/src/views/DealFormView.vue index e2241a2..297c908 100644 --- a/apps/web/src/views/DealFormView.vue +++ b/apps/web/src/views/DealFormView.vue @@ -435,7 +435,7 @@ onMounted(load); >