rename: کاریز فروش → مسیر فروش, کاریز پروژه → تابلوی پروژه

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-06 20:51:50 +03:30
co-authored by Cursor
parent b3784818fc
commit b4ffef9b6f
29 changed files with 131 additions and 120 deletions
@@ -0,0 +1,2 @@
-- Rename the default sales pipeline: «کاریز فروش» → «مسیر فروش»
UPDATE "Pipeline" SET "name" = 'مسیر فروش' WHERE "name" = 'کاریز فروش';
+2 -2
View File
@@ -1231,7 +1231,7 @@ model PortalOtp {
@@index([expiresAt]) @@index([expiresAt])
} }
/// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از کاریز فروش) /// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از مسیر فروش)
model ProjectBoard { model ProjectBoard {
id String @id @default(cuid()) id String @id @default(cuid())
organizationId String organizationId String
@@ -1775,7 +1775,7 @@ model AuditLog {
@@index([organizationId, entityType, createdAt]) @@index([organizationId, entityType, createdAt])
} }
/// فیلترهای ذخیره‌شده برای لیست‌ها و کاریزها /// فیلترهای ذخیره‌شده برای لیست‌ها و مسیرهای فروش
/// ستون‌های مخفی هر جدول برای هر کاربر (به‌ازای هر سازمان) /// ستون‌های مخفی هر جدول برای هر کاربر (به‌ازای هر سازمان)
model UserTablePreference { model UserTablePreference {
id String @id @default(cuid()) id String @id @default(cuid())
+10 -10
View File
@@ -335,7 +335,7 @@ async function main() {
pipeline = await prisma.pipeline.create({ pipeline = await prisma.pipeline.create({
data: { data: {
organizationId: org.id, organizationId: org.id,
name: 'کاریز فروش', name: 'مسیر فروش',
isDefault: true, isDefault: true,
stages: { stages: {
create: DEFAULT_STAGES.map((s, index) => ({ create: DEFAULT_STAGES.map((s, index) => ({
@@ -354,13 +354,13 @@ async function main() {
} }
const servicesPipeline = await prisma.pipeline.findFirst({ const servicesPipeline = await prisma.pipeline.findFirst({
where: { organizationId: org.id, name: 'کاریز خدمات' }, where: { organizationId: org.id, name: 'مسیر خدمات' },
}); });
if (!servicesPipeline) { if (!servicesPipeline) {
await prisma.pipeline.create({ await prisma.pipeline.create({
data: { data: {
organizationId: org.id, organizationId: org.id,
name: 'کاریز خدمات', name: 'مسیر خدمات',
isDefault: false, isDefault: false,
stages: { stages: {
create: DEFAULT_STAGES.map((s, index) => ({ create: DEFAULT_STAGES.map((s, index) => ({
@@ -377,7 +377,7 @@ async function main() {
console.log('Services pipeline already seeded'); console.log('Services pipeline already seeded');
} }
// کاریز منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه) // مسیر منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه)
const WAITING_STAGES = [ const WAITING_STAGES = [
{ name: 'پیگیری', isWon: false, isLost: false }, { name: 'پیگیری', isWon: false, isLost: false },
{ name: 'مذاکرات اولیه', isWon: false, isLost: false }, { name: 'مذاکرات اولیه', isWon: false, isLost: false },
@@ -483,7 +483,7 @@ async function main() {
]; ];
let waitingPipeline = await prisma.pipeline.findFirst({ let waitingPipeline = await prisma.pipeline.findFirst({
where: { organizationId: org.id, name: 'کاریز منتظر' }, where: { organizationId: org.id, name: 'مسیر منتظر' },
include: { stages: { orderBy: { position: 'asc' } } }, include: { stages: { orderBy: { position: 'asc' } } },
}); });
@@ -491,7 +491,7 @@ async function main() {
waitingPipeline = await prisma.pipeline.create({ waitingPipeline = await prisma.pipeline.create({
data: { data: {
organizationId: org.id, organizationId: org.id,
name: 'کاریز منتظر', name: 'مسیر منتظر',
isDefault: false, isDefault: false,
stages: { stages: {
create: WAITING_STAGES.map((s, index) => ({ create: WAITING_STAGES.map((s, index) => ({
@@ -504,7 +504,7 @@ async function main() {
}, },
include: { stages: { orderBy: { position: 'asc' } } }, include: { stages: { orderBy: { position: 'asc' } } },
}); });
console.log('Seeded waiting pipeline (کاریز منتظر)'); console.log('Seeded waiting pipeline (مسیر منتظر)');
} else { } else {
console.log('Waiting pipeline already seeded'); console.log('Waiting pipeline already seeded');
} }
@@ -534,7 +534,7 @@ async function main() {
expectedCloseAt: new Date(d.expectedCloseAt), expectedCloseAt: new Date(d.expectedCloseAt),
ownerId: owner?.id, ownerId: owner?.id,
position: d.position, position: d.position,
tags: ['دمو-کاریز'], tags: ['دمو-مسیر'],
}; };
}), }),
}); });
@@ -763,7 +763,7 @@ async function main() {
{ {
organizationId: org.id, organizationId: org.id,
authorId: owner?.id, authorId: owner?.id,
body: 'در دمو نوا روی گزارش کاریز خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.', body: 'در دمو نوا روی گزارش مسیر فروش خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.',
noteType: 'call_result', noteType: 'call_result',
color: 'sky', color: 'sky',
tags: ['دمو'], tags: ['دمو'],
@@ -971,7 +971,7 @@ async function main() {
}; };
await ensure('deal_moved', { await ensure('deal_moved', {
title: 'جابه‌جایی معامله در کاریز', title: 'جابه‌جایی معامله در مسیر فروش',
body: sampleDeal body: sampleDeal
? `«${sampleDeal.title}» به مرحله جدید منتقل شد` ? `«${sampleDeal.title}» به مرحله جدید منتقل شد`
: 'یک معامله جابه‌جا شد', : 'یک معامله جابه‌جا شد',
@@ -677,7 +677,7 @@ export class LeadFormsService {
})); }));
if (!pipeline?.stages.length) { if (!pipeline?.stages.length) {
throw new BadRequestException('کاریز فعالی برای ثبت معامله وجود ندارد'); throw new BadRequestException('مسیر فروش فعالی برای ثبت معامله وجود ندارد');
} }
const stage = const stage =
@@ -744,7 +744,7 @@ export class LeadFormsService {
where: { id, organizationId }, where: { id, organizationId },
select: { id: true }, select: { id: true },
}); });
if (!found) throw new BadRequestException('کاریز انتخاب‌شده معتبر نیست'); if (!found) throw new BadRequestException('مسیر فروش انتخاب‌شده معتبر نیست');
} }
private async assertLeadStage( private async assertLeadStage(
@@ -502,7 +502,7 @@ export class DealsExcelService {
} }
/** /**
* مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از کاریزهای واردشده حذف می‌شوند * مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از مسیرهای فروش واردشده حذف می‌شوند
* تا فقط مراحل خود فایل دیده شوند. * تا فقط مراحل خود فایل دیده شوند.
*/ */
private async removeUnusedDefaultStages(organizationId: string, pipelineIds: Set<string>) { private async removeUnusedDefaultStages(organizationId: string, pipelineIds: Set<string>) {
@@ -634,7 +634,8 @@ export class DealsExcelService {
status: DealStatus, status: DealStatus,
ctx: RowContext, ctx: RowContext,
) { ) {
const name = pipelineName?.trim() || 'کاریز فروش'; const rawName = pipelineName?.trim();
const name = !rawName || rawName === 'کاریز فروش' ? 'مسیر فروش' : rawName;
const cacheKey = name.toLowerCase(); const cacheKey = name.toLowerCase();
let pipeline = ctx.pipelines.get(cacheKey); let pipeline = ctx.pipelines.get(cacheKey);
if (!pipeline) { if (!pipeline) {
@@ -677,7 +678,7 @@ export class DealsExcelService {
} }
const added = await this.pipelines.createStage(organizationId, pipeline.id, wanted); const added = await this.pipelines.createStage(organizationId, pipeline.id, wanted);
const stage = added.stages.find((s) => s.name === wanted) ?? added.stages.at(-1); 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) => ({ pipeline.stages = added.stages.map((s) => ({
id: s.id, id: s.id,
name: s.name, name: s.name,
@@ -688,7 +689,7 @@ export class DealsExcelService {
} }
const stage = byStatusExisting ?? pipeline.stages[0]; 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 }; return { pipelineId: pipeline.id, stageId: stage.id, pipelineName: pipeline.name };
} }
+3 -3
View File
@@ -92,7 +92,7 @@ export class DealsService {
resolvedPipeline.stages[0]; resolvedPipeline.stages[0];
if (!stage) { if (!stage) {
throw new BadRequestException('کاریز مرحله‌ای ندارد'); throw new BadRequestException('مسیر فروش مرحله‌ای ندارد');
} }
await this.assertLinks(organizationId, actor, dto.personId, dto.companyId); await this.assertLinks(organizationId, actor, dto.personId, dto.companyId);
@@ -337,7 +337,7 @@ export class DealsService {
where: { id: dto.stageId, pipelineId: deal.pipelineId }, where: { id: dto.stageId, pipelineId: deal.pipelineId },
}); });
if (!stage) { if (!stage) {
throw new BadRequestException('مرحله در این کاریز معتبر نیست'); throw new BadRequestException('مرحله در این مسیر فروش معتبر نیست');
} }
const status = stage.isWon ? 'won' : stage.isLost ? 'lost' : 'open'; const status = stage.isWon ? 'won' : stage.isLost ? 'lost' : 'open';
@@ -369,7 +369,7 @@ export class DealsService {
organizationId, organizationId,
userId: deal.ownerId, userId: deal.ownerId,
type: 'deal_moved', type: 'deal_moved',
title: 'جابه‌جایی معامله در کاریز', title: 'جابه‌جایی معامله در مسیر فروش',
body: `«${deal.title}» به مرحله «${stage.name}» منتقل شد`, body: `«${deal.title}» به مرحله «${stage.name}» منتقل شد`,
link: `/deals/${deal.id}`, link: `/deals/${deal.id}`,
entityType: 'deal', entityType: 'deal',
+2 -2
View File
@@ -81,7 +81,7 @@ export class PipelinesService {
const created = await this.prisma.pipeline.create({ const created = await this.prisma.pipeline.create({
data: { data: {
organizationId, organizationId,
name: 'کاریز فروش', name: 'مسیر فروش',
isDefault: true, isDefault: true,
stages: { stages: {
create: DEFAULT_STAGES.map((s, index) => ({ create: DEFAULT_STAGES.map((s, index) => ({
@@ -389,7 +389,7 @@ export class PipelinesService {
where: { id: pipelineId, organizationId }, where: { id: pipelineId, organizationId },
include: { stages: { orderBy: { position: 'asc' } } }, include: { stages: { orderBy: { position: 'asc' } } },
}); });
if (!pipeline) throw new NotFoundException('کاریز پیدا نشد'); if (!pipeline) throw new NotFoundException('مسیر فروش پیدا نشد');
return pipeline; return pipeline;
} }
@@ -33,8 +33,8 @@ const CASE_COLUMNS = [
{ header: 'نام خانوادگی شخص کارت', key: 'personName', width: 22 }, { header: 'نام خانوادگی شخص کارت', key: 'personName', width: 22 },
{ header: 'کد دیدار شخص', key: 'personCode', width: 14 }, { header: 'کد دیدار شخص', key: 'personCode', width: 14 },
{ header: 'موبایل شخص کارت', key: 'personMobile', width: 16 }, { header: 'موبایل شخص کارت', key: 'personMobile', width: 16 },
{ header: 'کاریز کارت', key: 'boardName', width: 20 }, { header: 'تابلوی پروژه کارت', key: 'boardName', width: 20 },
{ header: 'مرحله کاریز کارت', key: 'stageName', width: 18 }, { header: 'مرحله تابلوی پروژه کارت', key: 'stageName', width: 18 },
{ header: 'تاریخ انجام کارت', key: 'doneDate', width: 16 }, { header: 'تاریخ انجام کارت', key: 'doneDate', width: 16 },
{ header: 'برچسب', key: 'tag', width: 16 }, { header: 'برچسب', key: 'tag', width: 16 },
]; ];
@@ -53,6 +53,9 @@ const CASE_ALIASES: Record<string, string> = {
نامخانوادگیشخصکارت: 'personName', نامخانوادگیشخصکارت: 'personName',
کددیدارشخص: 'personCode', کددیدارشخص: 'personCode',
موبایلشخصکارت: 'personMobile', موبایلشخصکارت: 'personMobile',
تابلویپروژهکارت: 'boardName',
مرحلهتابلویپروژهکارت: 'stageName',
// سرستون‌های قدیمی (قبل از تغییر نام کاریز → تابلوی پروژه)
کاریزکارت: 'boardName', کاریزکارت: 'boardName',
مرحلهکاریزکارت: 'stageName', مرحلهکاریزکارت: 'stageName',
تاریخانجامکارت: 'doneDate', تاریخانجامکارت: 'doneDate',
@@ -49,7 +49,7 @@ const cols = computed<Col[]>(() => {
<HomePanelEmpty <HomePanelEmpty
v-if="!cols.length" v-if="!cols.length"
title="هنوز معامله‌ای در کاریز نیست" title="هنوز معامله‌ای در مسیر فروش نیست"
description="اولین معامله را بسازید تا جریان فروش اینجا دیده شود." description="اولین معامله را بسازید تا جریان فروش اینجا دیده شود."
action-label="معامله جدید" action-label="معامله جدید"
:action-to="{ name: 'deal-new' }" :action-to="{ name: 'deal-new' }"
@@ -18,7 +18,7 @@ withDefaults(
{ {
canCreate: false, canCreate: false,
icon: 'briefcase', icon: 'briefcase',
createOptionLabel: 'کاریز جدید…', createOptionLabel: 'مسیر فروش جدید…',
showAmountFilters: true, showAmountFilters: true,
}, },
); );
@@ -34,7 +34,7 @@ const amountMin = defineModel<string>('amountMin', { default: '' });
const amountMax = defineModel<string>('amountMax', { default: '' }); const amountMax = defineModel<string>('amountMax', { default: '' });
const viewOptions = [ 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 }, { value: 'list' as const, label: 'نمای لیست', icon: 'list' as const },
]; ];
+6 -6
View File
@@ -108,7 +108,7 @@ export const NAV_HUBS: NavHubDef[] = [
tint: 'sky', tint: 'sky',
shortcuts: [ shortcuts: [
{ to: '/leads', title: 'فهرست لیدها', icon: 'users', module: 'leads' }, { to: '/leads', title: 'فهرست لیدها', icon: 'users', module: 'leads' },
{ to: '/leads/board', title: 'کاریز لیدها', icon: 'filter', module: 'leads' }, { to: '/leads/board', title: 'مسیر لیدها', icon: 'filter', module: 'leads' },
], ],
cards: [ cards: [
{ {
@@ -121,8 +121,8 @@ export const NAV_HUBS: NavHubDef[] = [
}, },
{ {
to: '/leads/board', to: '/leads/board',
title: 'کاریز لیدها', title: 'مسیر لیدها',
desc: 'نمای کانبان و حرکت بین مراحل', desc: 'نمای ستونی و حرکت بین مراحل',
icon: 'filter', icon: 'filter',
tint: 'violet', tint: 'violet',
module: 'leads', module: 'leads',
@@ -130,7 +130,7 @@ export const NAV_HUBS: NavHubDef[] = [
{ {
to: '/settings/lead-stages', to: '/settings/lead-stages',
title: 'مراحل و وضعیت‌ها', title: 'مراحل و وضعیت‌ها',
desc: 'تعریف وضعیت‌ها و ستون‌های کاریز لید', desc: 'تعریف وضعیت‌ها و ستون‌های مسیر لید',
icon: 'layers', icon: 'layers',
tint: 'peach', tint: 'peach',
admin: true, admin: true,
@@ -160,7 +160,7 @@ export const NAV_HUBS: NavHubDef[] = [
cards: [ cards: [
{ {
to: '/pipeline', to: '/pipeline',
title: 'کاریز فروش', title: 'مسیر فروش',
desc: 'معاملات و مراحل فروش', desc: 'معاملات و مراحل فروش',
icon: 'filter', icon: 'filter',
tint: 'mint', tint: 'mint',
@@ -329,7 +329,7 @@ export const NAV_HUBS: NavHubDef[] = [
cards: [ cards: [
{ {
to: '/projects', to: '/projects',
title: 'کاریز پروژه', title: 'تابلوی پروژه',
desc: 'میز کار تیمی: صف، انجام، بازبینی و ارجاع کارت', desc: 'میز کار تیمی: صف، انجام، بازبینی و ارجاع کارت',
icon: 'flag', icon: 'flag',
tint: 'violet', tint: 'violet',
+1 -1
View File
@@ -125,7 +125,7 @@ export function currencyUnitLabel(currency: string): string {
return currency; return currency;
} }
/** نگاشت برد API به مدل UI کاریز */ /** نگاشت برد API به مدل UI مسیر فروش */
export function mapBoardToStages( export function mapBoardToStages(
board: { board: {
stages: Array<{ stages: Array<{
+3 -3
View File
@@ -435,7 +435,7 @@ onMounted(load);
> >
<template #lead> <template #lead>
<span class="crumb"> <span class="crumb">
<RouterLink :to="{ name: 'pipeline' }">کاریز فروش</RouterLink> <RouterLink :to="{ name: 'pipeline' }">مسیر فروش</RouterLink>
<span>›</span> <span>›</span>
<strong>{{ pageTitle }}</strong> <strong>{{ pageTitle }}</strong>
</span> </span>
@@ -678,13 +678,13 @@ onMounted(load);
</UiSelect> </UiSelect>
</UiField> </UiField>
<UiField label="کاریز فروش"> <UiField label="مسیر فروش">
<UiSelect :model-value="pipelineId" @update:model-value="onPipelineChange($event)"> <UiSelect :model-value="pipelineId" @update:model-value="onPipelineChange($event)">
<option v-for="p in pipelines" :key="p.id" :value="p.id">{{ p.name }}</option> <option v-for="p in pipelines" :key="p.id" :value="p.id">{{ p.name }}</option>
</UiSelect> </UiSelect>
</UiField> </UiField>
<UiField label="مرحله کاریز" required> <UiField label="مرحله" required>
<UiSelect v-model="stageId" required> <UiSelect v-model="stageId" required>
<option v-for="s in stages" :key="s.id" :value="s.id">{{ s.name }}</option> <option v-for="s in stages" :key="s.id" :value="s.id">{{ s.name }}</option>
</UiSelect> </UiSelect>
+4 -4
View File
@@ -33,7 +33,7 @@ async function load() {
error.value = error.value =
axios.isAxiosError(e) && !e.response axios.isAxiosError(e) && !e.response
? 'اتصال به API برقرار نشد.' ? 'اتصال به API برقرار نشد.'
: 'بارگذاری کاریز لیدها ناموفق بود.'; : 'بارگذاری مسیر لیدها ناموفق بود.';
} finally { } finally {
loading.value = false; loading.value = false;
} }
@@ -107,7 +107,7 @@ onMounted(load);
<template> <template>
<section class="page"> <section class="page">
<UiPageHeader title="کاریز لیدها" lead="کشیدن و رها کردن برای تغییر وضعیت"> <UiPageHeader title="مسیر لیدها" lead="کشیدن و رها کردن برای تغییر وضعیت">
<template #actions> <template #actions>
<RouterLink :to="{ name: 'leads' }" class="link-btn"> <RouterLink :to="{ name: 'leads' }" class="link-btn">
<UiButton variant="ghost" size="sm">فهرست لیدها</UiButton> <UiButton variant="ghost" size="sm">فهرست لیدها</UiButton>
@@ -166,8 +166,8 @@ onMounted(load);
<UiEmptyState <UiEmptyState
v-else-if="!loading" v-else-if="!loading"
title="مرحله‌ای برای کاریز نیست." title="مرحله‌ای برای نمایش در مسیر لیدها نیست."
description="در تنظیمات مراحل لید، نمایش روی کاریز را فعال کنید." description="در تنظیمات مراحل لید، «نمایش روی مسیر» را فعال کنید."
/> />
</section> </section>
</template> </template>
+1 -1
View File
@@ -594,7 +594,7 @@ onMounted(load);
<input v-model="createDeal" type="checkbox" /> <input v-model="createDeal" type="checkbox" />
</label> </label>
</div> </div>
<label v-if="createDeal" class="field pipeline-field">کاریز مقصد <label v-if="createDeal" class="field pipeline-field">مسیر فروش مقصد
<select v-model="pipelineId"> <select v-model="pipelineId">
<option value="">پیش‌فرض سازمان</option> <option value="">پیش‌فرض سازمان</option>
<option v-for="p in pipelines" :key="p.id" :value="p.id">{{ p.name }}</option> <option v-for="p in pipelines" :key="p.id" :value="p.id">{{ p.name }}</option>
@@ -170,7 +170,7 @@ onMounted(load);
<section class="crm-page"> <section class="crm-page">
<UiPageHeader <UiPageHeader
title="مراحل لید" title="مراحل لید"
lead="تعریف وضعیت‌های کاریز لیدها و برچسب‌های تبدیل/از دست‌رفته" lead="تعریف وضعیت‌های مسیر لیدها و برچسب‌های تبدیل/از دست‌رفته"
/> />
<p v-if="okMsg" class="ok">{{ okMsg }}</p> <p v-if="okMsg" class="ok">{{ okMsg }}</p>
@@ -194,7 +194,7 @@ onMounted(load);
</UiField> </UiField>
<label class="check"> <label class="check">
<input v-model="showOnBoard" type="checkbox" /> <input v-model="showOnBoard" type="checkbox" />
نمایش روی کاریز نمایش روی مسیر
</label> </label>
<label class="check"> <label class="check">
<input v-model="isConverted" type="checkbox" /> <input v-model="isConverted" type="checkbox" />
@@ -227,7 +227,7 @@ onMounted(load);
</UiField> </UiField>
<label class="check"> <label class="check">
<input v-model="editShowOnBoard" type="checkbox" /> <input v-model="editShowOnBoard" type="checkbox" />
نمایش روی کاریز نمایش روی مسیر
</label> </label>
<label class="check"> <label class="check">
<input v-model="editConverted" type="checkbox" /> <input v-model="editConverted" type="checkbox" />
@@ -253,7 +253,7 @@ onMounted(load);
<div class="info"> <div class="info">
<strong>{{ stage.name }}</strong> <strong>{{ stage.name }}</strong>
<span class="muted"> <span class="muted">
{{ stage.showOnBoard ? 'روی کاریز' : 'مخفی از کاریز' }} {{ stage.showOnBoard ? 'روی مسیر' : 'مخفی از مسیر' }}
<template v-if="stage.leadCount != null"> <template v-if="stage.leadCount != null">
· {{ toFaDigits(stage.leadCount) }} لید · {{ toFaDigits(stage.leadCount) }} لید
</template> </template>
+2 -2
View File
@@ -1230,11 +1230,11 @@ onBeforeUnmount(() => {
</ul> </ul>
</div> </div>
<TableColumnsMenu table-key="leads" align="end" /> <TableColumnsMenu table-key="leads" align="end" />
<div class="view-toggle" title="نمای لیست / کاریز"> <div class="view-toggle" title="نمای لیست / مسیر">
<button type="button" class="view-btn on" title="لیست"> <button type="button" class="view-btn on" title="لیست">
<UiIcon name="list" :size="16" /> <UiIcon name="list" :size="16" />
</button> </button>
<RouterLink class="view-btn" :to="{ name: 'leads-board' }" title="کاریز"> <RouterLink class="view-btn" :to="{ name: 'leads-board' }" title="مسیر لیدها">
<UiIcon name="grid" :size="16" /> <UiIcon name="grid" :size="16" />
</RouterLink> </RouterLink>
</div> </div>
+1 -1
View File
@@ -870,7 +870,7 @@ watch(() => route.query.tab, readTab, { immediate: true });
<ProfileEmptyState <ProfileEmptyState
v-if="dealsEmpty" v-if="dealsEmpty"
title="هنوز معامله‌ای ثبت نشده!" title="هنوز معامله‌ای ثبت نشده!"
description="اولین معامله این مشتری را در کاریز ثبت کنید یا یک فعالیت پیگیری بسازید." description="اولین معامله این مشتری را در مسیر فروش ثبت کنید یا یک فعالیت پیگیری بسازید."
primary-label="افزودن معامله" primary-label="افزودن معامله"
:primary-to="{ name: 'deal-new', query: { personId: id } }" :primary-to="{ name: 'deal-new', query: { personId: id } }"
secondary-label="افزودن اولین فعالیت" secondary-label="افزودن اولین فعالیت"
+8 -8
View File
@@ -171,7 +171,7 @@ async function load() {
if (axios.isAxiosError(e) && !e.response) { if (axios.isAxiosError(e) && !e.response) {
error.value = 'اتصال به API برقرار نشد.'; error.value = 'اتصال به API برقرار نشد.';
} else { } else {
error.value = 'بارگذاری کاریز ناموفق بود.'; error.value = 'بارگذاری مسیر فروش ناموفق بود.';
} }
} finally { } finally {
loading.value = false; loading.value = false;
@@ -189,7 +189,7 @@ async function createPipeline() {
showCreate.value = false; showCreate.value = false;
await router.replace({ name: 'pipeline', query: { pipelineId: data.id } }); await router.replace({ name: 'pipeline', query: { pipelineId: data.id } });
} catch { } catch {
error.value = 'ساخت کاریز ناموفق بود.'; error.value = 'ساخت مسیر فروش ناموفق بود.';
} finally { } finally {
saving.value = false; saving.value = false;
} }
@@ -451,7 +451,7 @@ function onImportStart() {
async function onImportDone(data: ImportContactsResult) { async function onImportDone(data: ImportContactsResult) {
importMsg.value = `وارد شد: ${data.created} · تکراری رد شد: ${data.skipped}${ importMsg.value = `وارد شد: ${data.created} · تکراری رد شد: ${data.skipped}${
data.errors.length ? ` · خطا: ${data.errors.length}` : '' data.errors.length ? ` · خطا: ${data.errors.length}` : ''
}${data.boards?.length ? ` · کاریز: ${data.boards.join('، ')}` : ''}`; }${data.boards?.length ? ` · مسیر فروش: ${data.boards.join('، ')}` : ''}`;
if (data.errors.length) { if (data.errors.length) {
error.value = data.errors error.value = data.errors
.slice(0, 3) .slice(0, 3)
@@ -504,7 +504,7 @@ defineExpose({ updateDealStage: applyUpdateDealStage });
<section class="page" dir="rtl"> <section class="page" dir="rtl">
<UiBusyOverlay :open="pageBusy" :message="busyMessage" /> <UiBusyOverlay :open="pageBusy" :message="busyMessage" />
<PipelineToolbar <PipelineToolbar
pipeline-name="کاریز فروش" pipeline-name="مسیر فروش"
:pipeline-options="pipelineOptions" :pipeline-options="pipelineOptions"
v-model:selected-pipeline-id="selectedPipelineId" v-model:selected-pipeline-id="selectedPipelineId"
v-model:view-mode="viewMode" v-model:view-mode="viewMode"
@@ -546,7 +546,7 @@ defineExpose({ updateDealStage: applyUpdateDealStage });
<p v-if="error" class="error">{{ error }}</p> <p v-if="error" class="error">{{ error }}</p>
<p v-if="importMsg" class="ok">{{ importMsg }}</p> <p v-if="importMsg" class="ok">{{ importMsg }}</p>
<p v-else-if="loading && !stages.length" class="muted">در حال بارگذاری کاریز…</p> <p v-else-if="loading && !stages.length" class="muted">در حال بارگذاری مسیر فروش…</p>
<PipelineList <PipelineList
v-if="viewMode === 'list' && (!loading || stages.length)" v-if="viewMode === 'list' && (!loading || stages.length)"
@@ -602,15 +602,15 @@ defineExpose({ updateDealStage: applyUpdateDealStage });
<UiModal <UiModal
:open="showCreate && canCreatePipeline" :open="showCreate && canCreatePipeline"
title="کاریز جدید" title="مسیر فروش جدید"
@close="showCreate = false" @close="showCreate = false"
> >
<form class="create-form" @submit.prevent="createPipeline"> <form class="create-form" @submit.prevent="createPipeline">
<label class="field"> <label class="field">
<span>نام کاریز</span> <span>نام مسیر فروش</span>
<UiInput <UiInput
v-model="newName" v-model="newName"
placeholder="نام کاریز جدید…" placeholder="نام مسیر فروش جدید…"
:maxlength="120" :maxlength="120"
required required
:minlength="2" :minlength="2"
+1 -1
View File
@@ -647,7 +647,7 @@ watch(
<ProfileEmptyState <ProfileEmptyState
v-if="dealsEmpty" v-if="dealsEmpty"
title="هنوز معامله‌ای ثبت نشده!" title="هنوز معامله‌ای ثبت نشده!"
description="اولین معامله را در کاریز ثبت کنید یا یک فعالیت پیگیری بسازید." description="اولین معامله را در مسیر فروش ثبت کنید یا یک فعالیت پیگیری بسازید."
primary-label="افزودن معامله" primary-label="افزودن معامله"
:primary-to="{ name: 'deal-new' }" :primary-to="{ name: 'deal-new' }"
secondary-label="افزودن اولین فعالیت" secondary-label="افزودن اولین فعالیت"
+9 -9
View File
@@ -146,7 +146,7 @@ async function load() {
if (axios.isAxiosError(e) && !e.response) { if (axios.isAxiosError(e) && !e.response) {
error.value = 'اتصال به API برقرار نشد.'; error.value = 'اتصال به API برقرار نشد.';
} else { } else {
error.value = 'بارگذاری برد پروژه ناموفق بود.'; error.value = 'بارگذاری تابلوی پروژه ناموفق بود.';
} }
} finally { } finally {
loading.value = false; loading.value = false;
@@ -164,7 +164,7 @@ async function createBoard() {
showCreate.value = false; showCreate.value = false;
await router.replace({ name: 'projects', query: { boardId: data.id } }); await router.replace({ name: 'projects', query: { boardId: data.id } });
} catch { } catch {
error.value = 'ساخت برد ناموفق بود.'; error.value = 'ساخت تابلو ناموفق بود.';
} finally { } finally {
saving.value = false; saving.value = false;
} }
@@ -417,7 +417,7 @@ function onImportStart() {
async function onImportDone(data: ImportContactsResult) { async function onImportDone(data: ImportContactsResult) {
importMsg.value = `وارد شد: ${data.created} · تکراری رد شد: ${data.skipped}${ importMsg.value = `وارد شد: ${data.created} · تکراری رد شد: ${data.skipped}${
data.errors.length ? ` · خطا: ${data.errors.length}` : '' data.errors.length ? ` · خطا: ${data.errors.length}` : ''
}${data.boards?.length ? ` · برد: ${data.boards.join('، ')}` : ''}`; }${data.boards?.length ? ` · تابلو: ${data.boards.join('، ')}` : ''}`;
if (data.errors.length) { if (data.errors.length) {
error.value = data.errors error.value = data.errors
.slice(0, 3) .slice(0, 3)
@@ -468,9 +468,9 @@ onBeforeUnmount(() => {
<section class="page" dir="rtl"> <section class="page" dir="rtl">
<UiBusyOverlay :open="pageBusy" :message="busyMessage" /> <UiBusyOverlay :open="pageBusy" :message="busyMessage" />
<PipelineToolbar <PipelineToolbar
pipeline-name="کاریز پروژه — کارهای تیمی" pipeline-name="تابلوی پروژه — کارهای تیمی"
icon="grid" icon="grid"
create-option-label="برد جدید…" create-option-label="تابلوی جدید…"
:pipeline-options="boardOptions" :pipeline-options="boardOptions"
v-model:selected-pipeline-id="selectedBoardId" v-model:selected-pipeline-id="selectedBoardId"
v-model:view-mode="viewMode" v-model:view-mode="viewMode"
@@ -505,7 +505,7 @@ onBeforeUnmount(() => {
<p v-if="error" class="error">{{ error }}</p> <p v-if="error" class="error">{{ error }}</p>
<p v-if="importMsg" class="ok">{{ importMsg }}</p> <p v-if="importMsg" class="ok">{{ importMsg }}</p>
<p v-else-if="loading && !stages.length" class="muted">در حال بارگذاری برد…</p> <p v-else-if="loading && !stages.length" class="muted">در حال بارگذاری تابلو…</p>
<PipelineList <PipelineList
v-if="viewMode === 'list' && (!loading || stages.length)" v-if="viewMode === 'list' && (!loading || stages.length)"
@@ -564,15 +564,15 @@ onBeforeUnmount(() => {
<UiModal <UiModal
:open="showCreate && canCreateBoard" :open="showCreate && canCreateBoard"
title="برد جدید" title="تابلوی جدید"
@close="showCreate = false" @close="showCreate = false"
> >
<form class="create-form" @submit.prevent="createBoard"> <form class="create-form" @submit.prevent="createBoard">
<label class="field"> <label class="field">
<span>نام برد</span> <span>نام تابلو</span>
<UiInput <UiInput
v-model="newName" v-model="newName"
placeholder="نام برد جدید…" placeholder="نام تابلوی جدید…"
:maxlength="120" :maxlength="120"
required required
:minlength="2" :minlength="2"
+4 -4
View File
@@ -1404,7 +1404,7 @@ const activityMax = computed(() =>
<!-- Deep analytics (BI phase 1) --> <!-- Deep analytics (BI phase 1) -->
<div class="deep-grid"> <div class="deep-grid">
<UiCard class="chart-card" title="تبدیل مراحل کاریز"> <UiCard class="chart-card" title="تبدیل مراحل مسیر فروش">
<p class="deep-hint"> <p class="deep-hint">
سهم هر مرحله از معاملات باز و نرخ عبور نسبت به مرحله قبل. برای دیدن پرونده‌ها روی مرحله کلیک کنید. سهم هر مرحله از معاملات باز و نرخ عبور نسبت به مرحله قبل. برای دیدن پرونده‌ها روی مرحله کلیک کنید.
</p> </p>
@@ -1435,16 +1435,16 @@ const activityMax = computed(() =>
/> />
</div> </div>
<div class="funnel-row-meta"> <div class="funnel-row-meta">
<small>سهم کاریز: {{ toFaDigits(s.shareOfPipelinePercent) }}٪</small> <small>سهم از مسیر: {{ toFaDigits(s.shareOfPipelinePercent) }}٪</small>
<small v-if="s.conversionFromPrevPercent != null"> <small v-if="s.conversionFromPrevPercent != null">
تبدیل از قبل: {{ toFaDigits(s.conversionFromPrevPercent) }}٪ تبدیل از قبل: {{ toFaDigits(s.conversionFromPrevPercent) }}٪
</small> </small>
<small v-else>شروع کاریز</small> <small v-else>شروع مسیر</small>
</div> </div>
</button> </button>
</div> </div>
</div> </div>
<p v-else class="empty-msg">معامله بازی در کاریز نیست.</p> <p v-else class="empty-msg">معامله بازی در مسیر فروش نیست.</p>
</UiCard> </UiCard>
<UiCard class="chart-card" title="مقایسه با دوره قبل"> <UiCard class="chart-card" title="مقایسه با دوره قبل">
+2 -2
View File
@@ -152,7 +152,7 @@ const forecastChartOption = computed<UiChartOption>(() => {
grid: { left: '3%', right: '4%', bottom: '8%', top: '8%', containLabel: true }, grid: { left: '3%', right: '4%', bottom: '8%', top: '8%', containLabel: true },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['هدف دوره', 'تحقق‌یافته', 'کاریز باز', 'پیش‌بینی'], data: ['هدف دوره', 'تحقق‌یافته', 'معاملات باز', 'پیش‌بینی'],
axisLine: { lineStyle: { color: '#cbd5e1' } }, axisLine: { lineStyle: { color: '#cbd5e1' } },
axisLabel: { color: '#64748b', fontFamily: 'IRANSans' }, axisLabel: { color: '#64748b', fontFamily: 'IRANSans' },
}, },
@@ -721,7 +721,7 @@ onMounted(() => {
<h3>خلاصه پیش‌بینی</h3> <h3>خلاصه پیش‌بینی</h3>
<div class="sum-row"><span>هدف دوره</span><strong>{{ formatMoney(forecast?.targetAmount || 0) }}</strong></div> <div class="sum-row"><span>هدف دوره</span><strong>{{ formatMoney(forecast?.targetAmount || 0) }}</strong></div>
<div class="sum-row"><span>تحقق (برنده)</span><strong>{{ formatMoney(forecast?.wonAmount || 0) }}</strong></div> <div class="sum-row"><span>تحقق (برنده)</span><strong>{{ formatMoney(forecast?.wonAmount || 0) }}</strong></div>
<div class="sum-row"><span>کاریز باز</span><strong>{{ formatMoney(forecast?.openPipelineAmount || 0) }}</strong></div> <div class="sum-row"><span>معاملات باز</span><strong>{{ formatMoney(forecast?.openPipelineAmount || 0) }}</strong></div>
<div class="sum-row payable"><span>پیش‌بینی درآمد</span><strong>{{ formatMoney(forecast?.forecastAmount || 0) }}</strong></div> <div class="sum-row payable"><span>پیش‌بینی درآمد</span><strong>{{ formatMoney(forecast?.forecastAmount || 0) }}</strong></div>
<div class="sum-row"><span>مشتریان جدید</span><strong>{{ toFaDigits(forecast?.newCustomersCount || 0) }}</strong></div> <div class="sum-row"><span>مشتریان جدید</span><strong>{{ toFaDigits(forecast?.newCustomersCount || 0) }}</strong></div>
<div class="sum-row"><span>معاملات جدید</span><strong>{{ toFaDigits(forecast?.newDealsCount || 0) }}</strong></div> <div class="sum-row"><span>معاملات جدید</span><strong>{{ toFaDigits(forecast?.newDealsCount || 0) }}</strong></div>
+5 -5
View File
@@ -17,7 +17,7 @@
CRM فروش و پشتیبانی برای تیم‌های ایرانی — از سرنخ تا فاکتور، در یک پنل. CRM فروش و پشتیبانی برای تیم‌های ایرانی — از سرنخ تا فاکتور، در یک پنل.
**متن کوتاه (۲–۳ خط):** **متن کوتاه (۲–۳ خط):**
مُنتَکت دفتر کار دیجیتال فروش شماست: مشتری و لید، کاریز معامله، کار روزانه، گفتگو و پشتیبانی، گزارش و اتوماسیون — با تاریخ شمسی، پیامک و رابط موبایل. مُنتَکت دفتر کار دیجیتال فروش شماست: مشتری و لید، مسیر فروش، کار روزانه، گفتگو و پشتیبانی، گزارش و اتوماسیون — با تاریخ شمسی، پیامک و رابط موبایل.
**دکمه‌ها:** **دکمه‌ها:**
| دکمه | مقصد | | دکمه | مقصد |
@@ -40,11 +40,11 @@ CRM فروش و پشتیبانی برای تیم‌های ایرانی — از
مُنتَکت یک CRM **فروش‌محور و پشتیبانی‌محور** برای کسب‌وکارهای ایرانی است. حلقهٔ اصلی محصول این است: مُنتَکت یک CRM **فروش‌محور و پشتیبانی‌محور** برای کسب‌وکارهای ایرانی است. حلقهٔ اصلی محصول این است:
```text ```text
سرنخ → شخص / شرکت → معامله در کاریز → فعالیت و یادداشت هوشمند → فاکتور و پیش‌فاکتور → پس از فروش (تیکت، پرتال، وفاداری) سرنخ → شخص / شرکت → معامله در مسیر فروش → فعالیت و یادداشت هوشمند → فاکتور و پیش‌فاکتور → پس از فروش (تیکت، پرتال، وفاداری)
``` ```
**برای چه کسب‌وکارهایی مناسب است؟** **برای چه کسب‌وکارهایی مناسب است؟**
فروش B2B/B2C، تیم‌های چندنفره، و کسب‌وکارهایی که به **پیگیری منظم مشتری**، **کاریز فروش** و **پشتیبانی بعد از فروش** نیاز دارند — از جمله حوزه **طلا و جواهر** با اتصال **ته‌حساب**. فروش B2B/B2C، تیم‌های چندنفره، و کسب‌وکارهایی که به **پیگیری منظم مشتری**، **مسیر فروش** و **پشتیبانی بعد از فروش** نیاز دارند — از جمله حوزه **طلا و جواهر** با اتصال **ته‌حساب**.
**تمایز کلی (یک جمله):** **تمایز کلی (یک جمله):**
هویت بصری مستقل، تمرکز روی ثبت سریع اطلاعات (Smart Notes) و دستیار هوشمند روی پرونده مشتری — نه فقط جدول و فرم. هویت بصری مستقل، تمرکز روی ثبت سریع اطلاعات (Smart Notes) و دستیار هوشمند روی پرونده مشتری — نه فقط جدول و فرم.
@@ -58,9 +58,9 @@ CRM فروش و پشتیبانی برای تیم‌های ایرانی — از
| # | قابلیت | توضیح یک‌خطی برای لندینگ | | # | قابلیت | توضیح یک‌خطی برای لندینگ |
|---|---|---| |---|---|---|
| 1 | **کاریز فروش (Kanban)** | معاملات را بین مراحل بکشید؛ چند قیف فروش برای یک سازمان. | | 1 | **مسیر فروش (Kanban)** | معاملات را بین مراحل بکشید؛ چند قیف فروش برای یک سازمان. |
| 2 | **اشخاص و شرکت‌ها** | دفترچه مشتری با جستجو، ضدتکراری، اکسل و فیلد سفارشی. | | 2 | **اشخاص و شرکت‌ها** | دفترچه مشتری با جستجو، ضدتکراری، اکسل و فیلد سفارشی. |
| 3 | **مدیریت لیدها** | لید جدا از مشتری؛ ایمپورت اکسل، تخصیص، کاریز لید، تبدیل به مشتری. | | 3 | **مدیریت لیدها** | لید جدا از مشتری؛ ایمپورت اکسل، تخصیص، مسیر لید، تبدیل به مشتری. |
| 4 | **فرم‌ساز سرنخ** | فرم عمومی با لینک، iframe و QR برای جذب سرنخ. | | 4 | **فرم‌ساز سرنخ** | فرم عمومی با لینک، iframe و QR برای جذب سرنخ. |
| 5 | **Smart Notes و Montact Memory** | یادداشت سریع روی پرونده + خلاصه پایدار مشتری با منبع. | | 5 | **Smart Notes و Montact Memory** | یادداشت سریع روی پرونده + خلاصه پایدار مشتری با منبع. |
| 6 | **Ask Montact** | پرسش به زبان طبیعی از روی یادداشت‌ها و زمینه پرونده. | | 6 | **Ask Montact** | پرسش به زبان طبیعی از روی یادداشت‌ها و زمینه پرونده. |
+3 -3
View File
@@ -14,13 +14,13 @@
|---|---| |---|---|
| `images/01-dashboard-desktop.png` | داشبورد دسکتاپ | | `images/01-dashboard-desktop.png` | داشبورد دسکتاپ |
| `images/02-contacts-people.png` | اشخاص | | `images/02-contacts-people.png` | اشخاص |
| `images/03-pipeline-desktop.png` | کاریز فروش | | `images/03-pipeline-desktop.png` | مسیر فروش |
| `images/04-conversations-desktop.png` | گفتگو | | `images/04-conversations-desktop.png` | گفتگو |
| `images/05-reports-desktop.png` | گزارش‌ها | | `images/05-reports-desktop.png` | گزارش‌ها |
| `images/06-dashboard-mobile.png` | داشبورد موبایل‌وار | | `images/06-dashboard-mobile.png` | داشبورد موبایل‌وار |
| `images/07-contacts-companies.png` | شرکت‌ها | | `images/07-contacts-companies.png` | شرکت‌ها |
| `images/08-leads-list.png` | فهرست لیدها | | `images/08-leads-list.png` | فهرست لیدها |
| `images/09-leads-board.png` | کاریز لیدها | | `images/09-leads-board.png` | مسیر لیدها |
| `images/10-activities.png` | میز کار | | `images/10-activities.png` | میز کار |
| `images/11-calendar.png` | تقویم | | `images/11-calendar.png` | تقویم |
| `images/12-notes.png` | یادداشت‌ها | | `images/12-notes.png` | یادداشت‌ها |
@@ -31,4 +31,4 @@
| `images/17-message-automations.png` | اتوماسیون پیامک | | `images/17-message-automations.png` | اتوماسیون پیامک |
| `images/18-settings-team.png` | تنظیمات تیم | | `images/18-settings-team.png` | تنظیمات تیم |
| `images/19-products.png` | محصولات | | `images/19-products.png` | محصولات |
| `images/20-projects.png` | کاریز پروژه | | `images/20-projects.png` | تابلوی پروژه |
+3 -3
View File
@@ -16,7 +16,7 @@
منتکت یک CRM فروش و پشتیبانی برای کسب‌وکارهای ایرانی است با این حلقه اصلی: منتکت یک CRM فروش و پشتیبانی برای کسب‌وکارهای ایرانی است با این حلقه اصلی:
```text ```text
سرنخ → شخص / شرکت → معامله در کاریز → فعالیت / Smart Notes → فروش مالی سبک → پس‌ازفروش سرنخ → شخص / شرکت → معامله در مسیر فروش → فعالیت / Smart Notes → فروش مالی سبک → پس‌ازفروش
``` ```
الهام اولیه از محصولات ایرانی مثل دیدار بوده، ولی هویت بصری و تمایز محصول مستقل است: الهام اولیه از محصولات ایرانی مثل دیدار بوده، ولی هویت بصری و تمایز محصول مستقل است:
@@ -49,7 +49,7 @@
| مایل‌استون | وضعیت | محتوا | | مایل‌استون | وضعیت | محتوا |
|---|---|---| |---|---|---|
| **۱-الف** هسته فروش | انجام شده | مخاطب، کاریز، فعالیت، تقویم، اکسل، نقش، چندارزی، UI | | **۱-الف** هسته فروش | انجام شده | مخاطب، مسیر فروش، فعالیت، تقویم، اکسل، نقش، چندارزی، UI |
| **۱-ب** Smart Notes / AI | انجام شده | Composer، Memory، Ask، Voice؛ شخصی/تیمی + تاریخچه؛ خلاصه خودکار تماس | | **۱-ب** Smart Notes / AI | انجام شده | Composer، Memory، Ask، Voice؛ شخصی/تیمی + تاریخچه؛ خلاصه خودکار تماس |
| **۱-ج** سرنخ و مالی | انجام شده | فرم‌ساز، پیش‌فاکتور، فاکتور/اقساط، هدف فروش | | **۱-ج** سرنخ و مالی | انجام شده | فرم‌ساز، پیش‌فاکتور، فاکتور/اقساط، هدف فروش |
| **۱-د** پس‌ازفروش | انجام شده | تیکت، پرتال OTP، پروژه، باشگاه، ته‌حساب | | **۱-د** پس‌ازفروش | انجام شده | تیکت، پرتال OTP، پروژه، باشگاه، ته‌حساب |
@@ -81,7 +81,7 @@
| احراز هویت (رمز + OTP موبایل) | آماده | | احراز هویت (رمز + OTP موبایل) | آماده |
| خانه / داشبورد | آماده | | خانه / داشبورد | آماده |
| اشخاص و شرکت‌ها + پروفایل | آماده | | اشخاص و شرکت‌ها + پروفایل | آماده |
| کاریز فروش و معامله | آماده | | مسیر فروش و معامله | آماده |
| فعالیت، تقویم، یادآور SMS/اپ | آماده | | فعالیت، تقویم، یادآور SMS/اپ | آماده |
| یادداشت هوشمند / Memory / Ask | آماده | | یادداشت هوشمند / Memory / Ask | آماده |
| پیش‌فاکتور و فاکتور | آماده | | پیش‌فاکتور و فاکتور | آماده |
+19 -19
View File
@@ -140,7 +140,7 @@
- **منابع مشتریان:** مشتری‌ها از کجا آمده‌اند - **منابع مشتریان:** مشتری‌ها از کجا آمده‌اند
- **دستیار هوشمند:** پیشنهاد سؤال و دستورهای سریع (حتی با میکروفون در صورت فعال بودن) - **دستیار هوشمند:** پیشنهاد سؤال و دستورهای سریع (حتی با میکروفون در صورت فعال بودن)
- **فعالیت‌های اخیر** - **فعالیت‌های اخیر**
- **نمای کوچک کاریز فروش** - **نمای کوچک مسیر فروش**
- **برترین مشتریان** - **برترین مشتریان**
### دسکتاپ در برابر موبایل ### دسکتاپ در برابر موبایل
@@ -199,13 +199,13 @@
- تخصیص به همکار - تخصیص به همکار
- ورود از اکسل (اگر دسترسی دارید) - ورود از اکسل (اگر دسترسی دارید)
### کاریز لیدها ### مسیر لیدها
![کاریز لیدها](user-guide/images/09-leads-board.png) ![مسیر لیدها](user-guide/images/09-leads-board.png)
نمای ستون‌به‌ستون (کانبان). کارت را بین مراحل بکشید تا وضعیت عوض شود. نمای ستون‌به‌ستون (کانبان). کارت را بین مراحل بکشید تا وضعیت عوض شود.
### مراحل و وضعیت‌ها (معمولاً فقط مدیر) ### مراحل و وضعیت‌ها (معمولاً فقط مدیر)
تعریف ستون‌های کاریز لید. تعریف ستون‌های مسیر لید.
### فرم‌ساز سرنخ (معمولاً فقط مدیر) ### فرم‌ساز سرنخ (معمولاً فقط مدیر)
- لینک عمومی برای جذب سرنخ - لینک عمومی برای جذب سرنخ
@@ -218,10 +218,10 @@
مسیر: منو → **فروش** مسیر: منو → **فروش**
![کاریز فروش](user-guide/images/03-pipeline-desktop.png) ![مسیر فروش](user-guide/images/03-pipeline-desktop.png)
### کاریز فروش ### مسیر فروش
- نمای **کاریز** (ستون‌ها) یا **لیست** - نمای **ستونی** یا **لیست**
- هر ستون یک مرحله است (مثلاً سرنخ، تماس اول، پیشنهاد، …) - هر ستون یک مرحله است (مثلاً سرنخ، تماس اول، پیشنهاد، …)
- کارت معامله را بکشید تا مرحله عوض شود - کارت معامله را بکشید تا مرحله عوض شود
- روی کارت بزنید تا جزئیات باز شود: مبلغ، مسئول، محصولات، یادداشت، … - روی کارت بزنید تا جزئیات باز شود: مبلغ، مسئول، محصولات، یادداشت، …
@@ -372,7 +372,7 @@
- بازه زمانی: امروز / هفته / ماه / سه‌ماهه / سال یا تاریخ دلخواه - بازه زمانی: امروز / هفته / ماه / سه‌ماهه / سال یا تاریخ دلخواه
- کارت‌هایی مثل: مشتریان جدید، تعداد فاکتور، مبلغ فروش، میانگین‌ها - کارت‌هایی مثل: مشتریان جدید، تعداد فاکتور، مبلغ فروش، میانگین‌ها
- مقایسه با دوره قبل - مقایسه با دوره قبل
- قیف تبدیل مراحل کاریز - قیف تبدیل مراحل مسیر فروش
- نمودارها و جداول فروش، منابع، فعالیت، تیم، تیکت - نمودارها و جداول فروش، منابع، فعالیت، تیم، تیکت
- خروجی اکسل/CSV - خروجی اکسل/CSV
- ذخیره نما و کپی لینک اشتراک (اگر فعال باشد) - ذخیره نما و کپی لینک اشتراک (اگر فعال باشد)
@@ -419,11 +419,11 @@
مسیر: منو → **ابزارها** مسیر: منو → **ابزارها**
![کاریز پروژه](user-guide/images/20-projects.png) ![تابلوی پروژه](user-guide/images/20-projects.png)
| بخش | کار ساده | | بخش | کار ساده |
|---|---| |---|---|
| کاریز پروژه | کارهای داخلی تیم بعد از فروش (ستون‌ها و کارت‌ها) | | تابلوی پروژه | کارهای داخلی تیم بعد از فروش (ستون‌ها و کارت‌ها) |
| انبار | موجودی کالا و جابه‌جایی داخلی | | انبار | موجودی کالا و جابه‌جایی داخلی |
| چک‌ها | چک دریافتی و پرداختی + یادآوری | | چک‌ها | چک دریافتی و پرداختی + یادآوری |
| رزرو و نوبت‌دهی | خدمات، زمان آزاد، ثبت نوبت مشتری | | رزرو و نوبت‌دهی | خدمات، زمان آزاد، ثبت نوبت مشتری |
@@ -463,7 +463,7 @@
1. منو را با آیکون همبرگر باز کنید. 1. منو را با آیکون همبرگر باز کنید.
2. لیست‌ها کارت‌مانند هستند؛ برای دیدن جزئیات روی کارت بزنید. 2. لیست‌ها کارت‌مانند هستند؛ برای دیدن جزئیات روی کارت بزنید.
3. در فرم‌ها دکمهٔ ذخیره اغلب **بالا** و با آیکون تیک است. 3. در فرم‌ها دکمهٔ ذخیره اغلب **بالا** و با آیکون تیک است.
4. کاریز فروش/لید را افقی ورق بزنید. 4. مسیر فروش/لید را افقی ورق بزنید.
5. در گفتگو: لمس طولانی روی پیام = منوی کارها. 5. در گفتگو: لمس طولانی روی پیام = منوی کارها.
6. برای گزارش‌های شلوغ و اکسل، دسکتاپ راحت‌تر است. 6. برای گزارش‌های شلوغ و اکسل، دسکتاپ راحت‌تر است.
7. ورود از مخاطبین گوشی در صفحه اشخاص در دسترس است. 7. ورود از مخاطبین گوشی در صفحه اشخاص در دسترس است.
@@ -474,11 +474,11 @@
### کارشناس فروش ### کارشناس فروش
1. لید جدید (دستی یا از فرم سایت) 1. لید جدید (دستی یا از فرم سایت)
2. تبدیل/ساخت معامله روی کاریز 2. تبدیل/ساخت معامله روی مسیر فروش
3. ثبت تماس و یادداشت 3. ثبت تماس و یادداشت
4. پیش‌فاکتور → فاکتور 4. پیش‌فاکتور → فاکتور
5. بردن معامله به مرحله برنده 5. بردن معامله به مرحله برنده
6. در صورت نیاز کارت در کاریز پروژه 6. در صورت نیاز کارت در تابلوی پروژه
### پشتیبانی ### پشتیبانی
1. تیکت جدید یا پیام از پرتال 1. تیکت جدید یا پیام از پرتال
@@ -508,7 +508,7 @@
## جمع‌بندی یک‌خطی ## جمع‌بندی یک‌خطی
مُنتَکت دفتر کار دیجیتال تیم شماست: مشتری و لید را نگه می‌دارد، فروش را روی کاریز جلو می‌برد، کار و گفتگو و پشتیبانی را یک‌جا می‌کند، و با گزارش و اتوماسیون به مدیر نشان می‌دهد واقعاً چه خبر است — هم روی کامپیوتر، هم روی گوشی. مُنتَکت دفتر کار دیجیتال تیم شماست: مشتری و لید را نگه می‌دارد، فروش را روی مسیر فروش جلو می‌برد، کار و گفتگو و پشتیبانی را یک‌جا می‌کند، و با گزارش و اتوماسیون به مدیر نشان می‌دهد واقعاً چه خبر است — هم روی کامپیوتر، هم روی گوشی.
--- ---
@@ -517,8 +517,8 @@
``` ```
داشبورد داشبورد
مشتریان → اشخاص / شرکت‌ها مشتریان → اشخاص / شرکت‌ها
لیدها → فهرست / کاریز / مراحل / فرم‌ساز لیدها → فهرست / مسیر لیدها / مراحل / فرم‌ساز
فروش → کاریز / محصولات / فاکتور / پیش‌فاکتور / پورسانت / هدف فروش → مسیر فروش / محصولات / فاکتور / پیش‌فاکتور / پورسانت / هدف
کارها → میز کار / تقویم کارها → میز کار / تقویم
گفتگو گفتگو
یادداشت‌ها یادداشت‌ها
@@ -536,13 +536,13 @@
|---|---| |---|---|
| `01-dashboard-desktop.png` | داشبورد دسکتاپ | | `01-dashboard-desktop.png` | داشبورد دسکتاپ |
| `02-contacts-people.png` | اشخاص | | `02-contacts-people.png` | اشخاص |
| `03-pipeline-desktop.png` | کاریز فروش | | `03-pipeline-desktop.png` | مسیر فروش |
| `04-conversations-desktop.png` | گفتگو | | `04-conversations-desktop.png` | گفتگو |
| `05-reports-desktop.png` | گزارش‌ها | | `05-reports-desktop.png` | گزارش‌ها |
| `06-dashboard-mobile.png` | داشبورد موبایل‌وار | | `06-dashboard-mobile.png` | داشبورد موبایل‌وار |
| `07-contacts-companies.png` | شرکت‌ها | | `07-contacts-companies.png` | شرکت‌ها |
| `08-leads-list.png` | فهرست لیدها | | `08-leads-list.png` | فهرست لیدها |
| `09-leads-board.png` | کاریز لیدها | | `09-leads-board.png` | مسیر لیدها |
| `10-activities.png` | میز کار و فعالیت‌ها | | `10-activities.png` | میز کار و فعالیت‌ها |
| `11-calendar.png` | تقویم فعالیت‌ها | | `11-calendar.png` | تقویم فعالیت‌ها |
| `12-notes.png` | یادداشت‌ها | | `12-notes.png` | یادداشت‌ها |
@@ -553,4 +553,4 @@
| `17-message-automations.png` | اتوماسیون پیامک | | `17-message-automations.png` | اتوماسیون پیامک |
| `18-settings-team.png` | تنظیمات تیم و کاربران | | `18-settings-team.png` | تنظیمات تیم و کاربران |
| `19-products.png` | محصولات | | `19-products.png` | محصولات |
| `20-projects.png` | کاریز پروژه | | `20-projects.png` | تابلوی پروژه |
+6 -6
View File
@@ -33,7 +33,7 @@
## خانه و پروفایل ## خانه و پروفایل
### داشبورد خانه امروز ### داشبورد خانه امروز
خلاصه فروش، فعالیت، کاریز، میانبرها و دستیار Ask Montact در یک صفحه. خلاصه فروش، فعالیت، مسیر فروش، میانبرها و دستیار Ask Montact در یک صفحه.
### پروفایل کاربر ### پروفایل کاربر
از کارت پایین سایدبار: وضعیت کارها و میانبرهای شخصی. از کارت پایین سایدبار: وضعیت کارها و میانبرهای شخصی.
@@ -62,12 +62,12 @@
--- ---
## فروش و کاریز ## فروش و مسیر فروش
### کاریز فروش کانبان ### مسیر فروش (نمای ستونی)
تخته ستونی معاملات با کشیدن کارت بین مراحل. تخته ستونی معاملات با کشیدن کارت بین مراحل.
### چند کاریز ### چند مسیر فروش
سازمان می‌تواند بیش از یک قیف فروش داشته باشد. سازمان می‌تواند بیش از یک قیف فروش داشته باشد.
### پرونده معامله ### پرونده معامله
@@ -152,7 +152,7 @@ AI پیشنهاد Next Action می‌دهد؛ اعمال فقط با تأیید
## سرنخ و مالی سبک ## سرنخ و مالی سبک
### مدیریت لیدها ### مدیریت لیدها
دیتابیس جدا از مشتریان برای مخاطبین بالقوه: ایمپورت اکسل، تخصیص به کارشناس، کاریز لید، ثبت تماس/پیگیری، و تبدیل به مشتری. دیتابیس جدا از مشتریان برای مخاطبین بالقوه: ایمپورت اکسل، تخصیص به کارشناس، مسیر لید، ثبت تماس/پیگیری، و تبدیل به مشتری.
### فرم‌ساز سرنخ ### فرم‌ساز سرنخ
فرم وب عمومی با لینک، iframe و QR؛ ساخت شخص و معامله. فرم وب عمومی با لینک، iframe و QR؛ ساخت شخص و معامله.
@@ -198,7 +198,7 @@ AI پیشنهاد Next Action می‌دهد؛ اعمال فقط با تأیید
## پروژه، وفاداری، اتوماسیون ## پروژه، وفاداری، اتوماسیون
### کاریز پروژه ### تابلوی پروژه
تخته کارهای داخلی (صف / انجام / بازبینی / تمام) جدا از فروش. تخته کارهای داخلی (صف / انجام / بازبینی / تمام) جدا از فروش.
### باشگاه مشتریان ### باشگاه مشتریان
+15 -10
View File
@@ -180,7 +180,7 @@ export const ORG_MODULE_KEYS: OrgModuleKey[] = [
export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = { export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = {
customers: "مشتریان", customers: "مشتریان",
pipeline: "کاریز فروش", pipeline: "مسیر فروش",
products: "محصولات", products: "محصولات",
invoices: "فاکتورها", invoices: "فاکتورها",
quotes: "پیش‌فاکتورها", quotes: "پیش‌فاکتورها",
@@ -192,7 +192,7 @@ export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = {
message_automations: "اتوماسیون پیامک", message_automations: "اتوماسیون پیامک",
lead_forms: "فرم‌ساز سرنخ", lead_forms: "فرم‌ساز سرنخ",
leads: "مدیریت لیدها", leads: "مدیریت لیدها",
project_boards: "کاریز پروژه", project_boards: "تابلوی پروژه",
inventory: "انبار", inventory: "انبار",
cheques: "چک‌ها", cheques: "چک‌ها",
attachments: "پیوست‌ها", attachments: "پیوست‌ها",
@@ -1056,8 +1056,8 @@ export const DEAL_EXCEL_COLUMNS: Array<{ header: string; key: string; width?: nu
{ header: "معرف معامله", key: "referrer", width: 16 }, { header: "معرف معامله", key: "referrer", width: 16 },
{ header: "شیوه آشنایی دلخواه", key: "sourceDetail", width: 16 }, { header: "شیوه آشنایی دلخواه", key: "sourceDetail", width: 16 },
{ header: "برچسب", key: "tags", width: 16 }, { header: "برچسب", key: "tags", width: 16 },
{ header: "کاریز معامله", key: "pipelineName", width: 20 }, { header: "مسیر فروش معامله", key: "pipelineName", width: 20 },
{ header: "مرحله کاریز معامله", key: "stageName", width: 20 }, { header: "مرحله مسیر فروش معامله", key: "stageName", width: 20 },
{ header: "نام شخص معامله", key: "personFirstName", width: 16 }, { header: "نام شخص معامله", key: "personFirstName", width: 16 },
{ header: "نام خانوادگی شخص معامله", key: "personName", width: 22 }, { header: "نام خانوادگی شخص معامله", key: "personName", width: 22 },
{ header: "کد دیدار شخص معامله", key: "personCode", width: 14 }, { header: "کد دیدار شخص معامله", key: "personCode", width: 14 },
@@ -1104,7 +1104,12 @@ export const DEAL_EXCEL_ALIASES: Record<string, string> = {
شرکت: "companyName", شرکت: "companyName",
منبعمعامله: "source", منبعمعامله: "source",
منبع: "source", منبع: "source",
مسیرفروش: "pipelineName",
مسیر: "pipelineName",
// سرستون‌های قدیمی (قبل از تغییر نام کاریز → مسیر فروش)
کاریز: "pipelineName", کاریز: "pipelineName",
کاریزمعامله: "pipelineName",
مرحلهکاریزمعامله: "stageName",
مرحله: "stageName", مرحله: "stageName",
}; };
/** فیلتر ذخیره‌شدهٔ لیست اشخاص */ /** فیلتر ذخیره‌شدهٔ لیست اشخاص */
@@ -1115,7 +1120,7 @@ export interface PeopleViewFilters {
page?: number; page?: number;
} }
/** فیلتر ذخیره‌شدهٔ کاریز / معاملات */ /** فیلتر ذخیره‌شدهٔ مسیر فروش / معاملات */
export interface DealsViewFilters { export interface DealsViewFilters {
q?: string; q?: string;
amountMin?: string; amountMin?: string;
@@ -2251,7 +2256,7 @@ export interface SalesForecastDto {
wonAmount: number; wonAmount: number;
/** معاملات باز فعلی */ /** معاملات باز فعلی */
openPipelineAmount: number; openPipelineAmount: number;
/** پیش‌بینی: تحقق + کاریز باز × میانگین احتمال */ /** پیش‌بینی: تحقق + معاملات باز × میانگین احتمال */
forecastAmount: number; forecastAmount: number;
progressPercent: number | null; progressPercent: number | null;
openDealsCount: number; openDealsCount: number;
@@ -2501,9 +2506,9 @@ export interface ReportsFunnelStageDto {
pipelineName: string; pipelineName: string;
count: number; count: number;
amount: number; amount: number;
/** درصد تبدیل نسبت به مرحله قبل همان کاریز؛ null برای اولین مرحله */ /** درصد تبدیل نسبت به مرحله قبل همان مسیر فروش؛ null برای اولین مرحله */
conversionFromPrevPercent: number | null; conversionFromPrevPercent: number | null;
/** سهم از کل معاملات باز همان کاریز */ /** سهم از کل معاملات باز همان مسیر فروش */
shareOfPipelinePercent: number; shareOfPipelinePercent: number;
} }
@@ -2658,7 +2663,7 @@ export interface HomeDashboardDto {
topCustomers: HomeDashboardTopCustomerDto[]; topCustomers: HomeDashboardTopCustomerDto[];
} }
/** برد پروژه / کاریز کارت */ /** تابلوی پروژه / کارت */
export interface ProjectBoardStageDto { export interface ProjectBoardStageDto {
id: string; id: string;
name: string; name: string;
@@ -3586,7 +3591,7 @@ export const REPORTS_BUILDER_DIMENSION_LABELS: Record<
owner: "کاربر / مسئول", owner: "کاربر / مسئول",
team: "تیم", team: "تیم",
source: "منبع", source: "منبع",
stage: "مرحله کاریز", stage: "مرحله مسیر فروش",
month: "ماه", month: "ماه",
status: "وضعیت معامله", status: "وضعیت معامله",
}; };