11<template >
2- <div >
3- <div v-if =" !typeName" >
4- <p class =" text-red-700" >Could not create view for unknown <b >type</b > {{ typeName }}</p >
5- </div >
6- <div v-else-if =" formStyle=='card'" :class =" panelClass" >
7- <div :class =" formClass" >
8- <div >
9- <div v-if =" $slots['heading']" ><slot name =" heading" ></slot ></div >
10- <h3 v-else :class =" headingClass" >{{ title }}</h3 >
11-
12- <div v-if =" $slots['subheading']" ><slot name =" subheading" ></slot ></div >
13- <p v-else-if =" subHeading" :class =" subHeadingClass" >{{ subHeading }}</p >
14- <p v-else-if =" metaType?.notes" :class =" ['notes',subHeadingClass]" v-html =" metaType?.notes" ></p >
15- </div >
16- <MarkupModel :value =" model" />
2+ <div >
3+ <div v-if =" !typeName" >
4+ <p class =" text-red-700" >Could not create view for unknown <b >type</b > {{ typeName }}</p >
5+ </div >
6+ <div v-else-if =" formStyle=='card'" :class =" panelClass" >
7+ <div :class =" formClass" >
8+ <div >
9+ <div v-if =" $slots['heading']" ><slot name =" heading" ></slot ></div >
10+ <h3 v-else :class =" headingClass" >{{ title }}</h3 >
11+
12+ <div v-if =" $slots['subheading']" ><slot name =" subheading" ></slot ></div >
13+ <p v-else-if =" subHeading" :class =" subHeadingClass" >{{ subHeading }}</p >
14+ <p v-else-if =" metaType?.notes" :class =" ['notes',subHeadingClass]" v-html =" metaType?.notes" ></p >
1715 </div >
16+ <MarkupModel :value =" model" />
1817 </div >
19- <div v-else class =" relative z-10" aria-labelledby =" slide-over-title" role =" dialog" aria-modal =" true" >
20- <div class =" fixed inset-0" ></div >
21- <div class =" fixed inset-0 overflow-hidden" >
22- <div @mousedown =" close" class =" absolute inset-0 overflow-hidden" >
23- <div @mousedown.stop =" " class =" pointer-events-none fixed inset-y-0 right-0 flex pl-10" >
24- <div :class =" ['pointer-events-auto w-screen xl:max-w-3xl md:max-w-xl max-w-lg',transition1]" >
25- <div :class =" formClass" >
26- <div class =" flex min-h-0 flex-1 flex-col overflow-auto" >
27- <div class =" flex-1" >
28- <!-- Header -->
29- <div class =" bg-gray-50 dark:bg-gray-900 px-4 py-6 sm:px-6" >
30- <div class =" flex items-start justify-between space-x-3" >
31- <div class =" space-y-1" >
32- <div v-if =" $slots['heading']" ><slot name =" heading" ></slot ></div >
33- <h3 v-else :class =" headingClass" >{{ title }}</h3 >
34-
35- <div v-if =" $slots['subheading']" ><slot name =" subheading" ></slot ></div >
36- <p v-else-if =" subHeading" :class =" subHeadingClass" >{{ subHeading }}</p >
37- <p v-else-if =" metaType?.notes" :class =" ['notes',subHeadingClass]" v-html =" metaType?.notes" ></p >
38- </div >
39- <div class =" flex h-7 items-center" >
40- <CloseButton button-class =" bg-gray-50 dark:bg-gray-900" @close =" close" />
41- </div >
18+ </div >
19+ <div v-else class =" relative z-10" aria-labelledby =" slide-over-title" role =" dialog" aria-modal =" true" >
20+ <div class =" fixed inset-0" ></div >
21+ <div class =" fixed inset-0 overflow-hidden" >
22+ <div @mousedown =" close" class =" absolute inset-0 overflow-hidden" >
23+ <div @mousedown.stop =" " class =" pointer-events-none fixed inset-y-0 right-0 flex pl-10" >
24+ <div :class =" ['pointer-events-auto w-screen xl:max-w-3xl md:max-w-xl max-w-lg',transition1]" >
25+ <div :class =" formClass" >
26+ <div class =" flex min-h-0 flex-1 flex-col overflow-auto" >
27+ <div class =" flex-1" >
28+ <!-- Header -->
29+ <div class =" bg-gray-50 dark:bg-gray-900 px-4 py-6 sm:px-6" >
30+ <div class =" flex items-start justify-between space-x-3" >
31+ <div class =" space-y-1" >
32+ <div v-if =" $slots['heading']" ><slot name =" heading" ></slot ></div >
33+ <h3 v-else :class =" headingClass" >{{ title }}</h3 >
34+
35+ <div v-if =" $slots['subheading']" ><slot name =" subheading" ></slot ></div >
36+ <p v-else-if =" subHeading" :class =" subHeadingClass" >{{ subHeading }}</p >
37+ <p v-else-if =" metaType?.notes" :class =" ['notes',subHeadingClass]" v-html =" metaType?.notes" ></p >
4238 </div >
43- </div >
44- <MarkupModel :value =" model" />
45- </div >
39+ <div class =" flex h-7 items-center" >
40+ <CloseButton button-class =" bg-gray-50 dark:bg-gray-900" @close =" close" />
41+ </div >
42+ </div >
43+ </div >
44+ <MarkupModel :value =" model" />
4645 </div >
4746 </div >
47+ <div :class =" form.buttonsClass" >
48+ <div >
49+ <ConfirmDelete v-if =" deleteType" @delete =" onDelete" />
50+ </div >
51+ <div >
52+ <FormLoading v-if =" showLoading && loading" />
53+ </div >
54+ <div class =" flex justify-end" ></div >
55+ </div >
4856 </div >
4957 </div >
5058 </div >
5159 </div >
5260 </div >
5361 </div >
54- </template >
55-
56- <script setup lang="ts">
57- import { useMetadata , Apis } from ' @/use/metadata'
58- import { form } from ' ./css'
59- import { computed , onMounted , onUnmounted , ref , watch } from ' vue'
60- import { transition } from ' @/use/utils'
61- import { Sole } from ' @/use/config'
62- import { humanize } from ' @servicestack/client'
63-
64- const props = withDefaults (defineProps <{
65- model: any
66- apis? : Apis ,
67- typeName? : string ,
68- done? : Function ,
69- formStyle? : " slideOver" | " card"
70- panelClass? : string
71- formClass? : string
72- headingClass? : string
73- subHeadingClass? : string
74- heading? : string
75- subHeading? : string
76- }>(), {
77- formStyle: " slideOver" ,
78- })
79-
80- const emit = defineEmits <{
81- (e : ' done' ): void
82- }>()
83-
84- const { typeOf } = useMetadata ()
85-
86- const typeName = computed (() => props .typeName ?? props .apis ! .dataModel ! .name )
87- const metaType = computed (() => typeOf (typeName .value ))
88- const panelClass = computed (() => props .panelClass || form .panelClass (props .formStyle ))
89- const formClass = computed (() => props .formClass || form .formClass (props .formStyle ))
90- const headingClass = computed (() => props .headingClass || form .headingClass (props .formStyle ))
91- const subHeadingClass = computed (() => props .subHeadingClass || form .subHeadingClass (props .formStyle ))
92-
93- const title = computed (() => props .heading || typeOf (typeName .value )?.description ||
94- (props .model ?.id ? ` ${humanize (typeName .value )} ${props .model .id } ` : ' View ' + humanize (typeName .value )))
95-
96- if (Sole .interceptors .has (' AutoViewForm.new' )) Sole .interceptors .invoke (' AutoViewForm.new' , { props })
97-
98- function done() {
99- if (props .done ) {
100- props .done ()
101- }
62+ </div >
63+ </template >
64+
65+ <script setup lang="ts">
66+ import type { ApiResponse , ResponseStatus } from ' @/types'
67+ import { useMetadata , toFormValues , Apis } from ' @/use/metadata'
68+ import { form } from ' ./css'
69+ import { computed , onMounted , onUnmounted , ref , watch } from ' vue'
70+ import { transition } from ' @/use/utils'
71+ import { Sole } from ' @/use/config'
72+ import { useClient } from ' @/use/client'
73+ import { ApiResult , humanize , map , mapGet } from ' @servicestack/client'
74+
75+ const props = withDefaults (defineProps <{
76+ model: any
77+ apis? : Apis ,
78+ typeName? : string ,
79+ done? : Function ,
80+ formStyle? : " slideOver" | " card"
81+ panelClass? : string
82+ formClass? : string
83+ headingClass? : string
84+ subHeadingClass? : string
85+ heading? : string
86+ subHeading? : string
87+ showLoading? : boolean
88+ deleteType? : string | InstanceType <any >| Function
89+ }>(), {
90+ formStyle: " slideOver" ,
91+ })
92+
93+ const emit = defineEmits <{
94+ (e : ' done' ): void
95+ (e : ' save' , response : any ): () => void
96+ (e : ' delete' , response : any ): () => void
97+ (e : ' error' , status : ResponseStatus ): void
98+ }>()
99+
100+ const { typeOf, getPrimaryKey, Crud, createDto } = useMetadata ()
101+
102+ const typeName = computed (() => props .typeName ?? props .apis ! .dataModel ! .name )
103+ const metaType = computed (() => typeOf (typeName .value ))
104+ const panelClass = computed (() => props .panelClass || form .panelClass (props .formStyle ))
105+ const formClass = computed (() => props .formClass || form .formClass (props .formStyle ))
106+ const headingClass = computed (() => props .headingClass || form .headingClass (props .formStyle ))
107+ const subHeadingClass = computed (() => props .subHeadingClass || form .subHeadingClass (props .formStyle ))
108+
109+ const title = computed (() => props .heading || typeOf (typeName .value )?.description ||
110+ (props .model ?.id ? ` ${humanize (typeName .value )} ${props .model .id } ` : ' View ' + humanize (typeName .value )))
111+
112+ const api = ref <ApiResponse >(new ApiResult <any >())
113+ let origModel = Object .assign ({}, toFormValues (props .model ))
114+ if (Sole .interceptors .has (' AutoViewForm.new' )) Sole .interceptors .invoke (' AutoViewForm.new' , { props })
115+
116+ let client = useClient ()
117+ let loading = computed (() => client .loading .value )
118+ const getPk = () => map (metaType .value , dataModel => getPrimaryKey (dataModel ))
119+ const dataModel = computed (() => metaType .value )
120+
121+ async function onDelete(e : Event ) {
122+ let pk = getPk ()
123+ const id = pk ? mapGet (props .model , pk .name ) : null
124+ if (! id ) {
125+ console .error (` Could not find Primary Key for Type ${typeName .value } (${dataModel .value }) ` )
126+ return
102127 }
103-
104- /* SlideOver */
105- const show = ref (false )
106- const transition1 = ref (' ' )
107- const rule1 = {
108- entering: { cls: ' transform transition ease-in-out duration-500 sm:duration-700' , from: ' translate-x-full' , to: ' translate-x-0' },
109- leaving: { cls: ' transform transition ease-in-out duration-500 sm:duration-700' , from: ' translate-x-0' , to: ' translate-x-full' }
128+ const args = { [pk ! .name ]: id }
129+ const request = typeof props .deleteType == ' string'
130+ ? createDto (props .deleteType , args )
131+ : (props .deleteType ? new props .deleteType (args ) : null )
132+
133+ let returnsVoid = map (request [' createResponse' ], fn => typeof fn == ' function' ? fn () : null ) == null
134+ if (! returnsVoid ) {
135+ api .value = await client .api (request )
136+ } else {
137+ api .value = await client .apiVoid (request )
110138 }
111- watch (show , () => {
112- transition (rule1 , transition1 , show .value )
113- if (! show .value ) setTimeout (done , 700 )
114- })
115- show .value = true
116- function close() {
117- if (props .formStyle == ' slideOver' ) {
118- show .value = false
119- } else {
120- done ()
121- }
139+
140+ if (api .value .succeeded ) {
141+ emit (' delete' , api .value .response )
142+ } else {
143+ emit (' error' , api .value .error ! )
122144 }
145+ }
123146
124- const globalKeyHandler = (e : KeyboardEvent ) => { if (e .key === ' Escape' ) close () }
125- onMounted (() => window .addEventListener (' keydown' , globalKeyHandler ))
126- onUnmounted (() => window .removeEventListener (' keydown' , globalKeyHandler ))
127- </script >
147+ function done() {
148+ if (props .done ) {
149+ props .done ()
150+ }
151+ }
152+
153+ /* SlideOver */
154+ const show = ref (false )
155+ const transition1 = ref (' ' )
156+ const rule1 = {
157+ entering: { cls: ' transform transition ease-in-out duration-500 sm:duration-700' , from: ' translate-x-full' , to: ' translate-x-0' },
158+ leaving: { cls: ' transform transition ease-in-out duration-500 sm:duration-700' , from: ' translate-x-0' , to: ' translate-x-full' }
159+ }
160+ watch (show , () => {
161+ transition (rule1 , transition1 , show .value )
162+ if (! show .value ) setTimeout (done , 700 )
163+ })
164+ show .value = true
165+ function close() {
166+ if (props .formStyle == ' slideOver' ) {
167+ show .value = false
168+ } else {
169+ done ()
170+ }
171+ }
172+
173+ const globalKeyHandler = (e : KeyboardEvent ) => { if (e .key === ' Escape' ) close () }
174+ onMounted (() => window .addEventListener (' keydown' , globalKeyHandler ))
175+ onUnmounted (() => window .removeEventListener (' keydown' , globalKeyHandler ))
176+ </script >
0 commit comments