fix(boards): keep card order inside a column and show activity priority

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-03 21:22:30 +03:30
co-authored by Cursor
parent bffe174162
commit 52edccf74e
14 changed files with 249 additions and 62 deletions
+29 -14
View File
@@ -32,6 +32,7 @@ import {
import { api } from '@/lib/api';
import { formatJalaliNumeric, toFaDigits } from '@/lib/jalali';
import { canManageSettings } from '@/lib/roles';
import { pointerDropIndex } from '@/lib/pipeline-board';
import { useAuthStore } from '@/stores/auth';
import type { LeadBoardColumnDto, LeadDto, LeadStageDto } from '@mento/shared';
@@ -481,28 +482,41 @@ function onDragOver(stageId: string, event: DragEvent) {
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';
}
async function onDrop(stageId: string, event: DragEvent) {
async function onDrop(stageId: string, event: DragEvent, index?: number) {
event.preventDefault();
event.stopPropagation();
const leadId =
event.dataTransfer?.getData('text/plain') || draggingId.value || '';
draggingId.value = null;
dropStageId.value = null;
if (!leadId) return;
if (!leadId || moving.value) return;
const fromCol = columns.value.find((c) =>
c.leads.some((l) => l.id === leadId),
);
if (!fromCol || fromCol.stage.id === stageId) return;
const lead = fromCol.leads.find((l) => l.id === leadId);
const target = columns.value.find((c) => c.stage.id === stageId);
if (!fromCol || !target) return;
const fromIndex = fromCol.leads.findIndex((l) => l.id === leadId);
const lead = fromCol.leads[fromIndex];
if (!lead) return;
const same = fromCol.stage.id === stageId;
if (same && index == null) return;
const without = (same ? fromCol.leads : target.leads).filter((l) => l.id !== leadId);
let insertAt = index == null || index < 0 ? 0 : index;
if (same && index != null && index > fromIndex) insertAt -= 1;
insertAt = Math.max(0, Math.min(insertAt, without.length));
if (same && insertAt === fromIndex) return;
const nextLeads = [...without];
nextLeads.splice(insertAt, 0, { ...lead, stageId, stageName: target.stage.name });
const prev = columns.value.map((c) => ({
...c,
leads: [...c.leads],
}));
columns.value = columns.value.map((c) => {
if (same && c.stage.id === stageId) return { ...c, leads: nextLeads };
if (c.stage.id === fromCol.stage.id) {
return {
...c,
@@ -511,11 +525,7 @@ async function onDrop(stageId: string, event: DragEvent) {
};
}
if (c.stage.id === stageId) {
return {
...c,
total: c.total + 1,
leads: [{ ...lead, stageId, stageName: c.stage.name }, ...c.leads],
};
return { ...c, total: c.total + 1, leads: nextLeads };
}
return c;
});
@@ -523,7 +533,7 @@ async function onDrop(stageId: string, event: DragEvent) {
moving.value = true;
error.value = '';
try {
await api.patch(`/leads/${leadId}/move`, { stageId });
await api.patch(`/leads/${leadId}/move`, { stageId, position: insertAt });
} catch {
columns.value = prev;
error.value = 'جابه‌جایی لید ناموفق بود.';
@@ -543,7 +553,7 @@ onMounted(load);
مسیر لیدها
</a-typography-title>
<a-typography-text type="secondary">
کشیدن کارت برای تغییر وضعیت. از منوی هر ستون می‌توانید نام، ترتیب و حذف را عوض کنید.
کشیدن کارت برای تغییر ستون یا جابه‌جایی داخل همان ستون. از منوی هر ستون می‌توانید نام، ترتیب و حذف را عوض کنید.
</a-typography-text>
</div>
<a-space :size="GAP">
@@ -647,9 +657,13 @@ onMounted(load);
</div>
<div :style="columnBodyStyle">
<a-card
v-for="lead in col.leads"
<div
v-for="(lead, index) in col.leads"
:key="lead.id"
@dragover.prevent="onDragOver(col.stage.id, $event)"
@drop.stop="onDrop(col.stage.id, $event, pointerDropIndex(index, $event))"
>
<a-card
hoverable
:bordered="true"
:body-style="cardBodyStyle"
@@ -789,6 +803,7 @@ onMounted(load);
</a-flex>
</a-flex>
</a-card>
</div>
<a-typography-text
v-if="!col.leads.length"
+12 -5
View File
@@ -312,11 +312,18 @@ async function onDrop(stageId: string, event: DragEvent, index?: number) {
const dealId = raw;
if (!dealId || moving.value) return;
const current = stages.value.find((s) => s.deals.some((d) => d.id === dealId));
const currentIndex = current?.deals.findIndex((d) => d.id === dealId) ?? -1;
if (current?.id === stageId && (index == null || index === currentIndex)) {
return;
}
const current = stages.value.find((stage) => stage.deals.some((deal) => deal.id === dealId));
if (current?.id === stageId && index == null) return;
const next = updateDealStage(stages.value, dealId, stageId, index);
const unchanged = next.every((stage, stageIndex) => {
const prev = stages.value[stageIndex];
return (
stage.deals.length === (prev?.deals.length ?? -1) &&
stage.deals.every((deal, dealIndex) => deal.id === prev?.deals[dealIndex]?.id)
);
});
if (unchanged) return;
await applyUpdateDealStage(dealId, stageId, index);
}