Unify the web UI on shared controls and let pipeline columns be managed on the board.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-10 01:22:24 +03:30
co-authored by Cursor
parent f1aba1b386
commit b8ad4037b6
40 changed files with 2334 additions and 788 deletions
+49 -7
View File
@@ -206,22 +206,64 @@ export function updateDealStage(
});
}
export type PipelineBoardFilter = {
query?: string;
amountMin?: number | null;
amountMax?: number | null;
};
export function filterStagesByQuery(
stages: PipelineStage[],
query: string,
): PipelineStage[] {
const q = query.trim().toLowerCase();
if (!q) return stages;
return filterStages(stages, { query });
}
export function filterStages(
stages: PipelineStage[],
filter: PipelineBoardFilter,
): PipelineStage[] {
const q = filter.query?.trim().toLowerCase() ?? '';
const min = filter.amountMin;
const max = filter.amountMax;
const hasFilter =
Boolean(q) ||
(min != null && !Number.isNaN(min)) ||
(max != null && !Number.isNaN(max));
if (!hasFilter) return stages;
return stages.map((stage) => ({
...stage,
deals: stage.deals.filter(
(d) =>
d.title.toLowerCase().includes(q) ||
d.description.toLowerCase().includes(q),
),
deals: stage.deals.filter((d) => {
if (
q &&
!d.title.toLowerCase().includes(q) &&
!d.description.toLowerCase().includes(q)
) {
return false;
}
if (min != null && !Number.isNaN(min) && d.amount < min) return false;
if (max != null && !Number.isNaN(max) && d.amount > max) return false;
return true;
}),
}));
}
export function toFaDealCount(n: number): string {
return `${new Intl.NumberFormat('fa-IR').format(n)} معامله`;
}
/** جابه‌جایی یک ستون به ایندکس جدید */
export function moveStageToIndex(
stages: PipelineStage[],
stageId: string,
toIndex: number,
): PipelineStage[] {
const from = stages.findIndex((s) => s.id === stageId);
if (from < 0) return stages;
const next = [...stages];
const [item] = next.splice(from, 1);
if (!item) return stages;
const clamped = Math.max(0, Math.min(toIndex, next.length));
next.splice(clamped, 0, item);
return next;
}