feat: Add conditions row component (#10496)
--------- Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -57,3 +57,5 @@ exclude_patterns:
|
||||
- 'app/javascript/shared/constants/locales.js'
|
||||
- 'app/javascript/dashboard/helper/specs/macrosFixtures.js'
|
||||
- 'app/javascript/dashboard/routes/dashboard/settings/macros/constants.js'
|
||||
- '**/fixtures/**'
|
||||
- '**/*/fixtures.js'
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import ConditionRow from './ConditionRow.vue';
|
||||
import Button from 'next/button/Button.vue';
|
||||
import { filterTypes } from './fixtures/filterTypes.js';
|
||||
|
||||
const DEFAULT_FILTER = {
|
||||
attributeKey: 'status',
|
||||
filterOperator: 'equal_to',
|
||||
values: [],
|
||||
queryOperator: 'and',
|
||||
};
|
||||
|
||||
const filters = ref([{ ...DEFAULT_FILTER }]);
|
||||
|
||||
const removeFilter = index => {
|
||||
filters.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const showQueryOperator = true;
|
||||
|
||||
const addFilter = () => {
|
||||
filters.value.push({ ...DEFAULT_FILTER });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Components/Filters/ConditionRow"
|
||||
:layout="{ type: 'grid', width: '600px' }"
|
||||
>
|
||||
<div class="min-h-[400px] p-2 space-y-2">
|
||||
<template v-for="(filter, index) in filters" :key="`filter-${index}`">
|
||||
<ConditionRow
|
||||
v-if="index === 0"
|
||||
v-model:attribute-key="filter.attributeKey"
|
||||
v-model:filter-operator="filter.filterOperator"
|
||||
v-model:values="filter.values"
|
||||
:show-query-operator="false"
|
||||
:filter-types="filterTypes"
|
||||
@remove="removeFilter(index)"
|
||||
/>
|
||||
|
||||
<ConditionRow
|
||||
v-else
|
||||
v-model:attribute-key="filter.attributeKey"
|
||||
v-model:filter-operator="filter.filterOperator"
|
||||
v-model:values="filter.values"
|
||||
v-model:query-operator="filters[index - 1].queryOperator"
|
||||
:show-query-operator
|
||||
:filter-types="filterTypes"
|
||||
@remove="removeFilter(index)"
|
||||
/>
|
||||
</template>
|
||||
<Button sm label="Add Filter" @click="addFilter" />
|
||||
</div>
|
||||
</Story>
|
||||
</template>
|
||||
197
app/javascript/dashboard/components-next/filter/ConditionRow.vue
Normal file
197
app/javascript/dashboard/components-next/filter/ConditionRow.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<script setup>
|
||||
import { computed, defineModel, h, watch, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Button from 'next/button/Button.vue';
|
||||
import FilterSelect from './inputs/FilterSelect.vue';
|
||||
import MultiSelect from './inputs/MultiSelect.vue';
|
||||
import SingleSelect from './inputs/SingleSelect.vue';
|
||||
|
||||
import { validateSingleFilter } from 'dashboard/helper/validations.js';
|
||||
|
||||
// filterTypes: import('vue').ComputedRef<FilterType[]>
|
||||
const { filterTypes } = defineProps({
|
||||
showQueryOperator: { type: Boolean, default: false },
|
||||
filterTypes: { type: Array, required: true },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['remove']);
|
||||
const { t } = useI18n();
|
||||
const showErrors = ref(false);
|
||||
|
||||
const attributeKey = defineModel('attributeKey', {
|
||||
type: String,
|
||||
required: true,
|
||||
});
|
||||
|
||||
const values = defineModel('values', {
|
||||
type: [String, Number, Array, Object],
|
||||
required: true,
|
||||
});
|
||||
|
||||
const filterOperator = defineModel('filterOperator', {
|
||||
type: String,
|
||||
required: true,
|
||||
});
|
||||
|
||||
const queryOperator = defineModel('queryOperator', {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
validator: value => ['and', 'or'].includes(value),
|
||||
});
|
||||
|
||||
const getFilterFromFilterTypes = key =>
|
||||
filterTypes.find(filterObj => filterObj.attributeKey === key);
|
||||
|
||||
const currentFilter = computed(() =>
|
||||
getFilterFromFilterTypes(attributeKey.value)
|
||||
);
|
||||
|
||||
const getOperator = (filter, selectedOperator) => {
|
||||
const operatorFromOptions = filter.filterOperators.find(
|
||||
operator => operator.value === selectedOperator
|
||||
);
|
||||
|
||||
if (!operatorFromOptions) {
|
||||
return filter.filterOperators[0];
|
||||
}
|
||||
|
||||
return operatorFromOptions;
|
||||
};
|
||||
|
||||
const currentOperator = computed(() =>
|
||||
getOperator(currentFilter.value, filterOperator.value)
|
||||
);
|
||||
|
||||
const getInputType = (operator, filter) =>
|
||||
operator.inputOverride ?? filter.inputType;
|
||||
|
||||
const inputType = computed(() =>
|
||||
getInputType(currentOperator.value, currentFilter.value)
|
||||
);
|
||||
|
||||
const queryOperatorOptions = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: t(`FILTER.QUERY_DROPDOWN_LABELS.AND`),
|
||||
value: 'and',
|
||||
icon: h('span', { class: 'i-lucide-ampersands !text-n-blue-text' }),
|
||||
},
|
||||
{
|
||||
label: t(`FILTER.QUERY_DROPDOWN_LABELS.OR`),
|
||||
value: 'or',
|
||||
icon: h('span', { class: 'i-woot-logic-or !text-n-blue-text' }),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const booleanOptions = computed(() => [
|
||||
{ id: true, name: t('FILTER.ATTRIBUTE_LABELS.TRUE') },
|
||||
{ id: false, name: t('FILTER.ATTRIBUTE_LABELS.FALSE') },
|
||||
]);
|
||||
|
||||
const validationError = computed(() => {
|
||||
return validateSingleFilter({
|
||||
attributeKey: attributeKey.value,
|
||||
filter_operator: filterOperator.value,
|
||||
values: values.value,
|
||||
});
|
||||
});
|
||||
|
||||
const resetModelOnAttributeKeyChange = newAttributeKey => {
|
||||
/**
|
||||
* Resets the filter values and operator when the attribute key changes. This ensures that
|
||||
* the values and operator remain compatible with the new attribute type. For example,
|
||||
* switching from a text field to a multi-select should reset the value from '' (empty string)
|
||||
* to an empty array.
|
||||
*/
|
||||
const filter = getFilterFromFilterTypes(newAttributeKey);
|
||||
const newOperator = getOperator(filter, filterOperator.value);
|
||||
const newInputType = getInputType(newOperator, filter);
|
||||
if (newInputType === 'multiSelect') {
|
||||
values.value = [];
|
||||
} else if (['searchSelect', 'booleanSelect'].includes(newInputType)) {
|
||||
values.value = {};
|
||||
} else {
|
||||
values.value = '';
|
||||
}
|
||||
filterOperator.value = newOperator.value;
|
||||
};
|
||||
|
||||
watch([attributeKey, values, filterOperator], () => {
|
||||
showErrors.value = false;
|
||||
});
|
||||
|
||||
const validate = () => {
|
||||
showErrors.value = true;
|
||||
return !validationError.value;
|
||||
};
|
||||
|
||||
defineExpose({ validate });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="list-none">
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md"
|
||||
:class="{
|
||||
'animate-wiggle': showErrors && validationError,
|
||||
}"
|
||||
>
|
||||
<FilterSelect
|
||||
v-if="showQueryOperator"
|
||||
v-model="queryOperator"
|
||||
variant="faded"
|
||||
hide-icon
|
||||
class="text-sm"
|
||||
:options="queryOperatorOptions"
|
||||
/>
|
||||
<FilterSelect
|
||||
v-model="attributeKey"
|
||||
variant="faded"
|
||||
:options="filterTypes"
|
||||
@update:model-value="resetModelOnAttributeKeyChange"
|
||||
/>
|
||||
<FilterSelect
|
||||
v-model="filterOperator"
|
||||
variant="ghost"
|
||||
:options="currentFilter.filterOperators"
|
||||
/>
|
||||
<template v-if="currentOperator.hasInput">
|
||||
<MultiSelect
|
||||
v-if="inputType === 'multiSelect'"
|
||||
v-model="values"
|
||||
:options="currentFilter.options"
|
||||
/>
|
||||
<SingleSelect
|
||||
v-else-if="inputType === 'searchSelect'"
|
||||
v-model="values"
|
||||
:options="currentFilter.options"
|
||||
/>
|
||||
<SingleSelect
|
||||
v-else-if="inputType === 'booleanSelect'"
|
||||
v-model="values"
|
||||
disable-search
|
||||
:options="booleanOptions"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
v-model="values"
|
||||
:type="inputType === 'date' ? 'date' : 'text'"
|
||||
class="py-1.5 px-3 text-n-slate-12 bg-n-alpha-1 text-sm rounded-lg reset-base"
|
||||
:placeholder="t('FILTER.INPUT_PLACEHOLDER')"
|
||||
/>
|
||||
</template>
|
||||
<Button
|
||||
sm
|
||||
solid
|
||||
slate
|
||||
icon="i-lucide-trash"
|
||||
@click.stop="emit('remove')"
|
||||
/>
|
||||
</div>
|
||||
<span v-if="showErrors && validationError" class="text-sm text-n-ruby-11">
|
||||
{{ t(`FILTER.ERRORS.${validationError}`) }}
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
@@ -0,0 +1,558 @@
|
||||
export const filterTypes = [
|
||||
{
|
||||
attributeKey: 'status',
|
||||
value: 'status',
|
||||
attributeName: 'Status',
|
||||
label: 'Status',
|
||||
inputType: 'multiSelect',
|
||||
options: [
|
||||
{ id: 'open', name: 'Open' },
|
||||
{ id: 'resolved', name: 'Resolved' },
|
||||
{ id: 'pending', name: 'Pending' },
|
||||
{ id: 'snoozed', name: 'Snoozed' },
|
||||
{ id: 'all', name: 'All' },
|
||||
],
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'assignee_id',
|
||||
value: 'assignee_id',
|
||||
attributeName: 'Assignee name',
|
||||
label: 'Assignee name',
|
||||
inputType: 'searchSelect',
|
||||
options: [
|
||||
{ id: 14, name: 'Ben Nugent' },
|
||||
{ id: 30, name: 'Bruce' },
|
||||
{ id: 16, name: 'Cathy Simms' },
|
||||
{ id: 7, name: 'Charles Miner' },
|
||||
{ id: 10, name: 'Craig D' },
|
||||
{ id: 9, name: 'Dan Gore' },
|
||||
{ id: 13, name: 'Danny Cordray' },
|
||||
{ id: 3, name: 'David Wallace' },
|
||||
{ id: 4, name: 'Deangelo Vickers' },
|
||||
{ id: 33, name: 'Devon White' },
|
||||
{ id: 8, name: 'Ed Truck' },
|
||||
{ id: 31, name: 'Frank' },
|
||||
{ id: 29, name: 'Gideon' },
|
||||
{ id: 24, name: 'Glenn Max' },
|
||||
],
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-member-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-member-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'team_id',
|
||||
value: 'team_id',
|
||||
attributeName: 'Team name',
|
||||
label: 'Team name',
|
||||
inputType: 'searchSelect',
|
||||
options: [
|
||||
{ id: 223, name: '💰 sales' },
|
||||
{ id: 224, name: '💼 management' },
|
||||
{ id: 225, name: '👩💼 administration' },
|
||||
{ id: 226, name: '🚛 warehouse' },
|
||||
],
|
||||
dataType: 'number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-member-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-member-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'display_id',
|
||||
value: 'display_id',
|
||||
attributeName: 'Conversation identifier',
|
||||
label: 'Conversation identifier',
|
||||
inputType: 'plainText',
|
||||
datatype: 'number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-superset-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-superset-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'campaign_id',
|
||||
value: 'campaign_id',
|
||||
attributeName: 'Campaign name',
|
||||
label: 'Campaign name',
|
||||
inputType: 'searchSelect',
|
||||
options: [],
|
||||
datatype: 'number',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-member-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-member-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'labels',
|
||||
value: 'labels',
|
||||
attributeName: 'Labels',
|
||||
label: 'Labels',
|
||||
inputType: 'multiSelect',
|
||||
options: [
|
||||
{ id: 'billing', name: 'billing' },
|
||||
{ id: 'delivery', name: 'delivery' },
|
||||
{ id: 'lead', name: 'lead' },
|
||||
{ id: 'ops-handover', name: 'ops-handover' },
|
||||
{ id: 'premium-customer', name: 'premium-customer' },
|
||||
{ id: 'software', name: 'software' },
|
||||
],
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-member-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-member-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'referer',
|
||||
value: 'referer',
|
||||
attributeName: 'Referer link',
|
||||
label: 'Referer link',
|
||||
inputType: 'plainText',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'contains',
|
||||
label: 'Contains',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-superset-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'does_not_contain',
|
||||
label: 'Does not contain',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-superset-of',
|
||||
},
|
||||
],
|
||||
attributeModel: 'additional',
|
||||
},
|
||||
{
|
||||
attributeKey: 'created_at',
|
||||
value: 'created_at',
|
||||
attributeName: 'Created at',
|
||||
label: 'Created at',
|
||||
inputType: 'date',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'is_greater_than',
|
||||
label: 'Is greater than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-greater-than-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_less_than',
|
||||
label: 'Is lesser than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-less-than-bold',
|
||||
},
|
||||
{
|
||||
value: 'days_before',
|
||||
label: 'Is x days before',
|
||||
hasInput: true,
|
||||
inputOverride: 'plainText',
|
||||
icon: 'i-ph-calendar-minus-bold',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'last_activity_at',
|
||||
value: 'last_activity_at',
|
||||
attributeName: 'Last activity',
|
||||
label: 'Last activity',
|
||||
inputType: 'date',
|
||||
dataType: 'text',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'is_greater_than',
|
||||
label: 'Is greater than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-greater-than-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_less_than',
|
||||
label: 'Is lesser than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-less-than-bold',
|
||||
},
|
||||
{
|
||||
value: 'days_before',
|
||||
label: 'Is x days before',
|
||||
hasInput: true,
|
||||
inputOverride: 'plainText',
|
||||
icon: 'i-ph-calendar-minus-bold',
|
||||
},
|
||||
],
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'are_you_a_paid_customer',
|
||||
value: 'are_you_a_paid_customer',
|
||||
attributeName: 'Are you a paid customer?',
|
||||
label: 'Are you a paid customer?',
|
||||
inputType: 'booleanSelect',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
options: [],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'date_of_purchase',
|
||||
value: 'date_of_purchase',
|
||||
attributeName: 'Date of Purchase',
|
||||
label: 'Date of Purchase',
|
||||
inputType: 'date',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_present',
|
||||
label: 'Is present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-member-of-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_not_present',
|
||||
label: 'Is not present',
|
||||
hasInput: false,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-member-of',
|
||||
},
|
||||
{
|
||||
value: 'is_greater_than',
|
||||
label: 'Is greater than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-greater-than-bold',
|
||||
},
|
||||
{
|
||||
value: 'is_less_than',
|
||||
label: 'Is lesser than',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-less-than-bold',
|
||||
},
|
||||
],
|
||||
options: [],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'your_website',
|
||||
value: 'your_website',
|
||||
attributeName: 'Your website',
|
||||
label: 'Your website',
|
||||
inputType: 'plainText',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
options: [],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'are_you_residing_in_india',
|
||||
value: 'are_you_residing_in_india',
|
||||
attributeName: 'Are you residing in India?',
|
||||
label: 'Are you residing in India?',
|
||||
inputType: 'booleanSelect',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
options: [],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'cloud',
|
||||
value: 'cloud',
|
||||
attributeName: 'Cloud',
|
||||
label: 'Cloud',
|
||||
inputType: 'booleanSelect',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
options: [],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
{
|
||||
attributeKey: 'license_type',
|
||||
value: 'license_type',
|
||||
attributeName: 'License Type',
|
||||
label: 'License Type',
|
||||
inputType: 'searchSelect',
|
||||
filterOperators: [
|
||||
{
|
||||
value: 'equal_to',
|
||||
label: 'Equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-equals-bold',
|
||||
},
|
||||
{
|
||||
value: 'not_equal_to',
|
||||
label: 'Not equal to',
|
||||
hasInput: true,
|
||||
inputOverride: null,
|
||||
icon: 'i-ph-not-equals-bold',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
id: 'Personal',
|
||||
name: 'Personal',
|
||||
},
|
||||
{
|
||||
id: 'Enterprise',
|
||||
name: 'Enterprise',
|
||||
},
|
||||
{
|
||||
id: 'Teams',
|
||||
name: 'Teams',
|
||||
},
|
||||
{
|
||||
id: 'Professional',
|
||||
name: 'Professional',
|
||||
},
|
||||
],
|
||||
attributeModel: 'customAttributes',
|
||||
},
|
||||
];
|
||||
@@ -20,7 +20,7 @@ export const ATLEAST_ONE_ACTION_REQUIRED = 'ATLEAST_ONE_ACTION_REQUIRED';
|
||||
*
|
||||
* @returns {string|null} An error message if validation fails, or null if validation passes.
|
||||
*/
|
||||
const validateSingleFilter = filter => {
|
||||
export const validateSingleFilter = filter => {
|
||||
if (!filter.attribute_key) {
|
||||
return ATTRIBUTE_KEY_REQUIRED;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ const tailwindConfig = {
|
||||
'./app/javascript/shared/**/*.vue',
|
||||
'./app/javascript/survey/**/*.vue',
|
||||
'./app/javascript/dashboard/helper/**/*.js',
|
||||
'./app/javascript/dashboard/components-next/**/*.js',
|
||||
'./app/views/**/*.html.erb',
|
||||
],
|
||||
theme: {
|
||||
@@ -112,6 +113,11 @@ const tailwindConfig = {
|
||||
collections: {
|
||||
woot: {
|
||||
icons: {
|
||||
'logic-or': {
|
||||
body: `<rect x="14" y="5" width="2" height="13" rx="1" fill="currentColor"/><rect x="8" y="5" width="2" height="13" rx="1" fill="currentColor"/>`,
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
alert: {
|
||||
body: `<path d="M1.81348 0.9375L1.69727 7.95117H0.302734L0.179688 0.9375H1.81348ZM1 11.1025C0.494141 11.1025 0.0908203 10.7061 0.0976562 10.2207C0.0908203 9.72852 0.494141 9.33203 1 9.33203C1.49219 9.33203 1.89551 9.72852 1.90234 10.2207C1.89551 10.7061 1.49219 11.1025 1 11.1025Z" fill="currentColor" />`,
|
||||
width: 2,
|
||||
|
||||
Reference in New Issue
Block a user