Redesign sales pipeline UI and reconnect it to live board APIs.

Keep the employer kanban layout while loading pipelines/deals from the server, persisting drag-and-drop moves, and seeding a Waiting pipeline with demo deals.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 00:18:39 +03:30
co-authored by Cursor
parent 4b47b753c3
commit aa3fe49ce6
11 changed files with 1519 additions and 365 deletions
+166
View File
@@ -239,6 +239,172 @@ async function main() {
console.log('Services pipeline already seeded');
}
// کاریز منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه)
const WAITING_STAGES = [
{ name: 'پیگیری', isWon: false, isLost: false },
{ name: 'مذاکرات اولیه', isWon: false, isLost: false },
{ name: 'قیمت و نهایی کردن', isWon: false, isLost: false },
] as const;
const WAITING_DEALS: Array<{
stageName: (typeof WAITING_STAGES)[number]['name'];
title: string;
notes: string;
amount: number;
expectedCloseAt: string;
position: number;
}> = [
{
stageName: 'پیگیری',
title: 'نرم‌افزار حسابان',
notes: 'نصب سیستم یکپارچه',
amount: 250_000_000,
expectedCloseAt: '2024-07-15T00:00:00.000Z',
position: 0,
},
{
stageName: 'پیگیری',
title: 'همکاران سیستم',
notes: 'نسخه سازمانی',
amount: 350_000_000,
expectedCloseAt: '2024-07-08T00:00:00.000Z',
position: 1,
},
{
stageName: 'پیگیری',
title: 'بازرگانی امید',
notes: 'کاربران سازمانی',
amount: 180_000_000,
expectedCloseAt: '2024-07-17T00:00:00.000Z',
position: 2,
},
{
stageName: 'پیگیری',
title: 'کالای صنعتی پارس',
notes: 'ماژول انبارداری',
amount: 120_000_000,
expectedCloseAt: '2024-07-23T00:00:00.000Z',
position: 3,
},
{
stageName: 'مذاکرات اولیه',
title: 'فروشگاه تک‌کالا',
notes: 'ماژول فروش',
amount: 280_000_000,
expectedCloseAt: '2024-07-13T00:00:00.000Z',
position: 0,
},
{
stageName: 'مذاکرات اولیه',
title: 'شرکت داده‌پرداز',
notes: 'اتصال به حسابداری',
amount: 190_000_000,
expectedCloseAt: '2024-07-10T00:00:00.000Z',
position: 1,
},
{
stageName: 'مذاکرات اولیه',
title: 'گروه فنی مهندسی آوا',
notes: 'ماژول پروژه‌ها',
amount: 315_000_000,
expectedCloseAt: '2024-07-05T00:00:00.000Z',
position: 2,
},
{
stageName: 'مذاکرات اولیه',
title: 'رستوران زنجیره‌ای نیک',
notes: 'ماژول رستوران',
amount: 165_000_000,
expectedCloseAt: '2024-07-09T00:00:00.000Z',
position: 3,
},
{
stageName: 'قیمت و نهایی کردن',
title: 'شرکت تجارت',
notes: 'یکپارچه‌سازی کامل',
amount: 640_000_000,
expectedCloseAt: '2024-06-30T00:00:00.000Z',
position: 0,
},
{
stageName: 'قیمت و نهایی کردن',
title: 'فروشگاه اینترنتی دیجی‌خونه',
notes: 'فروشگاه اینترنتی',
amount: 420_000_000,
expectedCloseAt: '2024-06-28T00:00:00.000Z',
position: 1,
},
{
stageName: 'قیمت و نهایی کردن',
title: 'شرکت داروسازی بهداشت',
notes: 'ماژول مالی و انبار',
amount: 780_000_000,
expectedCloseAt: '2024-07-02T00:00:00.000Z',
position: 2,
},
];
let waitingPipeline = await prisma.pipeline.findFirst({
where: { organizationId: org.id, name: 'کاریز منتظر' },
include: { stages: { orderBy: { position: 'asc' } } },
});
if (!waitingPipeline) {
waitingPipeline = await prisma.pipeline.create({
data: {
organizationId: org.id,
name: 'کاریز منتظر',
isDefault: false,
stages: {
create: WAITING_STAGES.map((s, index) => ({
name: s.name,
position: index,
isWon: s.isWon,
isLost: s.isLost,
})),
},
},
include: { stages: { orderBy: { position: 'asc' } } },
});
console.log('Seeded waiting pipeline (کاریز منتظر)');
} else {
console.log('Waiting pipeline already seeded');
}
const waitingDealCount = await prisma.deal.count({
where: { organizationId: org.id, pipelineId: waitingPipeline.id },
});
if (waitingDealCount === 0) {
const stageByName = new Map(
waitingPipeline.stages.map((s) => [s.name, s] as const),
);
await prisma.deal.createMany({
data: WAITING_DEALS.map((d) => {
const stage = stageByName.get(d.stageName);
if (!stage) {
throw new Error(`Missing stage ${d.stageName} on waiting pipeline`);
}
return {
organizationId: org.id,
pipelineId: waitingPipeline!.id,
stageId: stage.id,
title: d.title,
notes: d.notes,
amount: d.amount,
currency: 'IRR',
expectedCloseAt: new Date(d.expectedCloseAt),
ownerId: owner?.id,
position: d.position,
tags: ['دمو-کاریز'],
};
}),
});
console.log('Seeded waiting pipeline deals:', WAITING_DEALS.length);
} else {
console.log('Waiting pipeline deals already seeded');
}
const dealsCount = await prisma.deal.count({
where: { organizationId: org.id },
});