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
+59 -15
View File
@@ -29,11 +29,28 @@ import type {
PipelineBoardDto,
} from '@mento/shared';
const props = withDefaults(
defineProps<{
embedded?: boolean;
entityId?: string;
initialPersonId?: string;
initialType?: ActivityType;
initialTitle?: string;
}>(),
{ embedded: false },
);
const emit = defineEmits<{ saved: []; cancel: [] }>();
const route = useRoute();
const router = useRouter();
const auth = useAuthStore();
const id = computed(() => (typeof route.params.id === 'string' ? route.params.id : ''));
const isNew = computed(() => route.name === 'activity-new');
const id = computed(() => {
if (props.embedded) return props.entityId ?? '';
return typeof route.params.id === 'string' ? route.params.id : '';
});
const isNew = computed(() =>
props.embedded ? !props.entityId : route.name === 'activity-new',
);
const loading = ref(false);
const saving = ref(false);
@@ -190,18 +207,21 @@ async function load() {
isDone.value = data.status === 'done';
} else {
resetForm();
if (typeof route.query.personId === 'string' && route.query.personId) {
personId.value = route.query.personId;
}
const incomingPerson =
props.initialPersonId ||
(typeof route.query.personId === 'string' ? route.query.personId : '');
if (incomingPerson) personId.value = incomingPerson;
const incomingType = props.initialType || route.query.type;
if (
typeof route.query.type === 'string' &&
typeOptions.some((o) => o.value === route.query.type)
typeof incomingType === 'string' &&
typeOptions.some((o) => o.value === incomingType)
) {
type.value = route.query.type as ActivityType;
}
if (typeof route.query.title === 'string' && route.query.title) {
title.value = route.query.title;
type.value = incomingType as ActivityType;
}
const incomingTitle =
props.initialTitle ||
(typeof route.query.title === 'string' ? route.query.title : '');
if (incomingTitle) title.value = incomingTitle;
if (typeof route.query.dueAt === 'string' && route.query.dueAt) {
splitDue(new Date(route.query.dueAt).toISOString());
}
@@ -271,6 +291,10 @@ async function submit(next: 'detail' | 'new' = 'detail') {
status: isDone.value ? 'done' : 'open',
});
}
if (props.embedded) {
emit('saved');
return;
}
if (next === 'new') {
if (!isNew.value) {
await router.push({ name: 'activity-new' });
@@ -299,6 +323,10 @@ function bumpDuration(delta: number) {
}
function cancel() {
if (props.embedded) {
emit('cancel');
return;
}
if (!isNew.value && id.value) {
void router.push({ name: 'activity-detail', params: { id: id.value } });
return;
@@ -310,8 +338,14 @@ watch([id, isNew], () => void load(), { immediate: true });
</script>
<template>
<section class="page" @click="typeOpen = assigneeOpen = personOpen = false">
<header class="desktop-head">
<section class="page" :class="{ embedded }" @click="typeOpen = assigneeOpen = personOpen = false">
<div v-if="embedded" class="embed-actions">
<button class="btn primary" type="button" :disabled="saving" @click="submit('detail')">
{{ saving ? 'در حال ذخیره…' : 'ذخیره فعالیت' }}
</button>
<button class="btn cancel" type="button" :disabled="saving" @click="cancel">انصراف</button>
</div>
<header v-if="!embedded" class="desktop-head">
<p class="crumb">
<RouterLink :to="{ name: 'activities' }">فعالیت‌ها</RouterLink>
<span>‹</span>
@@ -333,7 +367,7 @@ watch([id, isNew], () => void load(), { immediate: true });
</div>
</header>
<header class="mobile-head">
<header v-if="!embedded" class="mobile-head">
<button class="icon-btn" type="button" aria-label="ذخیره" :disabled="saving" @click="submit('detail')">
<UiIcon name="check-plain" :size="22" />
</button>
@@ -579,7 +613,7 @@ watch([id, isNew], () => void load(), { immediate: true });
</aside>
</div>
<footer class="mobile-save">
<footer v-if="!embedded" class="mobile-save">
<button class="btn primary" type="button" :disabled="saving || loading" @click="submit('detail')">
{{ saving ? 'در حال ذخیره…' : 'ذخیره فعالیت' }}
</button>
@@ -594,6 +628,16 @@ watch([id, isNew], () => void load(), { immediate: true });
padding-bottom: 5.5rem;
}
.page.embedded {
padding-bottom: 0;
}
.embed-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.desktop-head {
display: none;
justify-content: space-between;