fix: updated organization page design based on new design
This commit is contained in:
parent
9461e0185b
commit
db1e6ed7f4
@ -8,169 +8,130 @@
|
|||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</template>
|
</template>
|
||||||
</LayoutHeader>
|
</LayoutHeader>
|
||||||
<div v-if="organization.doc" class="flex flex-1 flex-col overflow-hidden">
|
<div ref="parentRef" class="flex h-full">
|
||||||
<FileUploader
|
<Resizer
|
||||||
@success="changeOrganizationImage"
|
v-if="organization.doc"
|
||||||
:validateFile="validateFile"
|
:parent="$refs.parentRef"
|
||||||
|
class="flex h-full flex-col overflow-hidden border-r"
|
||||||
>
|
>
|
||||||
<template #default="{ openFileSelector, error }">
|
<div class="border-b">
|
||||||
<div class="flex items-start justify-start gap-6 p-5 sm:items-center">
|
<FileUploader
|
||||||
<div class="group relative h-24 w-24">
|
@success="changeOrganizationImage"
|
||||||
<Avatar
|
:validateFile="validateFile"
|
||||||
size="3xl"
|
>
|
||||||
:image="organization.doc.organization_logo"
|
<template #default="{ openFileSelector, error }">
|
||||||
:label="organization.doc.name"
|
<div class="flex flex-col items-start justify-start gap-4 p-5">
|
||||||
class="!h-24 !w-24"
|
<div class="flex gap-4 items-center">
|
||||||
/>
|
<div class="group relative h-15.5 w-15.5">
|
||||||
<component
|
<Avatar
|
||||||
:is="organization.doc.organization_logo ? Dropdown : 'div'"
|
size="3xl"
|
||||||
v-bind="
|
class="h-15.5 w-15.5"
|
||||||
organization.doc.organization_logo
|
:label="organization.doc.organization_name"
|
||||||
? {
|
:image="organization.doc.organization_logo"
|
||||||
options: [
|
/>
|
||||||
{
|
<component
|
||||||
icon: 'upload',
|
:is="organization.doc.image ? Dropdown : 'div'"
|
||||||
label: organization.doc.organization_logo
|
v-bind="
|
||||||
? __('Change image')
|
organization.doc.image
|
||||||
: __('Upload image'),
|
? {
|
||||||
onClick: openFileSelector,
|
options: [
|
||||||
},
|
{
|
||||||
{
|
icon: 'upload',
|
||||||
icon: 'trash-2',
|
label: organization.doc.image
|
||||||
label: __('Remove image'),
|
? __('Change image')
|
||||||
onClick: () => changeOrganizationImage(''),
|
: __('Upload image'),
|
||||||
},
|
onClick: openFileSelector,
|
||||||
],
|
},
|
||||||
}
|
{
|
||||||
: { onClick: openFileSelector }
|
icon: 'trash-2',
|
||||||
"
|
label: __('Remove image'),
|
||||||
class="!absolute bottom-0 left-0 right-0"
|
onClick: () => changeOrganizationImage(''),
|
||||||
>
|
},
|
||||||
<div
|
],
|
||||||
class="z-1 absolute bottom-0 left-0 right-0 flex h-13 cursor-pointer items-center justify-center rounded-b-full bg-black bg-opacity-40 pt-3 opacity-0 duration-300 ease-in-out group-hover:opacity-100"
|
}
|
||||||
style="
|
: { onClick: openFileSelector }
|
||||||
-webkit-clip-path: inset(12px 0 0 0);
|
"
|
||||||
clip-path: inset(12px 0 0 0);
|
class="!absolute bottom-0 left-0 right-0"
|
||||||
"
|
>
|
||||||
>
|
<div
|
||||||
<CameraIcon class="h-6 w-6 cursor-pointer text-white" />
|
class="z-1 absolute bottom-0 left-0 right-0 flex h-14 cursor-pointer items-center justify-center rounded-b-full bg-black bg-opacity-40 pt-5 opacity-0 duration-300 ease-in-out group-hover:opacity-100"
|
||||||
|
style="
|
||||||
|
-webkit-clip-path: inset(22px 0 0 0);
|
||||||
|
clip-path: inset(22px 0 0 0);
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<CameraIcon class="h-6 w-6 cursor-pointer text-white" />
|
||||||
|
</div>
|
||||||
|
</component>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 truncate">
|
||||||
|
<div class="truncate text-2xl font-medium">
|
||||||
|
<span>{{ organization.doc.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="organization.doc.website"
|
||||||
|
class="flex items-center gap-1.5 text-base text-gray-800"
|
||||||
|
>
|
||||||
|
<WebsiteIcon class="size-4" />
|
||||||
|
<span>{{ website(organization.doc.website) }}</span>
|
||||||
|
</div>
|
||||||
|
<ErrorMessage :message="__(error)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-1.5">
|
||||||
|
<Button
|
||||||
|
:label="__('Delete')"
|
||||||
|
theme="red"
|
||||||
|
size="sm"
|
||||||
|
@click="deleteOrganization"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FeatherIcon name="trash-2" class="h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
<Button size="sm" @click="openWebsite">
|
||||||
|
<FeatherIcon name="link" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</component>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col justify-center gap-2 sm:gap-0.5">
|
|
||||||
<div class="text-3xl font-semibold text-gray-900">
|
|
||||||
{{ organization.doc.name }}
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
</template>
|
||||||
class="flex flex-col flex-wrap gap-3 text-base text-gray-700 sm:flex-row sm:items-center sm:gap-2"
|
</FileUploader>
|
||||||
>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="organization.doc.website"
|
v-if="fieldsLayout.data"
|
||||||
class="flex items-center gap-1.5"
|
class="flex flex-1 flex-col justify-between overflow-hidden"
|
||||||
>
|
>
|
||||||
<WebsiteIcon class="h-4 w-4" />
|
<div class="flex flex-col overflow-y-auto">
|
||||||
<span class="">{{ website(organization.doc.website) }}</span>
|
<div
|
||||||
</div>
|
v-for="(section, i) in fieldsLayout.data"
|
||||||
<span
|
:key="section.label"
|
||||||
v-if="organization.doc.website"
|
class="flex flex-col p-3"
|
||||||
class="hidden text-3xl leading-[0] text-gray-600 sm:flex"
|
:class="{ 'border-b': i !== fieldsLayout.data.length - 1 }"
|
||||||
>
|
>
|
||||||
·
|
<Section :is-opened="section.opened" :label="section.label">
|
||||||
</span>
|
<template #actions>
|
||||||
<div
|
<Button
|
||||||
v-if="organization.doc.industry"
|
v-if="i == 0 && isManager()"
|
||||||
class="flex items-center gap-1.5"
|
variant="ghost"
|
||||||
>
|
class="w-7"
|
||||||
<FeatherIcon name="briefcase" class="h-4 w-4" />
|
@click="showSidePanelModal = true"
|
||||||
<span class="">{{ organization.doc.industry }}</span>
|
>
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="organization.doc.industry"
|
|
||||||
class="hidden text-3xl leading-[0] text-gray-600 sm:flex"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
v-if="organization.doc.territory"
|
|
||||||
class="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
<TerritoryIcon class="h-4 w-4" />
|
|
||||||
<span class="">{{ organization.doc.territory }}</span>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="organization.doc.territory"
|
|
||||||
class="hidden text-3xl leading-[0] text-gray-600 sm:flex"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
v-if="organization.doc.annual_revenue"
|
|
||||||
class="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
<MoneyIcon class="size-4" />
|
|
||||||
<span class="">{{
|
|
||||||
formatNumberIntoCurrency(
|
|
||||||
organization.doc.annual_revenue,
|
|
||||||
organization.doc.currency,
|
|
||||||
)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="organization.doc.annual_revenue"
|
|
||||||
class="hidden text-3xl leading-[0] text-gray-600 sm:flex"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
v-if="
|
|
||||||
organization.doc.website ||
|
|
||||||
organization.doc.industry ||
|
|
||||||
organization.doc.territory ||
|
|
||||||
organization.doc.annual_revenue
|
|
||||||
"
|
|
||||||
variant="ghost"
|
|
||||||
:label="__('More')"
|
|
||||||
class="w-fit cursor-pointer hover:text-gray-900 sm:-ml-1"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
detailMode = true
|
|
||||||
showOrganizationModal = true
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="mt-2 flex gap-1.5">
|
|
||||||
<Button
|
|
||||||
:label="__('Edit')"
|
|
||||||
size="sm"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
detailMode = false
|
|
||||||
showOrganizationModal = true
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<EditIcon class="h-4 w-4" />
|
<EditIcon class="h-4 w-4" />
|
||||||
</template>
|
</Button>
|
||||||
</Button>
|
</template>
|
||||||
<Button
|
<SectionFields
|
||||||
:label="__('Delete')"
|
v-if="section.fields"
|
||||||
theme="red"
|
:fields="section.fields"
|
||||||
size="sm"
|
:isLastSection="i == fieldsLayout.data.length - 1"
|
||||||
@click="deleteOrganization"
|
v-model="organization.doc"
|
||||||
>
|
@update="updateField"
|
||||||
<template #prefix>
|
/>
|
||||||
<FeatherIcon name="trash-2" class="h-4 w-4" />
|
</Section>
|
||||||
</template>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<ErrorMessage class="mt-2" :message="__(error)" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</FileUploader>
|
</Resizer>
|
||||||
<Tabs v-model="tabIndex" :tabs="tabs">
|
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
||||||
<template #tab="{ tab, selected }">
|
<template #tab="{ tab, selected }">
|
||||||
<button
|
<button
|
||||||
class="group flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
class="group flex items-center gap-2 border-b border-transparent py-2.5 text-base text-gray-600 duration-300 ease-in-out hover:border-gray-400 hover:text-gray-900"
|
||||||
@ -216,29 +177,32 @@
|
|||||||
</template>
|
</template>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<OrganizationModal
|
<SidePanelModal
|
||||||
v-model="showOrganizationModal"
|
v-if="showSidePanelModal"
|
||||||
v-model:quickEntry="showQuickEntryModal"
|
v-model="showSidePanelModal"
|
||||||
v-model:organization="organization"
|
doctype="CRM Organization"
|
||||||
:options="{ detailMode }"
|
@reload="() => fieldsLayout.reload()"
|
||||||
/>
|
/>
|
||||||
<QuickEntryModal
|
<QuickEntryModal
|
||||||
v-if="showQuickEntryModal"
|
v-if="showQuickEntryModal"
|
||||||
v-model="showQuickEntryModal"
|
v-model="showQuickEntryModal"
|
||||||
doctype="CRM Organization"
|
doctype="CRM Organization"
|
||||||
/>
|
/>
|
||||||
|
<AddressModal v-model="showAddressModal" v-model:address="_address" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import Resizer from '@/components/Resizer.vue'
|
||||||
|
import Section from '@/components/Section.vue'
|
||||||
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
|
import SidePanelModal from '@/components/Settings/SidePanelModal.vue'
|
||||||
import Icon from '@/components/Icon.vue'
|
import Icon from '@/components/Icon.vue'
|
||||||
import LayoutHeader from '@/components/LayoutHeader.vue'
|
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
|
||||||
import QuickEntryModal from '@/components/Modals/QuickEntryModal.vue'
|
import QuickEntryModal from '@/components/Modals/QuickEntryModal.vue'
|
||||||
|
import AddressModal from '@/components/Modals/AddressModal.vue'
|
||||||
import DealsListView from '@/components/ListViews/DealsListView.vue'
|
import DealsListView from '@/components/ListViews/DealsListView.vue'
|
||||||
import ContactsListView from '@/components/ListViews/ContactsListView.vue'
|
import ContactsListView from '@/components/ListViews/ContactsListView.vue'
|
||||||
import WebsiteIcon from '@/components/Icons/WebsiteIcon.vue'
|
import WebsiteIcon from '@/components/Icons/WebsiteIcon.vue'
|
||||||
import TerritoryIcon from '@/components/Icons/TerritoryIcon.vue'
|
|
||||||
import MoneyIcon from '@/components/Icons/MoneyIcon.vue'
|
|
||||||
import EditIcon from '@/components/Icons/EditIcon.vue'
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
||||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||||
@ -252,6 +216,7 @@ import {
|
|||||||
dateTooltipFormat,
|
dateTooltipFormat,
|
||||||
timeAgo,
|
timeAgo,
|
||||||
formatNumberIntoCurrency,
|
formatNumberIntoCurrency,
|
||||||
|
createToast,
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
import {
|
import {
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
@ -263,6 +228,7 @@ import {
|
|||||||
createListResource,
|
createListResource,
|
||||||
createDocumentResource,
|
createDocumentResource,
|
||||||
usePageMeta,
|
usePageMeta,
|
||||||
|
createResource,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { h, computed, ref } from 'vue'
|
import { h, computed, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
@ -274,11 +240,11 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { getUser, isManager } = usersStore()
|
||||||
const { $dialog } = globalStore()
|
const { $dialog } = globalStore()
|
||||||
const { getDealStatus } = statusesStore()
|
const { getDealStatus } = statusesStore()
|
||||||
const showOrganizationModal = ref(false)
|
const showSidePanelModal = ref(false)
|
||||||
const showQuickEntryModal = ref(false)
|
const showQuickEntryModal = ref(false)
|
||||||
const detailMode = ref(false)
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -291,6 +257,17 @@ const organization = createDocumentResource({
|
|||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function updateField(fieldname, value) {
|
||||||
|
await organization.setValue.submit({
|
||||||
|
[fieldname]: value,
|
||||||
|
})
|
||||||
|
createToast({
|
||||||
|
title: __('Organization updated'),
|
||||||
|
icon: 'check',
|
||||||
|
iconClasses: 'text-green-600',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
||||||
|
|
||||||
@ -372,6 +349,60 @@ function website(url) {
|
|||||||
return url && url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '')
|
return url && url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openWebsite() {
|
||||||
|
if (!organization.doc.website)
|
||||||
|
createToast({
|
||||||
|
title: __('Website not found'),
|
||||||
|
icon: 'x',
|
||||||
|
iconClasses: 'text-red-600',
|
||||||
|
})
|
||||||
|
else window.open(organization.doc.website, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
const showAddressModal = ref(false)
|
||||||
|
const _organization = ref({})
|
||||||
|
const _address = ref({})
|
||||||
|
|
||||||
|
const fieldsLayout = createResource({
|
||||||
|
url: 'crm.api.doc.get_sidebar_fields',
|
||||||
|
cache: ['fieldsLayout', props.organizationId],
|
||||||
|
params: { doctype: 'CRM Organization', name: props.organizationId },
|
||||||
|
auto: true,
|
||||||
|
transform: (data) => getParsedFields(data),
|
||||||
|
})
|
||||||
|
|
||||||
|
function getParsedFields(data) {
|
||||||
|
return data.map((section) => {
|
||||||
|
return {
|
||||||
|
...section,
|
||||||
|
fields: computed(() =>
|
||||||
|
section.fields.map((field) => {
|
||||||
|
if (field.name === 'address') {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
create: (value, close) => {
|
||||||
|
_organization.value.address = value
|
||||||
|
_address.value = {}
|
||||||
|
showAddressModal.value = true
|
||||||
|
close()
|
||||||
|
},
|
||||||
|
edit: async (addr) => {
|
||||||
|
_address.value = await call('frappe.client.get', {
|
||||||
|
doctype: 'Address',
|
||||||
|
name: addr,
|
||||||
|
})
|
||||||
|
showAddressModal.value = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const tabIndex = ref(0)
|
const tabIndex = ref(0)
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
@ -386,8 +417,6 @@ const tabs = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
|
||||||
|
|
||||||
const deals = createListResource({
|
const deals = createListResource({
|
||||||
type: 'list',
|
type: 'list',
|
||||||
doctype: 'CRM Deal',
|
doctype: 'CRM Deal',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user