1
1
<template >
2
2
3
+ <div v-if =" !metaType" >
4
+ <p class =" text-red-700" >Could not create form for unknown <b >type</b > {{ typeName }}</p >
5
+ </div >
3
6
<div v-if =" formStyle=='card'" :class =" panelClass" >
4
7
<form @submit.prevent =" save" >
5
8
<div :class =" formClass" >
9
12
<p v-else-if =" notes" :class =" ['notes',subHeadingClass]" v-html =" notes" ></p >
10
13
</div >
11
14
12
- <AutoFormFields :modelValue =" modelValue " @update:modelValue =" update" :api =" api" />
13
-
15
+ <AutoFormFields :modelValue =" model " @update:modelValue =" update" :api =" api" />
16
+
14
17
</div >
15
18
<div :class =" Css.form.buttonsClass" >
16
- <div ></div >
19
+ <div >
20
+ <FormLoading v-if =" showLoading && loading" />
21
+ </div >
17
22
<div class =" flex justify-end" >
18
- <SecondaryButton @click =" done " >Cancel</SecondaryButton >
19
- <PrimaryButton type =" submit" class =" ml-4" >Save</PrimaryButton >
23
+ <SecondaryButton @click =" close " :disabled = " loading " >Cancel</SecondaryButton >
24
+ <PrimaryButton type =" submit" class =" ml-4" :disabled = " loading " >Save</PrimaryButton >
20
25
</div >
21
26
</div >
22
27
</form >
44
49
</div >
45
50
</div >
46
51
47
- <AutoFormFields :modelValue =" modelValue " @update:modelValue =" update" :api =" api" />
52
+ <AutoFormFields :modelValue =" model " @update:modelValue =" update" :api =" api" />
48
53
49
54
</div >
50
55
</div >
51
56
<div :class =" Css.form.buttonsClass" >
52
- <div ></div >
57
+ <div >
58
+ <FormLoading v-if =" showLoading && loading" />
59
+ </div >
53
60
<div class =" flex justify-end" >
54
- <SecondaryButton @click =" close" >Cancel</SecondaryButton >
55
- <PrimaryButton type =" submit" class =" ml-4" >Save</PrimaryButton >
61
+ <SecondaryButton @click =" close" :disabled = " loading " >Cancel</SecondaryButton >
62
+ <PrimaryButton type =" submit" class =" ml-4" :disabled = " loading " >Save</PrimaryButton >
56
63
</div >
57
64
</div >
58
65
</form >
65
72
</template >
66
73
67
74
<script setup lang="ts">
68
- import type { ApiRequest } from ' @/types'
69
- import { useAppMetadata , Css } from ' @/api'
75
+ import type { ApiRequest , ApiResponse , ResponseStatus } from ' @/types'
76
+ import { useAppMetadata , Css , createDto , formValues , useClient } from ' @/api'
70
77
import { computed , onMounted , onUnmounted , ref , watch } from ' vue'
71
78
import { getTypeName , transition } from ' ./utils'
72
- import { ApiResult , humanize } from ' @servicestack/client'
79
+ import { ApiResult , HttpMethods , humanize , map } from ' @servicestack/client'
73
80
74
81
const props = withDefaults (defineProps <{
75
- modelValue : ApiRequest
82
+ type : string | InstanceType < any > | Function
76
83
formStyle? : " slideOver" | " card"
77
84
panelClass? : string
78
85
formClass? : string
@@ -81,32 +88,78 @@ const props = withDefaults(defineProps<{
81
88
heading? : string
82
89
subHeading? : string
83
90
notes? : string
91
+ autosave? : boolean
92
+ showLoading? : boolean
84
93
}>(), {
85
- formStyle: " slideOver" ,
94
+ formStyle: " slideOver" ,
95
+ autosave: true ,
96
+ showLoading: true
86
97
})
87
98
88
99
const emit = defineEmits <{
89
- (e : " update:modelValue" , o : ApiRequest ): void
90
100
(e : ' done' ): void
101
+ (e : ' save' , response : any ): () => void
102
+ (e : ' error' , status : ResponseStatus ): void
91
103
}>()
92
104
93
105
function update(value : ApiRequest ) {
94
- emit (' update:modelValue' , value )
95
106
}
96
107
108
+ const { typeOf, typeProperties } = useAppMetadata ()
109
+
110
+ const typeName = computed (() => typeof props .type == ' string'
111
+ ? props .type
112
+ : (props .type ? getTypeName (new props .type ()) : null ))
113
+
114
+ const metaType = computed (() => typeOf (typeName .value ))
115
+ const model = ref (typeof props .type == ' string' ? createDto (props .type ) : props .type ? new props .type () : null )
116
+
97
117
const panelClass = computed (() => props .panelClass || Css .form .panelClass (props .formStyle ))
98
118
const formClass = computed (() => props .formClass || Css .form .formClass (props .formStyle ))
99
119
const headingClass = computed (() => props .headingClass || Css .form .headingClass (props .formStyle ))
100
120
const subHeadingClass = computed (() => props .subHeadingClass || Css .form .subHeadingClass (props .formStyle ))
101
121
102
- const { typeOf } = useAppMetadata ()
103
- const typeName = computed (() => getTypeName (props .modelValue ))
104
-
105
122
const title = computed (() => props .heading || typeOf (typeName .value )?.description || ` New ${humanize (typeName .value )} ` )
106
123
107
- const api = ref (new ApiResult <any >())
108
-
109
- function save() {
124
+ const api = ref <ApiResponse >(new ApiResult <any >())
125
+
126
+ let client = useClient ()
127
+ let loading = computed (() => client .loading .value )
128
+
129
+ async function save(e : Event ) {
130
+ let form = e .target as HTMLFormElement
131
+ if (! props .autosave ) {
132
+ emit (' save' , new model .value .constructor (formValues (form , typeProperties (metaType .value ))))
133
+ return
134
+ }
135
+
136
+ let method = map (model .value ?.[' getMethod' ], fn => typeof fn == ' function' ? fn () : null ) || ' POST'
137
+ let returnsVoid = map (model .value ?.[' createResponse' ], fn => typeof fn == ' function' ? fn () : null ) == null
138
+
139
+ if (HttpMethods .hasRequestBody (method )) {
140
+ let requestDto = new model .value .constructor ()
141
+ let formData = new FormData (form )
142
+ if (! returnsVoid ) {
143
+ api .value = await client .apiForm (requestDto , formData , { jsconfig: ' eccn' })
144
+ } else {
145
+ api .value = await client .apiFormVoid (requestDto , formData , { jsconfig: ' eccn' })
146
+ }
147
+ } else {
148
+ let fieldValues = formValues (form , typeProperties (metaType .value ))
149
+ let requestDto = new model .value .constructor (fieldValues )
150
+ if (! returnsVoid ) {
151
+ api .value = await client .api (requestDto , { jsconfig: ' eccn' })
152
+ } else {
153
+ api .value = await client .apiVoid (requestDto , { jsconfig: ' eccn' })
154
+ }
155
+ }
156
+
157
+ if (api .value .succeeded ) {
158
+ form .reset ()
159
+ emit (' save' , api .value .response )
160
+ } else {
161
+ emit (' error' , api .value .error ! )
162
+ }
110
163
}
111
164
112
165
function done() {
@@ -125,7 +178,13 @@ watch(show, () => {
125
178
if (! show .value ) setTimeout (done , 700 )
126
179
})
127
180
show .value = true
128
- const close = () => show .value = false
181
+ function close() {
182
+ if (props .formStyle == ' slideOver' ) {
183
+ show .value = false
184
+ } else {
185
+ done ()
186
+ }
187
+ }
129
188
130
189
const globalKeyHandler = (e : KeyboardEvent ) => { if (e .key === ' Escape' ) close () }
131
190
onMounted (() => window .addEventListener (' keydown' , globalKeyHandler ))
0 commit comments