fix: send tabs instead of sections to FieldLayout component and render Tabs if exists
This commit is contained in:
parent
851449bbc3
commit
38565a14f6
@ -2,6 +2,7 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
@ -10,19 +11,26 @@ from frappe.model.document import Document
|
|||||||
class CRMFieldsLayout(Document):
|
class CRMFieldsLayout(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_fields_layout(doctype: str, type: str):
|
def get_fields_layout(doctype: str, type: str):
|
||||||
sections = []
|
tabs = []
|
||||||
if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": type}):
|
if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": type}):
|
||||||
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": type})
|
layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": type})
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if layout.layout:
|
if layout.layout:
|
||||||
sections = json.loads(layout.layout)
|
tabs = json.loads(layout.layout)
|
||||||
|
|
||||||
|
has_tabs = tabs[0].get("sections") if tabs and tabs[0] else False
|
||||||
|
|
||||||
|
if not has_tabs:
|
||||||
|
tabs = [{"no_tabs": True, "sections": tabs}]
|
||||||
|
|
||||||
allowed_fields = []
|
allowed_fields = []
|
||||||
for section in sections:
|
for tab in tabs:
|
||||||
|
for section in tab.get("sections"):
|
||||||
if not section.get("fields"):
|
if not section.get("fields"):
|
||||||
continue
|
continue
|
||||||
allowed_fields.extend(section.get("fields"))
|
allowed_fields.extend(section.get("fields"))
|
||||||
@ -30,7 +38,8 @@ def get_fields_layout(doctype: str, type: str):
|
|||||||
fields = frappe.get_meta(doctype).fields
|
fields = frappe.get_meta(doctype).fields
|
||||||
fields = [field for field in fields if field.fieldname in allowed_fields]
|
fields = [field for field in fields if field.fieldname in allowed_fields]
|
||||||
|
|
||||||
for section in sections:
|
for tab in tabs:
|
||||||
|
for section in tab.get("sections"):
|
||||||
for field in section.get("fields") if section.get("fields") else []:
|
for field in section.get("fields") if section.get("fields") else []:
|
||||||
field = next((f for f in fields if f.fieldname == field), None)
|
field = next((f for f in fields if f.fieldname == field), None)
|
||||||
if field:
|
if field:
|
||||||
@ -45,11 +54,11 @@ def get_fields_layout(doctype: str, type: str):
|
|||||||
"options": field.options,
|
"options": field.options,
|
||||||
"mandatory": field.reqd,
|
"mandatory": field.reqd,
|
||||||
"placeholder": field.get("placeholder"),
|
"placeholder": field.get("placeholder"),
|
||||||
"filters": field.get("link_filters")
|
"filters": field.get("link_filters"),
|
||||||
}
|
}
|
||||||
section["fields"][section.get("fields").index(field["name"])] = field
|
section["fields"][section.get("fields").index(field["name"])] = field
|
||||||
|
|
||||||
return sections or []
|
return tabs or []
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -59,11 +68,13 @@ def save_fields_layout(doctype: str, type: str, layout: str):
|
|||||||
else:
|
else:
|
||||||
doc = frappe.new_doc("CRM Fields Layout")
|
doc = frappe.new_doc("CRM Fields Layout")
|
||||||
|
|
||||||
doc.update({
|
doc.update(
|
||||||
|
{
|
||||||
"dt": doctype,
|
"dt": doctype,
|
||||||
"type": type,
|
"type": type,
|
||||||
"layout": layout,
|
"layout": layout,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
return doc.layout
|
return doc.layout
|
||||||
|
|||||||
@ -31,16 +31,8 @@
|
|||||||
<LoadingIndicator class="h-6 w-6" />
|
<LoadingIndicator class="h-6 w-6" />
|
||||||
<span>{{ __('Loading...') }}</span>
|
<span>{{ __('Loading...') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else>
|
||||||
v-else
|
<FieldLayout v-if="tabs.data" :tabs="tabs.data" :data="data.doc" />
|
||||||
class="flex flex-col gap-3 border border-outline-gray-1 rounded-lg"
|
|
||||||
>
|
|
||||||
<FieldLayout
|
|
||||||
v-if="sections.data"
|
|
||||||
:sections="sections.data"
|
|
||||||
:data="data.doc"
|
|
||||||
:allowTabs="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<DataFieldsModal
|
<DataFieldsModal
|
||||||
v-if="showDataFieldsModal"
|
v-if="showDataFieldsModal"
|
||||||
@ -98,7 +90,7 @@ const data = createDocumentResource({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['DataFields', props.doctype],
|
cache: ['DataFields', props.doctype],
|
||||||
params: { doctype: props.doctype, type: 'Data Fields' },
|
params: { doctype: props.doctype, type: 'Data Fields' },
|
||||||
|
|||||||
@ -1,17 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col" :class="{ 'my-4 sm:my-6': allowTabs }">
|
|
||||||
<div
|
<div
|
||||||
v-for="(section, i) in sections"
|
class="flex flex-col"
|
||||||
|
:class="{
|
||||||
|
'border border-outline-gray-1 rounded-lg': hasTabs,
|
||||||
|
'border-outline-gray-modals': modal && hasTabs,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
v-model="tabIndex"
|
||||||
|
class="!h-full"
|
||||||
|
:tabs="tabs"
|
||||||
|
v-slot="{ tab }"
|
||||||
|
:tablistClass="
|
||||||
|
!hasTabs ? 'hidden' : modal ? 'border-outline-gray-modals' : ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div :class="{ 'my-4 sm:my-6': hasTabs }">
|
||||||
|
<div
|
||||||
|
v-for="(section, i) in tab.sections"
|
||||||
:key="section.label"
|
:key="section.label"
|
||||||
class="section first:border-t-0 border-outline-gray-modals first:pt-0"
|
class="section"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!section.hideBorder && i != 0"
|
v-if="i != 0"
|
||||||
class="h-px w-full border-t my-5"
|
class="w-full"
|
||||||
|
:class="[section.hideBorder ? 'mt-4' : 'h-px border-t my-5']"
|
||||||
/>
|
/>
|
||||||
<Section
|
<Section
|
||||||
class="text-lg font-medium"
|
class="text-lg font-medium"
|
||||||
:class="{ 'px-3 sm:px-5': allowTabs }"
|
:class="{ 'px-3 sm:px-5': hasTabs }"
|
||||||
:label="section.label"
|
:label="section.label"
|
||||||
:hideLabel="section.hideLabel"
|
:hideLabel="section.hideLabel"
|
||||||
:opened="section.opened"
|
:opened="section.opened"
|
||||||
@ -19,8 +36,12 @@
|
|||||||
collapseIconPosition="right"
|
collapseIconPosition="right"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="grid gap-4 mt-6"
|
class="grid gap-4"
|
||||||
:class="[gridClass(section.columns), { 'px-3 sm:px-5': allowTabs }]"
|
:class="[
|
||||||
|
gridClass(section.columns),
|
||||||
|
{ 'px-3 sm:px-5': hasTabs },
|
||||||
|
{ 'mt-6': !section.hideLabel },
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<div v-for="field in section.fields" :key="field.name">
|
<div v-for="field in section.fields" :key="field.name">
|
||||||
<div
|
<div
|
||||||
@ -84,7 +105,9 @@
|
|||||||
@click="data[field.name] = !data[field.name]"
|
@click="data[field.name] = !data[field.name]"
|
||||||
>
|
>
|
||||||
{{ __(field.label) }}
|
{{ __(field.label) }}
|
||||||
<span class="text-ink-red-3" v-if="field.mandatory">*</span>
|
<span class="text-ink-red-3" v-if="field.mandatory"
|
||||||
|
>*</span
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-1" v-else-if="field.type === 'Link'">
|
<div class="flex gap-1" v-else-if="field.type === 'Link'">
|
||||||
@ -120,7 +143,11 @@
|
|||||||
:hideMe="true"
|
:hideMe="true"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<UserAvatar class="mr-2" :user="data[field.name]" size="sm" />
|
<UserAvatar
|
||||||
|
class="mr-2"
|
||||||
|
:user="data[field.name]"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #item-prefix="{ option }">
|
<template #item-prefix="{ option }">
|
||||||
<UserAvatar class="mr-2" :user="option.value" size="sm" />
|
<UserAvatar class="mr-2" :user="option.value" size="sm" />
|
||||||
@ -176,6 +203,8 @@
|
|||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -186,19 +215,24 @@ import UserAvatar from '@/components/UserAvatar.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 { getFormat } from '@/utils'
|
import { getFormat } from '@/utils'
|
||||||
import { Tooltip, DatePicker, DateTimePicker } from 'frappe-ui'
|
import { Tabs, Tooltip, DatePicker, DateTimePicker } from 'frappe-ui'
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
sections: Array,
|
tabs: Array,
|
||||||
data: Object,
|
data: Object,
|
||||||
allowTabs: {
|
modal: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const hasTabs = computed(() => !props.tabs[0].no_tabs)
|
||||||
|
|
||||||
|
const tabIndex = ref(0)
|
||||||
|
|
||||||
function gridClass(columns) {
|
function gridClass(columns) {
|
||||||
columns = columns || 3
|
columns = columns || 3
|
||||||
let griColsMap = {
|
let griColsMap = {
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="sections.data">
|
<div v-if="tabs.data">
|
||||||
<FieldLayout :sections="sections.data" :data="_address" />
|
<FieldLayout :tabs="tabs.data" :data="_address" />
|
||||||
<ErrorMessage class="mt-2" :message="error" />
|
<ErrorMessage class="mt-2" :message="error" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -106,7 +106,7 @@ const dialogOptions = computed(() => {
|
|||||||
return { title, size, actions }
|
return { title, size, actions }
|
||||||
})
|
})
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntry', 'Address'],
|
cache: ['QuickEntry', 'Address'],
|
||||||
params: { doctype: 'Address', type: 'Quick Entry' },
|
params: { doctype: 'Address', type: 'Quick Entry' },
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<FieldLayout
|
<FieldLayout
|
||||||
v-else-if="filteredSections.length"
|
v-else-if="filteredSections.length"
|
||||||
:sections="filteredSections"
|
:tabs="filteredSections"
|
||||||
:data="_contact"
|
:data="_contact"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -245,7 +245,7 @@ const detailFields = computed(() => {
|
|||||||
return details.filter((detail) => detail.value)
|
return details.filter((detail) => detail.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntry', 'Contact'],
|
cache: ['QuickEntry', 'Contact'],
|
||||||
params: { doctype: 'Contact', type: 'Quick Entry' },
|
params: { doctype: 'Contact', type: 'Quick Entry' },
|
||||||
@ -253,7 +253,7 @@ const sections = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filteredSections = computed(() => {
|
const filteredSections = computed(() => {
|
||||||
let allSections = sections.data || []
|
let allSections = tabs.data?.[0]?.sections || []
|
||||||
if (!allSections.length) return []
|
if (!allSections.length) return []
|
||||||
|
|
||||||
allSections.forEach((s) => {
|
allSections.forEach((s) => {
|
||||||
@ -276,7 +276,7 @@ const filteredSections = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return allSections
|
return [{ no_tabs: true, sections: allSections }]
|
||||||
})
|
})
|
||||||
|
|
||||||
const dirty = computed(() => {
|
const dirty = computed(() => {
|
||||||
|
|||||||
@ -29,13 +29,13 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="sections?.data">
|
<div v-if="tabs?.data">
|
||||||
<FieldLayoutEditor
|
<FieldLayoutEditor
|
||||||
v-if="!preview"
|
v-if="!preview"
|
||||||
:sections="sections.data"
|
:sections="tabs.data"
|
||||||
:doctype="_doctype"
|
:doctype="_doctype"
|
||||||
/>
|
/>
|
||||||
<FieldLayout v-else :sections="sections.data" :data="{}" />
|
<FieldLayout v-else :tabs="tabs.data" :data="{}" :modal="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -77,20 +77,20 @@ function getParams() {
|
|||||||
return { doctype: _doctype.value, type: 'Data Fields' }
|
return { doctype: _doctype.value, type: 'Data Fields' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['DataFieldsModal', _doctype.value],
|
cache: ['DataFieldsModal', _doctype.value],
|
||||||
params: getParams(),
|
params: getParams(),
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
sections.originalData = JSON.parse(JSON.stringify(data))
|
tabs.originalData = JSON.parse(JSON.stringify(data))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => sections?.data,
|
() => tabs?.data,
|
||||||
() => {
|
() => {
|
||||||
dirty.value =
|
dirty.value =
|
||||||
JSON.stringify(sections?.data) !== JSON.stringify(sections?.originalData)
|
JSON.stringify(tabs?.data) !== JSON.stringify(tabs?.originalData)
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
)
|
)
|
||||||
@ -99,26 +99,29 @@ onMounted(() => useDebounceFn(reload, 100)())
|
|||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
sections.params = getParams()
|
tabs.params = getParams()
|
||||||
sections.reload()
|
tabs.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveChanges() {
|
function saveChanges() {
|
||||||
let _sections = JSON.parse(JSON.stringify(sections.data))
|
let _tabs = JSON.parse(JSON.stringify(tabs.data))
|
||||||
_sections.forEach((section) => {
|
_tabs.forEach((tab) => {
|
||||||
|
if (!tab.sections) return
|
||||||
|
tab.sections.forEach((section) => {
|
||||||
if (!section.fields) return
|
if (!section.fields) return
|
||||||
section.fields = section.fields.map(
|
section.fields = section.fields.map(
|
||||||
(field) => field.fieldname || field.name,
|
(field) => field.fieldname || field.name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
loading.value = true
|
loading.value = true
|
||||||
call(
|
call(
|
||||||
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
||||||
{
|
{
|
||||||
doctype: _doctype.value,
|
doctype: _doctype.value,
|
||||||
type: 'Data Fields',
|
type: 'Data Fields',
|
||||||
layout: JSON.stringify(_sections),
|
layout: JSON.stringify(_tabs),
|
||||||
},
|
},
|
||||||
).then(() => {
|
).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
<div class="h-px w-full border-t my-5" />
|
<div class="h-px w-full border-t my-5" />
|
||||||
<FieldLayout
|
<FieldLayout
|
||||||
v-if="filteredSections.length"
|
v-if="filteredSections.length"
|
||||||
:sections="filteredSections"
|
:tabs="filteredSections"
|
||||||
:data="deal"
|
:data="deal"
|
||||||
/>
|
/>
|
||||||
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
||||||
@ -100,13 +100,14 @@ const isDealCreating = ref(false)
|
|||||||
const chooseExistingContact = ref(false)
|
const chooseExistingContact = ref(false)
|
||||||
const chooseExistingOrganization = ref(false)
|
const chooseExistingOrganization = ref(false)
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntry', 'CRM Deal'],
|
cache: ['QuickEntry', 'CRM Deal'],
|
||||||
params: { doctype: 'CRM Deal', type: 'Quick Entry' },
|
params: { doctype: 'CRM Deal', type: 'Quick Entry' },
|
||||||
auto: true,
|
auto: true,
|
||||||
transform: (data) => {
|
transform: (_tabs) => {
|
||||||
return data.forEach((section) => {
|
return _tabs.forEach((tab) => {
|
||||||
|
tab.sections.forEach((section) => {
|
||||||
section.fields.forEach((field) => {
|
section.fields.forEach((field) => {
|
||||||
if (field.name == 'status') {
|
if (field.name == 'status') {
|
||||||
field.type = 'Select'
|
field.type = 'Select'
|
||||||
@ -117,11 +118,12 @@ const sections = createResource({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const filteredSections = computed(() => {
|
const filteredSections = computed(() => {
|
||||||
let allSections = sections.data || []
|
let allSections = tabs.data?.[0]?.sections || []
|
||||||
if (!allSections.length) return []
|
if (!allSections.length) return []
|
||||||
|
|
||||||
let _filteredSections = []
|
let _filteredSections = []
|
||||||
@ -159,7 +161,7 @@ const filteredSections = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return _filteredSections
|
return [{ no_tabs: true, sections: _filteredSections }]
|
||||||
})
|
})
|
||||||
|
|
||||||
const dealStatuses = computed(() => {
|
const dealStatuses = computed(() => {
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<FieldLayout v-if="sections.data" :sections="sections.data" :data="lead" />
|
<FieldLayout v-if="tabs.data" :tabs="tabs.data" :data="lead" />
|
||||||
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,13 +63,14 @@ const router = useRouter()
|
|||||||
const error = ref(null)
|
const error = ref(null)
|
||||||
const isLeadCreating = ref(false)
|
const isLeadCreating = ref(false)
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntry', 'CRM Lead'],
|
cache: ['QuickEntry', 'CRM Lead'],
|
||||||
params: { doctype: 'CRM Lead', type: 'Quick Entry' },
|
params: { doctype: 'CRM Lead', type: 'Quick Entry' },
|
||||||
auto: true,
|
auto: true,
|
||||||
transform: (data) => {
|
transform: (_tabs) => {
|
||||||
return data.forEach((section) => {
|
return _tabs.forEach((tab) => {
|
||||||
|
tab.sections.forEach((section) => {
|
||||||
section.fields.forEach((field) => {
|
section.fields.forEach((field) => {
|
||||||
if (field.name == 'status') {
|
if (field.name == 'status') {
|
||||||
field.type = 'Select'
|
field.type = 'Select'
|
||||||
@ -80,6 +81,7 @@ const sections = createResource({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<FieldLayout
|
<FieldLayout
|
||||||
v-else-if="filteredSections.length"
|
v-else-if="filteredSections.length"
|
||||||
:sections="filteredSections"
|
:tabs="filteredSections"
|
||||||
:data="_organization"
|
:data="_organization"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -241,7 +241,7 @@ const fields = computed(() => {
|
|||||||
return details.filter((field) => field.value)
|
return details.filter((field) => field.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntry', 'CRM Organization'],
|
cache: ['QuickEntry', 'CRM Organization'],
|
||||||
params: { doctype: 'CRM Organization', type: 'Quick Entry' },
|
params: { doctype: 'CRM Organization', type: 'Quick Entry' },
|
||||||
@ -249,7 +249,7 @@ const sections = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filteredSections = computed(() => {
|
const filteredSections = computed(() => {
|
||||||
let allSections = sections.data || []
|
let allSections = tabs.data?.[0]?.sections || []
|
||||||
if (!allSections.length) return []
|
if (!allSections.length) return []
|
||||||
|
|
||||||
allSections.forEach((s) => {
|
allSections.forEach((s) => {
|
||||||
@ -272,7 +272,7 @@ const filteredSections = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return allSections
|
return [{ no_tabs: true, sections: allSections }]
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@ -35,13 +35,13 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="sections?.data">
|
<div v-if="tabs?.data">
|
||||||
<FieldLayoutEditor
|
<FieldLayoutEditor
|
||||||
v-if="!preview"
|
v-if="!preview"
|
||||||
:sections="sections.data"
|
:sections="tabs.data"
|
||||||
:doctype="_doctype"
|
:doctype="_doctype"
|
||||||
/>
|
/>
|
||||||
<FieldLayout v-else :sections="sections.data" :data="{}" />
|
<FieldLayout v-else :tabs="tabs.data" :data="{}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -83,20 +83,20 @@ function getParams() {
|
|||||||
return { doctype: _doctype.value, type: 'Quick Entry' }
|
return { doctype: _doctype.value, type: 'Quick Entry' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['QuickEntryModal', _doctype.value],
|
cache: ['QuickEntryModal', _doctype.value],
|
||||||
params: getParams(),
|
params: getParams(),
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
sections.originalData = JSON.parse(JSON.stringify(data))
|
tabs.originalData = JSON.parse(JSON.stringify(data))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => sections?.data,
|
() => tabs?.data,
|
||||||
() => {
|
() => {
|
||||||
dirty.value =
|
dirty.value =
|
||||||
JSON.stringify(sections?.data) !== JSON.stringify(sections?.originalData)
|
JSON.stringify(tabs?.data) !== JSON.stringify(tabs?.originalData)
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
)
|
)
|
||||||
@ -105,26 +105,29 @@ onMounted(() => useDebounceFn(reload, 100)())
|
|||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
sections.params = getParams()
|
tabs.params = getParams()
|
||||||
sections.reload()
|
tabs.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveChanges() {
|
function saveChanges() {
|
||||||
let _sections = JSON.parse(JSON.stringify(sections.data))
|
let _tabs = JSON.parse(JSON.stringify(tabs.data))
|
||||||
_sections.forEach((section) => {
|
_tabs.forEach((tab) => {
|
||||||
|
if (!tab.sections) return
|
||||||
|
tab.sections.forEach((section) => {
|
||||||
if (!section.fields) return
|
if (!section.fields) return
|
||||||
section.fields = section.fields.map(
|
section.fields = section.fields.map(
|
||||||
(field) => field.fieldname || field.name,
|
(field) => field.fieldname || field.name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
loading.value = true
|
loading.value = true
|
||||||
call(
|
call(
|
||||||
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
||||||
{
|
{
|
||||||
doctype: _doctype.value,
|
doctype: _doctype.value,
|
||||||
type: 'Quick Entry',
|
type: 'Quick Entry',
|
||||||
layout: JSON.stringify(_sections),
|
layout: JSON.stringify(_tabs),
|
||||||
},
|
},
|
||||||
).then(() => {
|
).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -29,18 +29,20 @@
|
|||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="sections.data" class="flex gap-4">
|
<div v-if="tabs.data?.[0]?.sections" class="flex gap-4">
|
||||||
<SidePanelLayoutEditor
|
<SidePanelLayoutEditor
|
||||||
class="flex flex-1 flex-col pr-2"
|
class="flex flex-1 flex-col pr-2"
|
||||||
:sections="sections.data"
|
:sections="tabs.data[0].sections"
|
||||||
:doctype="_doctype"
|
:doctype="_doctype"
|
||||||
/>
|
/>
|
||||||
<div v-if="preview" class="flex flex-1 flex-col border rounded">
|
<div v-if="preview" class="flex flex-1 flex-col border rounded">
|
||||||
<div
|
<div
|
||||||
v-for="(section, i) in sections.data"
|
v-for="(section, i) in tabs.data[0].sections"
|
||||||
:key="section.label"
|
:key="section.label"
|
||||||
class="flex flex-col py-1.5 px-1"
|
class="flex flex-col py-1.5 px-1"
|
||||||
:class="{ 'border-b': i !== sections.data?.length - 1 }"
|
:class="{
|
||||||
|
'border-b': i !== tabs.data[0].sections?.length - 1,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<Section
|
<Section
|
||||||
class="p-2"
|
class="p-2"
|
||||||
@ -106,20 +108,20 @@ function getParams() {
|
|||||||
return { doctype: _doctype.value, type: 'Side Panel' }
|
return { doctype: _doctype.value, type: 'Side Panel' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = createResource({
|
const tabs = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
cache: ['SidePanel', _doctype.value],
|
cache: ['SidePanel', _doctype.value],
|
||||||
params: getParams(),
|
params: getParams(),
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
sections.originalData = JSON.parse(JSON.stringify(data))
|
tabs.originalData = JSON.parse(JSON.stringify(data))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => sections?.data,
|
() => tabs?.data,
|
||||||
() => {
|
() => {
|
||||||
dirty.value =
|
dirty.value =
|
||||||
JSON.stringify(sections?.data) !== JSON.stringify(sections?.originalData)
|
JSON.stringify(tabs?.data) !== JSON.stringify(tabs?.originalData)
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
)
|
)
|
||||||
@ -128,26 +130,28 @@ onMounted(() => useDebounceFn(reload, 100)())
|
|||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
sections.params = getParams()
|
tabs.params = getParams()
|
||||||
sections.reload()
|
tabs.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveChanges() {
|
function saveChanges() {
|
||||||
let _sections = JSON.parse(JSON.stringify(sections.data))
|
let _tabs = JSON.parse(JSON.stringify(tabs.data))
|
||||||
_sections.forEach((section) => {
|
_tabs.forEach((tab) => {
|
||||||
|
tab.sections.forEach((section) => {
|
||||||
if (!section.fields) return
|
if (!section.fields) return
|
||||||
section.fields = section.fields
|
section.fields = section.fields
|
||||||
.map((field) => field.fieldname || field.name)
|
.map((field) => field.fieldname || field.name)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
loading.value = true
|
loading.value = true
|
||||||
call(
|
call(
|
||||||
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout',
|
||||||
{
|
{
|
||||||
doctype: _doctype.value,
|
doctype: _doctype.value,
|
||||||
type: 'Side Panel',
|
type: 'Side Panel',
|
||||||
layout: JSON.stringify(_sections),
|
layout: JSON.stringify(_tabs),
|
||||||
},
|
},
|
||||||
).then(() => {
|
).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@ -12,11 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
</h2>
|
</h2>
|
||||||
<div v-if="!data.get.loading" class="flex-1 overflow-y-auto">
|
<div v-if="!data.get.loading" class="flex-1 overflow-y-auto">
|
||||||
<FieldLayout
|
<FieldLayout v-if="data?.doc && tabs" :tabs="tabs" :data="data.doc" />
|
||||||
v-if="data?.doc && sections"
|
|
||||||
:sections="sections"
|
|
||||||
:data="data.doc"
|
|
||||||
/>
|
|
||||||
<ErrorMessage class="mt-2" :message="error" />
|
<ErrorMessage class="mt-2" :message="error" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex flex-1 items-center justify-center">
|
<div v-else class="flex flex-1 items-center justify-center">
|
||||||
@ -98,7 +94,7 @@ const data = createDocumentResource({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const sections = computed(() => {
|
const tabs = computed(() => {
|
||||||
if (!fields.data) return []
|
if (!fields.data) return []
|
||||||
let _sections = []
|
let _sections = []
|
||||||
let fieldsData = fields.data
|
let fieldsData = fields.data
|
||||||
@ -138,7 +134,7 @@ const sections = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return _sections
|
return [{ no_tabs: true, sections: _sections }]
|
||||||
})
|
})
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
@ -148,7 +144,8 @@ function update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateMandatoryFields() {
|
function validateMandatoryFields() {
|
||||||
for (let section of sections.value) {
|
if (!tabs.value) return false
|
||||||
|
for (let section of tabs.value[0].sections) {
|
||||||
for (let field of section.fields) {
|
for (let field of section.fields) {
|
||||||
if (
|
if (
|
||||||
(field.mandatory ||
|
(field.mandatory ||
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user