refactor: organization modal
This commit is contained in:
parent
d6f8106cfb
commit
7b8a15d224
@ -115,9 +115,9 @@
|
|||||||
v-for="action in dialogOptions.actions"
|
v-for="action in dialogOptions.actions"
|
||||||
:key="action.label"
|
:key="action.label"
|
||||||
v-bind="action"
|
v-bind="action"
|
||||||
>
|
:label="action.label"
|
||||||
{{ action.label }}
|
:loading="loading"
|
||||||
</Button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -130,16 +130,11 @@ import WebsiteIcon from '@/components/Icons/WebsiteIcon.vue'
|
|||||||
import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
|
import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
|
||||||
import TerritoryIcon from '@/components/Icons/TerritoryIcon.vue'
|
import TerritoryIcon from '@/components/Icons/TerritoryIcon.vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
|
||||||
import { call, FeatherIcon } from 'frappe-ui'
|
import { call, FeatherIcon } from 'frappe-ui'
|
||||||
import { ref, nextTick, watch, computed, h } from 'vue'
|
import { ref, nextTick, watch, computed, h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
organization: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
},
|
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {
|
default: {
|
||||||
@ -152,8 +147,9 @@ const props = defineProps({
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const { organizations } = organizationsStore()
|
const organization = defineModel('organization')
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
const title = ref(null)
|
const title = ref(null)
|
||||||
const detailMode = ref(false)
|
const detailMode = ref(false)
|
||||||
const editMode = ref(false)
|
const editMode = ref(false)
|
||||||
@ -165,13 +161,15 @@ let _organization = ref({
|
|||||||
industry: '',
|
industry: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let doc = ref({})
|
||||||
|
|
||||||
async function updateOrganization() {
|
async function updateOrganization() {
|
||||||
const old = { ...props.organization }
|
const old = { ...doc.value }
|
||||||
const newOrg = { ..._organization.value }
|
const newOrg = { ..._organization.value }
|
||||||
|
|
||||||
const nameChanged = old.name !== newOrg.name
|
const nameChanged = old.organization_name !== newOrg.organization_name
|
||||||
delete old.name
|
delete old.organization_name
|
||||||
delete newOrg.name
|
delete newOrg.organization_name
|
||||||
|
|
||||||
const otherFieldChanged = JSON.stringify(old) !== JSON.stringify(newOrg)
|
const otherFieldChanged = JSON.stringify(old) !== JSON.stringify(newOrg)
|
||||||
const values = newOrg
|
const values = newOrg
|
||||||
@ -182,21 +180,23 @@ async function updateOrganization() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let name
|
let name
|
||||||
|
loading.value = true
|
||||||
if (nameChanged) {
|
if (nameChanged) {
|
||||||
name = await callRenameDoc()
|
name = await callRenameDoc()
|
||||||
}
|
}
|
||||||
if (otherFieldChanged) {
|
if (otherFieldChanged) {
|
||||||
name = await callSetValue(values)
|
name = await callSetValue(values)
|
||||||
}
|
}
|
||||||
handleOrganizationUpdate({ name })
|
handleOrganizationUpdate({ name }, nameChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function callRenameDoc() {
|
async function callRenameDoc() {
|
||||||
const d = await call('frappe.client.rename_doc', {
|
const d = await call('frappe.client.rename_doc', {
|
||||||
doctype: 'CRM Organization',
|
doctype: 'CRM Organization',
|
||||||
old_name: props.organization.name,
|
old_name: doc.value?.organization_name,
|
||||||
new_name: _organization.value.name,
|
new_name: _organization.value.organization_name,
|
||||||
})
|
})
|
||||||
|
loading.value = false
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +206,7 @@ async function callSetValue(values) {
|
|||||||
name: _organization.value.name,
|
name: _organization.value.name,
|
||||||
fieldname: values,
|
fieldname: values,
|
||||||
})
|
})
|
||||||
|
loading.value = false
|
||||||
return d.name
|
return d.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,16 +217,18 @@ async function callInsertDoc() {
|
|||||||
..._organization.value,
|
..._organization.value,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
loading.value = false
|
||||||
doc.name && handleOrganizationUpdate(doc)
|
doc.name && handleOrganizationUpdate(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOrganizationUpdate(doc) {
|
function handleOrganizationUpdate(doc, renamed = false) {
|
||||||
organizations.reload()
|
if (doc.name && (props.options.redirect || renamed)) {
|
||||||
if (doc.name && props.options.redirect) {
|
|
||||||
router.push({
|
router.push({
|
||||||
name: 'Organization',
|
name: 'Organization',
|
||||||
params: { organizationId: doc.name },
|
params: { organizationId: doc.name },
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
organization.value.reload?.()
|
||||||
}
|
}
|
||||||
show.value = false
|
show.value = false
|
||||||
props.options.afterInsert && props.options.afterInsert(doc)
|
props.options.afterInsert && props.options.afterInsert(doc)
|
||||||
@ -296,7 +299,8 @@ watch(
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// TODO: Issue with FormControl
|
// TODO: Issue with FormControl
|
||||||
// title.value.el.focus()
|
// title.value.el.focus()
|
||||||
_organization.value = { ...props.organization }
|
doc.value = organization.value?.doc || organization.value || {}
|
||||||
|
_organization.value = { ...doc.value }
|
||||||
if (_organization.value.name) {
|
if (_organization.value.name) {
|
||||||
editMode.value = true
|
editMode.value = true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,10 +61,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<OrganizationModal
|
<OrganizationModal
|
||||||
v-model="showOrganizationModal"
|
v-model="showOrganizationModal"
|
||||||
:organization="_organization"
|
v-model:organization="_organization"
|
||||||
:options="{
|
:options="{
|
||||||
redirect: false,
|
redirect: false,
|
||||||
afterInsert: (doc) => (newLead.organization = doc.name),
|
afterInsert: (doc) => (newDeal.organization = doc.name),
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -59,20 +59,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<OrganizationModal
|
|
||||||
v-model="showOrganizationModal"
|
|
||||||
:organization="_organization"
|
|
||||||
:options="{
|
|
||||||
redirect: false,
|
|
||||||
afterInsert: (doc) => (newLead.organization = doc.name),
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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 OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
@ -89,9 +80,6 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const showOrganizationModal = ref(false)
|
|
||||||
const _organization = ref({})
|
|
||||||
|
|
||||||
const allFields = computed(() => {
|
const allFields = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -260,7 +260,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<OrganizationModal
|
<OrganizationModal
|
||||||
v-model="showOrganizationModal"
|
v-model="showOrganizationModal"
|
||||||
:organization="_organization"
|
v-model:organization="_organization"
|
||||||
:options="{
|
:options="{
|
||||||
redirect: false,
|
redirect: false,
|
||||||
afterInsert: (doc) =>
|
afterInsert: (doc) =>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<LayoutHeader v-if="organization">
|
<LayoutHeader v-if="organization.doc">
|
||||||
<template #left-header>
|
<template #left-header>
|
||||||
<Breadcrumbs :items="breadcrumbs" />
|
<Breadcrumbs :items="breadcrumbs" />
|
||||||
</template>
|
</template>
|
||||||
</LayoutHeader>
|
</LayoutHeader>
|
||||||
<div v-if="organization" class="flex flex-1 flex-col overflow-hidden">
|
<div v-if="organization.doc" class="flex flex-1 flex-col overflow-hidden">
|
||||||
<FileUploader
|
<FileUploader
|
||||||
@success="changeOrganizationImage"
|
@success="changeOrganizationImage"
|
||||||
:validateFile="validateFile"
|
:validateFile="validateFile"
|
||||||
@ -14,19 +14,19 @@
|
|||||||
<div class="group relative h-24 w-24">
|
<div class="group relative h-24 w-24">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="3xl"
|
size="3xl"
|
||||||
:image="organization.organization_logo"
|
:image="organization.doc.organization_logo"
|
||||||
:label="organization.name"
|
:label="organization.doc.name"
|
||||||
class="!h-24 !w-24"
|
class="!h-24 !w-24"
|
||||||
/>
|
/>
|
||||||
<component
|
<component
|
||||||
:is="organization.organization_logo ? Dropdown : 'div'"
|
:is="organization.doc.organization_logo ? Dropdown : 'div'"
|
||||||
v-bind="
|
v-bind="
|
||||||
organization.organization_logo
|
organization.doc.organization_logo
|
||||||
? {
|
? {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
icon: 'upload',
|
icon: 'upload',
|
||||||
label: organization.organization_logo
|
label: organization.doc.organization_logo
|
||||||
? 'Change image'
|
? 'Change image'
|
||||||
: 'Upload image',
|
: 'Upload image',
|
||||||
onClick: openFileSelector,
|
onClick: openFileSelector,
|
||||||
@ -55,67 +55,67 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-center gap-0.5">
|
<div class="flex flex-col justify-center gap-0.5">
|
||||||
<div class="text-3xl font-semibold text-gray-900">
|
<div class="text-3xl font-semibold text-gray-900">
|
||||||
{{ organization.name }}
|
{{ organization.doc.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 text-base text-gray-700">
|
<div class="flex items-center gap-2 text-base text-gray-700">
|
||||||
<div
|
<div
|
||||||
v-if="organization.website"
|
v-if="organization.doc.website"
|
||||||
class="flex items-center gap-1.5"
|
class="flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<WebsiteIcon class="h-4 w-4" />
|
<WebsiteIcon class="h-4 w-4" />
|
||||||
<span class="">{{ website(organization.website) }}</span>
|
<span class="">{{ website(organization.doc.website) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="organization.website"
|
v-if="organization.doc.website"
|
||||||
class="text-3xl leading-[0] text-gray-600"
|
class="text-3xl leading-[0] text-gray-600"
|
||||||
>
|
>
|
||||||
·
|
·
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
v-if="organization.industry"
|
v-if="organization.doc.industry"
|
||||||
class="flex items-center gap-1.5"
|
class="flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<FeatherIcon name="briefcase" class="h-4 w-4" />
|
<FeatherIcon name="briefcase" class="h-4 w-4" />
|
||||||
<span class="">{{ organization.industry }}</span>
|
<span class="">{{ organization.doc.industry }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="organization.industry"
|
v-if="organization.doc.industry"
|
||||||
class="text-3xl leading-[0] text-gray-600"
|
class="text-3xl leading-[0] text-gray-600"
|
||||||
>
|
>
|
||||||
·
|
·
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
v-if="organization.territory"
|
v-if="organization.doc.territory"
|
||||||
class="flex items-center gap-1.5"
|
class="flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<TerritoryIcon class="h-4 w-4" />
|
<TerritoryIcon class="h-4 w-4" />
|
||||||
<span class="">{{ organization.territory }}</span>
|
<span class="">{{ organization.doc.territory }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="organization.territory"
|
v-if="organization.doc.territory"
|
||||||
class="text-3xl leading-[0] text-gray-600"
|
class="text-3xl leading-[0] text-gray-600"
|
||||||
>
|
>
|
||||||
·
|
·
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
v-if="organization.annual_revenue"
|
v-if="organization.doc.annual_revenue"
|
||||||
class="flex items-center gap-1.5"
|
class="flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<FeatherIcon name="dollar-sign" class="h-4 w-4" />
|
<FeatherIcon name="dollar-sign" class="h-4 w-4" />
|
||||||
<span class="">{{ organization.annual_revenue }}</span>
|
<span class="">{{ organization.doc.annual_revenue }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="organization.annual_revenue"
|
v-if="organization.doc.annual_revenue"
|
||||||
class="text-3xl leading-[0] text-gray-600"
|
class="text-3xl leading-[0] text-gray-600"
|
||||||
>
|
>
|
||||||
·
|
·
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
v-if="
|
v-if="
|
||||||
organization.website ||
|
organization.doc.website ||
|
||||||
organization.industry ||
|
organization.doc.industry ||
|
||||||
organization.territory ||
|
organization.doc.territory ||
|
||||||
organization.annual_revenue
|
organization.doc.annual_revenue
|
||||||
"
|
"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
label="More"
|
label="More"
|
||||||
@ -191,7 +191,7 @@
|
|||||||
v-if="tab.label === 'Contacts' && rows.length"
|
v-if="tab.label === 'Contacts' && rows.length"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:options="{ selectable: false, showTooltip: false, }"
|
:options="{ selectable: false, showTooltip: false }"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="!rows.length"
|
v-if="!rows.length"
|
||||||
@ -207,7 +207,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<OrganizationModal
|
<OrganizationModal
|
||||||
v-model="showOrganizationModal"
|
v-model="showOrganizationModal"
|
||||||
:organization="organization"
|
v-model:organization="organization"
|
||||||
:options="{ detailMode }"
|
:options="{ detailMode }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -221,6 +221,7 @@ import {
|
|||||||
Tabs,
|
Tabs,
|
||||||
call,
|
call,
|
||||||
createListResource,
|
createListResource,
|
||||||
|
createDocumentResource,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import LayoutHeader from '@/components/LayoutHeader.vue'
|
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
||||||
@ -234,7 +235,6 @@ import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
|||||||
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { organizationsStore } from '@/stores/organizations.js'
|
|
||||||
import { statusesStore } from '@/stores/statuses'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import {
|
import {
|
||||||
dateFormat,
|
dateFormat,
|
||||||
@ -253,14 +253,19 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { $dialog } = globalStore()
|
const { $dialog } = globalStore()
|
||||||
const { organizations, getOrganization } = organizationsStore()
|
|
||||||
const { getDealStatus } = statusesStore()
|
const { getDealStatus } = statusesStore()
|
||||||
const showOrganizationModal = ref(false)
|
const showOrganizationModal = ref(false)
|
||||||
const detailMode = ref(false)
|
const detailMode = ref(false)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const organization = computed(() => getOrganization(props.organizationId))
|
const organization = createDocumentResource({
|
||||||
|
doctype: 'CRM Organization',
|
||||||
|
name: props.organizationId,
|
||||||
|
cache: ['organization', props.organizationId],
|
||||||
|
fields: ['*'],
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
let items = [{ label: 'Organizations', route: { name: 'Organizations' } }]
|
let items = [{ label: 'Organizations', route: { name: 'Organizations' } }]
|
||||||
@ -288,7 +293,7 @@ async function changeOrganizationImage(file) {
|
|||||||
fieldname: 'organization_logo',
|
fieldname: 'organization_logo',
|
||||||
value: file?.file_url || '',
|
value: file?.file_url || '',
|
||||||
})
|
})
|
||||||
organizations.reload()
|
organization.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteOrganization() {
|
async function deleteOrganization() {
|
||||||
|
|||||||
@ -52,7 +52,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<OrganizationModal v-model="showOrganizationModal" :organization="{}" />
|
<OrganizationModal v-model="showOrganizationModal" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
|
import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user