1
0
forked from test/crm

Merge pull request #16 from shariquerik/frappeui-tabs-component

This commit is contained in:
Shariq Ansari 2023-10-02 23:27:24 +05:30 committed by Shariq Ansari
commit 35b0bf9a8b
19 changed files with 711 additions and 9177 deletions

@ -1 +1 @@
Subproject commit 8762f85ef72c01c2dbf9fbe2bd7417911d347ac3 Subproject commit 167ff453cb525674f976d53b126900b80b9a80a2

8
frontend/.gitignore vendored
View File

@ -2,10 +2,4 @@ node_modules
.DS_Store .DS_Store
dist dist
dist-ssr dist-ssr
*.local *.local
*.pyc
*.egg-info
*.swp
tags
crm/public/frontend
__pycache__

View File

@ -16,7 +16,7 @@
"@vueuse/integrations": "^10.3.0", "@vueuse/integrations": "^10.3.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"feather-icons": "^4.28.0", "feather-icons": "^4.28.0",
"frappe-ui": "^0.1.5", "frappe-ui": "^0.1.6",
"pinia": "^2.0.33", "pinia": "^2.0.33",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"socket.io-client": "^4.7.2", "socket.io-client": "^4.7.2",

View File

@ -1,27 +0,0 @@
<template>
<div class="flex min-w-0 items-center text-ellipsis whitespace-nowrap">
<template v-for="(item, i) in items" :key="item.label">
<router-link
class="flex items-center rounded px-0.5 py-1 text-lg font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 text-gray-600 hover:text-gray-700 last:text-gray-900 last:hover:text-gray-900 transition-all duration-300 ease-in-out"
:to="item.route || ''"
>
<slot name="prefix" :item="item" />
<span>{{ item.label }}</span>
</router-link>
<span
v-if="i != items.length - 1"
class="mx-0.5 text-base text-gray-500"
>
/
</span>
</template>
</div>
</template>
<script setup>
const props = defineProps({
items: {
type: Array,
required: true,
},
})
</script>

View File

@ -67,7 +67,7 @@
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { dealStatuses, statusDropdownOptions } from '@/utils' import { dealStatuses, statusDropdownOptions, activeAgents } from '@/utils'
import { import {
FormControl, FormControl,
Button, Button,
@ -153,18 +153,4 @@ const allFields = [
], ],
}, },
] ]
const activeAgents = computed(() => {
const nonAgents = ['Administrator', 'Guest']
return users.data
.filter((user) => !nonAgents.includes(user.name))
.sort((a, b) => a.full_name - b.full_name)
.map((user) => {
return {
label: user.full_name,
value: user.email,
...user,
}
})
})
</script> </script>

View File

@ -3,7 +3,7 @@
<div v-for="section in allFields" :key="section.section"> <div v-for="section in allFields" :key="section.section">
<div class="grid grid-cols-3 gap-4"> <div class="grid grid-cols-3 gap-4">
<div v-for="field in section.fields" :key="field.name"> <div v-for="field in section.fields" :key="field.name">
<div class="text-gray-600 text-sm mb-2">{{ field.label }}</div> <div class="mb-2 text-sm text-gray-600">{{ field.label }}</div>
<FormControl <FormControl
v-if="field.type === 'select'" v-if="field.type === 'select'"
type="select" type="select"
@ -41,10 +41,12 @@
<template #default="{ open }"> <template #default="{ open }">
<Button <Button
:label="newLead[field.name]" :label="newLead[field.name]"
class="justify-between w-full" class="w-full justify-between"
> >
<template #prefix> <template #prefix>
<IndicatorIcon :class="leadStatuses[newLead[field.name]].color" /> <IndicatorIcon
:class="leadStatuses[newLead[field.name]].color"
/>
</template> </template>
<template #default>{{ newLead[field.name] }}</template> <template #default>{{ newLead[field.name] }}</template>
<template #suffix> <template #suffix>
@ -67,7 +69,7 @@
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { leadStatuses, statusDropdownOptions } from '@/utils' import { leadStatuses, statusDropdownOptions, activeAgents } from '@/utils'
import { import {
FormControl, FormControl,
Button, Button,
@ -153,19 +155,4 @@ const allFields = [
], ],
}, },
] ]
const activeAgents = computed(() => {
const nonAgents = ['Administrator', 'Guest']
return users.data
.filter((user) => !nonAgents.includes(user.name))
.sort((a, b) => a.full_name - b.full_name)
.map((user) => {
return {
label: user.full_name,
value: user.email,
...user,
}
})
})
</script> </script>

View File

@ -15,9 +15,9 @@
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="border-b"></div> <div class="border-b"></div>
<div v-if="callLog.data" class="p-6 max-w-lg"> <div v-if="callLog.data" class="max-w-lg p-6">
<div class="pb-3 text-base font-medium">Call details</div> <div class="pb-3 text-base font-medium">Call details</div>
<div class="flex flex-col gap-4 border rounded-lg p-4 mb-3 shadow-sm"> <div class="mb-3 flex flex-col gap-4 rounded-lg border p-4 shadow-sm">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<FeatherIcon <FeatherIcon
@ -26,7 +26,7 @@
? 'phone-incoming' ? 'phone-incoming'
: 'phone-outgoing' : 'phone-outgoing'
" "
class="w-4 h-4 text-gray-600" class="h-4 w-4 text-gray-600"
/> />
<div class="font-medium"> <div class="font-medium">
{{ callLog.data.type == 'Incoming' ? 'Inbound' : 'Outbound' }} call {{ callLog.data.type == 'Incoming' ? 'Inbound' : 'Outbound' }} call
@ -48,7 +48,7 @@
:label="callLog.data.caller.label" :label="callLog.data.caller.label"
size="xl" size="xl"
/> />
<div class="flex flex-col gap-1 ml-1"> <div class="ml-1 flex flex-col gap-1">
<div class="text-base font-medium"> <div class="text-base font-medium">
{{ callLog.data.caller.label }} {{ callLog.data.caller.label }}
</div> </div>
@ -56,13 +56,13 @@
{{ callLog.data.from }} {{ callLog.data.from }}
</div> </div>
</div> </div>
<FeatherIcon name="arrow-right" class="w-5 h-5 text-gray-600 mx-2" /> <FeatherIcon name="arrow-right" class="mx-2 h-5 w-5 text-gray-600" />
<Avatar <Avatar
:image="callLog.data.receiver.image" :image="callLog.data.receiver.image"
:label="callLog.data.receiver.label" :label="callLog.data.receiver.label"
size="xl" size="xl"
/> />
<div class="flex flex-col gap-1 ml-1"> <div class="ml-1 flex flex-col gap-1">
<div class="text-base font-medium"> <div class="text-base font-medium">
{{ callLog.data.receiver.label }} {{ callLog.data.receiver.label }}
</div> </div>
@ -74,13 +74,13 @@
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<DurationIcon class="w-4 h-4 text-gray-600" /> <DurationIcon class="h-4 w-4 text-gray-600" />
<div class="text-sm text-gray-600">Duration</div> <div class="text-sm text-gray-600">Duration</div>
<div class="text-sm">{{ callLog.data.duration }}</div> <div class="text-sm">{{ callLog.data.duration }}</div>
</div> </div>
<div> <div>
<Tooltip <Tooltip
class="text-gray-600 text-sm" class="text-sm text-gray-600"
:text="dateFormat(callLog.data.creation, dateTooltipFormat)" :text="dateFormat(callLog.data.creation, dateTooltipFormat)"
> >
{{ timeAgo(callLog.data.creation) }} {{ timeAgo(callLog.data.creation) }}
@ -91,7 +91,7 @@
<div v-if="callLog.data.recording_url" class="mt-6"> <div v-if="callLog.data.recording_url" class="mt-6">
<div class="mb-3 text-base font-medium">Call recording</div> <div class="mb-3 text-base font-medium">Call recording</div>
<div class="flex items-center justify-between border rounded shadow-sm"> <div class="flex items-center justify-between rounded border shadow-sm">
<audio <audio
class="audio-control" class="audio-control"
controls controls
@ -103,10 +103,10 @@
<div v-if="callLog.data.note" class="mt-6"> <div v-if="callLog.data.note" class="mt-6">
<div class="mb-3 text-base font-medium">Call note</div> <div class="mb-3 text-base font-medium">Call note</div>
<div <div
class="flex flex-col gap-3 border rounded p-4 shadow-sm cursor-pointer h-56" class="flex h-56 cursor-pointer flex-col gap-3 rounded border p-4 shadow-sm"
@click="showNoteModal = true" @click="showNoteModal = true"
> >
<div class="text-lg font-medium truncate"> <div class="truncate text-lg font-medium">
{{ callLog.data.note_doc.title }} {{ callLog.data.note_doc.title }}
</div> </div>
<TextEditor <TextEditor
@ -141,7 +141,6 @@
<script setup> <script setup>
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import DurationIcon from '@/components/Icons/DurationIcon.vue' import DurationIcon from '@/components/Icons/DurationIcon.vue'
import NoteModal from '@/components/NoteModal.vue' import NoteModal from '@/components/NoteModal.vue'
import { dateFormat, timeAgo, dateTooltipFormat } from '@/utils' import { dateFormat, timeAgo, dateTooltipFormat } from '@/utils'
@ -153,6 +152,7 @@ import {
Tooltip, Tooltip,
Badge, Badge,
createResource, createResource,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { contactsStore } from '@/stores/contacts' import { contactsStore } from '@/stores/contacts'
@ -232,7 +232,10 @@ function createLead() {
const breadcrumbs = computed(() => [ const breadcrumbs = computed(() => [
{ label: 'Call Logs', route: { name: 'Call Logs' } }, { label: 'Call Logs', route: { name: 'Call Logs' } },
{ label: callLog.data?.caller.label }, {
label: callLog.data?.caller.label,
route: { name: 'Call Log', params: { callLogId: props.callLogId } },
},
]) ])
</script> </script>

View File

@ -1,10 +1,10 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: list.title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="flex justify-between items-center px-5 pt-3 pb-4"> <div class="flex items-center justify-between px-5 pb-4 pt-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button label="Sort"> <Button label="Sort">
<template #prefix><SortIcon class="h-4" /></template> <template #prefix><SortIcon class="h-4" /></template>
@ -23,13 +23,12 @@
<script setup> <script setup>
import ListView from '@/components/ListView.vue' import ListView from '@/components/ListView.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import SortIcon from '@/components/Icons/SortIcon.vue' import SortIcon from '@/components/Icons/SortIcon.vue'
import FilterIcon from '@/components/Icons/FilterIcon.vue' import FilterIcon from '@/components/Icons/FilterIcon.vue'
import { secondsToDuration } from '@/utils' import { secondsToDuration } from '@/utils'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { contactsStore } from '@/stores/contacts' import { contactsStore } from '@/stores/contacts'
import { Button, createListResource } from 'frappe-ui' import { Button, createListResource, Breadcrumbs } from 'frappe-ui'
import { computed } from 'vue' import { computed } from 'vue'
const { getUser } = usersStore() const { getUser } = usersStore()
@ -41,6 +40,8 @@ const list = {
singular_label: 'Call Log', singular_label: 'Call Log',
} }
const breadcrumbs = [{ label: list.title, route: { name: 'Call Logs' } }]
const callLogs = createListResource({ const callLogs = createListResource({
type: 'list', type: 'list',
doctype: 'CRM Call Log', doctype: 'CRM Call Log',

View File

@ -1,7 +1,7 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: list.title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<Button variant="solid" label="Create"> <Button variant="solid" label="Create">
@ -9,7 +9,7 @@
</Button> </Button>
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="flex justify-between items-center px-5 pt-3 pb-4"> <div class="flex items-center justify-between px-5 pb-4 pt-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Dropdown :options="viewsDropdownOptions"> <Dropdown :options="viewsDropdownOptions">
<template #default="{ open }"> <template #default="{ open }">
@ -42,10 +42,9 @@
<script setup> <script setup>
import ListView from '@/components/ListView.vue' import ListView from '@/components/ListView.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import SortIcon from '@/components/Icons/SortIcon.vue' import SortIcon from '@/components/Icons/SortIcon.vue'
import FilterIcon from '@/components/Icons/FilterIcon.vue' import FilterIcon from '@/components/Icons/FilterIcon.vue'
import { FeatherIcon, Button, Dropdown } from 'frappe-ui' import { FeatherIcon, Button, Dropdown, Breadcrumbs } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { contactsStore } from '@/stores/contacts.js' import { contactsStore } from '@/stores/contacts.js'
@ -57,6 +56,8 @@ const list = {
singular_label: 'Contact', singular_label: 'Contact',
} }
const breadcrumbs = [{ label: list.title, route: { name: 'Contacts' } }]
const columns = [ const columns = [
{ {
label: 'Full name', label: 'Full name',

View File

@ -1,13 +1,14 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
</LayoutHeader> </LayoutHeader>
</template> </template>
<script setup> <script setup>
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue' import { Breadcrumbs } from 'frappe-ui'
let title = 'Dashboard' let title = 'Dashboard'
const breadcrumbs = [{ label: title, route: { name: 'Dashboard' } }]
</script> </script>

View File

@ -36,43 +36,9 @@
</template> </template>
</LayoutHeader> </LayoutHeader>
<div v-if="deal.data" class="flex h-full overflow-hidden"> <div v-if="deal.data" class="flex h-full overflow-hidden">
<TabGroup as="div" class="flex flex-1 flex-col" @change="onTabChange"> <Tabs v-model="tabIndex" v-slot="{ tab }" :tabs="tabs">
<TabList class="relative flex items-center gap-6 border-b pl-5"> <Activities :title="tab.label" v-model:reload="reload" v-model="deal" />
<Tab </Tabs>
ref="tabRef"
as="template"
v-for="tab in tabs"
:key="tab.label"
v-slot="{ selected }"
>
<button
class="-mb-[1px] flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 transition-all duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
:class="{ 'text-gray-900': selected }"
>
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
{{ tab.label }}
</button>
</Tab>
<div
ref="indicator"
class="absolute -bottom-[1px] h-[1px] w-[82px] bg-gray-900"
:style="{ left: `${indicatorLeftValue}px` }"
/>
</TabList>
<TabPanels class="flex flex-1 overflow-hidden">
<TabPanel
class="flex flex-1 flex-col overflow-y-auto"
v-for="tab in tabs"
:key="tab.label"
>
<Activities
:title="tab.label"
v-model:reload="reload"
v-model="deal"
/>
</TabPanel>
</TabPanels>
</TabGroup>
<div class="flex w-[352px] flex-col justify-between border-l"> <div class="flex w-[352px] flex-col justify-between border-l">
<div <div
class="flex h-[41px] items-center border-b px-5 py-2.5 text-lg font-semibold" class="flex h-[41px] items-center border-b px-5 py-2.5 text-lg font-semibold"
@ -350,15 +316,13 @@ import LinkIcon from '@/components/Icons/LinkIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Toggler from '@/components/Toggler.vue' import Toggler from '@/components/Toggler.vue'
import Activities from '@/components/Activities.vue' import Activities from '@/components/Activities.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
import { TransitionPresets, useTransition } from '@vueuse/core'
import { import {
dealStatuses, dealStatuses,
statusDropdownOptions, statusDropdownOptions,
openWebsite, openWebsite,
createToast, createToast,
activeAgents,
} from '@/utils' } from '@/utils'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { contactsStore } from '@/stores/contacts' import { contactsStore } from '@/stores/contacts'
@ -372,6 +336,8 @@ import {
Dropdown, Dropdown,
Tooltip, Tooltip,
Avatar, Avatar,
Tabs,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
@ -434,6 +400,7 @@ const breadcrumbs = computed(() => {
return items return items
}) })
const tabIndex = ref(0)
const tabs = [ const tabs = [
{ {
label: 'Activity', label: 'Activity',
@ -469,21 +436,6 @@ function validateFile(file) {
} }
} }
const tabRef = ref([])
const indicator = ref(null)
let indicatorLeft = ref(20)
const indicatorLeftValue = useTransition(indicatorLeft, {
duration: 250,
ease: TransitionPresets.easeOutCubic,
})
function onTabChange(index) {
const selectedTab = tabRef.value[index].el
indicator.value.style.width = `${selectedTab.offsetWidth}px`
indicatorLeft.value = selectedTab.offsetLeft
}
const detailSections = computed(() => { const detailSections = computed(() => {
return [ return [
{ {
@ -572,20 +524,6 @@ const detailSections = computed(() => {
] ]
}) })
const activeAgents = computed(() => {
const nonAgents = ['Administrator', 'Guest']
return users.data
.filter((user) => !nonAgents.includes(user.name))
.sort((a, b) => a.full_name - b.full_name)
.map((user) => {
return {
label: user.full_name,
value: user.email,
...user,
}
})
})
function updateAssignedAgent(email) { function updateAssignedAgent(email) {
deal.data.lead_owner = email deal.data.lead_owner = email
updateDeal('lead_owner', email) updateDeal('lead_owner', email)

View File

@ -1,7 +1,7 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: list.title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<Button variant="solid" label="Create" @click="showNewDialog = true"> <Button variant="solid" label="Create" @click="showNewDialog = true">
@ -9,7 +9,7 @@
</Button> </Button>
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="flex justify-between items-center px-5 pt-3 pb-4"> <div class="flex items-center justify-between px-5 pb-4 pt-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Dropdown :options="viewsDropdownOptions"> <Dropdown :options="viewsDropdownOptions">
<template #default="{ open }"> <template #default="{ open }">
@ -56,7 +56,6 @@
<script setup> <script setup>
import ListView from '@/components/ListView.vue' import ListView from '@/components/ListView.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import NewDeal from '@/components/NewDeal.vue' import NewDeal from '@/components/NewDeal.vue'
import SortBy from '@/components/SortBy.vue' import SortBy from '@/components/SortBy.vue'
import Filter from '@/components/Filter.vue' import Filter from '@/components/Filter.vue'
@ -72,6 +71,7 @@ import {
Dropdown, Dropdown,
createListResource, createListResource,
createResource, createResource,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ref, computed, reactive, watch } from 'vue' import { ref, computed, reactive, watch } from 'vue'
@ -81,6 +81,9 @@ const list = {
plural_label: 'Deals', plural_label: 'Deals',
singular_label: 'Deal', singular_label: 'Deal',
} }
const breadcrumbs = [{ label: list.title, route: { name: 'Deals' } }]
const { getUser } = usersStore() const { getUser } = usersStore()
const { get: getOrderBy } = useOrderBy() const { get: getOrderBy } = useOrderBy()
const { getArgs, storage } = useFilter() const { getArgs, storage } = useFilter()

View File

@ -39,43 +39,9 @@
</template> </template>
</LayoutHeader> </LayoutHeader>
<div v-if="lead?.data" class="flex h-full overflow-hidden"> <div v-if="lead?.data" class="flex h-full overflow-hidden">
<TabGroup as="div" class="flex flex-1 flex-col" @change="onTabChange"> <Tabs v-model="tabIndex" v-slot="{ tab }" :tabs="tabs">
<TabList class="relative flex items-center gap-6 border-b pl-5"> <Activities :title="tab.label" v-model:reload="reload" v-model="lead" />
<Tab </Tabs>
ref="tabRef"
as="template"
v-for="tab in tabs"
:key="tab.label"
v-slot="{ selected }"
>
<button
class="-mb-[1px] flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 transition-all duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
:class="{ 'text-gray-900': selected }"
>
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
{{ tab.label }}
</button>
</Tab>
<div
ref="indicator"
class="absolute -bottom-[1px] h-[1px] w-[82px] bg-gray-900"
:style="{ left: `${indicatorLeftValue}px` }"
/>
</TabList>
<TabPanels class="flex flex-1 overflow-hidden">
<TabPanel
class="flex flex-1 flex-col overflow-y-auto"
v-for="tab in tabs"
:key="tab.label"
>
<Activities
:title="tab.label"
v-model:reload="reload"
v-model="lead"
/>
</TabPanel>
</TabPanels>
</TabGroup>
<div class="flex w-[352px] flex-col justify-between border-l"> <div class="flex w-[352px] flex-col justify-between border-l">
<div <div
class="flex h-[41px] items-center border-b px-5 py-2.5 text-lg font-semibold" class="flex h-[41px] items-center border-b px-5 py-2.5 text-lg font-semibold"
@ -321,15 +287,13 @@ import LinkIcon from '@/components/Icons/LinkIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Toggler from '@/components/Toggler.vue' import Toggler from '@/components/Toggler.vue'
import Activities from '@/components/Activities.vue' import Activities from '@/components/Activities.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
import { TransitionPresets, useTransition } from '@vueuse/core'
import { import {
leadStatuses, leadStatuses,
statusDropdownOptions, statusDropdownOptions,
openWebsite, openWebsite,
createToast, createToast,
activeAgents,
} from '@/utils' } from '@/utils'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { contactsStore } from '@/stores/contacts' import { contactsStore } from '@/stores/contacts'
@ -343,12 +307,13 @@ import {
Dropdown, Dropdown,
Tooltip, Tooltip,
Avatar, Avatar,
Tabs,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CameraIcon from '../components/Icons/CameraIcon.vue' import CameraIcon from '../components/Icons/CameraIcon.vue'
const { getUser, users } = usersStore() const { getUser, users } = usersStore()
const { contacts } = contactsStore() const { contacts } = contactsStore()
const router = useRouter() const router = useRouter()
@ -412,6 +377,7 @@ const breadcrumbs = computed(() => {
return items return items
}) })
const tabIndex = ref(0)
const tabs = [ const tabs = [
{ {
label: 'Activity', label: 'Activity',
@ -447,21 +413,6 @@ function validateFile(file) {
} }
} }
const tabRef = ref([])
const indicator = ref(null)
let indicatorLeft = ref(20)
const indicatorLeftValue = useTransition(indicatorLeft, {
duration: 250,
ease: TransitionPresets.easeOutCubic,
})
function onTabChange(index) {
const selectedTab = tabRef.value[index].el
indicator.value.style.width = `${selectedTab.offsetWidth}px`
indicatorLeft.value = selectedTab.offsetLeft
}
const detailSections = computed(() => { const detailSections = computed(() => {
return [ return [
{ {
@ -566,20 +517,6 @@ const detailSections = computed(() => {
] ]
}) })
const activeAgents = computed(() => {
const nonAgents = ['Administrator', 'admin@example.com', 'Guest']
return users.data
.filter((user) => !nonAgents.includes(user.name))
.sort((a, b) => a.full_name - b.full_name)
.map((user) => {
return {
label: user.full_name,
value: user.email,
...user,
}
})
})
function convertToDeal() { function convertToDeal() {
lead.data.status = 'Qualified' lead.data.status = 'Qualified'
lead.data.is_deal = 1 lead.data.is_deal = 1

View File

@ -1,7 +1,7 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: list.title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<Button variant="solid" label="Create" @click="showNewDialog = true"> <Button variant="solid" label="Create" @click="showNewDialog = true">
@ -9,7 +9,7 @@
</Button> </Button>
</template> </template>
</LayoutHeader> </LayoutHeader>
<div class="flex justify-between items-center px-5 pt-3 pb-4"> <div class="flex items-center justify-between px-5 pb-4 pt-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Dropdown :options="viewsDropdownOptions"> <Dropdown :options="viewsDropdownOptions">
<template #default="{ open }"> <template #default="{ open }">
@ -55,7 +55,6 @@
<script setup> <script setup>
import ListView from '@/components/ListView.vue' import ListView from '@/components/ListView.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import NewLead from '@/components/NewLead.vue' import NewLead from '@/components/NewLead.vue'
import SortBy from '@/components/SortBy.vue' import SortBy from '@/components/SortBy.vue'
import Filter from '@/components/Filter.vue' import Filter from '@/components/Filter.vue'
@ -71,6 +70,7 @@ import {
Dropdown, Dropdown,
createListResource, createListResource,
createResource, createResource,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ref, computed, reactive, watch } from 'vue' import { ref, computed, reactive, watch } from 'vue'
@ -80,6 +80,9 @@ const list = {
plural_label: 'Leads', plural_label: 'Leads',
singular_label: 'Lead', singular_label: 'Lead',
} }
const breadcrumbs = [{ label: list.title, route: { name: 'Leads' } }]
const { getUser } = usersStore() const { getUser } = usersStore()
const { get: getOrderBy } = useOrderBy() const { get: getOrderBy } = useOrderBy()
const { getArgs, storage } = useFilter() const { getArgs, storage } = useFilter()

View File

@ -1,7 +1,7 @@
<template> <template>
<LayoutHeader> <LayoutHeader>
<template #left-header> <template #left-header>
<Breadcrumbs :items="[{ label: list.title }]" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<Button variant="solid" label="Create" @click="createNote"> <Button variant="solid" label="Create" @click="createNote">
@ -79,7 +79,6 @@
<script setup> <script setup>
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import NoteIcon from '@/components/Icons/NoteIcon.vue' import NoteIcon from '@/components/Icons/NoteIcon.vue'
import NoteModal from '@/components/NoteModal.vue' import NoteModal from '@/components/NoteModal.vue'
@ -92,6 +91,7 @@ import {
call, call,
Dropdown, Dropdown,
Tooltip, Tooltip,
Breadcrumbs,
} from 'frappe-ui' } from 'frappe-ui'
import { ref } from 'vue' import { ref } from 'vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
@ -104,6 +104,8 @@ const list = {
singular_label: 'Note', singular_label: 'Note',
} }
const breadcrumbs = [{ label: list.title, route: { name: 'Notes' } }]
const showNoteModal = ref(false) const showNoteModal = ref(false)
const currentNote = ref(null) const currentNote = ref(null)

View File

@ -157,7 +157,7 @@ export function startCase(str) {
const { users } = usersStore() const { users } = usersStore()
export const activeAgents = computed(() => { export const activeAgents = computed(() => {
const nonAgents = ['Administrator', 'Guest'] const nonAgents = ['Administrator', 'admin@example.com', 'Guest']
return users.data return users.data
.filter((user) => !nonAgents.includes(user.name)) .filter((user) => !nonAgents.includes(user.name))
.sort((a, b) => a.full_name - b.full_name) .sort((a, b) => a.full_name - b.full_name)

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"workspaces": ["frappe-ui", "frontend"], "aworkspaces": ["frappe-ui", "frontend"],
"scripts": { "scripts": {
"postinstall": "cd frontend && yarn install", "postinstall": "cd frontend && yarn install",
"dev": "cd frontend && yarn dev", "dev": "cd frontend && yarn dev",

8314
yarn.lock

File diff suppressed because it is too large Load Diff