fix: paddings and labels
(cherry picked from commit db577afc568b56b49846773b16b638e0cf1444fa) # Conflicts: # frontend/src/components/Settings/AssignmentRules/AssigneeRules.vue # frontend/src/components/Settings/AssignmentRules/AssignmentRuleView.vue # frontend/src/components/Settings/AssignmentRules/AssignmentRules.vue
This commit is contained in:
parent
49ed1ac174
commit
004923419c
@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||||
|
__('Assignee Rules')
|
||||||
|
}}</span>
|
||||||
|
<span class="text-p-sm text-ink-gray-6">
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
'Define who receives the {0} and how they’re distributed among agents.',
|
||||||
|
[documentType],
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-8 flex items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
<div class="text-base font-medium text-ink-gray-8">
|
||||||
|
{{
|
||||||
|
__('{0} Routing', [
|
||||||
|
assignmentRuleData.documentType == 'CRM Lead'
|
||||||
|
? __('Lead')
|
||||||
|
: __('Deal'),
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-p-sm text-ink-gray-6 mt-1">
|
||||||
|
{{
|
||||||
|
__('Choose how {0} are distributed among selected assignees.', [
|
||||||
|
documentType,
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Popover placement="bottom-end">
|
||||||
|
<template #target="{ togglePopover }">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between text-base rounded h-7 py-1.5 pl-2 pr-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors w-full dark:[color-scheme:dark] select-none min-w-40"
|
||||||
|
@click="togglePopover()"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
documentRoutingOptions.find(
|
||||||
|
(option) => option.value == assignmentRuleData.rule,
|
||||||
|
)?.label
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<FeatherIcon name="chevron-down" class="size-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ togglePopover }">
|
||||||
|
<div
|
||||||
|
class="p-1 text-ink-gray-7 mt-1 w-48 bg-white shadow-xl rounded"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="option in documentRoutingOptions"
|
||||||
|
:key="option.value"
|
||||||
|
class="p-2 cursor-pointer hover:bg-gray-50 text-sm flex items-center justify-between rounded"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
assignmentRuleData.rule = option.value
|
||||||
|
togglePopover()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ option.label }}
|
||||||
|
</span>
|
||||||
|
<FeatherIcon
|
||||||
|
v-if="assignmentRuleData.rule == option.value"
|
||||||
|
name="check"
|
||||||
|
class="size-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-7 flex items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
<div class="text-base font-medium text-ink-gray-8">
|
||||||
|
{{ __('Assignees') }}
|
||||||
|
</div>
|
||||||
|
<div class="text-p-sm text-ink-gray-6 mt-1">
|
||||||
|
{{ __('Choose who receives the {0}.', [documentType]) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AssigneeSearch @addAssignee="validateAssignmentRule('users')" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 flex flex-wrap gap-2">
|
||||||
|
<div
|
||||||
|
v-for="user in users"
|
||||||
|
:key="user.name"
|
||||||
|
class="flex items-center gap-2 text-sm bg-surface-gray-2 rounded-md p-1 w-max px-2 select-none"
|
||||||
|
>
|
||||||
|
<Avatar :image="user.user_image" :label="user.full_name" size="sm" />
|
||||||
|
<div class="text-ink-gray-7">
|
||||||
|
{{ user.full_name }}
|
||||||
|
</div>
|
||||||
|
<Tooltip
|
||||||
|
v-if="user.email == assignmentRuleData.lastUser"
|
||||||
|
:text="__('Last user assigned by this rule')"
|
||||||
|
:hover-delay="0.35"
|
||||||
|
:placement="'top'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="text-xs rounded-full select-none bg-blue-600 text-white p-0.5 px-2"
|
||||||
|
>
|
||||||
|
{{ __('Last') }}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
<Button variant="ghost" icon="x" @click="removeAssignedUser(user)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ErrorMessage :message="assignmentRuleErrors.users" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { Avatar, Button, ErrorMessage, Popover, Tooltip } from 'frappe-ui'
|
||||||
|
import AssigneeSearch from './AssigneeSearch.vue'
|
||||||
|
import { computed, inject } from 'vue'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
|
|
||||||
|
const { getUser } = usersStore()
|
||||||
|
const assignmentRuleData = inject('assignmentRuleData')
|
||||||
|
const assignmentRuleErrors = inject('assignmentRuleErrors')
|
||||||
|
const validateAssignmentRule = inject('validateAssignmentRule')
|
||||||
|
const documentType = computed(() =>
|
||||||
|
assignmentRuleData.value.documentType == 'CRM Lead'
|
||||||
|
? __('leads')
|
||||||
|
: __('deals'),
|
||||||
|
)
|
||||||
|
|
||||||
|
const documentRoutingOptions = [
|
||||||
|
{
|
||||||
|
label: 'Auto-rotate',
|
||||||
|
value: 'Round Robin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Assign by workload',
|
||||||
|
value: 'Load Balancing',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const removeAssignedUser = (user) => {
|
||||||
|
assignmentRuleData.value.users = assignmentRuleData.value.users.filter(
|
||||||
|
(u) => u.user !== user.name,
|
||||||
|
)
|
||||||
|
validateAssignmentRule('users')
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = computed(() => {
|
||||||
|
const _users = []
|
||||||
|
assignmentRuleData.value.users.forEach((user) => {
|
||||||
|
_users.push(getUser(user.user))
|
||||||
|
})
|
||||||
|
return _users
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,782 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="getAssignmentRuleData.loading"
|
||||||
|
class="flex items-center h-full justify-center"
|
||||||
|
>
|
||||||
|
<LoadingIndicator class="w-4" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!getAssignmentRuleData.loading"
|
||||||
|
class="sticky top-0 z-10 bg-white pb-6 px-10 py-8"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between w-full">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
icon-left="chevron-left"
|
||||||
|
:label="
|
||||||
|
assignmentRuleData.assignmentRuleName || __('New Assignment Rule')
|
||||||
|
"
|
||||||
|
size="md"
|
||||||
|
@click="goBack()"
|
||||||
|
class="cursor-pointer -ml-4 hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
|
||||||
|
/>
|
||||||
|
<Badge
|
||||||
|
:variant="'subtle'"
|
||||||
|
:theme="'orange'"
|
||||||
|
size="sm"
|
||||||
|
:label="__('Unsaved')"
|
||||||
|
v-if="isDirty"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-2"
|
||||||
|
@click="assignmentRuleData.disabled = !assignmentRuleData.disabled"
|
||||||
|
>
|
||||||
|
<Switch size="sm" :model-value="!assignmentRuleData.disabled" />
|
||||||
|
<span class="text-sm text-ink-gray-7">{{ __('Enabled') }}</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
:disabled="Boolean(!isDirty && step.data)"
|
||||||
|
:label="__('Save')"
|
||||||
|
theme="gray"
|
||||||
|
variant="solid"
|
||||||
|
@click="saveAssignmentRule()"
|
||||||
|
:loading="isLoading || getAssignmentRuleData.loading"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!getAssignmentRuleData.loading" class="overflow-y-auto px-10 pb-8">
|
||||||
|
<div class="grid grid-cols-2 gap-5">
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
:type="'text'"
|
||||||
|
size="sm"
|
||||||
|
variant="subtle"
|
||||||
|
:placeholder="__('Name')"
|
||||||
|
:label="__('Name')"
|
||||||
|
v-model="assignmentRuleData.assignmentRuleName"
|
||||||
|
required
|
||||||
|
maxlength="50"
|
||||||
|
@change="validateAssignmentRule('assignmentRuleName')"
|
||||||
|
/>
|
||||||
|
<ErrorMessage
|
||||||
|
:message="assignmentRuleErrors.assignmentRuleName"
|
||||||
|
class="mt-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<FormLabel :label="__('Priority')" />
|
||||||
|
<Popover>
|
||||||
|
<template #target="{ togglePopover }">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between text-base rounded h-7 py-1.5 pl-2 pr-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors w-full dark:[color-scheme:dark] cursor-default"
|
||||||
|
@click="togglePopover()"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
priorityOptions.find(
|
||||||
|
(option) => option.value == assignmentRuleData.priority,
|
||||||
|
)?.label
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<FeatherIcon name="chevron-down" class="size-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ togglePopover }">
|
||||||
|
<div
|
||||||
|
class="p-1 text-ink-gray-6 top-1 absolute w-full bg-white shadow-2xl rounded"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="option in priorityOptions"
|
||||||
|
:key="option.value"
|
||||||
|
class="p-2 cursor-pointer hover:bg-gray-50 text-base flex items-center justify-between rounded"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
assignmentRuleData.priority = option.value
|
||||||
|
togglePopover()
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
<FeatherIcon
|
||||||
|
v-if="assignmentRuleData.priority == option.value"
|
||||||
|
name="check"
|
||||||
|
class="size-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
:type="'textarea'"
|
||||||
|
size="sm"
|
||||||
|
variant="subtle"
|
||||||
|
:placeholder="__('Description')"
|
||||||
|
:label="__('Description')"
|
||||||
|
required
|
||||||
|
maxlength="250"
|
||||||
|
@change="validateAssignmentRule('description')"
|
||||||
|
v-model="assignmentRuleData.description"
|
||||||
|
/>
|
||||||
|
<ErrorMessage
|
||||||
|
:message="assignmentRuleErrors.description"
|
||||||
|
class="mt-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<FormLabel :label="__('Apply on')" />
|
||||||
|
<Select
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: 'Lead',
|
||||||
|
value: 'CRM Lead',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Deal',
|
||||||
|
value: 'CRM Deal',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
v-model="assignmentRuleData.documentType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-8" />
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||||
|
__('Assignment condition')
|
||||||
|
}}</span>
|
||||||
|
<div class="flex items-center justify-between gap-6">
|
||||||
|
<span class="text-p-sm text-ink-gray-6">
|
||||||
|
{{
|
||||||
|
__('Choose which {0} are affected by this assignment rule.', [
|
||||||
|
documentType,
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
class="font-medium underline"
|
||||||
|
href="https://docs.frappe.io/crm/assignment-rule"
|
||||||
|
target="_blank"
|
||||||
|
>{{ __('Learn about conditions') }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<div v-if="isOldSla && step.data">
|
||||||
|
<Popover trigger="hover" :hoverDelay="0.25" placement="top-end">
|
||||||
|
<template #target>
|
||||||
|
<div
|
||||||
|
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap items-center"
|
||||||
|
>
|
||||||
|
<span>{{ __('Old Condition') }}</span>
|
||||||
|
<FeatherIcon name="info" class="size-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body-main>
|
||||||
|
<div
|
||||||
|
class="text-sm text-ink-gray-6 p-2 bg-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||||
|
>
|
||||||
|
<code>{{ assignmentRuleData.assignCondition }}</code>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<div
|
||||||
|
class="flex flex-col gap-3 items-center text-center text-ink-gray-7 text-sm mb-2 border border-gray-300 rounded-md p-3 py-4"
|
||||||
|
v-if="!useNewUI && assignmentRuleData.assignCondition"
|
||||||
|
>
|
||||||
|
<span class="text-p-sm">
|
||||||
|
{{ __('Conditions for this rule were created from') }}
|
||||||
|
<a :href="deskUrl" target="_blank" class="underline">{{
|
||||||
|
__('desk')
|
||||||
|
}}</a>
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
:label="__('I understand, add conditions')"
|
||||||
|
variant="subtle"
|
||||||
|
theme="gray"
|
||||||
|
@click="useNewUI = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AssignmentRulesSection
|
||||||
|
:conditions="assignmentRuleData.assignConditionJson"
|
||||||
|
name="assignCondition"
|
||||||
|
:errors="assignmentRuleErrors.assignConditionError"
|
||||||
|
:doctype="assignmentRuleData.documentType"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<ErrorMessage
|
||||||
|
:message="assignmentRuleErrors.assignCondition"
|
||||||
|
class="mt-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-8" />
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||||
|
__('Unassignment condition')
|
||||||
|
}}</span>
|
||||||
|
<div class="flex items-center justify-between gap-6">
|
||||||
|
<span class="text-p-sm text-ink-gray-6">
|
||||||
|
{{
|
||||||
|
__('Choose which {0} are affected by this un-assignment rule.', [
|
||||||
|
documentType,
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
class="font-medium underline"
|
||||||
|
href="https://docs.frappe.io/crm/assignment-rule"
|
||||||
|
target="_blank"
|
||||||
|
>{{ __('Learn about conditions') }}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
v-if="isOldSla && step.data && assignmentRuleData.unassignCondition"
|
||||||
|
>
|
||||||
|
<Popover trigger="hover" :hoverDelay="0.25" placement="top-end">
|
||||||
|
<template #target>
|
||||||
|
<div
|
||||||
|
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap items-center"
|
||||||
|
>
|
||||||
|
<span> {{ __('Old Condition') }} </span>
|
||||||
|
<FeatherIcon name="info" class="size-4" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body-main>
|
||||||
|
<div
|
||||||
|
class="text-sm text-ink-gray-6 p-2 bg-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||||
|
>
|
||||||
|
<code>{{ assignmentRuleData.unassignCondition }}</code>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<div
|
||||||
|
class="flex flex-col gap-3 items-center text-center text-ink-gray-7 text-sm mb-2 border border-gray-300 rounded-md p-3 py-4"
|
||||||
|
v-if="!useNewUI && assignmentRuleData.unassignCondition"
|
||||||
|
>
|
||||||
|
<span class="text-p-sm">
|
||||||
|
{{ __('Conditions for this rule were created from') }}
|
||||||
|
<a :href="deskUrl" target="_blank" class="underline">{{
|
||||||
|
__('desk')
|
||||||
|
}}</a>
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
:label="__('I understand, add conditions')"
|
||||||
|
variant="subtle"
|
||||||
|
theme="gray"
|
||||||
|
@click="useNewUI = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AssignmentRulesSection
|
||||||
|
:conditions="assignmentRuleData.unassignConditionJson"
|
||||||
|
name="unassignCondition"
|
||||||
|
:errors="assignmentRuleErrors.unassignConditionError"
|
||||||
|
:doctype="assignmentRuleData.documentType"
|
||||||
|
v-else
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-8" />
|
||||||
|
<div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||||
|
__('Assignment Schedule')
|
||||||
|
}}</span>
|
||||||
|
<span class="text-p-sm text-ink-gray-6">
|
||||||
|
{{
|
||||||
|
__('Choose the days of the week when this rule should be active.')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<AssignmentSchedule />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-8" />
|
||||||
|
<AssigneeRules />
|
||||||
|
</div>
|
||||||
|
<ConfirmDialog
|
||||||
|
v-model="showConfirmDialog.show"
|
||||||
|
:title="showConfirmDialog.title"
|
||||||
|
:message="showConfirmDialog.message"
|
||||||
|
:onConfirm="showConfirmDialog.onConfirm"
|
||||||
|
:onCancel="() => (showConfirmDialog.show = false)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
call,
|
||||||
|
createResource,
|
||||||
|
ErrorMessage,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
LoadingIndicator,
|
||||||
|
Popover,
|
||||||
|
Select,
|
||||||
|
Switch,
|
||||||
|
toast,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
inject,
|
||||||
|
watch,
|
||||||
|
provide,
|
||||||
|
computed,
|
||||||
|
} from 'vue'
|
||||||
|
import AssignmentRulesSection from './AssignmentRulesSection.vue'
|
||||||
|
import AssignmentSchedule from './AssignmentSchedule.vue'
|
||||||
|
import AssigneeRules from './AssigneeRules.vue'
|
||||||
|
import ConfirmDialog from 'frappe-ui/src/components/ConfirmDialog.vue'
|
||||||
|
import { globalStore } from '@/stores/global'
|
||||||
|
import { disableSettingModalOutsideClick } from '@/composables/settings'
|
||||||
|
import { convertToConditions, validateConditions } from '@/utils'
|
||||||
|
|
||||||
|
const isDirty = ref(false)
|
||||||
|
const initialData = ref(null)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const updateStep = inject('updateStep')
|
||||||
|
const step = inject('step')
|
||||||
|
const { $dialog } = globalStore()
|
||||||
|
|
||||||
|
const showConfirmDialog = ref({
|
||||||
|
show: false,
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
onConfirm: () => {},
|
||||||
|
})
|
||||||
|
const useNewUI = ref(true)
|
||||||
|
const isOldSla = ref(false)
|
||||||
|
const documentType = computed(() =>
|
||||||
|
assignmentRuleData.value.documentType == 'CRM Lead'
|
||||||
|
? __('leads')
|
||||||
|
: __('deals'),
|
||||||
|
)
|
||||||
|
const deskUrl = `${window.location.origin}/app/assignment-rule/${step.value.data?.name}`
|
||||||
|
|
||||||
|
const defaultAssignmentDays = [
|
||||||
|
'Monday',
|
||||||
|
'Tuesday',
|
||||||
|
'Wednesday',
|
||||||
|
'Thursday',
|
||||||
|
'Friday',
|
||||||
|
'Saturday',
|
||||||
|
'Sunday',
|
||||||
|
]
|
||||||
|
|
||||||
|
const assignmentRuleData = ref({
|
||||||
|
assignCondition: '',
|
||||||
|
unassignCondition: '',
|
||||||
|
assignConditionJson: [],
|
||||||
|
unassignConditionJson: [],
|
||||||
|
rule: 'Round Robin',
|
||||||
|
priority: 1,
|
||||||
|
users: [],
|
||||||
|
disabled: false,
|
||||||
|
description: '',
|
||||||
|
name: '',
|
||||||
|
assignmentRuleName: '',
|
||||||
|
assignmentDays: defaultAssignmentDays,
|
||||||
|
documentType: 'CRM Lead',
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateAssignmentRule = (key, skipConditionCheck = false) => {
|
||||||
|
const validateField = (field) => {
|
||||||
|
if (key && field !== key) return
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'assignmentRuleName':
|
||||||
|
if (assignmentRuleData.value.assignmentRuleName?.length == 0) {
|
||||||
|
assignmentRuleErrors.value.assignmentRuleName = __('Name is required')
|
||||||
|
} else {
|
||||||
|
assignmentRuleErrors.value.assignmentRuleName = ''
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'description':
|
||||||
|
assignmentRuleErrors.value.description =
|
||||||
|
assignmentRuleData.value.description?.length > 0
|
||||||
|
? ''
|
||||||
|
: __('Description is required')
|
||||||
|
break
|
||||||
|
case 'assignCondition':
|
||||||
|
if (skipConditionCheck) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
assignmentRuleErrors.value.assignCondition =
|
||||||
|
assignmentRuleData.value.assignConditionJson?.length > 0
|
||||||
|
? ''
|
||||||
|
: __('Assign condition is required')
|
||||||
|
|
||||||
|
if (!validateConditions(assignmentRuleData.value.assignConditionJson)) {
|
||||||
|
assignmentRuleErrors.value.assignConditionError = __(
|
||||||
|
'Assign conditions are invalid',
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
assignmentRuleErrors.value.assignConditionError = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case 'unassignCondition':
|
||||||
|
if (skipConditionCheck) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
assignmentRuleData.value.unassignConditionJson?.length > 0 &&
|
||||||
|
!validateConditions(assignmentRuleData.value.unassignConditionJson)
|
||||||
|
) {
|
||||||
|
assignmentRuleErrors.value.unassignConditionError = __(
|
||||||
|
'Unassign conditions are invalid',
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
assignmentRuleErrors.value.unassignConditionError = ''
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'users':
|
||||||
|
assignmentRuleErrors.value.users =
|
||||||
|
assignmentRuleData.value.users?.length > 0
|
||||||
|
? ''
|
||||||
|
: __('Users are required')
|
||||||
|
break
|
||||||
|
case 'assignmentDays':
|
||||||
|
assignmentRuleErrors.value.assignmentDays =
|
||||||
|
assignmentRuleData.value.assignmentDays?.length > 0
|
||||||
|
? ''
|
||||||
|
: __('Assignment days are required')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
validateField(key)
|
||||||
|
} else {
|
||||||
|
Object.keys(assignmentRuleErrors.value).forEach(validateField)
|
||||||
|
}
|
||||||
|
|
||||||
|
return assignmentRuleErrors.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetAssignmentRuleData = () => {
|
||||||
|
assignmentRuleData.value = {
|
||||||
|
assignCondition: '',
|
||||||
|
unassignCondition: '',
|
||||||
|
assignConditionJson: [],
|
||||||
|
unassignConditionJson: [],
|
||||||
|
rule: 'Round Robin',
|
||||||
|
priority: 1,
|
||||||
|
users: [],
|
||||||
|
disabled: false,
|
||||||
|
description: '',
|
||||||
|
name: '',
|
||||||
|
assignmentRuleName: '',
|
||||||
|
assignmentDays: defaultAssignmentDays,
|
||||||
|
documentType: 'CRM Lead',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignmentRuleErrors = ref({
|
||||||
|
assignmentRuleName: '',
|
||||||
|
assignCondition: '',
|
||||||
|
assignConditionError: '',
|
||||||
|
unassignConditionError: '',
|
||||||
|
users: '',
|
||||||
|
description: '',
|
||||||
|
assignmentDays: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const resetAssignmentRuleErrors = () => {
|
||||||
|
Object.keys(assignmentRuleErrors.value).forEach((key) => {
|
||||||
|
assignmentRuleErrors.value[key] = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
provide('assignmentRuleData', assignmentRuleData)
|
||||||
|
provide('assignmentRuleErrors', assignmentRuleErrors)
|
||||||
|
provide('validateAssignmentRule', validateAssignmentRule)
|
||||||
|
provide('resetAssignmentRuleData', resetAssignmentRuleData)
|
||||||
|
provide('resetAssignmentRuleErrors', resetAssignmentRuleErrors)
|
||||||
|
|
||||||
|
const getAssignmentRuleData = createResource({
|
||||||
|
url: 'frappe.client.get',
|
||||||
|
params: {
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
name: step.value.data?.name,
|
||||||
|
},
|
||||||
|
auto: Boolean(step.value.data),
|
||||||
|
onSuccess(data) {
|
||||||
|
assignmentRuleData.value = {
|
||||||
|
assignCondition: data.assign_condition,
|
||||||
|
unassignCondition: data.unassign_condition,
|
||||||
|
assignConditionJson: JSON.parse(data.assign_condition_json || '[]'),
|
||||||
|
unassignConditionJson: JSON.parse(data.unassign_condition_json || '[]'),
|
||||||
|
rule: data.rule,
|
||||||
|
priority: data.priority,
|
||||||
|
users: data.users,
|
||||||
|
disabled: data.disabled,
|
||||||
|
description: data.description,
|
||||||
|
name: data.name,
|
||||||
|
assignmentRuleName: data.name,
|
||||||
|
assignmentDays: data.assignment_days.map((day) => day.day),
|
||||||
|
documentType: data.document_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
initialData.value = JSON.stringify(assignmentRuleData.value)
|
||||||
|
|
||||||
|
const conditionsAvailable =
|
||||||
|
assignmentRuleData.value.assignCondition?.length > 0
|
||||||
|
const conditionsJsonAvailable =
|
||||||
|
assignmentRuleData.value.assignConditionJson?.length > 0
|
||||||
|
|
||||||
|
if (conditionsAvailable && !conditionsJsonAvailable) {
|
||||||
|
useNewUI.value = false
|
||||||
|
isOldSla.value = true
|
||||||
|
} else {
|
||||||
|
useNewUI.value = true
|
||||||
|
isOldSla.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!step.value.data) {
|
||||||
|
initialData.value = JSON.stringify(assignmentRuleData.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
if (isDirty.value && !showConfirmDialog.value.show) {
|
||||||
|
$dialog({
|
||||||
|
title: __('Unsaved changes'),
|
||||||
|
message: __(
|
||||||
|
'Are you sure you want to go back? Unsaved changes will be lost.',
|
||||||
|
),
|
||||||
|
variant: 'solid',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: __('Go back'),
|
||||||
|
variant: 'solid',
|
||||||
|
onClick: (close) => {
|
||||||
|
updateStep('list', null)
|
||||||
|
close()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateStep('list', null)
|
||||||
|
showConfirmDialog.value.show = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveAssignmentRule = () => {
|
||||||
|
const validationErrors = validateAssignmentRule(undefined, !useNewUI.value)
|
||||||
|
if (Object.values(validationErrors).some((error) => error)) {
|
||||||
|
toast.error(
|
||||||
|
__('Invalid fields, check if all are filled in and values are correct.'),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (step.value.data) {
|
||||||
|
if (isOldSla.value && useNewUI.value) {
|
||||||
|
showConfirmDialog.value = {
|
||||||
|
show: true,
|
||||||
|
title: __('Confirm overwrite'),
|
||||||
|
message: __(
|
||||||
|
'Your old condition will be overwritten. Are you sure you want to save?',
|
||||||
|
),
|
||||||
|
onConfirm: () => {
|
||||||
|
updateAssignmentRule()
|
||||||
|
showConfirmDialog.value.show = false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateAssignmentRule()
|
||||||
|
} else {
|
||||||
|
createAssignmentRule()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createAssignmentRule = () => {
|
||||||
|
isLoading.value = true
|
||||||
|
createResource({
|
||||||
|
url: 'frappe.client.insert',
|
||||||
|
params: {
|
||||||
|
doc: {
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
document_type: assignmentRuleData.value.documentType,
|
||||||
|
rule: assignmentRuleData.value.rule,
|
||||||
|
priority: assignmentRuleData.value.priority,
|
||||||
|
users: assignmentRuleData.value.users,
|
||||||
|
disabled: assignmentRuleData.value.disabled,
|
||||||
|
description: assignmentRuleData.value.description,
|
||||||
|
assignment_days: assignmentRuleData.value.assignmentDays.map((day) => ({
|
||||||
|
day: day,
|
||||||
|
})),
|
||||||
|
name: assignmentRuleData.value.assignmentRuleName,
|
||||||
|
assignment_rule_name: assignmentRuleData.value.assignmentRuleName,
|
||||||
|
assign_condition: convertToConditions({
|
||||||
|
conditions: assignmentRuleData.value.assignConditionJson,
|
||||||
|
}),
|
||||||
|
unassign_condition: convertToConditions({
|
||||||
|
conditions: assignmentRuleData.value.unassignConditionJson,
|
||||||
|
}),
|
||||||
|
assign_condition_json: JSON.stringify(
|
||||||
|
assignmentRuleData.value.assignConditionJson,
|
||||||
|
),
|
||||||
|
unassign_condition_json: JSON.stringify(
|
||||||
|
assignmentRuleData.value.unassignConditionJson,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
onSuccess(data) {
|
||||||
|
getAssignmentRuleData
|
||||||
|
.submit({
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
name: data.name,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
isLoading.value = false
|
||||||
|
toast.success(__('Assignment rule created'))
|
||||||
|
})
|
||||||
|
updateStep('view', data)
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
isLoading.value = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const priorityOptions = [
|
||||||
|
{ label: 'Low', value: '0' },
|
||||||
|
{ label: 'Low-Medium', value: '1' },
|
||||||
|
{ label: 'Medium', value: '2' },
|
||||||
|
{ label: 'Medium-High', value: '3' },
|
||||||
|
{ label: 'High', value: '4' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const updateAssignmentRule = async () => {
|
||||||
|
isLoading.value = true
|
||||||
|
await call('frappe.client.set_value', {
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
name: assignmentRuleData.value.name,
|
||||||
|
fieldname: {
|
||||||
|
rule: assignmentRuleData.value.rule,
|
||||||
|
priority: assignmentRuleData.value.priority,
|
||||||
|
users: assignmentRuleData.value.users,
|
||||||
|
disabled: assignmentRuleData.value.disabled,
|
||||||
|
description: assignmentRuleData.value.description,
|
||||||
|
document_type: assignmentRuleData.value.documentType,
|
||||||
|
assignment_days: assignmentRuleData.value.assignmentDays.map((day) => ({
|
||||||
|
day: day,
|
||||||
|
})),
|
||||||
|
assign_condition: useNewUI.value
|
||||||
|
? convertToConditions({
|
||||||
|
conditions: assignmentRuleData.value.assignConditionJson,
|
||||||
|
})
|
||||||
|
: assignmentRuleData.value.assignCondition,
|
||||||
|
unassign_condition: useNewUI.value
|
||||||
|
? convertToConditions({
|
||||||
|
conditions: assignmentRuleData.value.unassignConditionJson,
|
||||||
|
})
|
||||||
|
: assignmentRuleData.value.unassignCondition,
|
||||||
|
assign_condition_json: useNewUI.value
|
||||||
|
? JSON.stringify(assignmentRuleData.value.assignConditionJson)
|
||||||
|
: null,
|
||||||
|
unassign_condition_json: useNewUI.value
|
||||||
|
? JSON.stringify(assignmentRuleData.value.unassignConditionJson)
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
}).catch((er) => {
|
||||||
|
const error =
|
||||||
|
er?.messages?.[0] ||
|
||||||
|
__('Some error occurred while updating assignment rule')
|
||||||
|
toast.error(error)
|
||||||
|
isLoading.value = false
|
||||||
|
})
|
||||||
|
if (
|
||||||
|
assignmentRuleData.value.name !==
|
||||||
|
assignmentRuleData.value.assignmentRuleName
|
||||||
|
) {
|
||||||
|
await call('frappe.client.rename_doc', {
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
old_name: assignmentRuleData.value.name,
|
||||||
|
new_name: assignmentRuleData.value.assignmentRuleName,
|
||||||
|
}).catch(async (er) => {
|
||||||
|
const error =
|
||||||
|
er?.messages?.[0] ||
|
||||||
|
__('Some error occurred while renaming assignment rule')
|
||||||
|
toast.error(error)
|
||||||
|
// Reset assignment rule to previous state
|
||||||
|
await getAssignmentRuleData.reload()
|
||||||
|
isLoading.value = false
|
||||||
|
})
|
||||||
|
await getAssignmentRuleData.submit({
|
||||||
|
doctype: 'Assignment Rule',
|
||||||
|
name: assignmentRuleData.value.assignmentRuleName,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
getAssignmentRuleData.reload()
|
||||||
|
}
|
||||||
|
isLoading.value = false
|
||||||
|
toast.success(__('Assignment rule updated'))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
assignmentRuleData,
|
||||||
|
(newVal) => {
|
||||||
|
if (!initialData.value) return
|
||||||
|
isDirty.value = JSON.stringify(newVal) != initialData.value
|
||||||
|
if (isDirty.value) {
|
||||||
|
disableSettingModalOutsideClick.value = true
|
||||||
|
} else {
|
||||||
|
disableSettingModalOutsideClick.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
const beforeUnloadHandler = (event) => {
|
||||||
|
if (!isDirty.value) return
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
addEventListener('beforeunload', beforeUnloadHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
resetAssignmentRuleErrors()
|
||||||
|
resetAssignmentRuleData()
|
||||||
|
removeEventListener('beforeunload', beforeUnloadHandler)
|
||||||
|
disableSettingModalOutsideClick.value = false
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-8 sticky top-0">
|
||||||
|
<div class="flex items-start justify-between">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<h1 class="text-xl font-semibold text-ink-gray-8">
|
||||||
|
{{ __('Assignment rules') }}
|
||||||
|
</h1>
|
||||||
|
<p class="text-p-base text-ink-gray-6 max-w-md">
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
'Assignment Rules automatically route leads or deals to the right team members based on predefined conditions.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
:label="__('Create new')"
|
||||||
|
theme="gray"
|
||||||
|
variant="solid"
|
||||||
|
@click="goToNew()"
|
||||||
|
icon-left="plus"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-y-auto px-8 pb-6">
|
||||||
|
<AssignmentRulesList />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { createResource } from 'frappe-ui'
|
||||||
|
import AssignmentRulesList from './AssignmentRulesList.vue'
|
||||||
|
import { inject, provide } from 'vue'
|
||||||
|
|
||||||
|
const updateStep = inject('updateStep')
|
||||||
|
|
||||||
|
const assignmentRulesListData = createResource({
|
||||||
|
url: 'crm.api.assignment_rule.get_assignment_rules_list',
|
||||||
|
cache: ['assignmentRules', 'get_assignment_rules_list'],
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('assignmentRulesList', assignmentRulesListData)
|
||||||
|
|
||||||
|
const goToNew = () => {
|
||||||
|
updateStep('view', null)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
:label="__(template.name)"
|
:label="__(template.name)"
|
||||||
size="md"
|
size="md"
|
||||||
@click="() => emit('updateStep', 'template-list')"
|
@click="() => emit('updateStep', 'template-list')"
|
||||||
class="text-xl !h-7 font-semibold hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5"
|
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
"
|
"
|
||||||
size="md"
|
size="md"
|
||||||
@click="() => emit('updateStep', 'template-list')"
|
@click="() => emit('updateStep', 'template-list')"
|
||||||
class="text-xl !h-7 font-semibold hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5"
|
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
:label="__('Brand settings')"
|
:label="__('Brand settings')"
|
||||||
size="md"
|
size="md"
|
||||||
@click="() => emit('updateStep', 'general-settings')"
|
@click="() => emit('updateStep', 'general-settings')"
|
||||||
class="text-xl !h-7 font-semibold hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5"
|
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !justify-start"
|
||||||
/>
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="settings.isDirty"
|
v-if="settings.isDirty"
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
:label="__('Currency & Exchange rate provider')"
|
:label="__('Currency & Exchange rate provider')"
|
||||||
size="md"
|
size="md"
|
||||||
@click="() => emit('updateStep', 'general-settings')"
|
@click="() => emit('updateStep', 'general-settings')"
|
||||||
class="text-xl !h-7 font-semibold hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5"
|
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !justify-start"
|
||||||
/>
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="settings.isDirty"
|
v-if="settings.isDirty"
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
:label="__('Home actions')"
|
:label="__('Home actions')"
|
||||||
size="md"
|
size="md"
|
||||||
@click="() => emit('updateStep', 'general-settings')"
|
@click="() => emit('updateStep', 'general-settings')"
|
||||||
class="text-xl !h-7 font-semibold hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5"
|
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user