feat(automations): form-driven branching rules, round-robin assignment, lead-linked activities

This commit is contained in:
2026-10-11 14:12:36 +03:30
parent b30f68563d
commit b3b92abae9
27 changed files with 1160 additions and 91 deletions
+60 -1
View File
@@ -17,12 +17,15 @@ import {
} from '@/components/ui';
import RelatedTasksPanel from '@/components/routines/RelatedTasksPanel.vue';
import type {
ActivityDto,
ConvertLeadResult,
LeadDto,
LeadEventDto,
LeadStageDto,
OrgUserDto,
PaginatedResponse,
} from '@mento/shared';
import { ACTIVITY_TYPE_LABELS } from '@mento/shared';
type LeadDetail = LeadDto & { events: LeadEventDto[] };
@@ -39,6 +42,7 @@ const okMsg = ref('');
const lead = ref<LeadDetail | null>(null);
const stages = ref<LeadStageDto[]>([]);
const users = ref<OrgUserDto[]>([]);
const activities = ref<ActivityDto[]>([]);
const stageId = ref('');
const assigneeId = ref('');
@@ -85,12 +89,18 @@ async function load() {
loading.value = true;
error.value = '';
try {
const [leadRes, stagesRes, usersRes] = await Promise.all([
const [leadRes, stagesRes, usersRes, actRes] = await Promise.all([
api.get<LeadDetail>(`/leads/${id.value}`),
api.get<LeadStageDto[]>('/leads/stages'),
api.get<OrgUserDto[]>('/users').catch(() => ({ data: [] as OrgUserDto[] })),
api
.get<PaginatedResponse<ActivityDto>>('/activities', {
params: { filter: 'all', leadId: id.value, limit: 30 },
})
.catch(() => null),
]);
lead.value = leadRes.data;
activities.value = actRes?.data.items ?? [];
stages.value = stagesRes.data;
users.value = usersRes.data.filter((u) => u.isActive !== false);
stageId.value = leadRes.data.stageId;
@@ -394,6 +404,22 @@ onMounted(load);
<RelatedTasksPanel :entity="{ type: 'lead', id: lead.id, label: lead.title }" />
<UiCard v-if="activities.length" title="فعالیت‌های مرتبط">
<ul class="lead-acts">
<li v-for="a in activities" :key="a.id">
<RouterLink :to="{ name: 'activity-detail', params: { id: a.id } }">
<strong>{{ a.title }}</strong>
</RouterLink>
<span class="muted">
{{ a.typeDef?.name || ACTIVITY_TYPE_LABELS[a.type] }}
· {{ a.assignee?.fullName || 'بدون مسئول' }}
· {{ formatDate(a.dueAt) }}
· {{ a.status === 'done' ? 'انجام شد' : a.status === 'canceled' ? 'لغو شده' : 'باز' }}
</span>
</li>
</ul>
</UiCard>
<UiCard title="جدول زمانی">
<ul v-if="lead.events.length" class="timeline">
<li v-for="ev in lead.events" :key="ev.id">
@@ -538,6 +564,39 @@ onMounted(load);
margin-bottom: 0;
}
.lead-acts {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.6rem;
}
.lead-acts li {
display: grid;
gap: 0.15rem;
padding-bottom: 0.55rem;
border-bottom: 1px solid #f1f5f9;
}
.lead-acts li:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.lead-acts a {
color: inherit;
text-decoration: none;
}
.lead-acts a:hover strong {
color: var(--accent, #0f766e);
}
.lead-acts .muted {
font-size: 0.78rem;
}
.timeline {
list-style: none;
margin: 0;