mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-10-25 00:53:43 +00:00
350 lines
11 KiB
Vue
350 lines
11 KiB
Vue
<template>
|
|
<div>
|
|
<div class="mb-5">
|
|
<RadioGroup
|
|
class="flex"
|
|
:modelValue="ruleGroup.logical_op"
|
|
@update:modelValue="handleGroupOperator"
|
|
>
|
|
<div class="flex items-center space-x-2">
|
|
<RadioGroupItem value="OR" />
|
|
<Label
|
|
>{{ $t('admin.automation.match') }} <b>{{ $t('admin.automation.any') }}</b>
|
|
{{ $t('admin.automation.below') }}.</Label
|
|
>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<RadioGroupItem value="AND" />
|
|
<Label
|
|
>{{ $t('admin.automation.match') }} <b>{{ $t('admin.automation.all') }}</b>
|
|
{{ $t('admin.automation.below') }}.</Label
|
|
>
|
|
</div>
|
|
</RadioGroup>
|
|
</div>
|
|
|
|
<div class="space-y-5 rounded-lg" :class="{ 'box p-5': ruleGroup.rules?.length > 0 }">
|
|
<div class="space-y-5">
|
|
<div v-for="(rule, index) in ruleGroup.rules" :key="rule" class="space-y-5">
|
|
<div v-if="index > 0">
|
|
<hr class="border-t-2 border-dotted border-gray-200" />
|
|
</div>
|
|
|
|
<!-- Field -->
|
|
<div class="flex space-x-5 items-start">
|
|
<Select
|
|
v-model="rule.field"
|
|
@update:modelValue="(value) => handleFieldChange(value, index)"
|
|
>
|
|
<SelectTrigger class="w-56">
|
|
<SelectValue :placeholder="t('form.field.selectField')" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectLabel>{{ $t('globals.entities.conversation') }}</SelectLabel>
|
|
<SelectItem v-for="(field, key) in currentFilters" :key="key" :value="key">
|
|
{{ field.label }}
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
<!-- Operator -->
|
|
<Select
|
|
v-model="rule.operator"
|
|
@update:modelValue="(value) => handleOperatorChange(value, index)"
|
|
>
|
|
<SelectTrigger class="w-56">
|
|
<SelectValue :placeholder="t('form.field.selectOperator')" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectItem
|
|
v-for="(op, key) in getFieldOperators(rule.field)"
|
|
:key="key"
|
|
:value="op"
|
|
>
|
|
{{ op }}
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
<!-- Value -->
|
|
<div v-if="showInput(index)" class="flex-1">
|
|
<!-- Plain text input -->
|
|
<Input
|
|
type="text"
|
|
:placeholder="t('form.field.setValue')"
|
|
v-if="inputType(index) === 'text'"
|
|
v-model="rule.value"
|
|
@update:modelValue="(value) => handleValueChange(value, index)"
|
|
/>
|
|
|
|
<!-- Number input -->
|
|
<Input
|
|
type="number"
|
|
:placeholder="t('form.field.setValue')"
|
|
v-if="inputType(index) === 'number'"
|
|
v-model="rule.value"
|
|
@update:modelValue="(value) => handleValueChange(value, index)"
|
|
/>
|
|
|
|
<!-- Select input -->
|
|
<div v-if="inputType(index) === 'select'">
|
|
<ComboBox
|
|
v-model="rule.value"
|
|
:items="getFieldOptions(rule.field)"
|
|
@select="handleValueChange($event, index)"
|
|
>
|
|
<template #item="{ item }">
|
|
<div class="flex items-center gap-2 ml-2">
|
|
<Avatar v-if="rule.field === 'assigned_user'" class="w-7 h-7">
|
|
<AvatarImage :src="item.avatar_url ?? ''" :alt="item.label.slice(0, 2)" />
|
|
<AvatarFallback>
|
|
{{ item.label.slice(0, 2).toUpperCase() }}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<span v-if="rule.field === 'assigned_team'">
|
|
{{ item.emoji }}
|
|
</span>
|
|
<span>{{ item.label }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template #selected="{ selected }">
|
|
<div v-if="rule?.field === 'assigned_team'">
|
|
<div v-if="selected" class="flex items-center gap-2">
|
|
{{ selected.emoji }}
|
|
<span>{{ selected.label }}</span>
|
|
</div>
|
|
<span v-else>{{ $t('form.field.selectTeam') }}</span>
|
|
</div>
|
|
|
|
<div
|
|
v-else-if="rule?.field === 'assigned_user'"
|
|
class="flex items-center gap-2"
|
|
>
|
|
<div v-if="selected" class="flex items-center gap-2">
|
|
<Avatar class="w-7 h-7">
|
|
<AvatarImage
|
|
:src="selected.avatar_url ?? ''"
|
|
:alt="selected.label.slice(0, 2)"
|
|
/>
|
|
<AvatarFallback>
|
|
{{ selected.label.slice(0, 2).toUpperCase() }}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<span>{{ selected.label }}</span>
|
|
</div>
|
|
<span v-else>{{ $t('form.field.selectUser') }}</span>
|
|
</div>
|
|
<span v-else>
|
|
<span v-if="!selected"> {{ $t('form.field.select') }}</span>
|
|
<span v-else>{{ selected.label }} </span>
|
|
</span>
|
|
</template>
|
|
</ComboBox>
|
|
</div>
|
|
|
|
<!-- Tag input -->
|
|
<div v-if="inputType(index) === 'tag'">
|
|
<TagsInput
|
|
:defaultValue="fieldValueAsArray(rule.value)"
|
|
@update:modelValue="(value) => handleValueChange(value, index)"
|
|
>
|
|
<TagsInputItem
|
|
v-for="item in fieldValueAsArray(rule.value)"
|
|
:key="item"
|
|
:value="item"
|
|
>
|
|
<TagsInputItemText />
|
|
<TagsInputItemDelete />
|
|
</TagsInputItem>
|
|
<TagsInputInput :placeholder="t('form.field.selectValue')" />
|
|
</TagsInput>
|
|
<p class="text-xs text-gray-500 mt-1">
|
|
{{ $t('globals.messages.pressEnterToSelectAValue') }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Remove condition -->
|
|
<div class="cursor-pointer mt-2" @click.prevent="removeCondition(index)">
|
|
<X size="16" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="terms"
|
|
:defaultChecked="rule.case_sensitive_match"
|
|
@update:checked="(value) => handleCaseSensitiveCheck(value, index)"
|
|
/>
|
|
<label> {{ $t('globals.messages.caseSensitiveMatch') }} </label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<Button variant="outline" size="sm" @click.prevent="addCondition">
|
|
{{
|
|
$t('globals.messages.add', {
|
|
name: $t('globals.entities.condition')
|
|
})
|
|
}}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { toRefs, computed, watch } from 'vue'
|
|
import { Checkbox } from '@/components/ui/checkbox'
|
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectGroup,
|
|
SelectItem,
|
|
SelectLabel,
|
|
SelectTrigger,
|
|
SelectValue
|
|
} from '@/components/ui/select'
|
|
import {
|
|
TagsInput,
|
|
TagsInputInput,
|
|
TagsInputItem,
|
|
TagsInputItemDelete,
|
|
TagsInputItemText
|
|
} from '@/components/ui/tags-input'
|
|
import { X } from 'lucide-vue-next'
|
|
import { Label } from '@/components/ui/label'
|
|
import { Input } from '@/components/ui/input'
|
|
import ComboBox from '@/components/ui/combobox/ComboBox.vue'
|
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useConversationFilters } from '@/composables/useConversationFilters'
|
|
|
|
const props = defineProps({
|
|
ruleGroup: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
groupIndex: {
|
|
type: Number,
|
|
required: true
|
|
},
|
|
type: {
|
|
type: String,
|
|
required: true
|
|
}
|
|
})
|
|
|
|
const { conversationFilters, newConversationFilters } = useConversationFilters()
|
|
const { ruleGroup } = toRefs(props)
|
|
const emit = defineEmits(['update-group', 'add-condition', 'remove-condition'])
|
|
const { t } = useI18n()
|
|
|
|
// Computed property to get the correct filters based on type
|
|
const currentFilters = computed(() => {
|
|
return props.type === 'new_conversation'
|
|
? newConversationFilters.value
|
|
: conversationFilters.value
|
|
})
|
|
|
|
// Watch for type change and reset the rules as the fields will change
|
|
watch(
|
|
() => props.type,
|
|
(newType, oldType) => {
|
|
// Make sure types have values and they are different.
|
|
if (newType !== oldType && newType && oldType) {
|
|
ruleGroup.value.rules = []
|
|
emitUpdate()
|
|
}
|
|
}
|
|
)
|
|
|
|
const handleGroupOperator = (value) => {
|
|
ruleGroup.value.logical_op = value
|
|
emitUpdate()
|
|
}
|
|
|
|
const handleFieldChange = (value, ruleIndex) => {
|
|
ruleGroup.value.rules[ruleIndex].operator = ''
|
|
ruleGroup.value.rules[ruleIndex].value = ''
|
|
ruleGroup.value.rules[ruleIndex].field = value
|
|
emitUpdate()
|
|
}
|
|
|
|
const handleOperatorChange = (value, ruleIndex) => {
|
|
if (['contains', 'not contains'].includes(value)) {
|
|
ruleGroup.value.rules[ruleIndex].value = []
|
|
} else {
|
|
ruleGroup.value.rules[ruleIndex].value = ''
|
|
}
|
|
ruleGroup.value.rules[ruleIndex].operator = value
|
|
emitUpdate()
|
|
}
|
|
|
|
const handleValueChange = (value, ruleIndex) => {
|
|
// Get value from object if it's an object.
|
|
const val = typeof value === 'object' && !Array.isArray(value) ? value.value : value
|
|
|
|
// Fetch the rule.
|
|
const rule = ruleGroup.value.rules[ruleIndex]
|
|
|
|
// Array values are stored as comma separated string.
|
|
rule.value = ['contains', 'not contains'].includes(rule.operator)
|
|
? Array.isArray(val)
|
|
? val.join(',')
|
|
: val
|
|
: String(val)
|
|
|
|
emitUpdate()
|
|
}
|
|
|
|
const fieldValueAsArray = (value) => {
|
|
return Array.isArray(value) ? value : value ? value.split(',') : []
|
|
}
|
|
|
|
const handleCaseSensitiveCheck = (value, ruleIndex) => {
|
|
ruleGroup.value.rules[ruleIndex].case_sensitive_match = value
|
|
emitUpdate()
|
|
}
|
|
|
|
const removeCondition = (index) => {
|
|
emit('remove-condition', props.groupIndex, index)
|
|
}
|
|
|
|
const addCondition = () => {
|
|
emit('add-condition', props.groupIndex)
|
|
}
|
|
|
|
const emitUpdate = () => {
|
|
emit('update-group', ruleGroup, props.groupIndex)
|
|
}
|
|
|
|
const getFieldOperators = (field) => {
|
|
return currentFilters.value[field]?.operators || []
|
|
}
|
|
|
|
const getFieldOptions = (field) => {
|
|
return currentFilters.value[field]?.options || []
|
|
}
|
|
|
|
const inputType = (index) => {
|
|
const field = ruleGroup.value.rules[index]?.field
|
|
const operator = ruleGroup.value.rules[index]?.operator
|
|
if (['contains', 'not contains'].includes(operator)) return 'tag'
|
|
if (field) return currentFilters.value[field].type
|
|
return ''
|
|
}
|
|
|
|
const showInput = (index) => {
|
|
const operator = ruleGroup.value.rules[index]?.operator
|
|
return !['set', 'not set'].includes(operator)
|
|
}
|
|
</script>
|