feat: Improve Contact list (#10522)
This commit is contained in:
@@ -214,6 +214,10 @@ const resetValidation = () => {
|
|||||||
v$.value.$reset();
|
v$.value.$reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(state, defaultState);
|
||||||
|
};
|
||||||
|
|
||||||
watch(() => props.contactData, prepareStateBasedOnProps, {
|
watch(() => props.contactData, prepareStateBasedOnProps, {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
deep: true,
|
deep: true,
|
||||||
@@ -224,6 +228,7 @@ defineExpose({
|
|||||||
state,
|
state,
|
||||||
resetValidation,
|
resetValidation,
|
||||||
isFormInvalid,
|
isFormInvalid,
|
||||||
|
resetForm,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -261,7 +266,7 @@ defineExpose({
|
|||||||
:placeholder="item.placeholder"
|
:placeholder="item.placeholder"
|
||||||
:message-type="getMessageType(item.key)"
|
:message-type="getMessageType(item.key)"
|
||||||
:custom-input-class="`h-8 !pt-1 !pb-1 ${
|
:custom-input-class="`h-8 !pt-1 !pb-1 ${
|
||||||
!isDetailsView ? '[&:not(.error)]:!border-transparent' : ''
|
!isDetailsView ? '[&:not(.error,.focus)]:!border-transparent' : ''
|
||||||
}`"
|
}`"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@input="
|
@input="
|
||||||
|
|||||||
@@ -27,11 +27,16 @@ const handleDialogConfirm = async () => {
|
|||||||
emit('create', contact.value);
|
emit('create', contact.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSuccess = () => {
|
||||||
|
contactsFormRef.value?.resetForm();
|
||||||
|
dialogRef.value.close();
|
||||||
|
};
|
||||||
|
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
dialogRef.value.close();
|
dialogRef.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({ dialogRef, contactsFormRef });
|
defineExpose({ dialogRef, contactsFormRef, onSuccess });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { useAlert, useTrack } from 'dashboard/composables';
|
|||||||
import { CONTACTS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
import { CONTACTS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
import filterQueryGenerator from 'dashboard/helper/filterQueryGenerator';
|
import filterQueryGenerator from 'dashboard/helper/filterQueryGenerator';
|
||||||
import contactFilterItems from 'dashboard/routes/dashboard/contacts/contactFilterItems';
|
import contactFilterItems from 'dashboard/routes/dashboard/contacts/contactFilterItems';
|
||||||
|
import {
|
||||||
|
DuplicateContactException,
|
||||||
|
ExceptionWithMessage,
|
||||||
|
} from 'shared/helpers/CustomErrors';
|
||||||
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
||||||
import countries from 'shared/constants/countries';
|
import countries from 'shared/constants/countries';
|
||||||
import {
|
import {
|
||||||
@@ -23,38 +27,14 @@ import DeleteSegmentDialog from 'dashboard/components-next/Contacts/ContactsForm
|
|||||||
import ContactsFilter from 'dashboard/components-next/filter/ContactsFilter.vue';
|
import ContactsFilter from 'dashboard/components-next/filter/ContactsFilter.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
showSearch: {
|
showSearch: { type: Boolean, default: true },
|
||||||
type: Boolean,
|
searchValue: { type: String, default: '' },
|
||||||
default: true,
|
activeSort: { type: String, default: 'last_activity_at' },
|
||||||
},
|
activeOrdering: { type: String, default: '' },
|
||||||
searchValue: {
|
headerTitle: { type: String, default: '' },
|
||||||
type: String,
|
segmentsId: { type: [String, Number], default: 0 },
|
||||||
default: '',
|
activeSegment: { type: Object, default: null },
|
||||||
},
|
hasAppliedFilters: { type: Boolean, default: false },
|
||||||
activeSort: {
|
|
||||||
type: String,
|
|
||||||
default: 'last_activity_at',
|
|
||||||
},
|
|
||||||
activeOrdering: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
headerTitle: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
segmentsId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
activeSegment: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
hasAppliedFilters: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@@ -99,8 +79,26 @@ const openDeleteSegmentDialog = () =>
|
|||||||
deleteSegmentDialogRef.value?.dialogRef.open();
|
deleteSegmentDialogRef.value?.dialogRef.open();
|
||||||
|
|
||||||
const onCreate = async contact => {
|
const onCreate = async contact => {
|
||||||
|
try {
|
||||||
await store.dispatch('contacts/create', contact);
|
await store.dispatch('contacts/create', contact);
|
||||||
createNewContactDialogRef.value?.dialogRef.close();
|
createNewContactDialogRef.value?.onSuccess();
|
||||||
|
useAlert(
|
||||||
|
t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.SUCCESS_MESSAGE')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
const i18nPrefix = 'CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION';
|
||||||
|
if (error instanceof DuplicateContactException) {
|
||||||
|
if (error.data.includes('email')) {
|
||||||
|
useAlert(t(`${i18nPrefix}.EMAIL_ADDRESS_DUPLICATE`));
|
||||||
|
} else if (error.data.includes('phone_number')) {
|
||||||
|
useAlert(t(`${i18nPrefix}.PHONE_NUMBER_DUPLICATE`));
|
||||||
|
}
|
||||||
|
} else if (error instanceof ExceptionWithMessage) {
|
||||||
|
useAlert(error.data);
|
||||||
|
} else {
|
||||||
|
useAlert(t(`${i18nPrefix}.ERROR_MESSAGE`));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onImport = async file => {
|
const onImport = async file => {
|
||||||
@@ -135,11 +133,19 @@ const onCreateSegment = async payload => {
|
|||||||
...payload,
|
...payload,
|
||||||
query: segmentsQuery.value,
|
query: segmentsQuery.value,
|
||||||
};
|
};
|
||||||
await store.dispatch('customViews/create', payloadData);
|
const response = await store.dispatch('customViews/create', payloadData);
|
||||||
createSegmentDialogRef.value?.dialogRef.close();
|
createSegmentDialogRef.value?.dialogRef.close();
|
||||||
useAlert(
|
useAlert(
|
||||||
t('CONTACTS_LAYOUT.HEADER.ACTIONS.FILTERS.CREATE_SEGMENT.SUCCESS_MESSAGE')
|
t('CONTACTS_LAYOUT.HEADER.ACTIONS.FILTERS.CREATE_SEGMENT.SUCCESS_MESSAGE')
|
||||||
);
|
);
|
||||||
|
const segmentId = response?.data?.id;
|
||||||
|
if (!segmentId) return;
|
||||||
|
// Navigate to the created segment
|
||||||
|
router.push({
|
||||||
|
name: 'contacts_dashboard_segments_index',
|
||||||
|
params: { segmentId },
|
||||||
|
query: { page: 1 },
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
useAlert(
|
useAlert(
|
||||||
t('CONTACTS_LAYOUT.HEADER.ACTIONS.FILTERS.CREATE_SEGMENT.ERROR_MESSAGE')
|
t('CONTACTS_LAYOUT.HEADER.ACTIONS.FILTERS.CREATE_SEGMENT.ERROR_MESSAGE')
|
||||||
|
|||||||
@@ -91,13 +91,18 @@ const activeCountry = computed(() =>
|
|||||||
const inputBorderClass = computed(() => {
|
const inputBorderClass = computed(() => {
|
||||||
const errorClass =
|
const errorClass =
|
||||||
'border-n-ruby-8 dark:border-n-ruby-8 hover:border-n-ruby-9 dark:hover:border-n-ruby-9 disabled:border-n-ruby-8 dark:disabled:border-n-ruby-8';
|
'border-n-ruby-8 dark:border-n-ruby-8 hover:border-n-ruby-9 dark:hover:border-n-ruby-9 disabled:border-n-ruby-8 dark:disabled:border-n-ruby-8';
|
||||||
|
const focusClass =
|
||||||
|
'has-[:focus]:border-n-brand dark:has-[:focus]:border-n-brand';
|
||||||
|
|
||||||
if (!props.showBorder) {
|
if (!props.showBorder) {
|
||||||
return hasError.value ? errorClass : 'border-transparent';
|
if (hasError.value) return errorClass;
|
||||||
|
return `border-transparent ${focusClass}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasError.value) {
|
if (hasError.value) {
|
||||||
return errorClass;
|
return errorClass;
|
||||||
}
|
}
|
||||||
return 'has-[:focus]:border-n-brand dark:has-[:focus]:border-n-brand border-n-weak dark:border-n-weak hover:border-n-slate-6 dark:hover:border-n-slate-6 disabled:border-n-weak dark:disabled:border-n-weak';
|
return `${focusClass} border-n-weak dark:border-n-weak hover:border-n-slate-6 dark:hover:border-n-slate-6 disabled:border-n-weak dark:disabled:border-n-weak`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const phoneNumberError = computed(() => {
|
const phoneNumberError = computed(() => {
|
||||||
|
|||||||
@@ -401,7 +401,11 @@
|
|||||||
"ADD_CONTACT": "Add contact",
|
"ADD_CONTACT": "Add contact",
|
||||||
"EXPORT_CONTACT": "Export contacts",
|
"EXPORT_CONTACT": "Export contacts",
|
||||||
"IMPORT_CONTACT": "Import contacts",
|
"IMPORT_CONTACT": "Import contacts",
|
||||||
"SAVE_CONTACT": "Save contact"
|
"SAVE_CONTACT": "Save contact",
|
||||||
|
"EMAIL_ADDRESS_DUPLICATE": "This email address is in use for another contact.",
|
||||||
|
"PHONE_NUMBER_DUPLICATE": "This phone number is in use for another contact.",
|
||||||
|
"SUCCESS_MESSAGE": "Contact saved successfully",
|
||||||
|
"ERROR_MESSAGE": "Unable to save contact. Please try again later."
|
||||||
},
|
},
|
||||||
"IMPORT_CONTACT": {
|
"IMPORT_CONTACT": {
|
||||||
"TITLE": "Import contacts",
|
"TITLE": "Import contacts",
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export const actions = {
|
|||||||
data: response.data,
|
data: response.data,
|
||||||
filterType: FILTER_KEYS[obj.filter_type],
|
filterType: FILTER_KEYS[obj.filter_type],
|
||||||
});
|
});
|
||||||
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error?.response?.data?.message;
|
const errorMessage = error?.response?.data?.message;
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
|
|||||||
Reference in New Issue
Block a user