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/shared/constants/locales.js'
|
||||||
- 'app/javascript/dashboard/helper/specs/macrosFixtures.js'
|
- 'app/javascript/dashboard/helper/specs/macrosFixtures.js'
|
||||||
- 'app/javascript/dashboard/routes/dashboard/settings/macros/constants.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.
|
* @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) {
|
if (!filter.attribute_key) {
|
||||||
return ATTRIBUTE_KEY_REQUIRED;
|
return ATTRIBUTE_KEY_REQUIRED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const tailwindConfig = {
|
|||||||
'./app/javascript/shared/**/*.vue',
|
'./app/javascript/shared/**/*.vue',
|
||||||
'./app/javascript/survey/**/*.vue',
|
'./app/javascript/survey/**/*.vue',
|
||||||
'./app/javascript/dashboard/helper/**/*.js',
|
'./app/javascript/dashboard/helper/**/*.js',
|
||||||
|
'./app/javascript/dashboard/components-next/**/*.js',
|
||||||
'./app/views/**/*.html.erb',
|
'./app/views/**/*.html.erb',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
@@ -112,6 +113,11 @@ const tailwindConfig = {
|
|||||||
collections: {
|
collections: {
|
||||||
woot: {
|
woot: {
|
||||||
icons: {
|
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: {
|
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" />`,
|
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,
|
width: 2,
|
||||||
|
|||||||
Reference in New Issue
Block a user