@motor-cms/ui-admin 4.0.4 → 4.0.5

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.
@@ -160,7 +160,14 @@ function onEditFooter(): void {
160
160
  }
161
161
  }
162
162
 
163
+ const unlinkConfirmOpen = ref(false)
164
+
163
165
  function onUnlinkFooter(): void {
166
+ unlinkConfirmOpen.value = true
167
+ }
168
+
169
+ function executeUnlink(): void {
170
+ unlinkConfirmOpen.value = false
164
171
  emit('unlinked')
165
172
  }
166
173
  </script>
@@ -169,7 +176,7 @@ function onUnlinkFooter(): void {
169
176
  <div class="flex items-center justify-between gap-4 rounded-lg border border-default px-4 py-3">
170
177
  <!-- Left: info -->
171
178
  <div class="flex items-center gap-3 min-w-0">
172
- <UIcon name="i-lucide-panel-bottom" class="size-5 text-muted shrink-0" />
179
+ <UIcon :name="pageInfo ? 'i-lucide-link' : 'i-lucide-unlink'" class="size-5 shrink-0" :class="pageInfo ? 'text-primary' : 'text-dimmed'" />
173
180
 
174
181
  <div class="min-w-0">
175
182
  <!-- Label -->
@@ -234,6 +241,7 @@ function onUnlinkFooter(): void {
234
241
  {{ t('motor-admin.clients.global_components.edit_footer') }}
235
242
  </UButton>
236
243
  <UButton
244
+ icon="i-lucide-unlink"
237
245
  variant="ghost"
238
246
  color="error"
239
247
  size="sm"
@@ -258,4 +266,47 @@ function onUnlinkFooter(): void {
258
266
  </template>
259
267
  </div>
260
268
  </div>
269
+
270
+ <!-- Unlink confirmation modal -->
271
+ <UModal v-model:open="unlinkConfirmOpen">
272
+ <template #header>
273
+ {{ t('motor-admin.clients.global_components.unlink_footer') }}
274
+ </template>
275
+ <template #body>
276
+ <div class="space-y-3 text-sm">
277
+ <p>{{ t('motor-admin.clients.global_components.unlink_confirm') }}</p>
278
+ <div
279
+ v-if="pageInfo"
280
+ class="rounded-md bg-[var(--ui-bg-elevated)] px-3 py-2"
281
+ >
282
+ <div class="flex items-center gap-1.5">
283
+ <UIcon
284
+ name="i-lucide-panel-bottom"
285
+ class="size-3.5 shrink-0 text-muted"
286
+ />
287
+ <span class="font-medium">{{ pageInfo.name }}</span>
288
+ </div>
289
+ </div>
290
+ <p class="text-muted">{{ t('motor-admin.clients.global_components.unlink_effect') }}</p>
291
+ </div>
292
+ </template>
293
+ <template #footer>
294
+ <div class="flex justify-end gap-2">
295
+ <UButton
296
+ color="neutral"
297
+ variant="outline"
298
+ @click="unlinkConfirmOpen = false"
299
+ >
300
+ {{ t('motor-core.global.cancel') }}
301
+ </UButton>
302
+ <UButton
303
+ color="error"
304
+ icon="i-lucide-unlink"
305
+ @click="executeUnlink"
306
+ >
307
+ {{ t('motor-admin.clients.global_components.unlink_footer') }}
308
+ </UButton>
309
+ </div>
310
+ </template>
311
+ </UModal>
261
312
  </template>
@@ -53,7 +53,7 @@ export function useClientFrontendConfig(
53
53
  const raw = options.clientRecord.value?.data?.frontend_config
54
54
  if (!raw) return
55
55
 
56
- const parsed = frontendConfigSchema.safeParse(raw)
56
+ const parsed = frontendConfigSchema(t).safeParse(raw)
57
57
 
58
58
  if (parsed.success) {
59
59
  Object.assign(state, parsed.data)
@@ -85,7 +85,7 @@ export function useClientFrontendConfig(
85
85
  // ============================================
86
86
 
87
87
  function validate(): boolean {
88
- const result = frontendConfigSchema.safeParse(state)
88
+ const result = frontendConfigSchema(t).safeParse(state)
89
89
  if (result.success) {
90
90
  errors.value = {}
91
91
  return true
@@ -55,6 +55,8 @@
55
55
  "footer_created": "Footer erfolgreich erstellt.",
56
56
  "published": "Veröffentlicht",
57
57
  "draft": "Entwurf",
58
+ "unlink_confirm": "Möchten Sie die Verknüpfung zu diesem Footer wirklich lösen?",
59
+ "unlink_effect": "Der Footer wird nicht gelöscht, sondern nur die Verknüpfung zum Mandanten entfernt.",
58
60
  "no_languages": "Keine Sprachen für diesen Mandanten konfiguriert. Erstellen Sie zuerst Navigationsbäume."
59
61
  }
60
62
  }
@@ -55,6 +55,8 @@
55
55
  "footer_created": "Footer created successfully.",
56
56
  "published": "Published",
57
57
  "draft": "Draft",
58
+ "unlink_confirm": "Are you sure you want to unlink this footer?",
59
+ "unlink_effect": "The footer will not be deleted, only the link to the client will be removed.",
58
60
  "no_languages": "No languages configured for this client. Add navigation trees first."
59
61
  }
60
62
  }
@@ -39,39 +39,45 @@ export interface FrontendConfig {
39
39
  // Zod Schema (editable fields only, no globalComponents)
40
40
  // ============================================
41
41
 
42
- const optionalUrlSchema = z.string().refine(
43
- (val) => val === '' || val === null || (() => { try { new URL(val); return true } catch { return false } })(),
44
- { message: 'Must be a valid URL or empty' }
45
- )
46
-
47
- export const frontendConfigSchema = z.object({
48
- brand: z.object({
49
- name: z.string().min(1, { message: 'Required' }),
50
- logoAlt: z.string().min(1, { message: 'Required' })
51
- }),
52
- colorScheme: z.string().min(1, { message: 'Required' }),
53
- logoSlug: z.string().min(1, { message: 'Required' }),
54
- contact: z.object({
55
- contactUrl: z.string().url({ message: 'Must be a valid URL' }),
56
- email: z.string().email({ message: 'Must be a valid email' }),
57
- whatsappUrl: optionalUrlSchema.nullable().optional().transform((v) => v ?? null)
58
- }),
59
- features: z.object({
60
- orderLine: z.boolean().default(false),
61
- appointments: z.boolean().default(false),
62
- clickpath: z.boolean().default(false),
63
- footerMenu: z.boolean().default(false)
64
- }),
65
- social: z.object({
66
- instagram: optionalUrlSchema.nullable().optional().transform((v) => v ?? null),
67
- facebook: optionalUrlSchema.nullable().optional().transform((v) => v ?? null)
68
- }),
69
- seo: z.object({
70
- siteName: z.string().min(1, { message: 'Required' })
42
+ type TranslateFunction = (key: string) => string
43
+
44
+ function optionalUrlSchema(t: TranslateFunction) {
45
+ return z.string().refine(
46
+ (val) => val === '' || val === null || (() => { try { new URL(val); return true } catch { return false } })(),
47
+ { message: t('motor-core.global.validation_url') }
48
+ )
49
+ }
50
+
51
+ export function frontendConfigSchema(t: TranslateFunction) {
52
+ return z.object({
53
+ brand: z.object({
54
+ name: z.string().min(1, { message: t('motor-core.global.validation_required') }),
55
+ logoAlt: z.string().min(1, { message: t('motor-core.global.validation_required') })
56
+ }),
57
+ colorScheme: z.string().min(1, { message: t('motor-core.global.validation_required') }),
58
+ logoSlug: z.string().min(1, { message: t('motor-core.global.validation_required') }),
59
+ contact: z.object({
60
+ contactUrl: z.string().url({ message: t('motor-core.global.validation_url') }),
61
+ email: z.string().email({ message: t('motor-core.global.validation_email') }),
62
+ whatsappUrl: optionalUrlSchema(t).nullable().optional().transform((v) => v ?? null)
63
+ }),
64
+ features: z.object({
65
+ orderLine: z.boolean().default(false),
66
+ appointments: z.boolean().default(false),
67
+ clickpath: z.boolean().default(false),
68
+ footerMenu: z.boolean().default(false)
69
+ }),
70
+ social: z.object({
71
+ instagram: optionalUrlSchema(t).nullable().optional().transform((v) => v ?? null),
72
+ facebook: optionalUrlSchema(t).nullable().optional().transform((v) => v ?? null)
73
+ }),
74
+ seo: z.object({
75
+ siteName: z.string().min(1, { message: t('motor-core.global.validation_required') })
76
+ })
71
77
  })
72
- })
78
+ }
73
79
 
74
- export type FrontendConfigFormState = z.infer<typeof frontendConfigSchema>
80
+ export type FrontendConfigFormState = z.infer<ReturnType<typeof frontendConfigSchema>>
75
81
 
76
82
  // ============================================
77
83
  // Form Field Definitions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motor-cms/ui-admin",
3
- "version": "4.0.4",
3
+ "version": "4.0.5",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [