feat(activities): step through the same desk list from a detail

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-02 14:30:17 +03:30
co-authored by Cursor
parent a0936bf19f
commit e6e90aabf3
5 changed files with 318 additions and 36 deletions
+43 -5
View File
@@ -260,6 +260,7 @@ const activityModal = ref<{
const menuId = ref<string | null>(null);
const boardMenuId = ref<string | null>(null);
const boardMenuActivity = ref<ActivityDto | null>(null);
const boardMenuStageId = ref('');
const boardMenuStyle = ref<Record<string, string> | null>(null);
const deleteActivityIds = ref<string[]>([]);
const deleteSingleTitle = ref('');
@@ -416,14 +417,49 @@ async function loadStages() {
}
}
function activityRoute(id: string, edit = false) {
/** زمینهٔ همان فهرست میز، برای فعالیت قبلی/بعدی. هر بار باز کردن یک nav تازه می‌سازد. */
function deskDetailQuery(columnStageId?: string): Record<string, string> {
const query: Record<string, string> = {
from: 'desk',
nav: crypto.randomUUID(),
view: viewMode.value,
filter: filter.value,
};
if (board.value) query.board = board.value;
if (showDeskTabs.value) query.desk = desk.value;
const search = typeof route.query.q === 'string' ? route.query.q.trim() : '';
if (search) query.q = search;
if (typeFilter.value !== 'all') query.typeDefId = typeFilter.value;
if (statusFilter.value !== 'all') query.status = statusFilter.value;
if (assigneeFilter.value) query.assigneeId = assigneeFilter.value;
if (titleFilter.value.trim()) query.title = titleFilter.value.trim();
if (customerFilter.value.trim()) query.customer = customerFilter.value.trim();
if (dueFilter.value) query.due = dueFilter.value;
const stageId =
viewMode.value === 'board'
? columnStageId
: stage.value !== 'all'
? stage.value
: undefined;
if (stageId) query.stage = stageId;
return query;
}
function activityRoute(id: string, edit = false, columnStageId?: string) {
if (!edit) {
return {
name: 'activity-detail' as const,
params: { id },
query: deskDetailQuery(columnStageId),
};
}
const query: Record<string, string> = {};
for (const key of ['board', 'view', 'desk', 'filter', 'stage', 'q', 'typeDefId', 'status', 'assigneeId', 'title', 'customer', 'due'] as const) {
const value = route.query[key];
if (typeof value === 'string' && value) query[key] = value;
}
return {
name: edit ? ('activity-edit' as const) : ('activity-detail' as const),
name: 'activity-edit' as const,
params: { id },
query,
};
@@ -917,9 +953,10 @@ async function confirmDeleteActivities() {
}
}
function onBoardMenu(activity: ActivityDto) {
function onBoardMenu(activity: ActivityDto, stageId?: string) {
boardMenuId.value = boardMenuId.value === activity.id ? null : activity.id;
boardMenuActivity.value = boardMenuId.value ? activity : null;
boardMenuStageId.value = boardMenuId.value ? stageId || activity.stageId || '' : '';
boardMenuStyle.value = null;
if (!boardMenuId.value) return;
void nextTick(() => {
@@ -932,6 +969,7 @@ const unbindBoardMenuDismiss = bindMenuDismiss(
() => {
boardMenuId.value = null;
boardMenuActivity.value = null;
boardMenuStageId.value = '';
},
);
@@ -1857,8 +1895,8 @@ onUnmounted(() => {
<button type="button" role="menuitem" @click="startSelect(boardMenuId)">انتخاب</button>
<RouterLink
role="menuitem"
:to="activityRoute(boardMenuId)"
@click="boardMenuId = null; boardMenuActivity = null"
:to="activityRoute(boardMenuId, false, viewMode === 'board' ? boardMenuStageId : undefined)"
@click="boardMenuId = null; boardMenuActivity = null; boardMenuStageId = ''"
>
مشاهده
</RouterLink>