feat: added global create document modal for link field
This commit is contained in:
parent
7d37c606cc
commit
76aaf7f37d
2
frontend/components.d.ts
vendored
2
frontend/components.d.ts
vendored
@ -53,6 +53,7 @@ declare module 'vue' {
|
|||||||
ContactsListView: typeof import('./src/components/ListViews/ContactsListView.vue')['default']
|
ContactsListView: typeof import('./src/components/ListViews/ContactsListView.vue')['default']
|
||||||
ConvertIcon: typeof import('./src/components/Icons/ConvertIcon.vue')['default']
|
ConvertIcon: typeof import('./src/components/Icons/ConvertIcon.vue')['default']
|
||||||
CountUpTimer: typeof import('./src/components/CountUpTimer.vue')['default']
|
CountUpTimer: typeof import('./src/components/CountUpTimer.vue')['default']
|
||||||
|
CreateDocumentModal: typeof import('./src/components/Modals/CreateDocumentModal.vue')['default']
|
||||||
CRMLogo: typeof import('./src/components/Icons/CRMLogo.vue')['default']
|
CRMLogo: typeof import('./src/components/Icons/CRMLogo.vue')['default']
|
||||||
CustomActions: typeof import('./src/components/CustomActions.vue')['default']
|
CustomActions: typeof import('./src/components/CustomActions.vue')['default']
|
||||||
DashboardIcon: typeof import('./src/components/Icons/DashboardIcon.vue')['default']
|
DashboardIcon: typeof import('./src/components/Icons/DashboardIcon.vue')['default']
|
||||||
@ -116,6 +117,7 @@ declare module 'vue' {
|
|||||||
FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default']
|
FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default']
|
||||||
GenderIcon: typeof import('./src/components/Icons/GenderIcon.vue')['default']
|
GenderIcon: typeof import('./src/components/Icons/GenderIcon.vue')['default']
|
||||||
GeneralSettings: typeof import('./src/components/Settings/GeneralSettings.vue')['default']
|
GeneralSettings: typeof import('./src/components/Settings/GeneralSettings.vue')['default']
|
||||||
|
GlobalModals: typeof import('./src/components/Modals/GlobalModals.vue')['default']
|
||||||
GoogleIcon: typeof import('./src/components/Icons/GoogleIcon.vue')['default']
|
GoogleIcon: typeof import('./src/components/Icons/GoogleIcon.vue')['default']
|
||||||
Grid: typeof import('./src/components/Controls/Grid.vue')['default']
|
Grid: typeof import('./src/components/Controls/Grid.vue')['default']
|
||||||
GridFieldsEditorModal: typeof import('./src/components/Controls/GridFieldsEditorModal.vue')['default']
|
GridFieldsEditorModal: typeof import('./src/components/Controls/GridFieldsEditorModal.vue')['default']
|
||||||
|
|||||||
@ -126,6 +126,7 @@
|
|||||||
"
|
"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
@change="(v) => fieldChange(v, field, row)"
|
@change="(v) => fieldChange(v, field, row)"
|
||||||
|
:onCreate="field.create"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
v-else-if="field.fieldtype === 'User'"
|
v-else-if="field.fieldtype === 'User'"
|
||||||
@ -321,6 +322,7 @@ import { getRandom, getFormat, isTouchScreenDevice } from '@/utils'
|
|||||||
import { flt } from '@/utils/numberFormat.js'
|
import { flt } from '@/utils/numberFormat.js'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { getMeta } from '@/stores/meta'
|
import { getMeta } from '@/stores/meta'
|
||||||
|
import { createDocument } from '@/composables/document'
|
||||||
import {
|
import {
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -399,6 +401,12 @@ const allFields = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function getFieldObj(field) {
|
function getFieldObj(field) {
|
||||||
|
if (field.fieldtype === 'Link' && field.options !== 'User') {
|
||||||
|
if (!field.create) {
|
||||||
|
field.create = (obj, close) => createDocument(field.options, obj, close)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
filters: field.link_filters && JSON.parse(field.link_filters),
|
filters: field.link_filters && JSON.parse(field.link_filters),
|
||||||
|
|||||||
@ -215,6 +215,7 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import TableMultiselectInput from '@/components/Controls/TableMultiselectInput.vue'
|
import TableMultiselectInput from '@/components/Controls/TableMultiselectInput.vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import Grid from '@/components/Controls/Grid.vue'
|
import Grid from '@/components/Controls/Grid.vue'
|
||||||
|
import { createDocument } from '@/composables/document'
|
||||||
import { getFormat, evaluateDependsOnValue } from '@/utils'
|
import { getFormat, evaluateDependsOnValue } from '@/utils'
|
||||||
import { flt } from '@/utils/numberFormat.js'
|
import { flt } from '@/utils/numberFormat.js'
|
||||||
import { getMeta } from '@/stores/meta'
|
import { getMeta } from '@/stores/meta'
|
||||||
@ -272,6 +273,12 @@ const field = computed(() => {
|
|||||||
field.fieldtype = 'User'
|
field.fieldtype = 'User'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.fieldtype === 'Link' && field.options !== 'User') {
|
||||||
|
if (!field.create) {
|
||||||
|
field.create = (obj, close) => createDocument(field.options, obj, close)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _field = {
|
let _field = {
|
||||||
...field,
|
...field,
|
||||||
filters: field.link_filters && JSON.parse(field.link_filters),
|
filters: field.link_filters && JSON.parse(field.link_filters),
|
||||||
|
|||||||
@ -7,9 +7,11 @@
|
|||||||
<AppHeader />
|
<AppHeader />
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
<GlobalModals />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import AppSidebar from '@/components/Layouts/AppSidebar.vue'
|
import AppSidebar from '@/components/Layouts/AppSidebar.vue'
|
||||||
import AppHeader from '@/components/Layouts/AppHeader.vue'
|
import AppHeader from '@/components/Layouts/AppHeader.vue'
|
||||||
|
import GlobalModals from '@/components/Modals/GlobalModals.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
146
frontend/src/components/Modals/CreateDocumentModal.vue
Normal file
146
frontend/src/components/Modals/CreateDocumentModal.vue
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="show" :options="dialogOptions">
|
||||||
|
<template #body>
|
||||||
|
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||||
|
<div class="mb-5 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||||
|
{{ __(dialogOptions.title) || __('Untitled') }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
v-if="isManager() && !isMobileView"
|
||||||
|
variant="ghost"
|
||||||
|
class="w-7"
|
||||||
|
@click="openQuickEntryModal"
|
||||||
|
>
|
||||||
|
<EditIcon class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" class="w-7" @click="show = false">
|
||||||
|
<FeatherIcon name="x" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="tabs.data">
|
||||||
|
<FieldLayout :tabs="tabs.data" :data="_data" :doctype="doctype" />
|
||||||
|
<ErrorMessage class="mt-2" :message="error" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 pb-7 pt-4 sm:px-6">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Button
|
||||||
|
class="w-full"
|
||||||
|
v-for="action in dialogOptions.actions"
|
||||||
|
:key="action.label"
|
||||||
|
v-bind="action"
|
||||||
|
:label="__(action.label)"
|
||||||
|
:loading="loading"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
|
||||||
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { isMobileView } from '@/composables/settings'
|
||||||
|
import { FeatherIcon, createResource, ErrorMessage, call } from 'frappe-ui'
|
||||||
|
import { ref, nextTick, watch, computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
doctype: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['showQuickEntryModal'])
|
||||||
|
|
||||||
|
const { isManager } = usersStore()
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
|
let _data = ref({})
|
||||||
|
|
||||||
|
const dialogOptions = computed(() => {
|
||||||
|
let doctype = props.doctype
|
||||||
|
|
||||||
|
if (doctype.startsWith('CRM ') || doctype.startsWith('FCRM ')) {
|
||||||
|
doctype = doctype.replace(/^(CRM |FCRM )/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = __('New {0}', [doctype])
|
||||||
|
let size = 'xl'
|
||||||
|
let actions = [
|
||||||
|
{
|
||||||
|
label: __('Create'),
|
||||||
|
variant: 'solid',
|
||||||
|
onClick: () => create(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return { title, size, actions }
|
||||||
|
})
|
||||||
|
|
||||||
|
const tabs = createResource({
|
||||||
|
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout',
|
||||||
|
cache: ['QuickEntry', props.doctype],
|
||||||
|
params: { doctype: props.doctype, type: 'Quick Entry' },
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function create(close) {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
|
||||||
|
await call(
|
||||||
|
'frappe.client.insert',
|
||||||
|
{
|
||||||
|
doc: {
|
||||||
|
doctype: props.doctype,
|
||||||
|
..._data.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onError: (err) => {
|
||||||
|
loading.value = false
|
||||||
|
if (err.error) {
|
||||||
|
error.value = err.error.messages?.[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => show.value,
|
||||||
|
(value) => {
|
||||||
|
if (!value) return
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
_data.value = { ...props.data }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function openQuickEntryModal() {
|
||||||
|
emit('showQuickEntryModal', props.doctype)
|
||||||
|
nextTick(() => {
|
||||||
|
show.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
32
frontend/src/components/Modals/GlobalModals.vue
Normal file
32
frontend/src/components/Modals/GlobalModals.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<CreateDocumentModal
|
||||||
|
v-if="showCreateDocumentModal"
|
||||||
|
v-model="showCreateDocumentModal"
|
||||||
|
:doctype="createDocumentDoctype"
|
||||||
|
:data="createDocumentData"
|
||||||
|
@showQuickEntryModal="(dt) => openQuickEntryModal(dt)"
|
||||||
|
/>
|
||||||
|
<QuickEntryModal
|
||||||
|
v-if="showQuickEntryModal"
|
||||||
|
v-model="showQuickEntryModal"
|
||||||
|
:doctype="quickEntryDoctype"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import CreateDocumentModal from '@/components/Modals/CreateDocumentModal.vue'
|
||||||
|
import QuickEntryModal from '@/components/Modals/QuickEntryModal.vue'
|
||||||
|
import {
|
||||||
|
showCreateDocumentModal,
|
||||||
|
createDocumentDoctype,
|
||||||
|
createDocumentData,
|
||||||
|
} from '@/composables/document'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const showQuickEntryModal = ref(false)
|
||||||
|
const quickEntryDoctype = ref('')
|
||||||
|
|
||||||
|
function openQuickEntryModal(dt) {
|
||||||
|
showQuickEntryModal.value = true
|
||||||
|
quickEntryDoctype.value = dt
|
||||||
|
}
|
||||||
|
</script>
|
||||||
14
frontend/src/composables/document.js
Normal file
14
frontend/src/composables/document.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const showCreateDocumentModal = ref(false)
|
||||||
|
export const createDocumentDoctype = ref('')
|
||||||
|
export const createDocumentData = ref({})
|
||||||
|
|
||||||
|
export function createDocument(doctype, obj, close) {
|
||||||
|
if (doctype) {
|
||||||
|
close()
|
||||||
|
createDocumentDoctype.value = doctype
|
||||||
|
createDocumentData.value = obj || {}
|
||||||
|
showCreateDocumentModal.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user