feat(automations): form-driven branching rules, round-robin assignment, lead-linked activities
This commit is contained in:
@@ -11,8 +11,12 @@ import {
|
||||
MAX_AUTOMATION_STEPS,
|
||||
isAutomationCondition,
|
||||
type AutomationAction,
|
||||
type AutomationChainMode,
|
||||
type AutomationStep,
|
||||
type AutomationTrigger,
|
||||
type LeadFormDto,
|
||||
type OrgUserDto,
|
||||
type TeamDto,
|
||||
} from '@mento/shared';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
|
||||
@@ -20,11 +24,44 @@ export type AutomationAddPayload =
|
||||
| { kind: 'action'; action: AutomationAction }
|
||||
| { kind: 'condition' };
|
||||
|
||||
const props = defineProps<{
|
||||
trigger: AutomationTrigger;
|
||||
steps: AutomationStep[];
|
||||
selected: 'trigger' | string;
|
||||
}>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
trigger: AutomationTrigger;
|
||||
steps: AutomationStep[];
|
||||
selected: 'trigger' | string;
|
||||
chainMode?: AutomationChainMode;
|
||||
form?: LeadFormDto | null;
|
||||
users?: OrgUserDto[];
|
||||
teams?: TeamDto[];
|
||||
}>(),
|
||||
{ chainMode: 'stop', form: null, users: () => [], teams: () => [] },
|
||||
);
|
||||
|
||||
/** شرطی که یک شاخهٔ جدید را شروع میکند (گام قبلی شرط نیست) */
|
||||
function isBranchStart(index: number) {
|
||||
if (props.chainMode !== 'branches') return false;
|
||||
const step = props.steps[index];
|
||||
if (!step || !isAutomationCondition(step)) return false;
|
||||
const prev = props.steps[index - 1];
|
||||
return !prev || !isAutomationCondition(prev);
|
||||
}
|
||||
|
||||
function userName(id?: string | null) {
|
||||
return props.users.find((u) => u.id === id)?.fullName ?? 'کاربر';
|
||||
}
|
||||
|
||||
function assignHint(step: AutomationStep) {
|
||||
const mode = step.assignMode ?? (step.assigneeId ? 'user' : 'owner');
|
||||
if (mode === 'user') return userName(step.assigneeId);
|
||||
if (mode === 'team_round_robin') {
|
||||
const t = props.teams.find((x) => x.id === step.assigneeTeamId);
|
||||
return `نوبتی: ${t?.name ?? 'تیم'}`;
|
||||
}
|
||||
if (mode === 'users_round_robin') {
|
||||
return `نوبتی بین ${toFaDigits((step.assigneeUserIds ?? []).length)} نفر`;
|
||||
}
|
||||
return 'مسئول پرونده';
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
select: [id: 'trigger' | string];
|
||||
@@ -55,6 +92,10 @@ function canRemove(step: AutomationStep) {
|
||||
function stepTitle(step: AutomationStep) {
|
||||
if (isAutomationCondition(step)) {
|
||||
const field = step.conditionField ?? 'has_person';
|
||||
if (field === 'form_field') {
|
||||
const f = props.form?.fields.find((x) => x.key === step.conditionKey);
|
||||
return f?.label ?? AUTOMATION_CONDITION_FIELD_LABELS[field];
|
||||
}
|
||||
return AUTOMATION_CONDITION_FIELD_LABELS[field];
|
||||
}
|
||||
return AUTOMATION_ACTION_LABELS[step.action ?? 'create_activity'];
|
||||
@@ -64,6 +105,10 @@ function stepHint(step: AutomationStep) {
|
||||
if (isAutomationCondition(step)) {
|
||||
const op = step.conditionOp ?? 'is_set';
|
||||
const opLabel = AUTOMATION_CONDITION_OP_LABELS[op];
|
||||
if (step.conditionField === 'form_field') {
|
||||
if (op === 'is_set' || op === 'is_empty') return opLabel;
|
||||
return `${opLabel} «${step.conditionValue ?? ''}»`;
|
||||
}
|
||||
if (step.conditionField === 'deal_amount' && step.conditionValue != null) {
|
||||
return `${opLabel} ${toFaDigits(Number(step.conditionValue))}`;
|
||||
}
|
||||
@@ -90,7 +135,7 @@ function stepHint(step: AutomationStep) {
|
||||
task: 'وظیفه',
|
||||
note: 'یادداشت',
|
||||
};
|
||||
return types[step.activityType ?? 'task'] ?? 'وظیفه';
|
||||
return `${types[step.activityType ?? 'task'] ?? 'وظیفه'} — ${assignHint(step)}`;
|
||||
}
|
||||
|
||||
function openAdd(index: number) {
|
||||
@@ -148,10 +193,11 @@ function nodeClass(step: AutomationStep) {
|
||||
</div>
|
||||
|
||||
<div class="node-wrap">
|
||||
<span v-if="isBranchStart(i)" class="branch-tag">شاخه</span>
|
||||
<button
|
||||
class="node action"
|
||||
type="button"
|
||||
:class="[nodeClass(step), { on: selected === step.id }]"
|
||||
:class="[nodeClass(step), { on: selected === step.id, 'branch-start': isBranchStart(i) }]"
|
||||
@click="emit('select', step.id)"
|
||||
>
|
||||
<span class="icon">
|
||||
@@ -301,6 +347,25 @@ function nodeClass(step: AutomationStep) {
|
||||
filter: drop-shadow(0 8px 14px rgba(180, 83, 9, 0.14));
|
||||
}
|
||||
|
||||
.branch-tag {
|
||||
position: absolute;
|
||||
top: -1.35rem;
|
||||
inset-inline-start: 50%;
|
||||
transform: translateX(50%);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
color: #b45309;
|
||||
background: #fff7ed;
|
||||
border: 1px solid #fed7aa;
|
||||
border-radius: 999px;
|
||||
padding: 0.05rem 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.node.condition.branch-start {
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
.node.on {
|
||||
border-color: #14b8a6;
|
||||
box-shadow:
|
||||
|
||||
Reference in New Issue
Block a user