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

View File

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