feat: group activities into boards with their own stages, like sales pipelines
Existing stages and activities move into a default board per environment; new activities pick a board in the form.
This commit is contained in:
@@ -25,11 +25,13 @@ import {
|
||||
UiInput,
|
||||
UiModal,
|
||||
UiPageHeader,
|
||||
UiSelect,
|
||||
UiTextarea,
|
||||
} from '@/components/ui';
|
||||
import ActivityFieldsEditor from '@/components/activities/ActivityFieldsEditor.vue';
|
||||
import { typeColorStyle, useActivityTypes } from '@/lib/activity-types';
|
||||
import type {
|
||||
ActivityBoardDto,
|
||||
ActivityDto,
|
||||
ActivityFieldValue,
|
||||
ActivityFieldValues,
|
||||
@@ -52,6 +54,8 @@ const props = withDefaults(
|
||||
initialType?: ActivityType;
|
||||
initialTitle?: string;
|
||||
initialStageId?: string;
|
||||
/** گروه فعالیت پیشفرض برای فعالیت جدید (گروهی که کاربر در آن است) */
|
||||
initialBoardId?: string;
|
||||
}>(),
|
||||
{ embedded: false },
|
||||
);
|
||||
@@ -74,6 +78,9 @@ const error = ref('');
|
||||
const people = ref<PersonDto[]>([]);
|
||||
const deals = ref<DealDto[]>([]);
|
||||
const users = ref<OrgUserDto[]>([]);
|
||||
const boards = ref<ActivityBoardDto[]>([]);
|
||||
const boardId = ref('');
|
||||
const defaultBoardId = computed(() => boards.value.find((b) => b.isDefault)?.id ?? '');
|
||||
|
||||
const activityTypes = useActivityTypes();
|
||||
const title = ref('');
|
||||
@@ -326,19 +333,25 @@ async function load() {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
const [peopleRes, boardRes, usersRes] = await Promise.all([
|
||||
const [peopleRes, boardRes, usersRes, activityBoardsRes] = await Promise.all([
|
||||
api.get<PaginatedResponse<PersonDto>>('/people', { params: { limit: 100 } }),
|
||||
api.get<PipelineBoardDto>('/pipelines/board'),
|
||||
api.get<OrgUserDto[]>('/users').catch(() => ({ data: [] as OrgUserDto[] })),
|
||||
api
|
||||
.get<ActivityBoardDto[]>('/activities/boards')
|
||||
.catch(() => ({ data: [] as ActivityBoardDto[] })),
|
||||
activityTypes.load(true),
|
||||
]);
|
||||
people.value = peopleRes.data.items;
|
||||
deals.value = boardRes.data.stages.flatMap((s) => s.deals);
|
||||
users.value = usersRes.data;
|
||||
boards.value = activityBoardsRes.data;
|
||||
boardId.value = props.initialBoardId || defaultBoardId.value;
|
||||
|
||||
if (!isNew.value && id.value) {
|
||||
const { data } = await api.get<ActivityDto>(`/activities/${id.value}`);
|
||||
title.value = data.title;
|
||||
boardId.value = data.boardId || defaultBoardId.value;
|
||||
type.value = data.type;
|
||||
typeDefId.value =
|
||||
data.typeDef?.id ?? activityTypes.systemOf.value[data.type]?.id ?? '';
|
||||
@@ -421,6 +434,7 @@ function payload(): CreateActivityRequest {
|
||||
const keys = new Set(typeFields.value.map((f) => f.key));
|
||||
return {
|
||||
title: title.value,
|
||||
boardId: boardId.value || null,
|
||||
type: type.value || 'task',
|
||||
typeDefId: typeDefId.value || null,
|
||||
fields: Object.fromEntries(
|
||||
@@ -631,6 +645,15 @@ watch([id, isNew], () => void load(), { immediate: true });
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="boards.length > 1" class="card">
|
||||
<div class="field">
|
||||
<label for="activity-board">گروه فعالیت</label>
|
||||
<UiSelect id="activity-board" v-model="boardId">
|
||||
<option v-for="b in boards" :key="b.id" :value="b.id">{{ b.name }}</option>
|
||||
</UiSelect>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="typeFields.length" class="card type-fields">
|
||||
<p class="type-fields-title">
|
||||
<span class="type-ico sm" :style="typeColorStyle(typeMeta!.color)">
|
||||
|
||||
Reference in New Issue
Block a user