Skip to content

Commit 3191cf2

Browse files
committed
Add support for creating entities from lookup modal
1 parent 21ad366 commit 3191cf2

File tree

5 files changed

+67
-11
lines changed

5 files changed

+67
-11
lines changed

src/components/AutoCreateForm.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@
7979
</div>
8080
</div>
8181

82-
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone" />
82+
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone"
83+
:configureField="configureField"/>
8384
</div>
8485
</template>
8586

src/components/AutoEditForm.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@
8585
</div>
8686
</div>
8787

88-
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone" />
88+
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone"
89+
:configureField="configureField"/>
8990
</div>
9091
</template>
9192

src/components/AutoForm.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
</div>
9191
</div>
9292
</div>
93-
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone" />
93+
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone"
94+
:configureField="configureField"/>
9495
</div>
9596
</template>
9697

src/components/ModalDialog.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
</div>
2828
</div>
2929

30-
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone" />
30+
<ModalLookup v-if="modal?.name == 'ModalLookup' && modal.ref" :ref-info="modal.ref" @done="openModalDone"
31+
:configureField="configureField"/>
3132
</div>
3233
</template>
3334

3435
<script setup lang="ts">
35-
import type { ModalProvider } from "@/types"
36+
import type { ModalProvider, InputProp } from "@/types"
3637
import { onMounted, onUnmounted, watch, ref, provide, useSlots } from "vue"
3738
import { transition } from '@/use/utils'
3839
import * as css from "./css"
@@ -44,6 +45,7 @@ const props = withDefaults(defineProps<{
4445
modalClass?: string
4546
sizeClass?: string
4647
closeButtonClass?: string
48+
configureField?: (field:InputProp) => void
4749
}>(), {
4850
id: 'ModalDialog',
4951
modalClass: css.modal.modalClass,

src/components/ModalLookup.vue

+57-6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@
3939
<span v-else-if="api.completed">No Results</span>
4040
</div>
4141
</div>
42+
<div v-if="apis.Create && canCreate" class="pl-2 mt-1">
43+
<button type="button" @click="onShowNewItem()" title="modelTitle" :class="grid.toolbarButtonClass">
44+
<svg class="w-5 h-5 mr-1 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor"></path></svg>
45+
<span class="whitespace-nowrap">{{ newButtonLabel }}</span>
46+
</button>
47+
<AutoCreateForm v-if="create" ref="createForm" :type="apis.Create.request.name" :configure="configureField" @done="createDone" @save="createSave">
48+
<template #header>
49+
<slot name="formheader" form="create" :formInstance="createForm" :apis="apis" :type="dataModelName" :updateModel="setCreate"></slot>
50+
</template>
51+
<template #footer>
52+
<slot name="formfooter" form="create" :formInstance="createForm" :apis="apis" :type="dataModelName" :updateModel="setCreate"></slot>
53+
</template>
54+
</AutoCreateForm>
55+
</div>
4256

4357
<div v-if="hasPrefs && showResetPreferences" class="pl-2">
4458
<button type="button" @click="resetPreferences" title="Reset Preferences & Filters" :class="toolbarButtonClass">
@@ -111,13 +125,14 @@
111125
</template>
112126

113127
<script setup lang="ts">
114-
import type { ApiPrefs, ApiResponse, Column, ColumnSettings, MetadataPropertyType, RefInfo } from '@/types'
115-
import { computed, inject, nextTick, onMounted, ref, useSlots } from 'vue'
128+
import type { ApiPrefs, ApiResponse, Column, ColumnSettings, InputProp, MetadataPropertyType, RefInfo } from '@/types'
129+
import { computed, getCurrentInstance, inject, nextTick, onMounted, ref, useSlots } from 'vue'
116130
import { ApiResult, delaySet, humanize, JsonServiceClient, mapGet } from '@servicestack/client'
117-
import { parseJson } from '@/use/utils'
131+
import { parseJson, getTypeName } from '@/use/utils'
118132
import { useConfig } from '@/use/config'
119-
import { createDto, Crud, getPrimaryKey, typeOf, typeProperties, useMetadata } from '@/use/metadata'
133+
import { Apis, createDto, Crud, getPrimaryKey, typeOf, typeProperties, useMetadata } from '@/use/metadata'
120134
import { grid } from './css'
135+
import { canAccess } from '@/use/auth'
121136
122137
import FilterColumn from './grids/FilterColumn.vue'
123138
import FilterViews from './grids/FilterViews.vue'
@@ -139,6 +154,11 @@ const props = withDefaults(defineProps<{
139154
toolbarButtonClass?: string
140155
141156
canFilter?:(column:string) => boolean
157+
158+
type?: string|InstanceType<any>|Function
159+
modelTitle?: string
160+
newButtonLabel?: string
161+
configureField?: (field:InputProp) => void
142162
}>(), {
143163
id: 'ModalLookup',
144164
skip: 0,
@@ -224,6 +244,39 @@ const modalDialog = ref()
224244
const showQueryPrefs = ref(false)
225245
const showFilters = ref<{ column:Column, topLeft:{x:number,y:number}}|null>()
226246
247+
const typeName = computed(() => getTypeName(props.refInfo.model))
248+
const apis = computed(() => Apis.forType(typeName.value, metadataApi.value))
249+
const dataModelName = computed(() => typeName.value || queryOp.value?.dataModel.name)
250+
const modelTitle = computed(() => props.modelTitle || dataModelName.value)
251+
const newButtonLabel = computed(() => props.newButtonLabel || `New ${modelTitle.value}`)
252+
const canCreate = computed(() => canAccess(apis.value.Create))
253+
254+
const createForm = ref()
255+
const create = ref(false)
256+
function onShowNewItem() {
257+
create.value = true
258+
}
259+
260+
function createDone() {
261+
create.value = false
262+
}
263+
async function createSave(result:any) {
264+
createDone()
265+
emit('done', result)
266+
}
267+
function setCreate(props:any) {
268+
if (!createForm.value) return
269+
Object.assign(createForm.value?.model, props)
270+
console.log('setCreate', JSON.stringify(props, null, 2))
271+
forceUpdate()
272+
}
273+
function forceUpdate() {
274+
createForm.value?.forceUpdate()
275+
const instance = getCurrentInstance()
276+
instance?.proxy?.$forceUpdate()
277+
}
278+
279+
227280
const prefsCacheKey = () => `${props.id}/ApiPrefs/${props.refInfo.model}`
228281
const columnCacheKey = (name:string) => `Column/${props.id}:${props.refInfo.model}.${name}`
229282
@@ -267,7 +320,6 @@ function onHeaderSelected(name:string, e:MouseEvent) {
267320
}
268321
}
269322
270-
271323
function onFilterDone() {
272324
showFilters.value = null
273325
}
@@ -354,7 +406,6 @@ async function resetPreferences() {
354406
await update()
355407
}
356408
357-
358409
onMounted(async () => {
359410
//console.debug('ModalLookup.onMounted', props.id, props.refInfo?.model, props.refInfo)
360411
const prefs = props.prefs || parseJson(storage.getItem(prefsCacheKey()))

0 commit comments

Comments
 (0)