Let automations fire on more CRM events and open the studio from the main nav.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-09 21:43:17 +03:30
co-authored by Cursor
parent 51684daa61
commit f1aba1b386
20 changed files with 419 additions and 49 deletions
@@ -221,6 +221,8 @@ function nodeClass(step: AutomationStep) {
<style scoped>
.canvas {
position: relative;
width: 100%;
min-width: 0;
min-height: 22rem;
border-radius: 16px;
overflow: auto;
@@ -17,8 +17,8 @@ import {
AUTOMATION_CONDITION_OP_LABELS,
AUTOMATION_NOTIFY_CHANNEL_LABELS,
AUTOMATION_NOTIFY_CHANNELS,
AUTOMATION_TRIGGER_GROUPS,
AUTOMATION_TRIGGER_LABELS,
AUTOMATION_TRIGGERS,
conditionFieldsForTrigger,
isAutomationCondition,
} from '@mento/shared';
@@ -104,9 +104,15 @@ watch(
<label>
اگر
<select v-model="trigger">
<option v-for="t in AUTOMATION_TRIGGERS" :key="t" :value="t">
{{ AUTOMATION_TRIGGER_LABELS[t] }}
</option>
<optgroup
v-for="group in AUTOMATION_TRIGGER_GROUPS"
:key="group.label"
:label="group.label"
>
<option v-for="t in group.items" :key="t" :value="t">
{{ AUTOMATION_TRIGGER_LABELS[t] }}
</option>
</optgroup>
</select>
</label>
</template>
+12 -4
View File
@@ -40,7 +40,6 @@ const salesPaths = ['/quotes', '/invoices', '/sales/forecast', '/reports', '/not
const morePaths = ['/activities/calendar', '/projects', '/project-cards', '/loyalty'];
const settingsPaths = [
'/settings/team',
'/settings/automations',
'/settings/lead-forms',
'/settings/custom-fields',
'/settings/products',
@@ -59,6 +58,7 @@ const salesActive = computed(() => pathIn(salesPaths));
const moreActive = computed(() => pathIn(morePaths));
const settingsActive = computed(() => pathIn(settingsPaths));
const productsActive = computed(() => route.path.startsWith('/settings/products'));
const automationsActive = computed(() => route.path.startsWith('/settings/automations'));
const extraActive = computed(() =>
pathIn([
'/quotes',
@@ -66,7 +66,6 @@ const extraActive = computed(() =>
'/sales/forecast',
'/notes',
'/tickets',
'/settings/automations',
'/settings/lead-forms',
'/settings/custom-fields',
'/settings/currencies',
@@ -348,6 +347,17 @@ onUnmounted(() => {
<UiIcon name="settings" :size="collapsed ? 20 : 18" />
<span>تنظیمات</span>
</RouterLink>
<RouterLink
v-if="canSettings"
to="/settings/automations"
class="nav-item"
title="اتوماسیون"
:class="{ 'router-link-active': automationsActive }"
@click="closeMenu"
>
<UiIcon name="layers" :size="collapsed ? 20 : 18" />
<span>اتوماسیون</span>
</RouterLink>
</div>
<div v-if="collapsed" ref="moreBtnRef" class="nav-more desktop-only">
@@ -419,7 +429,6 @@ onUnmounted(() => {
</button>
<div v-show="settingsOpen" class="nav-sub">
<RouterLink to="/settings/team" @click="closeMenu">تیم و نقش‌ها</RouterLink>
<RouterLink to="/settings/automations" @click="closeMenu">اتوماسیون</RouterLink>
<RouterLink to="/settings/lead-forms" @click="closeMenu">فرم‌ساز سرنخ</RouterLink>
<RouterLink to="/settings/custom-fields" @click="closeMenu">فیلدهای سفارشی</RouterLink>
<RouterLink to="/settings/currencies" @click="closeMenu">ارزها</RouterLink>
@@ -477,7 +486,6 @@ onUnmounted(() => {
<RouterLink to="/loyalty" @click="closeMenu">باشگاه مشتریان</RouterLink>
<template v-if="canSettings">
<p>تنظیمات</p>
<RouterLink to="/settings/automations" @click="closeMenu">اتوماسیون</RouterLink>
<RouterLink to="/settings/lead-forms" @click="closeMenu">فرم‌ساز سرنخ</RouterLink>
<RouterLink to="/settings/custom-fields" @click="closeMenu">فیلدهای سفارشی</RouterLink>
<RouterLink to="/settings/currencies" @click="closeMenu">ارزها</RouterLink>
+55 -10
View File
@@ -61,14 +61,15 @@ function makeStep(action: AutomationAction): AutomationStep {
}
function makeCondition(): AutomationStep {
const field =
trigger.value === 'deal_won' ? 'deal_amount' : 'ticket_priority';
const field = conditionFieldsForTrigger(trigger.value)[0] ?? 'has_person';
return {
id: `s-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
kind: 'condition',
conditionField: field,
conditionOp: field === 'deal_amount' ? 'gte' : 'eq',
conditionValue: field === 'deal_amount' ? 0 : 'normal',
conditionOp:
field === 'deal_amount' ? 'gte' : field === 'ticket_priority' ? 'eq' : 'is_set',
conditionValue:
field === 'deal_amount' ? 0 : field === 'ticket_priority' ? 'normal' : undefined,
};
}
@@ -256,12 +257,23 @@ onMounted(async () => {
watch(trigger, (next) => {
const allowed = conditionFieldsForTrigger(next);
const fallback = allowed[0] ?? 'has_person';
for (const step of steps.value) {
if (!isAutomationCondition(step)) continue;
if (step.conditionField && allowed.includes(step.conditionField)) continue;
step.conditionField = next === 'deal_won' ? 'deal_amount' : 'ticket_priority';
step.conditionOp = next === 'deal_won' ? 'gte' : 'eq';
step.conditionValue = next === 'deal_won' ? 0 : 'normal';
step.conditionField = fallback;
step.conditionOp =
fallback === 'deal_amount'
? 'gte'
: fallback === 'ticket_priority'
? 'eq'
: 'is_set';
step.conditionValue =
fallback === 'deal_amount'
? 0
: fallback === 'ticket_priority'
? 'normal'
: undefined;
}
});
</script>
@@ -398,6 +410,7 @@ h1 {
grid-template-columns: 1fr;
gap: 0.9rem;
align-items: start;
min-width: 0;
}
.rules,
@@ -524,11 +537,16 @@ h2 {
.stage {
display: grid;
grid-template-columns: 1fr;
grid-template-columns: minmax(0, 1fr);
gap: 0.75rem;
min-width: 0;
}
.stage :deep(.canvas) {
width: 100%;
min-width: 0;
}
.btn {
display: inline-flex;
align-items: center;
@@ -573,11 +591,38 @@ h2 {
@media (min-width: 1280px) {
.workspace {
grid-template-columns: minmax(15rem, 17.5rem) minmax(0, 1fr);
grid-template-columns: minmax(15rem, 20rem) minmax(0, 1fr);
grid-template-areas:
'rules toolbar'
'stage stage';
}
.rules {
grid-area: rules;
height: 100%;
}
.studio {
display: contents;
border: 0;
padding: 0;
box-shadow: none;
background: transparent;
}
.toolbar {
grid-area: toolbar;
border: 1px solid #e2e8f0;
border-radius: 18px;
background: #fff;
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.04);
padding: 0.85rem 1rem;
align-items: end;
}
.stage {
grid-template-columns: minmax(0, 1fr) minmax(16rem, 19rem);
grid-area: stage;
grid-template-columns: minmax(0, 1fr);
}
}