Add activity priority desk filter and keep detail browsing inside the same column after stage changes.

When done/reopen/stage moves leave a column, advance to the next activity there or return to the list if empty.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-14 12:02:13 +03:30
co-authored by Cursor
parent ae16e43013
commit 33eeb3242a
7 changed files with 209 additions and 32 deletions
+41 -2
View File
@@ -43,6 +43,7 @@ import type {
ActivityBoardDto,
ActivityDto,
ActivityFilter,
ActivityPriority,
ActivityStageCountsDto,
ActivityStageDto,
ActivityStatus,
@@ -51,7 +52,7 @@ import type {
OrgUserDto,
PaginatedResponse,
} from '@mento/shared';
import { brand } from '@mento/shared';
import { ACTIVITY_PRIORITY_KEYS, brand } from '@mento/shared';
import { useAuthStore } from '@/stores/auth';
import { canMutateActivity } from '@/lib/activity-access';
import { canManageSettings, canViewAllRecords } from '@/lib/roles';
@@ -95,6 +96,7 @@ const actGridStyle = computed(() => ({
/** 'all' یا شناسهٔ نوع فعالیت */
type TypeFilter = string;
type PriorityFilter = 'all' | ActivityPriority;
type StatusFilter = 'all' | ActivityStatus;
type UiStatus = 'done' | 'progress' | 'planned' | 'overdue' | 'canceled';
type DeskScope = 'mine' | 'team';
@@ -122,6 +124,18 @@ const STATUS_FILTERS: Array<{ key: StatusFilter; label: string }> = [
{ key: 'canceled', label: 'لغو شده' },
];
const activityPriorities = useActivityPriorities();
const PRIORITY_FILTERS = computed<
Array<{ key: PriorityFilter; label: string; color?: string }>
>(() => [
{ key: 'all', label: 'همه اولویت‌ها' },
...activityPriorities.items.value.map((row) => ({
key: row.key as PriorityFilter,
label: row.label,
color: row.color,
})),
]);
const pageSize = usePageSize('activities', 10);
const route = useRoute();
@@ -181,7 +195,7 @@ const boardDeleteOpen = ref(false);
async function onTypesChanged() {
await activityTypes.load(true);
await useActivityPriorities().load(true);
await activityPriorities.load(true);
kanbanReload.value += 1;
await refreshAll();
}
@@ -245,6 +259,13 @@ const statusFilter = computed<StatusFilter>(() => {
: 'all';
});
const priorityFilter = computed<PriorityFilter>(() => {
const value = typeof route.query.priority === 'string' ? route.query.priority : 'all';
return ACTIVITY_PRIORITY_KEYS.includes(value as ActivityPriority)
? (value as ActivityPriority)
: 'all';
});
const assigneeFilter = computed(() =>
typeof route.query.assigneeId === 'string' ? route.query.assigneeId : '',
);
@@ -323,6 +344,7 @@ const sharedParams = computed<Record<string, string | undefined>>(() => ({
? legacyBaseType.value
: undefined,
status: statusFilter.value === 'all' ? undefined : statusFilter.value,
priority: priorityFilter.value === 'all' ? undefined : priorityFilter.value,
assigneeId: assigneeFilter.value || undefined,
title: titleFilter.value.trim() || undefined,
customer: customerFilter.value.trim() || undefined,
@@ -335,6 +357,7 @@ function stageDotStyle(s: ActivityStageDto) {
const activeFacetCount = computed(() => {
let n = 0;
if (typeFilter.value !== 'all') n += 1;
if (priorityFilter.value !== 'all') n += 1;
if (statusFilter.value !== 'all') n += 1;
if (assigneeFilter.value) n += 1;
if (titleFilter.value.trim()) n += 1;
@@ -437,6 +460,7 @@ function deskDetailQuery(columnStageId?: string): Record<string, string> {
if (search) query.q = search;
if (typeFilter.value !== 'all') query.typeDefId = typeFilter.value;
if (statusFilter.value !== 'all') query.status = statusFilter.value;
if (priorityFilter.value !== 'all') query.priority = priorityFilter.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();
@@ -775,6 +799,13 @@ function setStatusFilter(next: string) {
patchFacetQuery({ status: value === 'all' ? undefined : value });
}
function setPriorityFilter(next: string) {
const value: PriorityFilter = ACTIVITY_PRIORITY_KEYS.includes(next as ActivityPriority)
? (next as ActivityPriority)
: 'all';
patchFacetQuery({ priority: value === 'all' ? undefined : value });
}
function setAssigneeFilter(next: string) {
patchFacetQuery({
assigneeId: next || undefined,
@@ -799,6 +830,7 @@ function clearFacets() {
type: undefined,
typeDefId: undefined,
status: undefined,
priority: undefined,
assigneeId: undefined,
title: undefined,
customer: undefined,
@@ -1164,6 +1196,7 @@ onMounted(() => {
mobileQuery.addEventListener('change', syncMobile);
void loadUsers();
void activityTypes.load(true);
void activityPriorities.load();
void loadBoards()
.then(() => loadStages())
.then(() => {
@@ -1461,16 +1494,19 @@ onUnmounted(() => {
:customer="customerFilter"
:due="dueFilter"
:type-key="typeFilter"
:priority-key="priorityFilter"
:status-key="statusFilter"
:assignee-id="assigneeFilter"
:show-assignee="showDeskTabs"
:users="users"
:types="TYPE_FILTERS"
:priorities="PRIORITY_FILTERS"
:statuses="STATUS_FILTERS"
@title="setTitleFilter"
@customer="setCustomerFilter"
@due="setDueFilter"
@type="setTypeFilter"
@priority="setPriorityFilter"
@status="setStatusFilter"
@assignee="setAssigneeFilter"
@clear="clearFacets"
@@ -1680,16 +1716,19 @@ onUnmounted(() => {
:customer="customerFilter"
:due="dueFilter"
:type-key="typeFilter"
:priority-key="priorityFilter"
:status-key="statusFilter"
:assignee-id="assigneeFilter"
:show-assignee="showDeskTabs"
:users="users"
:types="TYPE_FILTERS"
:priorities="PRIORITY_FILTERS"
:statuses="STATUS_FILTERS"
@title="setTitleFilter"
@customer="setCustomerFilter"
@due="setDueFilter"
@type="setTypeFilter"
@priority="setPriorityFilter"
@status="setStatusFilter"
@assignee="setAssigneeFilter"
@done="onFacetDone"