Add desk-style task workflow on Activities and Project boards.
Managers can switch my desk vs team, reassign activities, and filter project cards to their work; assignees can see and move cards.
This commit is contained in:
@@ -12,6 +12,8 @@ export type PipelineDeal = {
|
||||
currencyUnit?: string;
|
||||
/** نام مسئولان برای نمایش روی کارت */
|
||||
assigneeNames?: string[];
|
||||
ownerId?: string | null;
|
||||
assigneeIds?: string[];
|
||||
};
|
||||
|
||||
export type PipelineUiCopy = {
|
||||
@@ -206,6 +208,8 @@ export function mapProjectBoardToStages(
|
||||
person: { fullName: string } | null;
|
||||
company: { name: string } | null;
|
||||
deal: { title: string } | null;
|
||||
owner?: { id: string; fullName: string } | null;
|
||||
assignees?: Array<{ id: string; fullName: string }>;
|
||||
}>;
|
||||
}>;
|
||||
},
|
||||
@@ -215,22 +219,33 @@ export function mapProjectBoardToStages(
|
||||
id: stage.id,
|
||||
title: stage.name,
|
||||
color: ACCENT_CYCLE[index % ACCENT_CYCLE.length]!,
|
||||
deals: stage.cards.map((card) => ({
|
||||
id: card.id,
|
||||
title: card.title,
|
||||
description:
|
||||
card.company?.name ||
|
||||
card.person?.fullName ||
|
||||
card.deal?.title ||
|
||||
card.notes?.trim() ||
|
||||
'',
|
||||
amount: null,
|
||||
date: formatDate(card.createdAt),
|
||||
initials: initialsFromTitle(
|
||||
card.company?.name || card.person?.fullName || card.title,
|
||||
),
|
||||
stageId: card.stageId,
|
||||
})),
|
||||
deals: stage.cards.map((card) => {
|
||||
const assigneeNames = [
|
||||
...(card.owner?.fullName ? [card.owner.fullName] : []),
|
||||
...(card.assignees ?? [])
|
||||
.map((a) => a.fullName)
|
||||
.filter((n) => n && n !== card.owner?.fullName),
|
||||
];
|
||||
return {
|
||||
id: card.id,
|
||||
title: card.title,
|
||||
description:
|
||||
card.company?.name ||
|
||||
card.person?.fullName ||
|
||||
card.deal?.title ||
|
||||
card.notes?.trim() ||
|
||||
'',
|
||||
amount: null,
|
||||
date: formatDate(card.createdAt),
|
||||
initials: initialsFromTitle(
|
||||
card.company?.name || card.person?.fullName || card.title,
|
||||
),
|
||||
stageId: card.stageId,
|
||||
assigneeNames,
|
||||
ownerId: card.owner?.id ?? null,
|
||||
assigneeIds: (card.assignees ?? []).map((a) => a.id),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -292,6 +307,8 @@ export type PipelineBoardFilter = {
|
||||
query?: string;
|
||||
amountMin?: number | null;
|
||||
amountMax?: number | null;
|
||||
/** اگر ست باشد فقط کارتهای مربوط به این کاربر (مالک یا مسئول) */
|
||||
mineUserId?: string | null;
|
||||
};
|
||||
|
||||
export function filterStagesByQuery(
|
||||
@@ -308,14 +325,22 @@ export function filterStages(
|
||||
const q = filter.query?.trim().toLowerCase() ?? '';
|
||||
const min = filter.amountMin;
|
||||
const max = filter.amountMax;
|
||||
const mineUserId = filter.mineUserId?.trim() || null;
|
||||
const hasFilter =
|
||||
Boolean(q) ||
|
||||
Boolean(mineUserId) ||
|
||||
(min != null && !Number.isNaN(min)) ||
|
||||
(max != null && !Number.isNaN(max));
|
||||
if (!hasFilter) return stages;
|
||||
return stages.map((stage) => ({
|
||||
...stage,
|
||||
deals: stage.deals.filter((d) => {
|
||||
if (mineUserId) {
|
||||
const isMine =
|
||||
d.ownerId === mineUserId ||
|
||||
(d.assigneeIds ?? []).includes(mineUserId);
|
||||
if (!isMine) return false;
|
||||
}
|
||||
if (
|
||||
q &&
|
||||
!d.title.toLowerCase().includes(q) &&
|
||||
|
||||
Reference in New Issue
Block a user