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
@@ -511,6 +511,7 @@ function activityQuery(id: string) {
if (source.typeDefId) query.typeDefId = source.typeDefId;
else if (source.type) query.type = source.type;
if (source.status) query.status = source.status;
if (source.priority) query.priority = source.priority;
if (source.assigneeId) query.assigneeId = source.assigneeId;
if (source.title) query.title = source.title;
if (source.customer) query.customer = source.customer;
@@ -1,7 +1,14 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
export type DeskFilterField = 'title' | 'who' | 'customer' | 'type' | 'when' | 'status';
export type DeskFilterField =
| 'title'
| 'who'
| 'customer'
| 'type'
| 'priority'
| 'when'
| 'status';
const props = defineProps<{
/** یک ستون، یا پنل کامل بالای صفحه */
@@ -10,11 +17,13 @@ const props = defineProps<{
customer: string;
due: '' | 'set' | 'unset';
typeKey: string;
priorityKey: string;
statusKey: string;
assigneeId: string;
showAssignee: boolean;
users: Array<{ id: string; fullName: string }>;
types: Array<{ key: string; label: string }>;
priorities: Array<{ key: string; label: string; color?: string }>;
statuses: Array<{ key: string; label: string }>;
}>();
@@ -23,6 +32,7 @@ const emit = defineEmits<{
customer: [value: string];
due: [value: '' | 'set' | 'unset'];
type: [value: string];
priority: [value: string];
status: [value: string];
assignee: [value: string];
clear: [];
@@ -86,6 +96,7 @@ const hasAny = computed(
() =>
Boolean(props.title.trim() || props.customer.trim() || props.due || props.assigneeId) ||
props.typeKey !== 'all' ||
props.priorityKey !== 'all' ||
props.statusKey !== 'all',
);
@@ -101,6 +112,9 @@ const whoLabel = computed(
const typeLabel = computed(
() => props.types.find((item) => item.key === props.typeKey)?.label || 'همه',
);
const priorityLabel = computed(
() => props.priorities.find((item) => item.key === props.priorityKey)?.label || 'همه',
);
const statusLabel = computed(
() => props.statuses.find((item) => item.key === props.statusKey)?.label || 'همه',
);
@@ -144,6 +158,11 @@ function pickType(next: string) {
finish();
}
function pickPriority(next: string) {
emit('priority', props.priorityKey === next && next !== 'all' ? 'all' : next);
finish();
}
function pickStatus(next: string) {
emit('status', props.statusKey === next && next !== 'all' ? 'all' : next);
finish();
@@ -282,6 +301,36 @@ function pickAssignee(next: string) {
</div>
</section>
<section v-if="shows('priority')">
<button
v-if="field === 'all'"
type="button"
class="acc"
:class="{ on: priorityKey !== 'all' }"
@click="toggleSection('priority')"
>
<span>اولویت</span>
<em>{{ priorityLabel }}</em>
</button>
<div v-if="opened('priority')" class="pick" role="listbox" aria-label="اولویت">
<button
v-for="opt in priorities"
:key="opt.key"
type="button"
:class="{ on: priorityKey === opt.key }"
@click="pickPriority(opt.key)"
>
<span
v-if="opt.color && opt.key !== 'all'"
class="prio-dot"
:style="{ background: opt.color }"
aria-hidden="true"
/>
{{ opt.label }}
</button>
</div>
</section>
<section v-if="shows('when')">
<button
v-if="field === 'all'"
@@ -434,6 +483,9 @@ function pickAssignee(next: string) {
}
.pick button {
display: flex;
align-items: center;
gap: 0.4rem;
text-align: start;
border-radius: 8px;
padding: 0.42rem 0.5rem;
@@ -450,6 +502,13 @@ function pickAssignee(next: string) {
font-weight: 700;
}
.prio-dot {
width: 0.55rem;
height: 0.55rem;
border-radius: 50%;
flex: none;
}
.seg {
display: flex;
gap: 0.25rem;