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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user