feat: Create contact
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ContactSortMenu from './ContactSortMenu.vue';
|
||||
import ContactMoreActions from './ContactMoreActions.vue';
|
||||
|
||||
defineProps({
|
||||
buttonLabel: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isDetailView: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -21,7 +18,7 @@ defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['filter', 'more', 'message', 'update:sort']);
|
||||
const emit = defineEmits(['filter', 'more', 'update:sort']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -30,6 +27,7 @@ const emit = defineEmits(['filter', 'more', 'message', 'update:sort']);
|
||||
v-if="!isDetailView"
|
||||
icon="i-lucide-list-filter"
|
||||
color="slate"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
@click="emit('filter')"
|
||||
/>
|
||||
@@ -39,13 +37,6 @@ const emit = defineEmits(['filter', 'more', 'message', 'update:sort']);
|
||||
:active-ordering="activeOrdering"
|
||||
@update:sort="emit('update:sort', $event)"
|
||||
/>
|
||||
<Button
|
||||
v-if="!isDetailView"
|
||||
icon="i-lucide-ellipsis-vertical"
|
||||
color="slate"
|
||||
variant="ghost"
|
||||
@click="emit('more')"
|
||||
/>
|
||||
<Button :label="buttonLabel" size="sm" @click="emit('message')" />
|
||||
<ContactMoreActions v-if="!isDetailView" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||
import CreateNewContactDialog from 'dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const contactMenuItems = [
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.ADD_CONTACT'),
|
||||
action: 'add',
|
||||
value: 'add',
|
||||
icon: 'i-lucide-plus',
|
||||
},
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.EXPORT_CONTACT'),
|
||||
action: 'export',
|
||||
value: 'export',
|
||||
icon: 'i-lucide-upload',
|
||||
},
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.IMPORT_CONTACT'),
|
||||
action: 'import',
|
||||
value: 'import',
|
||||
icon: 'i-lucide-download',
|
||||
},
|
||||
];
|
||||
|
||||
const showActionsDropdown = ref(false);
|
||||
const createNewContactDialogRef = ref(null);
|
||||
|
||||
const handleContactAction = ({ action }) => {
|
||||
if (action === 'add') {
|
||||
createNewContactDialogRef.value?.dialogRef.open();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-on-clickaway="() => (showActionsDropdown = false)" class="relative">
|
||||
<Button
|
||||
icon="i-lucide-ellipsis-vertical"
|
||||
color="slate"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:class="showActionsDropdown ? 'bg-n-alpha-2' : ''"
|
||||
@click="showActionsDropdown = !showActionsDropdown"
|
||||
/>
|
||||
<DropdownMenu
|
||||
v-if="showActionsDropdown"
|
||||
:menu-items="contactMenuItems"
|
||||
class="right-0 mt-1 w-52 top-full"
|
||||
@action="handleContactAction($event)"
|
||||
/>
|
||||
</div>
|
||||
<CreateNewContactDialog ref="createNewContactDialogRef" />
|
||||
</template>
|
||||
@@ -92,6 +92,7 @@ const handleOrderChange = value => {
|
||||
<Button
|
||||
icon="i-lucide-arrow-down-up"
|
||||
color="slate"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
:class="isMenuOpen ? 'bg-n-alpha-2' : ''"
|
||||
@click="isMenuOpen = !isMenuOpen"
|
||||
|
||||
@@ -12,12 +12,16 @@ import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
const props = defineProps({
|
||||
contactData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null,
|
||||
},
|
||||
isDetailsView: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isNewContact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update']);
|
||||
@@ -76,6 +80,7 @@ const validationRules = {
|
||||
const v$ = useVuelidate(validationRules, state);
|
||||
|
||||
const updateState = () => {
|
||||
if (props.isNewContact) return;
|
||||
const {
|
||||
id,
|
||||
name = '',
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useStore, useMapGetter } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Dialog from 'dashboard/components-next/dialog/Dialog.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useStore();
|
||||
|
||||
const dialogRef = ref(null);
|
||||
const contact = ref(null);
|
||||
|
||||
const uiFlags = useMapGetter('contacts/getUIFlags');
|
||||
const isCreatingContact = computed(() => uiFlags.value.isCreating);
|
||||
|
||||
const createNewContact = contactItem => {
|
||||
contact.value = contactItem;
|
||||
};
|
||||
|
||||
const handleDialogConfirm = async () => {
|
||||
if (!contact.value) return;
|
||||
await store.dispatch('contacts/create', contact.value);
|
||||
dialogRef.value.close();
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
dialogRef.value.close();
|
||||
};
|
||||
|
||||
defineExpose({ dialogRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog ref="dialogRef" max-width="max-w-3xl" @confirm="handleDialogConfirm">
|
||||
<template #form>
|
||||
<ContactsForm is-new-contact @update="createNewContact" />
|
||||
</template>
|
||||
<template #actions>
|
||||
<div class="flex items-center justify-between w-full gap-3">
|
||||
<Button
|
||||
:label="t('DIALOG.BUTTONS.CANCEL')"
|
||||
variant="link"
|
||||
class="h-10 hover:no-underline hover:text-n-brand"
|
||||
@click="closeDialog"
|
||||
/>
|
||||
<Button
|
||||
:label="
|
||||
t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.SAVE_CONTACT')
|
||||
"
|
||||
color="blue"
|
||||
:is-loading="isCreatingContact"
|
||||
@click="handleDialogConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Input from 'dashboard/components-next/input/Input.vue';
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
|
||||
@@ -121,12 +122,12 @@ const updateCurrentPage = page => {
|
||||
:items="breadcrumbItems"
|
||||
@click="handleBreadcrumbClick"
|
||||
/>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-4">
|
||||
<div v-if="!isDetailView" class="flex items-center gap-2">
|
||||
<Input
|
||||
:placeholder="$t('CONTACTS_LAYOUT.HEADER.SEARCH_PLACEHOLDER')"
|
||||
:custom-input-class="[
|
||||
'h-10 [&:not(.focus)]:!border-transparent bg-n-solid-1 ltr:!pl-8 rtl:!pr-8',
|
||||
'h-8 [&:not(.focus)]:!border-transparent bg-n-solid-1 ltr:!pl-8 !py-1 rtl:!pr-8',
|
||||
]"
|
||||
@input="emit('search', $event.target.value)"
|
||||
>
|
||||
@@ -139,15 +140,15 @@ const updateCurrentPage = page => {
|
||||
</Input>
|
||||
</div>
|
||||
<ContactActions
|
||||
:button-label="buttonLabel"
|
||||
:is-detail-view="isDetailView"
|
||||
:active-sort="activeSort"
|
||||
:active-ordering="activeOrdering"
|
||||
@filter="emit('filter')"
|
||||
@update:sort="emit('sort', $event)"
|
||||
@more="emit('more')"
|
||||
@message="emit('message')"
|
||||
/>
|
||||
<div class="w-px h-4 bg-n-strong" />
|
||||
<Button :label="buttonLabel" size="sm" @click="emit('message')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user