mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-16 11:51:43 +00:00
feat: translate automations
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
@update:modelValue="(value) => handleFieldChange(value, index)"
|
@update:modelValue="(value) => handleFieldChange(value, index)"
|
||||||
>
|
>
|
||||||
<SelectTrigger class="m-auto">
|
<SelectTrigger class="m-auto">
|
||||||
<SelectValue placeholder="Select action" />
|
<SelectValue :placeholder="t('form.field.selectAction')" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<SelectTag
|
<SelectTag
|
||||||
v-model="action.value"
|
v-model="action.value"
|
||||||
:items="tagsStore.tagNames.map((tag) => ({ label: tag, value: tag }))"
|
:items="tagsStore.tagNames.map((tag) => ({ label: tag, value: tag }))"
|
||||||
placeholder="Select tag"
|
:placeholder="t('form.field.selectTag')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<ComboBox
|
<ComboBox
|
||||||
v-model="action.value[0]"
|
v-model="action.value[0]"
|
||||||
:items="conversationActions[action.type]?.options"
|
:items="conversationActions[action.type]?.options"
|
||||||
placeholder="Select"
|
:placeholder="t('form.field.select')"
|
||||||
@select="handleValueChange($event, index)"
|
@select="handleValueChange($event, index)"
|
||||||
>
|
>
|
||||||
<template #item="{ item }">
|
<template #item="{ item }">
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
{{ selected.emoji }}
|
{{ selected.emoji }}
|
||||||
<span>{{ selected.label }}</span>
|
<span>{{ selected.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>Select team</span>
|
<span v-else>{{ $t('form.field.selectTeam') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="action.type === 'assign_user'" class="flex items-center gap-2">
|
<div v-else-if="action.type === 'assign_user'" class="flex items-center gap-2">
|
||||||
@@ -91,10 +91,10 @@
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
<span>{{ selected.label }}</span>
|
<span>{{ selected.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>Select user</span>
|
<span v-else>{{ $t('form.field.selectUser') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<span v-if="!selected"> Select</span>
|
<span v-if="!selected"> {{ $t('form.field.select') }}</span>
|
||||||
<span v-else>{{ selected.label }} </span>
|
<span v-else>{{ selected.label }} </span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -114,14 +114,18 @@
|
|||||||
<Editor
|
<Editor
|
||||||
v-model:htmlContent="action.value[0]"
|
v-model:htmlContent="action.value[0]"
|
||||||
@update:htmlContent="(value) => handleEditorChange(value, index)"
|
@update:htmlContent="(value) => handleEditorChange(value, index)"
|
||||||
:placeholder="'Shift + Enter to add new line'"
|
:placeholder="t('admin.macro.message_content.placeholder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button variant="outline" @click.prevent="addAction">Add action</Button>
|
<Button variant="outline" @click.prevent="addAction">{{
|
||||||
|
$t('globals.messages.add', {
|
||||||
|
name: $t('globals.entities.action')
|
||||||
|
})
|
||||||
|
}}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -144,6 +148,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
|||||||
import { SelectTag } from '@/components/ui/select'
|
import { SelectTag } from '@/components/ui/select'
|
||||||
import { useConversationFilters } from '@/composables/useConversationFilters'
|
import { useConversationFilters } from '@/composables/useConversationFilters'
|
||||||
import { getTextFromHTML } from '@/utils/strings.js'
|
import { getTextFromHTML } from '@/utils/strings.js'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import Editor from '@/features/conversation/ConversationTextEditor.vue'
|
import Editor from '@/features/conversation/ConversationTextEditor.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -154,6 +159,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { actions } = toRefs(props)
|
const { actions } = toRefs(props)
|
||||||
|
const { t } = useI18n()
|
||||||
const emit = defineEmits(['update-actions', 'add-action', 'remove-action'])
|
const emit = defineEmits(['update-actions', 'add-action', 'remove-action'])
|
||||||
const tagsStore = useTagStore()
|
const tagsStore = useTagStore()
|
||||||
const { conversationActions } = useConversationFilters()
|
const { conversationActions } = useConversationFilters()
|
||||||
|
|||||||
@@ -1,26 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<Tabs default-value="new_conversation" v-model="selectedTab">
|
<Tabs default-value="new_conversation" v-model="selectedTab">
|
||||||
<TabsList class="grid w-full grid-cols-3 mb-5">
|
<TabsList class="grid w-full grid-cols-3 mb-5">
|
||||||
<TabsTrigger value="new_conversation">New conversation</TabsTrigger>
|
<TabsTrigger value="new_conversation">{{
|
||||||
<TabsTrigger value="conversation_update">Conversation update</TabsTrigger>
|
$t('admin.automation.newConversation')
|
||||||
<TabsTrigger value="time_trigger">Time triggers</TabsTrigger>
|
}}</TabsTrigger>
|
||||||
|
<TabsTrigger value="conversation_update">{{
|
||||||
|
$t('admin.automation.conversationUpdate')
|
||||||
|
}}</TabsTrigger>
|
||||||
|
<TabsTrigger value="time_trigger">{{ $t('admin.automation.timeTriggers') }}</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="new_conversation">
|
<TabsContent value="new_conversation">
|
||||||
<RuleTab type="new_conversation" helptext="Rules that run when a new conversation is created, drag and drop to reorder rules." />
|
<RuleTab
|
||||||
|
type="new_conversation"
|
||||||
|
:helptext="t('admin.automation.newConversation.description')"
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="conversation_update">
|
<TabsContent value="conversation_update">
|
||||||
<RuleTab type="conversation_update" helptext="Rules that run when a conversation is updated." />
|
<RuleTab
|
||||||
|
type="conversation_update"
|
||||||
|
:helptext="t('admin.automation.conversationUpdate.description')"
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="time_trigger">
|
<TabsContent value="time_trigger">
|
||||||
<RuleTab type="time_trigger" helptext="Rules that run once an hour." />
|
<RuleTab type="time_trigger" :helptext="t('admin.automation.timeTriggers.description')" />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import RuleTab from './RuleTab.vue'
|
import RuleTab from './RuleTab.vue'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
const selectedTab = defineModel('automationsTab', {
|
const selectedTab = defineModel('automationsTab', {
|
||||||
default: 'new_conversation',
|
default: 'new_conversation',
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -8,11 +8,17 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="OR" />
|
<RadioGroupItem value="OR" />
|
||||||
<Label>Match <b>ANY</b> of below.</Label>
|
<Label
|
||||||
|
>{{ $t('admin.automation.match') }} <b>{{ $t('admin.automation.any') }}</b>
|
||||||
|
{{ $t('admin.automation.below') }}.</Label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="AND" />
|
<RadioGroupItem value="AND" />
|
||||||
<Label>Match <b>ALL</b> of below.</Label>
|
<Label
|
||||||
|
>{{ $t('admin.automation.match') }} <b>{{ $t('admin.automation.all') }}</b>
|
||||||
|
{{ $t('admin.automation.below') }}.</Label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,11 +37,11 @@
|
|||||||
@update:modelValue="(value) => handleFieldChange(value, index)"
|
@update:modelValue="(value) => handleFieldChange(value, index)"
|
||||||
>
|
>
|
||||||
<SelectTrigger class="w-56">
|
<SelectTrigger class="w-56">
|
||||||
<SelectValue placeholder="Select field" />
|
<SelectValue :placeholder="t('form.field.selectField')" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectLabel>Conversation</SelectLabel>
|
<SelectLabel>{{ $t('globals.entities.conversation') }}</SelectLabel>
|
||||||
<SelectItem v-for="(field, key) in currentFilters" :key="key" :value="key">
|
<SelectItem v-for="(field, key) in currentFilters" :key="key" :value="key">
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -49,7 +55,7 @@
|
|||||||
@update:modelValue="(value) => handleOperatorChange(value, index)"
|
@update:modelValue="(value) => handleOperatorChange(value, index)"
|
||||||
>
|
>
|
||||||
<SelectTrigger class="w-56">
|
<SelectTrigger class="w-56">
|
||||||
<SelectValue placeholder="Select operator" />
|
<SelectValue :placeholder="t('form.field.selectOperator')" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
@@ -69,7 +75,7 @@
|
|||||||
<!-- Plain text input -->
|
<!-- Plain text input -->
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Set value"
|
:placeholder="t('form.fields.setValue')"
|
||||||
v-if="inputType(index) === 'text'"
|
v-if="inputType(index) === 'text'"
|
||||||
v-model="rule.value"
|
v-model="rule.value"
|
||||||
@update:modelValue="(value) => handleValueChange(value, index)"
|
@update:modelValue="(value) => handleValueChange(value, index)"
|
||||||
@@ -78,7 +84,7 @@
|
|||||||
<!-- Number input -->
|
<!-- Number input -->
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Set value"
|
:placeholder="t('form.fields.setValue')"
|
||||||
v-if="inputType(index) === 'number'"
|
v-if="inputType(index) === 'number'"
|
||||||
v-model="rule.value"
|
v-model="rule.value"
|
||||||
@update:modelValue="(value) => handleValueChange(value, index)"
|
@update:modelValue="(value) => handleValueChange(value, index)"
|
||||||
@@ -112,7 +118,7 @@
|
|||||||
{{ selected.emoji }}
|
{{ selected.emoji }}
|
||||||
<span>{{ selected.label }}</span>
|
<span>{{ selected.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>Select team</span>
|
<span v-else>{{ $t('form.field.selectTeam') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -131,10 +137,10 @@
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
<span>{{ selected.label }}</span>
|
<span>{{ selected.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>Select user</span>
|
<span v-else>{{ $t('form.field.selectUser') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<span v-if="!selected"> Select</span>
|
<span v-if="!selected"> {{ $t('form.field.select') }}</span>
|
||||||
<span v-else>{{ selected.label }} </span>
|
<span v-else>{{ selected.label }} </span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -155,9 +161,11 @@
|
|||||||
<TagsInputItemText />
|
<TagsInputItemText />
|
||||||
<TagsInputItemDelete />
|
<TagsInputItemDelete />
|
||||||
</TagsInputItem>
|
</TagsInputItem>
|
||||||
<TagsInputInput placeholder="Select values" />
|
<TagsInputInput :placeholder="t('form.field.selectValue')" />
|
||||||
</TagsInput>
|
</TagsInput>
|
||||||
<p class="text-xs text-gray-500 mt-1">Press enter to select a value</p>
|
<p class="text-xs text-gray-500 mt-1">
|
||||||
|
{{ $t('globals.messages.pressEnterToSelectAValue') }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -173,12 +181,18 @@
|
|||||||
:defaultChecked="rule.case_sensitive_match"
|
:defaultChecked="rule.case_sensitive_match"
|
||||||
@update:checked="(value) => handleCaseSensitiveCheck(value, index)"
|
@update:checked="(value) => handleCaseSensitiveCheck(value, index)"
|
||||||
/>
|
/>
|
||||||
<label> Case sensitive match </label>
|
<label> {{ $t('globals.messages.caseSensitiveMatch') }} </label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button variant="outline" size="sm" @click.prevent="addCondition">Add condition</Button>
|
<Button variant="outline" size="sm" @click.prevent="addCondition">
|
||||||
|
{{
|
||||||
|
$t('globals.messages.add', {
|
||||||
|
name: $t('globals.entities.condition')
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,6 +224,7 @@ import { Label } from '@/components/ui/label'
|
|||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import ComboBox from '@/components/ui/combobox/ComboBox.vue'
|
import ComboBox from '@/components/ui/combobox/ComboBox.vue'
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useConversationFilters } from '@/composables/useConversationFilters'
|
import { useConversationFilters } from '@/composables/useConversationFilters'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -230,6 +245,7 @@ const props = defineProps({
|
|||||||
const { conversationFilters, newConversationFilters } = useConversationFilters()
|
const { conversationFilters, newConversationFilters } = useConversationFilters()
|
||||||
const { ruleGroup } = toRefs(props)
|
const { ruleGroup } = toRefs(props)
|
||||||
const emit = defineEmits(['update-group', 'add-condition', 'remove-condition'])
|
const emit = defineEmits(['update-group', 'add-condition', 'remove-condition'])
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// Computed property to get the correct filters based on type
|
// Computed property to get the correct filters based on type
|
||||||
const currentFilters = computed(() => {
|
const currentFilters = computed(() => {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
{{ rule.name }}
|
{{ rule.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-1">
|
<div class="mb-1">
|
||||||
<Badge v-if="rule.enabled" class="text-[9px]">Enabled</Badge>
|
<Badge v-if="rule.enabled" class="text-[9px]">{{ $t('form.field.enabled') }}</Badge>
|
||||||
<Badge v-else variant="secondary">Disabled</Badge>
|
<Badge v-else variant="secondary">{{ $t('form.field.disabled') }}</Badge>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,16 +21,16 @@
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuItem @click="navigateToEditRule(rule.id)">
|
<DropdownMenuItem @click="navigateToEditRule(rule.id)">
|
||||||
<span>Edit</span>
|
<span>{{ $t('globals.buttons.edit') }}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem @click="() => (alertOpen = true)">
|
<DropdownMenuItem @click="() => (alertOpen = true)">
|
||||||
<span>Delete</span>
|
<span>{{ $t('globals.buttons.delete') }}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem @click="$emit('toggle-rule', rule.id)" v-if="rule.enabled">
|
<DropdownMenuItem @click="$emit('toggle-rule', rule.id)" v-if="rule.enabled">
|
||||||
<span>Disable</span>
|
<span>{{ $t('globals.buttons.disable') }}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem @click="$emit('toggle-rule', rule.id)" v-else>
|
<DropdownMenuItem @click="$emit('toggle-rule', rule.id)" v-else>
|
||||||
<span>Enable</span>
|
<span>{{ $t('globals.buttons.enable') }}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -42,14 +42,16 @@
|
|||||||
<AlertDialog :open="alertOpen" @update:open="alertOpen = $event">
|
<AlertDialog :open="alertOpen" @update:open="alertOpen = $event">
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>Delete Rule</AlertDialogTitle>
|
<AlertDialogTitle>{{ $t('globals.messages.areYouAbsolutelySure') }}</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
This action cannot be undone. This will permanently delete the automation rule.
|
{{ $t('admin.automation.deleteConfirmation') }}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>{{ $t('globals.buttons.cancel') }}</AlertDialogCancel>
|
||||||
<AlertDialogAction @click="handleDelete">Delete</AlertDialogAction>
|
<AlertDialogAction @click="handleDelete">{{
|
||||||
|
$t('globals.buttons.delete')
|
||||||
|
}}</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
}}</SelectValue>
|
}}</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="first_match">Execute the first matching rule</SelectItem>
|
<SelectItem value="first_match">{{
|
||||||
<SelectItem value="all">Execute all matching rules</SelectItem>
|
$t('admin.automation.executeFirstMatchingRule')
|
||||||
|
}}</SelectItem>
|
||||||
|
<SelectItem value="all">{{ $t('admin.automation.executeAllMatchingRules') }}</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
|
|
||||||
export const formSchema = z
|
export const createFormSchema = (t) => z
|
||||||
.object({
|
.object({
|
||||||
name: z.string({
|
name: z.string({
|
||||||
required_error: 'Rule name is required.',
|
required_error: t('globals.messages.required'),
|
||||||
}),
|
}),
|
||||||
description: z.string({
|
description: z.string({
|
||||||
required_error: 'Rule description is required.',
|
required_error: t('globals.messages.required'),
|
||||||
}),
|
}),
|
||||||
enabled: z.boolean().default(true),
|
enabled: z.boolean().default(true),
|
||||||
type: z.string({
|
type: z.string({
|
||||||
required_error: 'Rule type is required.',
|
required_error: t('globals.messages.required'),
|
||||||
}),
|
}),
|
||||||
events: z.array(z.string()).optional(),
|
events: z.array(z.string()).optional(),
|
||||||
})
|
})
|
||||||
@@ -18,7 +18,9 @@ export const formSchema = z
|
|||||||
if (data.type === 'conversation_update' && (!data.events || data.events.length === 0)) {
|
if (data.type === 'conversation_update' && (!data.events || data.events.length === 0)) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
path: ['events'],
|
path: ['events'],
|
||||||
message: 'Please select at least one event.',
|
message: t('globals.messages.pleaseSelectAtLeastOne', {
|
||||||
|
name: t('globals.entities.event')
|
||||||
|
}),
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@
|
|||||||
<div v-if="router.currentRoute.value.name === 'automations'">
|
<div v-if="router.currentRoute.value.name === 'automations'">
|
||||||
<div class="flex justify-between mb-5">
|
<div class="flex justify-between mb-5">
|
||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
<Button @click="newRule">New rule</Button>
|
<Button @click="newRule">{{
|
||||||
|
$t('globals.messages.new', {
|
||||||
|
name: $t('globals.entities.rule')
|
||||||
|
})
|
||||||
|
}}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedTab">
|
<div v-if="selectedTab">
|
||||||
<AutomationTabs v-model="selectedTab" />
|
<AutomationTabs v-model:automationsTab="selectedTab" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<router-view />
|
<router-view />
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<Checkbox :checked="value" @update:checked="handleChange" />
|
<Checkbox :checked="value" @update:checked="handleChange" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<div class="space-y-1 leading-none">
|
<div class="space-y-1 leading-none">
|
||||||
<FormLabel> Enabled </FormLabel>
|
<FormLabel> {{ $t('form.field.enabled') }} </FormLabel>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -28,22 +28,24 @@
|
|||||||
|
|
||||||
<FormField v-slot="{ field }" name="name">
|
<FormField v-slot="{ field }" name="name">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Name</FormLabel>
|
<FormLabel>{{ $t('form.field.name') }}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="text" placeholder="My new rule" v-bind="field" />
|
<Input type="text" placeholder="" v-bind="field" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Name for the rule.</FormDescription>
|
<FormDescription>{{ $t('admin.automation.name.description') }}</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ field }" name="description">
|
<FormField v-slot="{ field }" name="description">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Description</FormLabel>
|
<FormLabel>{{ $t('form.field.description') }}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="text" placeholder="Description for new rule" v-bind="field" />
|
<Input type="text" placeholder="" v-bind="field" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Description for the rule.</FormDescription>
|
<FormDescription>{{
|
||||||
|
$t('admin.automation.description.description')
|
||||||
|
}}</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
@@ -54,18 +56,28 @@
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Select v-bind="componentField" @update:modelValue="handleInput">
|
<Select v-bind="componentField" @update:modelValue="handleInput">
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select a type" />
|
<SelectValue :placeholder="t('form.field.selectType')" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectItem value="new_conversation"> New conversation </SelectItem>
|
<SelectItem value="new_conversation">
|
||||||
<SelectItem value="conversation_update"> Conversation update </SelectItem>
|
{{ $t('admin.automation.newConversation') }}
|
||||||
<SelectItem value="time_trigger"> Time trigger </SelectItem>
|
</SelectItem>
|
||||||
|
<SelectItem value="conversation_update">
|
||||||
|
{{ $t('admin.automation.conversationUpdate') }}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="time_trigger">
|
||||||
|
{{ $t('admin.automation.timeTriggers') }}
|
||||||
|
</SelectItem>
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Type of rule.</FormDescription>
|
<FormDescription>{{
|
||||||
|
$t('globals.messages.typeOf', {
|
||||||
|
name: $t('globals.entities.rule')
|
||||||
|
})
|
||||||
|
}}</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
@@ -73,24 +85,26 @@
|
|||||||
<div :class="{ hidden: form.values.type !== 'conversation_update' }">
|
<div :class="{ hidden: form.values.type !== 'conversation_update' }">
|
||||||
<FormField v-slot="{ componentField, handleChange }" name="events">
|
<FormField v-slot="{ componentField, handleChange }" name="events">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Events</FormLabel>
|
<FormLabel>{{ $t('globals.entities.event', 2) }}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTag
|
<SelectTag
|
||||||
v-model="componentField.modelValue"
|
v-model="componentField.modelValue"
|
||||||
@update:modelValue="handleChange"
|
@update:modelValue="handleChange"
|
||||||
:items="conversationEventOptions"
|
:items="conversationEventOptions"
|
||||||
placeholder="Select events"
|
:placeholder="t('form.fields.selectEvents')"
|
||||||
>
|
>
|
||||||
</SelectTag>
|
</SelectTag>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Evaluate rule on these events.</FormDescription>
|
<FormDescription>{{
|
||||||
|
$t('admin.automation.evaluateRuleOnTheseEvents')
|
||||||
|
}}</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="font-semibold">Match these rules</p>
|
<p class="font-semibold">{{ $t('admin.automation.matchTheseRules') }}</p>
|
||||||
|
|
||||||
<RuleBox
|
<RuleBox
|
||||||
:ruleGroup="firstRuleGroup"
|
:ruleGroup="firstRuleGroup"
|
||||||
@@ -107,13 +121,13 @@
|
|||||||
:class="[groupOperator === 'AND' ? 'bg-black' : 'bg-gray-100 text-black']"
|
:class="[groupOperator === 'AND' ? 'bg-black' : 'bg-gray-100 text-black']"
|
||||||
@click.prevent="toggleGroupOperator('AND')"
|
@click.prevent="toggleGroupOperator('AND')"
|
||||||
>
|
>
|
||||||
AND
|
{{ $t('admin.automation.and') }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
:class="[groupOperator === 'OR' ? 'bg-black' : 'bg-gray-100 text-black']"
|
:class="[groupOperator === 'OR' ? 'bg-black' : 'bg-gray-100 text-black']"
|
||||||
@click.prevent="toggleGroupOperator('OR')"
|
@click.prevent="toggleGroupOperator('OR')"
|
||||||
>
|
>
|
||||||
OR
|
{{ $t('admin.automation.or') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +140,7 @@
|
|||||||
:type="form.values.type"
|
:type="form.values.type"
|
||||||
:groupIndex="1"
|
:groupIndex="1"
|
||||||
/>
|
/>
|
||||||
<p class="font-semibold">Perform these actions</p>
|
<p class="font-semibold">{{ $t('admin.automation.performTheseActions') }}</p>
|
||||||
|
|
||||||
<ActionBox
|
<ActionBox
|
||||||
:actions="getActions()"
|
:actions="getActions()"
|
||||||
@@ -134,7 +148,7 @@
|
|||||||
@add-action="handleAddAction"
|
@add-action="handleAddAction"
|
||||||
@remove-action="handleRemoveAction"
|
@remove-action="handleRemoveAction"
|
||||||
/>
|
/>
|
||||||
<Button type="submit" :isLoading="isLoading">Save</Button>
|
<Button type="submit" :isLoading="isLoading">{{ $t('globals.buttons.save') }}</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,12 +165,13 @@ import api from '@/api'
|
|||||||
import { Checkbox } from '@/components/ui/checkbox'
|
import { Checkbox } from '@/components/ui/checkbox'
|
||||||
import { useForm } from 'vee-validate'
|
import { useForm } from 'vee-validate'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import { formSchema } from '@/features/admin/automation/formSchema.js'
|
import { createFormSchema } from '@/features/admin/automation/formSchema.js'
|
||||||
import { EMITTER_EVENTS } from '@/constants/emitterEvents.js'
|
import { EMITTER_EVENTS } from '@/constants/emitterEvents.js'
|
||||||
import { useEmitter } from '@/composables/useEmitter'
|
import { useEmitter } from '@/composables/useEmitter'
|
||||||
import { handleHTTPError } from '@/utils/http'
|
import { handleHTTPError } from '@/utils/http'
|
||||||
import { SelectTag } from '@/components/ui/select'
|
import { SelectTag } from '@/components/ui/select'
|
||||||
import { OPERATOR } from '@/constants/filterConfig'
|
import { OPERATOR } from '@/constants/filterConfig'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -179,6 +194,7 @@ import { useRoute } from 'vue-router'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const emitter = useEmitter()
|
const emitter = useEmitter()
|
||||||
@@ -206,12 +222,12 @@ const rule = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const conversationEventOptions = [
|
const conversationEventOptions = [
|
||||||
{ label: 'User assigned', value: 'conversation.user.assigned' },
|
{ label: t('admin.automation.event.user.assigned'), value: 'conversation.user.assigned' },
|
||||||
{ label: 'Team assigned', value: 'conversation.team.assigned' },
|
{ label: t('admin.automation.event.team.assigned'), value: 'conversation.team.assigned' },
|
||||||
{ label: 'Priority change', value: 'conversation.priority.change' },
|
{ label: t('admin.automation.event.priority.change'), value: 'conversation.priority.change' },
|
||||||
{ label: 'Status change', value: 'conversation.status.change' },
|
{ label: t('admin.automation.event.status.change'), value: 'conversation.status.change' },
|
||||||
{ label: 'Outgoing message', value: 'conversation.message.outgoing' },
|
{ label: t('admin.automation.event.message.outgoing'), value: 'conversation.message.outgoing' },
|
||||||
{ label: 'Incoming message', value: 'conversation.message.incoming' }
|
{ label: t('admin.automation.event.message.incoming'), value: 'conversation.message.incoming' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -222,8 +238,13 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const breadcrumbPageLabel = () => {
|
const breadcrumbPageLabel = () => {
|
||||||
if (props.id > 0) return 'Edit rule'
|
if (props.id > 0)
|
||||||
return 'New rule'
|
return t('globals.messages.edit', {
|
||||||
|
name: t('globals.entities.rule')
|
||||||
|
})
|
||||||
|
return t('globals.messages.new', {
|
||||||
|
name: t('globals.entities.rule')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const formTitle = computed(() => {
|
const formTitle = computed(() => {
|
||||||
@@ -237,7 +258,7 @@ const isNewForm = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const breadcrumbLinks = [
|
const breadcrumbLinks = [
|
||||||
{ path: 'automations', label: 'Automations' },
|
{ path: 'automations', label: t('admin.automation') },
|
||||||
{ path: '', label: breadcrumbPageLabel() }
|
{ path: '', label: breadcrumbPageLabel() }
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -305,7 +326,7 @@ const handleRemoveAction = (index) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
validationSchema: toTypedSchema(formSchema)
|
validationSchema: toTypedSchema(createFormSchema(t))
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = form.handleSubmit(async (values) => {
|
const onSubmit = form.handleSubmit(async (values) => {
|
||||||
@@ -315,10 +336,8 @@ const onSubmit = form.handleSubmit(async (values) => {
|
|||||||
const handleSave = async (values) => {
|
const handleSave = async (values) => {
|
||||||
if (!areRulesValid()) {
|
if (!areRulesValid()) {
|
||||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||||
title: 'Invalid rules',
|
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
description:
|
description: t('admin.automation.invalid')
|
||||||
'Make sure you have atleast one action and one rule and their values are not empty.'
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -336,12 +355,12 @@ const handleSave = async (values) => {
|
|||||||
router.push({ name: 'automations' })
|
router.push({ name: 'automations' })
|
||||||
}
|
}
|
||||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||||
title: 'Success',
|
description: t('globals.messages.savedSuccessfully', {
|
||||||
description: 'Rule saved successfully'
|
name: t('globals.entities.rule')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||||
title: 'Could not save rule',
|
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
description: handleHTTPError(error).message
|
description: handleHTTPError(error).message
|
||||||
})
|
})
|
||||||
@@ -416,7 +435,6 @@ onMounted(async () => {
|
|||||||
form.setValues(resp.data.data)
|
form.setValues(resp.data.data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||||
title: 'Could not fetch rule',
|
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
description: handleHTTPError(error).message
|
description: handleHTTPError(error).message
|
||||||
})
|
})
|
||||||
|
|||||||
46
i18n/en.json
46
i18n/en.json
@@ -38,6 +38,8 @@
|
|||||||
"globals.entities.config": "Config | Configs",
|
"globals.entities.config": "Config | Configs",
|
||||||
"globals.entities.macro": "Macro | Macros",
|
"globals.entities.macro": "Macro | Macros",
|
||||||
"globals.entities.macroAction": "Macro Action | Macro Actions",
|
"globals.entities.macroAction": "Macro Action | Macro Actions",
|
||||||
|
"globals.entities.action": "Action | Actions",
|
||||||
|
"globals.entities.event": "Event | Events",
|
||||||
"globals.entities.automation": "Automation | Automations",
|
"globals.entities.automation": "Automation | Automations",
|
||||||
"globals.entities.oidc": "OIDC | OIDCs",
|
"globals.entities.oidc": "OIDC | OIDCs",
|
||||||
"globals.entities.oidcProvider": "OIDC Provider | OIDC Providers",
|
"globals.entities.oidcProvider": "OIDC Provider | OIDC Providers",
|
||||||
@@ -45,6 +47,7 @@
|
|||||||
"globals.entities.avatar": "Avatar | Avatars",
|
"globals.entities.avatar": "Avatar | Avatars",
|
||||||
"globals.entities.view": "View | Views",
|
"globals.entities.view": "View | Views",
|
||||||
"globals.entities.email": "Email | Emails",
|
"globals.entities.email": "Email | Emails",
|
||||||
|
"globals.entities.condition": "Condition | Conditions",
|
||||||
"globals.messages.adjustFilters": "Try adjusting your filters",
|
"globals.messages.adjustFilters": "Try adjusting your filters",
|
||||||
"globals.messages.errorUploadingFile": "Error uploading file",
|
"globals.messages.errorUploadingFile": "Error uploading file",
|
||||||
"globals.messages.errorUpdating": "Error updating {name}",
|
"globals.messages.errorUpdating": "Error updating {name}",
|
||||||
@@ -73,12 +76,15 @@
|
|||||||
"globals.messages.tooLong": "{name} is too long, should be at most {max} characters",
|
"globals.messages.tooLong": "{name} is too long, should be at most {max} characters",
|
||||||
"globals.messages.deletedSuccessfully": "{name} deleted successfully",
|
"globals.messages.deletedSuccessfully": "{name} deleted successfully",
|
||||||
"globals.messages.updatedSuccessfully": "{name} updated successfully",
|
"globals.messages.updatedSuccessfully": "{name} updated successfully",
|
||||||
|
"globals.messages.savedSuccessfully": "{name} saved successfully",
|
||||||
"globals.messages.edit": "Edit {name}",
|
"globals.messages.edit": "Edit {name}",
|
||||||
"globals.messages.delete": "Delete {name}",
|
"globals.messages.delete": "Delete {name}",
|
||||||
"globals.messages.create": "Create {name}",
|
"globals.messages.create": "Create {name}",
|
||||||
"globals.messages.new": "New {name}",
|
"globals.messages.new": "New {name}",
|
||||||
|
"globals.messages.add": "Add {name}",
|
||||||
"globals.messages.yes": "Yes",
|
"globals.messages.yes": "Yes",
|
||||||
"globals.messages.no": "No",
|
"globals.messages.no": "No",
|
||||||
|
"globals.messages.typeOf": "Type of {name}",
|
||||||
"globals.messages.createdSuccessfully": "{name} created successfully",
|
"globals.messages.createdSuccessfully": "{name} created successfully",
|
||||||
"globals.messages.invalidEmailAddress": "Invalid email address",
|
"globals.messages.invalidEmailAddress": "Invalid email address",
|
||||||
"globals.messages.pleaseSelectAtLeastOne": "Please select at least one {name}",
|
"globals.messages.pleaseSelectAtLeastOne": "Please select at least one {name}",
|
||||||
@@ -99,6 +105,8 @@
|
|||||||
"globals.messages.somethingWentWrong": "Something went wrong",
|
"globals.messages.somethingWentWrong": "Something went wrong",
|
||||||
"globals.messages.done": "Done",
|
"globals.messages.done": "Done",
|
||||||
"globals.messages.emptyState": "Nothing here",
|
"globals.messages.emptyState": "Nothing here",
|
||||||
|
"globals.messages.pressEnterToSelectAValue": "Press enter to select a value",
|
||||||
|
"globals.messages.caseSensitiveMatch": "Case sensitive match",
|
||||||
"auth.csrfTokenMismatch": "CSRF token mismatch",
|
"auth.csrfTokenMismatch": "CSRF token mismatch",
|
||||||
"authz.permissionDenied": "Permission denied",
|
"authz.permissionDenied": "Permission denied",
|
||||||
"user.userAlreadyLoggedIn": "User already logged in",
|
"user.userAlreadyLoggedIn": "User already logged in",
|
||||||
@@ -234,7 +242,17 @@
|
|||||||
"form.field.pickDate": "Pick a date",
|
"form.field.pickDate": "Pick a date",
|
||||||
"form.field.selectTLS": "Select TLS",
|
"form.field.selectTLS": "Select TLS",
|
||||||
"form.field.selectRoles": "Select roles",
|
"form.field.selectRoles": "Select roles",
|
||||||
|
"form.field.selectField": "Select field",
|
||||||
|
"form.field.selectTeam": "Select team",
|
||||||
|
"form.field.selectTag": "Select tag",
|
||||||
|
"form.field.selectAction": "Select action",
|
||||||
|
"form.field.selectUser": "Select user",
|
||||||
|
"form.field.selectValue": "Select value",
|
||||||
"form.field.selectTeams": "Select teams",
|
"form.field.selectTeams": "Select teams",
|
||||||
|
"form.field.selectType": "Select type",
|
||||||
|
"form.fields.setValue": "Set value",
|
||||||
|
"form.fields.selectEvents": "Select events",
|
||||||
|
"form.field.selectOperator": "Select operator",
|
||||||
"form.error.min": "Should be at least {min} characters",
|
"form.error.min": "Should be at least {min} characters",
|
||||||
"form.error.max": "Should be at most {max} characters",
|
"form.error.max": "Should be at most {max} characters",
|
||||||
"form.error.minmax": "Should be between {min} and {max} characters",
|
"form.error.minmax": "Should be between {min} and {max} characters",
|
||||||
@@ -427,6 +445,34 @@
|
|||||||
"admin.role.business_hours.manage": "Manage Business Hours",
|
"admin.role.business_hours.manage": "Manage Business Hours",
|
||||||
"admin.role.sla.manage": "Manage SLA Policies",
|
"admin.role.sla.manage": "Manage SLA Policies",
|
||||||
"admin.role.ai.manage": "Manage AI Features",
|
"admin.role.ai.manage": "Manage AI Features",
|
||||||
|
"admin.automation": "Automations",
|
||||||
|
"admin.automation.newConversation": "New Conversation",
|
||||||
|
"admin.automation.newConversation.description": "Rules that run when a new conversation is created, drag and drop to reorder rules.",
|
||||||
|
"admin.automation.conversationUpdate": "Conversation Update",
|
||||||
|
"admin.automation.conversationUpdate.description": "Rules that run when a conversation is updated.",
|
||||||
|
"admin.automation.timeTriggers": "Time Triggers",
|
||||||
|
"admin.automation.timeTriggers.description": "Rules that once an hour",
|
||||||
|
"admin.automation.match": "Match",
|
||||||
|
"admin.automation.any": "ANY",
|
||||||
|
"admin.automation.all": "ALL",
|
||||||
|
"admin.automation.below": "below",
|
||||||
|
"admin.automation.deleteConfirmation": "This action cannot be undone. This will permanently delete this automation rule.",
|
||||||
|
"admin.automation.executeFirstMatchingRule": "Execute first matching rule",
|
||||||
|
"admin.automation.executeAllMatchingRules": "Execute all matching rules",
|
||||||
|
"admin.automation.name.description": "Name for this automation rule.",
|
||||||
|
"admin.automation.description.description": "Short description of what this rule does.",
|
||||||
|
"admin.automation.evaluateRuleOnTheseEvents": "Evaluate rule on these events.",
|
||||||
|
"admin.automation.matchTheseRules": "Match these rules",
|
||||||
|
"admin.automation.and": "AND",
|
||||||
|
"admin.automation.or": "OR",
|
||||||
|
"admin.automation.performTheseActions": "Perform these actions",
|
||||||
|
"admin.automation.event.user.assigned": "User assigned",
|
||||||
|
"admin.automation.event.team.assigned": "Team assigned",
|
||||||
|
"admin.automation.event.priority.change": "Priority change",
|
||||||
|
"admin.automation.event.status.change": "Status change",
|
||||||
|
"admin.automation.event.message.outgoing": "Outgoing message",
|
||||||
|
"admin.automation.event.message.incoming": "Incoming message",
|
||||||
|
"admin.automation.invalid": "Make sure you have atleast one action and one rule and their values are not empty.",
|
||||||
"globals.buttons.save": "Save",
|
"globals.buttons.save": "Save",
|
||||||
"globals.buttons.save_changes": "Save changes",
|
"globals.buttons.save_changes": "Save changes",
|
||||||
"globals.buttons.cancel": "Cancel",
|
"globals.buttons.cancel": "Cancel",
|
||||||
|
|||||||
Reference in New Issue
Block a user