feat: contact page

This commit is contained in:
Shariq Ansari 2023-10-14 16:01:15 +05:30
parent 35b0bf9a8b
commit e7bc5a900e
4 changed files with 140 additions and 116 deletions

View File

@ -0,0 +1,16 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.5 4.5C2.5 3.39543 3.39543 2.5 4.5 2.5H8.5C8.77614 2.5 9 2.27614 9 2C9 1.72386 8.77614 1.5 8.5 1.5H4.5C2.84315 1.5 1.5 2.84315 1.5 4.5V11.5C1.5 13.1569 2.84315 14.5 4.5 14.5H11.5C13.1569 14.5 14.5 13.1569 14.5 11.5V7.5C14.5 7.22386 14.2761 7 14 7C13.7239 7 13.5 7.22386 13.5 7.5V11.5C13.5 12.6046 12.6046 13.5 11.5 13.5H4.5C3.39543 13.5 2.5 12.6046 2.5 11.5V4.5ZM14.1255 2.58446C14.3207 2.3892 14.3207 2.07261 14.1255 1.87735C13.9302 1.68209 13.6136 1.68209 13.4184 1.87735L6.68616 8.60954C6.4909 8.8048 6.4909 9.12139 6.68616 9.31665C6.88143 9.51191 7.19801 9.51191 7.39327 9.31665L14.1255 2.58446Z"
fill="currentColor"
/>
</svg>
</template>

View File

@ -0,0 +1,57 @@
<template>
<div class="flex gap-6 p-5">
<Avatar
size="3xl"
:image="contact.image"
:label="contact.full_name"
class="!h-24 !w-24"
/>
<div class="flex flex-col justify-center gap-2">
<div class="text-3xl font-semibold text-gray-900">
{{ contact.salutation + ' ' + contact.full_name }}
</div>
<div class="flex items-center gap-2 text-base text-gray-700">
<div class="flex items-center gap-1.5">
<EmailIcon class="h-4 w-4" />
<span class="">{{ contact.email_id }}</span>
</div>
<span class="text-3xl leading-[0] text-gray-600">&middot;</span>
<div class="flex items-center gap-1.5">
<PhoneIcon class="h-4 w-4" />
<span class="">{{ contact.mobile_no }}</span>
</div>
</div>
<div class="mt-1 flex gap-2">
<Button label="Edit" size="sm">
<template #prefix>
<EditIcon class="h-4 w-4" />
</template>
</Button>
<Button label="Add lead" size="sm">
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
</Button>
<Button label="Add deal" size="sm">
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
</Button>
</div>
</div>
</div>
</template>
<script setup>
import { FeatherIcon, Avatar } from 'frappe-ui'
import EmailIcon from '@/components/Icons/EmailIcon.vue'
import EditIcon from '@/components/Icons/EditIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
const props = defineProps({
contact: {
type: Object,
required: true,
},
})
</script>

View File

@ -9,136 +9,79 @@
</Button> </Button>
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="flex items-center justify-between px-5 pb-4 pt-3"> <div class="flex h-full overflow-hidden">
<div class="flex items-center gap-2"> <div class="flex flex-col overflow-y-auto border-r">
<Dropdown :options="viewsDropdownOptions"> <router-link
<template #default="{ open }"> :to="{ name: 'Contact', params: { contactId: contact.name } }"
<Button :label="currentView.label"> v-for="(contact, i) in contacts.data"
<template #prefix :key="i"
><FeatherIcon :name="currentView.icon" class="h-4" :class="[
/></template> current_contact?.name === contact.name
<template #suffix ? 'bg-gray-50 hover:bg-gray-100'
><FeatherIcon : 'hover:bg-gray-50',
:name="open ? 'chevron-up' : 'chevron-down'" ]"
class="h-4 text-gray-600" >
/></template> <div class="flex w-[352px] items-center gap-3 border-b px-5 py-4">
</Button> <Avatar :image="contact.image" :label="contact.full_name" size="xl" />
</template> <div class="flex flex-col items-start gap-1">
</Dropdown> <span class="text-base font-medium text-gray-900">
{{ contact.full_name }}
</span>
<span class="text-sm text-gray-700">{{ contact.email_id }}</span>
</div>
</div>
</router-link>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex-1">
<Button label="Sort"> <router-view v-if="current_contact" :contact="current_contact" />
<template #prefix><SortIcon class="h-4" /></template> <div
</Button> v-else
<Button label="Filter"> class="grid h-full place-items-center text-xl font-medium text-gray-500"
<template #prefix><FilterIcon class="h-4" /></template> >
</Button> <div class="flex flex-col items-center justify-center space-y-2">
<Button icon="more-horizontal" /> <ContactsIcon class="h-10 w-10" />
<div>No contact selected</div>
</div>
</div>
</div> </div>
</div> </div>
<ListView :list="list" :columns="columns" :rows="rows" row-key="name" />
</template> </template>
<script setup> <script setup>
import ListView from '@/components/ListView.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import SortIcon from '@/components/Icons/SortIcon.vue' import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
import FilterIcon from '@/components/Icons/FilterIcon.vue' import { FeatherIcon, Breadcrumbs, Avatar } from 'frappe-ui'
import { FeatherIcon, Button, Dropdown, Breadcrumbs } from 'frappe-ui'
import { ref, computed } from 'vue'
import { contactsStore } from '@/stores/contacts.js' import { contactsStore } from '@/stores/contacts.js'
import { computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const { contacts } = contactsStore() const { contacts } = contactsStore()
const route = useRoute()
const list = { const current_contact = computed(() => {
title: 'Contacts', return contacts.data.find(
plural_label: 'Contacts', (contact) => contact.name === route.params.contactId
singular_label: 'Contact', )
} })
const breadcrumbs = [{ label: list.title, route: { name: 'Contacts' } }] const breadcrumbs = computed(() => {
let items = [{ label: 'Contacts', route: { name: 'Contacts' } }]
const columns = [ if (!current_contact.value) return items
{ items.push({
label: 'Full name', label: current_contact.value.full_name,
key: 'full_name', route: {
type: 'avatar', name: 'Contact',
size: 'w-44', params: { contactId: current_contact.value.name },
}, },
{
label: 'Email',
key: 'email',
type: 'email',
size: 'w-44',
},
{
label: 'Phone',
key: 'mobile_no',
type: 'phone',
size: 'w-44',
},
]
const rows = computed(() => {
return contacts.data?.map((contact) => {
return {
name: contact.name,
full_name: {
label: contact.full_name,
image_label: contact.full_name,
image: contact.image,
},
email: contact.email_id,
mobile_no: contact.mobile_no,
}
}) })
return items
}) })
const currentView = ref({ onMounted(() => {
label: 'List', const el = document.querySelector('.router-link-active')
icon: 'list', if (el)
setTimeout(() => {
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
})
}) })
const viewsDropdownOptions = [
{
label: 'List',
icon: 'list',
onClick() {
currentView.value = {
label: 'List',
icon: 'list',
}
},
},
{
label: 'Table',
icon: 'grid',
onClick() {
currentView.value = {
label: 'Table',
icon: 'grid',
}
},
},
{
label: 'Calender',
icon: 'calendar',
onClick() {
currentView.value = {
label: 'Calender',
icon: 'calendar',
}
},
},
{
label: 'Board',
icon: 'columns',
onClick() {
currentView.value = {
label: 'Board',
icon: 'columns',
}
},
},
]
</script> </script>

View File

@ -38,6 +38,14 @@ const routes = [
path: '/contacts', path: '/contacts',
name: 'Contacts', name: 'Contacts',
component: () => import('@/pages/Contacts.vue'), component: () => import('@/pages/Contacts.vue'),
children: [
{
path: '/contacts/:contactId?',
name: 'Contact',
component: () => import('@/pages/Contact.vue'),
props: true,
},
],
}, },
{ {
path: '/call-logs', path: '/call-logs',