@motor-cms/ui-admin 1.16.3 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/UsersOnboarding.vue +0 -7
- package/app/components/client/FooterSlotCard.vue +261 -0
- package/app/components/client/FrontendConfigSection.vue +115 -0
- package/app/components/client/GlobalComponentsSection.vue +50 -0
- package/app/composables/useClientFrontendConfig.ts +145 -0
- package/app/composables/useClientLanguages.ts +81 -0
- package/app/data/footerTemplate.ts +283 -0
- package/app/lang/de/motor-admin/clients.json +34 -1
- package/app/lang/en/motor-admin/clients.json +34 -1
- package/app/pages/motor-admin/clients/[id]/edit.vue +158 -3
- package/app/types/frontend-config.ts +252 -0
- package/nuxt.config.ts +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { generateUuid } from '@motor-cms/ui-core/app/utils/uuid'
|
|
2
|
+
|
|
3
|
+
// Types mirror @zrmdev/ui-builder/app/types/builder/page-definition. Names
|
|
4
|
+
// (HeadlineParagraphComponent, ImageComponent, HeadlineAtom, ParagraphAtom,
|
|
5
|
+
// ButtonAtom, ImageAtom) match the renderer registry in
|
|
6
|
+
// motor-ui-components/app/builder-registry/bootstrap.ts.
|
|
7
|
+
|
|
8
|
+
// ============================================
|
|
9
|
+
// Local type definitions (mirror page-definition)
|
|
10
|
+
// ============================================
|
|
11
|
+
|
|
12
|
+
interface CssProp {
|
|
13
|
+
key: string
|
|
14
|
+
value: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface PageAtom {
|
|
18
|
+
uuid: string
|
|
19
|
+
display_name: string
|
|
20
|
+
component_name: string
|
|
21
|
+
classes: string
|
|
22
|
+
attributes: Record<string, unknown>
|
|
23
|
+
locked_attributes: string[]
|
|
24
|
+
disabled: boolean
|
|
25
|
+
visible: boolean
|
|
26
|
+
cssProps?: CssProp[]
|
|
27
|
+
admin_scss?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ComponentSlot {
|
|
31
|
+
uuid: string
|
|
32
|
+
name: string
|
|
33
|
+
display_name: string
|
|
34
|
+
allowedAtoms: string[]
|
|
35
|
+
atoms: PageAtom[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface PageComponent {
|
|
39
|
+
uuid: string
|
|
40
|
+
name: string
|
|
41
|
+
display_name: string
|
|
42
|
+
icon: string
|
|
43
|
+
classes: string
|
|
44
|
+
cssClassName: string
|
|
45
|
+
visible: boolean
|
|
46
|
+
disabled: boolean
|
|
47
|
+
attributes: Record<string, unknown>
|
|
48
|
+
component_slot_name: string | null
|
|
49
|
+
component_slot_prefix: string | null
|
|
50
|
+
is_removable: number
|
|
51
|
+
is_duplicatable: number
|
|
52
|
+
min_amount_in_another_component: number
|
|
53
|
+
display_viewports: string
|
|
54
|
+
slots: ComponentSlot[]
|
|
55
|
+
components: PageComponent[]
|
|
56
|
+
scorings: unknown[]
|
|
57
|
+
scoring_component_configuration: { score: number; topic_id: number; comparison_operator: string }
|
|
58
|
+
anchors: unknown[]
|
|
59
|
+
admin_scss?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface PageColumn {
|
|
63
|
+
uuid: string
|
|
64
|
+
classes: string
|
|
65
|
+
display_name: string
|
|
66
|
+
value_as_grid_column: number
|
|
67
|
+
rows: PageRow[]
|
|
68
|
+
components: PageComponent[]
|
|
69
|
+
admin_scss?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface PageRow {
|
|
73
|
+
uuid: string
|
|
74
|
+
display_name: string
|
|
75
|
+
classes: string
|
|
76
|
+
global_css: string
|
|
77
|
+
cols: PageColumn[]
|
|
78
|
+
admin_scss?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type PageDefinition = PageRow[]
|
|
82
|
+
|
|
83
|
+
// ============================================
|
|
84
|
+
// Builder helpers
|
|
85
|
+
// ============================================
|
|
86
|
+
|
|
87
|
+
function makeAtom(
|
|
88
|
+
display_name: string,
|
|
89
|
+
component_name: string,
|
|
90
|
+
attributes: Record<string, unknown> = {}
|
|
91
|
+
): PageAtom {
|
|
92
|
+
return {
|
|
93
|
+
uuid: generateUuid(),
|
|
94
|
+
display_name,
|
|
95
|
+
component_name,
|
|
96
|
+
classes: '',
|
|
97
|
+
attributes,
|
|
98
|
+
locked_attributes: [],
|
|
99
|
+
disabled: false,
|
|
100
|
+
visible: true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function makeSlot(
|
|
105
|
+
name: string,
|
|
106
|
+
display_name: string,
|
|
107
|
+
allowedAtoms: string[],
|
|
108
|
+
atoms: PageAtom[]
|
|
109
|
+
): ComponentSlot {
|
|
110
|
+
return {
|
|
111
|
+
uuid: generateUuid(),
|
|
112
|
+
name,
|
|
113
|
+
display_name,
|
|
114
|
+
allowedAtoms,
|
|
115
|
+
atoms
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function makeComponent(
|
|
120
|
+
name: string,
|
|
121
|
+
display_name: string,
|
|
122
|
+
cssClassName: string,
|
|
123
|
+
slots: ComponentSlot[],
|
|
124
|
+
attributes: Record<string, unknown> = {},
|
|
125
|
+
components: PageComponent[] = []
|
|
126
|
+
): PageComponent {
|
|
127
|
+
return {
|
|
128
|
+
uuid: generateUuid(),
|
|
129
|
+
name,
|
|
130
|
+
display_name,
|
|
131
|
+
icon: '',
|
|
132
|
+
classes: '',
|
|
133
|
+
cssClassName,
|
|
134
|
+
visible: true,
|
|
135
|
+
disabled: false,
|
|
136
|
+
attributes,
|
|
137
|
+
component_slot_name: null,
|
|
138
|
+
component_slot_prefix: null,
|
|
139
|
+
is_removable: 1,
|
|
140
|
+
is_duplicatable: 1,
|
|
141
|
+
min_amount_in_another_component: 0,
|
|
142
|
+
display_viewports: 's,t,m,l,xl',
|
|
143
|
+
slots,
|
|
144
|
+
components,
|
|
145
|
+
scorings: [],
|
|
146
|
+
scoring_component_configuration: { score: 0, topic_id: 0, comparison_operator: '' },
|
|
147
|
+
anchors: []
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function makeColumn(
|
|
152
|
+
display_name: string,
|
|
153
|
+
value_as_grid_column: number,
|
|
154
|
+
components: PageComponent[]
|
|
155
|
+
): PageColumn {
|
|
156
|
+
return {
|
|
157
|
+
uuid: generateUuid(),
|
|
158
|
+
classes: '',
|
|
159
|
+
display_name,
|
|
160
|
+
value_as_grid_column,
|
|
161
|
+
rows: [],
|
|
162
|
+
components
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function makeRow(display_name: string, cols: PageColumn[], classes = ''): PageRow {
|
|
167
|
+
return {
|
|
168
|
+
uuid: generateUuid(),
|
|
169
|
+
display_name,
|
|
170
|
+
classes,
|
|
171
|
+
global_css: '',
|
|
172
|
+
cols
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================
|
|
177
|
+
// Footer template factory
|
|
178
|
+
// ============================================
|
|
179
|
+
|
|
180
|
+
const PLACEHOLDER_IMAGE = '/images/general/frau_mit_kind.jpeg'
|
|
181
|
+
|
|
182
|
+
const TEXT_SLOT_ALLOWED = ['OverlineAtom', 'HeadlineAtom', 'ParagraphAtom', 'ButtonAtom', 'ImageAtom', 'VideoAtom']
|
|
183
|
+
|
|
184
|
+
function textContentComponent(
|
|
185
|
+
display_name: string,
|
|
186
|
+
atoms: PageAtom[],
|
|
187
|
+
attributes: Record<string, unknown> = { has_background: false }
|
|
188
|
+
): PageComponent {
|
|
189
|
+
return makeComponent(
|
|
190
|
+
'HeadlineParagraphComponent',
|
|
191
|
+
display_name,
|
|
192
|
+
'headline-paragraph',
|
|
193
|
+
[makeSlot('content', 'Content', TEXT_SLOT_ALLOWED, atoms)],
|
|
194
|
+
attributes
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function headlineAtom(text: string, level: 'h2' | 'h3' | 'h4' = 'h3'): PageAtom {
|
|
199
|
+
return makeAtom('Headline', 'HeadlineAtom', {
|
|
200
|
+
text,
|
|
201
|
+
type: level,
|
|
202
|
+
displayedLevel: level,
|
|
203
|
+
weight: 'bold'
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function paragraphAtom(text: string): PageAtom {
|
|
208
|
+
return makeAtom('Paragraph', 'ParagraphAtom', {
|
|
209
|
+
text,
|
|
210
|
+
bullet_type: 'check__default',
|
|
211
|
+
orderedlist_type: 'decimal'
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buttonAtom(title: string): PageAtom {
|
|
216
|
+
return makeAtom('Button', 'ButtonAtom', {
|
|
217
|
+
title,
|
|
218
|
+
link: '',
|
|
219
|
+
variant: 'dark',
|
|
220
|
+
has_arrow: false
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function imageAtom(display_name: string): PageAtom {
|
|
225
|
+
return makeAtom(display_name, 'ImageAtom', {
|
|
226
|
+
src: PLACEHOLDER_IMAGE,
|
|
227
|
+
alt: display_name
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function imageComponent(display_name: string, extraClasses = ''): PageComponent {
|
|
232
|
+
const c = makeComponent(
|
|
233
|
+
'ImageComponent',
|
|
234
|
+
display_name,
|
|
235
|
+
'image',
|
|
236
|
+
[makeSlot('content', 'Content', ['ImageAtom'], [imageAtom(display_name)])]
|
|
237
|
+
)
|
|
238
|
+
if (extraClasses) c.classes = extraClasses
|
|
239
|
+
return c
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function createFooterTemplate(): PageDefinition {
|
|
243
|
+
// ---- Row 1: 6/6 split ----
|
|
244
|
+
// Col 1: intro text (Headline + Paragraph)
|
|
245
|
+
// Col 2: three award badges, each its own ImageComponent
|
|
246
|
+
const row1 = makeRow('Row 1', [
|
|
247
|
+
makeColumn('Column 1', 6, [
|
|
248
|
+
textContentComponent('Intro', [
|
|
249
|
+
headlineAtom('Headline', 'h2'),
|
|
250
|
+
paragraphAtom('Paragraph')
|
|
251
|
+
])
|
|
252
|
+
]),
|
|
253
|
+
makeColumn('Column 2', 6, [
|
|
254
|
+
imageComponent('Badge 1', 'footer-badge'),
|
|
255
|
+
imageComponent('Badge 2', 'footer-badge'),
|
|
256
|
+
imageComponent('Badge 3', 'footer-badge')
|
|
257
|
+
])
|
|
258
|
+
])
|
|
259
|
+
|
|
260
|
+
// ---- Row 2: four 3/12 columns ----
|
|
261
|
+
// Col 1: Kontakt (pre-filled headline)
|
|
262
|
+
// Col 2-4: nav-style headline + paragraph + CTA button
|
|
263
|
+
// Row gets the white inset card chrome inside the peach FooterFrame.
|
|
264
|
+
const row2 = makeRow('Row 2', [
|
|
265
|
+
makeColumn('Column 1', 3, [
|
|
266
|
+
textContentComponent('Kontakt', [
|
|
267
|
+
headlineAtom('Kontakt', 'h3'),
|
|
268
|
+
paragraphAtom('Paragraph')
|
|
269
|
+
])
|
|
270
|
+
]),
|
|
271
|
+
...['Column 2', 'Column 3', 'Column 4'].map((label) =>
|
|
272
|
+
makeColumn(label, 3, [
|
|
273
|
+
textContentComponent(label, [
|
|
274
|
+
headlineAtom('Headline', 'h3'),
|
|
275
|
+
paragraphAtom('Paragraph'),
|
|
276
|
+
buttonAtom('CTA Button')
|
|
277
|
+
])
|
|
278
|
+
])
|
|
279
|
+
)
|
|
280
|
+
], 'footer-content-card')
|
|
281
|
+
|
|
282
|
+
return [row1, row2]
|
|
283
|
+
}
|
|
@@ -23,5 +23,38 @@
|
|
|
23
23
|
"contact_phone": "Telefon",
|
|
24
24
|
"group_address": "Adresse",
|
|
25
25
|
"group_contact": "Kontakt",
|
|
26
|
-
"group_other": "Sonstiges"
|
|
26
|
+
"group_other": "Sonstiges",
|
|
27
|
+
"frontend_config": {
|
|
28
|
+
"group_brand": "Marke",
|
|
29
|
+
"group_contact": "Kontakt (Frontend)",
|
|
30
|
+
"group_features": "Funktionen",
|
|
31
|
+
"group_social": "Social Media",
|
|
32
|
+
"group_seo": "SEO",
|
|
33
|
+
"brand_name": "Markenname",
|
|
34
|
+
"brand_logo_alt": "Logo Alt-Text",
|
|
35
|
+
"color_scheme": "Farbschema",
|
|
36
|
+
"logo_slug": "Logo",
|
|
37
|
+
"contact_url": "Kontaktseiten-URL",
|
|
38
|
+
"contact_email": "Kontakt-E-Mail",
|
|
39
|
+
"contact_whatsapp_url": "WhatsApp-URL",
|
|
40
|
+
"features_order_line": "Bestellstrecke",
|
|
41
|
+
"features_appointments": "Terminbuchung",
|
|
42
|
+
"features_clickpath": "Clickpath",
|
|
43
|
+
"features_footer_menu": "Footer-Menü",
|
|
44
|
+
"social_instagram": "Instagram",
|
|
45
|
+
"social_facebook": "Facebook",
|
|
46
|
+
"seo_site_name": "Seitenname"
|
|
47
|
+
},
|
|
48
|
+
"global_components": {
|
|
49
|
+
"title": "Globale Komponenten",
|
|
50
|
+
"footer": "Footer",
|
|
51
|
+
"no_footer": "Kein Footer konfiguriert",
|
|
52
|
+
"create_footer": "Footer erstellen",
|
|
53
|
+
"edit_footer": "Footer bearbeiten",
|
|
54
|
+
"unlink_footer": "Verknüpfung lösen",
|
|
55
|
+
"footer_created": "Footer erfolgreich erstellt.",
|
|
56
|
+
"published": "Veröffentlicht",
|
|
57
|
+
"draft": "Entwurf",
|
|
58
|
+
"no_languages": "Keine Sprachen für diesen Mandanten konfiguriert. Erstellen Sie zuerst Navigationsbäume."
|
|
59
|
+
}
|
|
27
60
|
}
|
|
@@ -23,5 +23,38 @@
|
|
|
23
23
|
"contact_phone": "Phone",
|
|
24
24
|
"group_address": "Address",
|
|
25
25
|
"group_contact": "Contact",
|
|
26
|
-
"group_other": "Other"
|
|
26
|
+
"group_other": "Other",
|
|
27
|
+
"frontend_config": {
|
|
28
|
+
"group_brand": "Brand",
|
|
29
|
+
"group_contact": "Contact (Frontend)",
|
|
30
|
+
"group_features": "Features",
|
|
31
|
+
"group_social": "Social Media",
|
|
32
|
+
"group_seo": "SEO",
|
|
33
|
+
"brand_name": "Brand Name",
|
|
34
|
+
"brand_logo_alt": "Logo Alt Text",
|
|
35
|
+
"color_scheme": "Color Scheme",
|
|
36
|
+
"logo_slug": "Logo",
|
|
37
|
+
"contact_url": "Contact Page URL",
|
|
38
|
+
"contact_email": "Contact Email",
|
|
39
|
+
"contact_whatsapp_url": "WhatsApp URL",
|
|
40
|
+
"features_order_line": "Order Line",
|
|
41
|
+
"features_appointments": "Appointments",
|
|
42
|
+
"features_clickpath": "Clickpath",
|
|
43
|
+
"features_footer_menu": "Footer Menu",
|
|
44
|
+
"social_instagram": "Instagram",
|
|
45
|
+
"social_facebook": "Facebook",
|
|
46
|
+
"seo_site_name": "Site Name"
|
|
47
|
+
},
|
|
48
|
+
"global_components": {
|
|
49
|
+
"title": "Global Components",
|
|
50
|
+
"footer": "Footer",
|
|
51
|
+
"no_footer": "No footer configured",
|
|
52
|
+
"create_footer": "Create Footer",
|
|
53
|
+
"edit_footer": "Edit Footer",
|
|
54
|
+
"unlink_footer": "Unlink",
|
|
55
|
+
"footer_created": "Footer created successfully.",
|
|
56
|
+
"published": "Published",
|
|
57
|
+
"draft": "Draft",
|
|
58
|
+
"no_languages": "No languages configured for this client. Add navigation trees first."
|
|
59
|
+
}
|
|
27
60
|
}
|
|
@@ -2,19 +2,148 @@
|
|
|
2
2
|
<script setup lang="ts">
|
|
3
3
|
import { clientFormMeta } from '../../../../types/generated/form-meta'
|
|
4
4
|
import { clientFormConfig } from '@motor-cms/ui-core/app/types/config/client'
|
|
5
|
+
import { useClientFrontendConfig } from '../../../../composables/useClientFrontendConfig'
|
|
6
|
+
import { useClientLanguages } from '../../../../composables/useClientLanguages'
|
|
5
7
|
|
|
6
8
|
definePageMeta({ layout: 'default', permission: 'clients.read' })
|
|
7
9
|
|
|
10
|
+
const { t } = useI18n()
|
|
11
|
+
const { error: notifyError } = useNotify()
|
|
8
12
|
const route = useRoute()
|
|
9
|
-
const
|
|
13
|
+
const clientId = route.params.id as string
|
|
14
|
+
|
|
15
|
+
const isFrontendConfigEnabled
|
|
16
|
+
= useRuntimeConfig().public.featureClientFrontendConfig === true
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
fields,
|
|
20
|
+
schema,
|
|
21
|
+
groups,
|
|
22
|
+
state,
|
|
23
|
+
loading,
|
|
24
|
+
fetching,
|
|
25
|
+
fetchError,
|
|
26
|
+
canWrite,
|
|
27
|
+
pageTitle,
|
|
28
|
+
formRef,
|
|
29
|
+
onSubmit,
|
|
30
|
+
onSaveAndContinue,
|
|
31
|
+
onSaveAndNew,
|
|
32
|
+
deleteRecord,
|
|
33
|
+
deleting
|
|
34
|
+
} = await useEntityForm({
|
|
10
35
|
apiEndpoint: '/api/v2/clients',
|
|
11
36
|
routePrefix: '/motor-admin/clients',
|
|
12
37
|
translationPrefix: 'motor-admin.clients',
|
|
13
38
|
formMeta: clientFormMeta,
|
|
14
39
|
formConfig: clientFormConfig,
|
|
15
40
|
mode: 'edit',
|
|
16
|
-
id:
|
|
41
|
+
id: clientId,
|
|
42
|
+
beforeSubmit: (data) => {
|
|
43
|
+
if (!isFrontendConfigEnabled) return
|
|
44
|
+
if (!validateFrontendConfig()) {
|
|
45
|
+
throw new Error(t('motor-core.global.validation_failed'))
|
|
46
|
+
}
|
|
47
|
+
data.frontend_config = getFrontendConfigSubmitData()
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const { data: clientRecord } = useNuxtData<{ data: Record<string, unknown> }>(
|
|
52
|
+
`entity-form-/api/v2/clients-${clientId}`
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
state: frontendConfigState,
|
|
57
|
+
errors: frontendConfigErrors,
|
|
58
|
+
fields: frontendConfigFields,
|
|
59
|
+
groups: frontendConfigGroups,
|
|
60
|
+
validate: validateFrontendConfig,
|
|
61
|
+
getSubmitData: getFrontendConfigSubmitData
|
|
62
|
+
} = useClientFrontendConfig({ clientRecord, fetching })
|
|
63
|
+
|
|
64
|
+
const colorSchemeOptions = [
|
|
65
|
+
{ label: 'energis', value: 'energis' },
|
|
66
|
+
{ label: 'highspeed', value: 'highspeed' },
|
|
67
|
+
{ label: 'jaeckel', value: 'jaeckel' }
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
const logoSlugOptions = [
|
|
71
|
+
{ label: 'energis', value: 'energis' },
|
|
72
|
+
{ label: 'highspeed', value: 'highspeed' },
|
|
73
|
+
{ label: 'jaeckel', value: 'jaeckel' }
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
const clientIdRef = computed(() =>
|
|
77
|
+
isFrontendConfigEnabled ? (route.params.id as string) : ''
|
|
78
|
+
)
|
|
79
|
+
const { languages, isMultiLanguage, loading: languagesLoading } = useClientLanguages(clientIdRef)
|
|
80
|
+
|
|
81
|
+
const footerMap = computed(() => {
|
|
82
|
+
const fc = clientRecord.value?.data?.frontend_config as Record<string, unknown> | undefined
|
|
83
|
+
const gc = fc?.globalComponents as Record<string, unknown> | undefined
|
|
84
|
+
return gc?.footer as Record<string, string> | undefined
|
|
17
85
|
})
|
|
86
|
+
|
|
87
|
+
const sanctumClient = useSanctumClient()
|
|
88
|
+
|
|
89
|
+
async function onFooterLinked(languageId: number, uuid: string, _pageId: number) {
|
|
90
|
+
try {
|
|
91
|
+
const freshClient = await sanctumClient<{ data: Record<string, unknown> }>(
|
|
92
|
+
`/api/v2/clients/${clientId}`
|
|
93
|
+
)
|
|
94
|
+
const freshConfig = (freshClient.data.frontend_config as Record<string, unknown>) ?? {}
|
|
95
|
+
const freshGc = (freshConfig.globalComponents as Record<string, unknown>) ?? {}
|
|
96
|
+
const freshFooter = { ...(freshGc.footer as Record<string, string>) ?? {} }
|
|
97
|
+
freshFooter[String(languageId)] = uuid
|
|
98
|
+
|
|
99
|
+
await sanctumClient(`/api/v2/clients/${clientId}`, {
|
|
100
|
+
method: 'PATCH',
|
|
101
|
+
body: {
|
|
102
|
+
name: freshClient.data.name,
|
|
103
|
+
slug: freshClient.data.slug,
|
|
104
|
+
frontend_config: {
|
|
105
|
+
...freshConfig,
|
|
106
|
+
globalComponents: { ...freshGc, footer: freshFooter }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await refreshNuxtData(`entity-form-/api/v2/clients-${clientId}`)
|
|
112
|
+
} catch (err: unknown) {
|
|
113
|
+
const message = err instanceof Error ? err.message : t('motor-core.errors.update_failed')
|
|
114
|
+
notifyError(t('motor-admin.clients.edit_title'), message)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function onFooterUnlinked(languageId: number) {
|
|
119
|
+
try {
|
|
120
|
+
const freshClient = await sanctumClient<{ data: Record<string, unknown> }>(
|
|
121
|
+
`/api/v2/clients/${clientId}`
|
|
122
|
+
)
|
|
123
|
+
const freshConfig = (freshClient.data.frontend_config as Record<string, unknown>) ?? {}
|
|
124
|
+
const freshGc = (freshConfig.globalComponents as Record<string, unknown>) ?? {}
|
|
125
|
+
const freshFooter = { ...(freshGc.footer as Record<string, string>) ?? {} }
|
|
126
|
+
delete freshFooter[String(languageId)]
|
|
127
|
+
|
|
128
|
+
await sanctumClient(`/api/v2/clients/${clientId}`, {
|
|
129
|
+
method: 'PATCH',
|
|
130
|
+
body: {
|
|
131
|
+
name: freshClient.data.name,
|
|
132
|
+
slug: freshClient.data.slug,
|
|
133
|
+
frontend_config: {
|
|
134
|
+
...freshConfig,
|
|
135
|
+
globalComponents: { ...freshGc, footer: freshFooter }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
await refreshNuxtData(`entity-form-/api/v2/clients-${clientId}`)
|
|
141
|
+
} catch (err: unknown) {
|
|
142
|
+
const message = err instanceof Error ? err.message : t('motor-core.errors.update_failed')
|
|
143
|
+
notifyError(t('motor-admin.clients.edit_title'), message)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
18
147
|
</script>
|
|
19
148
|
|
|
20
149
|
<template>
|
|
@@ -40,6 +169,32 @@ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite,
|
|
|
40
169
|
@submit="onSubmit"
|
|
41
170
|
@save-and-continue="onSaveAndContinue"
|
|
42
171
|
@save-and-new="onSaveAndNew"
|
|
43
|
-
|
|
172
|
+
>
|
|
173
|
+
<template
|
|
174
|
+
v-if="isFrontendConfigEnabled"
|
|
175
|
+
#after-fields
|
|
176
|
+
>
|
|
177
|
+
<ClientFrontendConfigSection
|
|
178
|
+
:state="frontendConfigState"
|
|
179
|
+
:fields="frontendConfigFields"
|
|
180
|
+
:groups="frontendConfigGroups"
|
|
181
|
+
:errors="frontendConfigErrors"
|
|
182
|
+
:disabled="!canWrite"
|
|
183
|
+
:color-scheme-options="colorSchemeOptions"
|
|
184
|
+
:logo-slug-options="logoSlugOptions"
|
|
185
|
+
/>
|
|
186
|
+
<ClientGlobalComponentsSection
|
|
187
|
+
:client-id="route.params.id"
|
|
188
|
+
:client-name="(state.name as string) ?? ''"
|
|
189
|
+
:footer-map="footerMap"
|
|
190
|
+
:languages="languages"
|
|
191
|
+
:is-multi-language="isMultiLanguage"
|
|
192
|
+
:languages-loading="languagesLoading"
|
|
193
|
+
:disabled="!canWrite"
|
|
194
|
+
@footer-linked="onFooterLinked"
|
|
195
|
+
@footer-unlinked="onFooterUnlinked"
|
|
196
|
+
/>
|
|
197
|
+
</template>
|
|
198
|
+
</FormBase>
|
|
44
199
|
</FormPage>
|
|
45
200
|
</template>
|