@motor-cms/ui-admin 3.0.1 → 4.0.0

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.
@@ -12,6 +12,19 @@ const props = defineProps<{
12
12
  logoSlugOptions: { label: string, value: string }[]
13
13
  }>()
14
14
 
15
+ const appSettings = useAppSettingsStore()
16
+ const isCompact = computed(() => appSettings.formLayout === 'compact')
17
+
18
+ const formFieldUi = computed(() => isCompact.value
19
+ ? { container: 'w-full' }
20
+ : { container: 'w-full max-w-2xl' }
21
+ )
22
+
23
+ const cardUi = computed(() => isCompact.value
24
+ ? { root: 'relative flex rounded-lg items-start', container: 'relative flex flex-col p-4 sm:p-6 gap-x-8 gap-y-4', wrapper: 'flex flex-col items-start', body: '' }
25
+ : undefined
26
+ )
27
+
15
28
  // ============================================
16
29
  // Dot-path helpers
17
30
  // ============================================
@@ -59,57 +72,62 @@ function optionsForField(fieldKey: string): { label: string, value: string }[] {
59
72
  </script>
60
73
 
61
74
  <template>
62
- <template
63
- v-for="group in groups"
64
- :key="group.key"
65
- >
66
- <UPageCard :title="group.label">
67
- <div class="space-y-4">
75
+ <div :class="isCompact ? 'grid grid-cols-2 gap-4' : 'flex flex-col gap-4'">
76
+ <UPageCard
77
+ v-for="(group, groupIdx) in groups"
78
+ :key="group.key"
79
+ :class="isCompact && groups.length % 2 === 1 && groupIdx === groups.length - 1 ? 'col-span-2' : ''"
80
+ :title="group.label"
81
+ :ui="cardUi"
82
+ >
83
+ <div :class="isCompact ? 'grid grid-cols-12 gap-x-4 gap-y-3' : 'space-y-4'">
68
84
  <template
69
85
  v-for="field in fieldsForGroup(group.key)"
70
86
  :key="field.key"
71
87
  >
72
- <UFormField
73
- :label="field.label"
74
- :name="field.key"
75
- :required="field.required"
76
- :error="errors[field.key]"
77
- orientation="horizontal"
78
- :ui="{ container: 'w-full max-w-2xl' }"
79
- >
80
- <!-- Toggle -->
81
- <USwitch
82
- v-if="field.input === 'toggle'"
83
- :model-value="(getNestedValue(state, field.key) as boolean) ?? false"
84
- :disabled="disabled"
85
- @update:model-value="setNestedValue(state, field.key, $event)"
86
- />
88
+ <div :class="isCompact ? 'col-span-12' : ''">
89
+ <UFormField
90
+ :label="field.label"
91
+ :name="field.key"
92
+ :required="field.required"
93
+ :error="errors[field.key]"
94
+ :orientation="isCompact ? 'vertical' : 'horizontal'"
95
+ :ui="formFieldUi"
96
+ >
97
+ <!-- Toggle -->
98
+ <USwitch
99
+ v-if="field.input === 'toggle'"
100
+ :model-value="(getNestedValue(state, field.key) as boolean) ?? false"
101
+ :disabled="disabled"
102
+ @update:model-value="setNestedValue(state, field.key, $event)"
103
+ />
87
104
 
88
- <!-- Select -->
89
- <USelectMenu
90
- v-else-if="field.input === 'select'"
91
- :model-value="(getNestedValue(state, field.key) as string | undefined)"
92
- :items="optionsForField(field.key)"
93
- value-key="value"
94
- label-key="label"
95
- :placeholder="field.label"
96
- :disabled="disabled"
97
- class="w-full"
98
- @update:model-value="setNestedValue(state, field.key, $event)"
99
- />
105
+ <!-- Select -->
106
+ <USelectMenu
107
+ v-else-if="field.input === 'select'"
108
+ :model-value="(getNestedValue(state, field.key) as string | undefined)"
109
+ :items="optionsForField(field.key)"
110
+ value-key="value"
111
+ label-key="label"
112
+ :placeholder="field.label"
113
+ :disabled="disabled"
114
+ class="w-full"
115
+ @update:model-value="setNestedValue(state, field.key, $event)"
116
+ />
100
117
 
101
- <!-- Text / Email / URL -->
102
- <UInput
103
- v-else
104
- :model-value="(getNestedValue(state, field.key) as string) ?? ''"
105
- :type="field.input === 'email' ? 'email' : field.input === 'url' ? 'url' : 'text'"
106
- :disabled="disabled"
107
- class="w-full"
108
- @update:model-value="setNestedValue(state, field.key, $event)"
109
- />
110
- </UFormField>
118
+ <!-- Text / Email / URL -->
119
+ <UInput
120
+ v-else
121
+ :model-value="(getNestedValue(state, field.key) as string) ?? ''"
122
+ :type="field.input === 'email' ? 'email' : field.input === 'url' ? 'url' : 'text'"
123
+ :disabled="disabled"
124
+ class="w-full"
125
+ @update:model-value="setNestedValue(state, field.key, $event)"
126
+ />
127
+ </UFormField>
128
+ </div>
111
129
  </template>
112
130
  </div>
113
131
  </UPageCard>
114
- </template>
132
+ </div>
115
133
  </template>
@@ -17,13 +17,24 @@ const emit = defineEmits<{
17
17
  'footer-unlinked': [languageId: number]
18
18
  }>()
19
19
 
20
+ const appSettings = useAppSettingsStore()
21
+ const isCompact = computed(() => appSettings.formLayout === 'compact')
22
+
23
+ const cardUi = computed(() => isCompact.value
24
+ ? { root: 'relative flex rounded-lg items-start', container: 'relative flex flex-col p-4 sm:p-6 gap-x-8 gap-y-4', wrapper: 'flex flex-col items-start', body: '' }
25
+ : undefined
26
+ )
27
+
20
28
  function getFooterUuid(languageId: number): string | null {
21
29
  return props.footerMap?.[String(languageId)] ?? null
22
30
  }
23
31
  </script>
24
32
 
25
33
  <template>
26
- <UPageCard :title="t('motor-admin.clients.global_components.title')">
34
+ <UPageCard
35
+ :title="t('motor-admin.clients.global_components.title')"
36
+ :ui="cardUi"
37
+ >
27
38
  <div v-if="languagesLoading" class="flex items-center gap-2 text-muted py-4">
28
39
  <UIcon name="i-lucide-loader-2" class="size-4 animate-spin" />
29
40
  <span class="text-sm">{{ t('motor-core.global.loading') }}</span>
@@ -31,20 +42,24 @@ function getFooterUuid(languageId: number): string | null {
31
42
  <div v-else-if="languages.length === 0" class="text-sm text-muted py-4">
32
43
  {{ t('motor-admin.clients.global_components.no_languages') }}
33
44
  </div>
34
- <template v-else>
35
- <ClientFooterSlotCard
45
+ <div v-else :class="isCompact ? 'grid grid-cols-12 gap-x-4 gap-y-3' : 'space-y-4'">
46
+ <div
36
47
  v-for="lang in languages"
37
48
  :key="lang.id"
38
- :client-id="clientId"
39
- :client-name="clientName"
40
- :language-id="lang.id"
41
- :language-name="lang.name"
42
- :show-language-label="isMultiLanguage"
43
- :builder-page-uuid="getFooterUuid(lang.id)"
44
- :disabled="disabled"
45
- @linked="(uuid: string, pageId: number) => emit('footer-linked', lang.id, uuid, pageId)"
46
- @unlinked="emit('footer-unlinked', lang.id)"
47
- />
48
- </template>
49
+ :class="isCompact ? 'col-span-12' : ''"
50
+ >
51
+ <ClientFooterSlotCard
52
+ :client-id="clientId"
53
+ :client-name="clientName"
54
+ :language-id="lang.id"
55
+ :language-name="lang.name"
56
+ :show-language-label="isMultiLanguage"
57
+ :builder-page-uuid="getFooterUuid(lang.id)"
58
+ :disabled="disabled"
59
+ @linked="(uuid: string, pageId: number) => emit('footer-linked', lang.id, uuid, pageId)"
60
+ @unlinked="emit('footer-unlinked', lang.id)"
61
+ />
62
+ </div>
63
+ </div>
49
64
  </UPageCard>
50
65
  </template>
@@ -28,16 +28,16 @@ export const clientFormMeta = {
28
28
  fields: {
29
29
  name: { input: 'text', required: true },
30
30
  slug: { input: 'text', required: true },
31
- address: { input: 'text' },
32
- zip: { input: 'text' },
33
- city: { input: 'text' },
34
- country_iso_3166_1: { input: 'text' },
31
+ address: { input: 'text', required: true },
32
+ zip: { input: 'text', required: true },
33
+ city: { input: 'text', required: true },
34
+ country_iso_3166_1: { input: 'text', required: true },
35
+ is_active: { input: 'toggle', required: true },
36
+ contact_name: { input: 'text', required: true },
37
+ contact_email: { input: 'email', required: true },
38
+ contact_phone: { input: 'text', required: true },
35
39
  website: { input: 'text' },
36
- description: { input: 'textarea' },
37
- is_active: { input: 'toggle' },
38
- contact_name: { input: 'text' },
39
- contact_email: { input: 'email' },
40
- contact_phone: { input: 'text' }
40
+ description: { input: 'textarea' }
41
41
  }
42
42
  },
43
43
  patch: {
@@ -45,16 +45,16 @@ export const clientFormMeta = {
45
45
  fields: {
46
46
  name: { input: 'text', required: true },
47
47
  slug: { input: 'text', required: true },
48
- address: { input: 'text' },
49
- zip: { input: 'text' },
50
- city: { input: 'text' },
51
- country_iso_3166_1: { input: 'text' },
48
+ address: { input: 'text', required: true },
49
+ zip: { input: 'text', required: true },
50
+ city: { input: 'text', required: true },
51
+ country_iso_3166_1: { input: 'text', required: true },
52
+ is_active: { input: 'toggle', required: true },
53
+ contact_name: { input: 'text', required: true },
54
+ contact_email: { input: 'email', required: true },
55
+ contact_phone: { input: 'text', required: true },
52
56
  website: { input: 'text' },
53
- description: { input: 'textarea' },
54
- is_active: { input: 'toggle' },
55
- contact_name: { input: 'text' },
56
- contact_email: { input: 'email' },
57
- contact_phone: { input: 'text' }
57
+ description: { input: 'textarea' }
58
58
  }
59
59
  }
60
60
  } as const
@@ -7,12 +7,10 @@ export const aISystemPromptMeta = {
7
7
  schemaName: 'AISystemPromptResource',
8
8
  fields: {
9
9
  id: { type: 'integer', renderer: 'number', hideable: false },
10
- name: { type: 'string', sortable: true, hideable: false },
11
- prompt: { type: 'string' },
12
10
  client: { type: 'ref', renderer: 'text', ref: 'ClientResource', labelKey: 'name' },
13
11
  client_id: { type: 'integer', renderer: 'number' },
14
- created_at: { type: 'string' },
15
- updated_at: { type: 'string' }
12
+ name: { type: 'string', sortable: true, hideable: false },
13
+ prompt: { type: 'string' }
16
14
  }
17
15
  } as const
18
16
 
@@ -31,9 +29,7 @@ export const clientMeta = {
31
29
  is_active: { type: 'boolean', renderer: 'boolean' },
32
30
  contact_name: { type: 'string' },
33
31
  contact_phone: { type: 'string' },
34
- contact_email: { type: 'string', renderer: 'link' },
35
- created_at: { type: 'string' },
36
- updated_at: { type: 'string' }
32
+ contact_email: { type: 'string', renderer: 'link' }
37
33
  }
38
34
  } as const
39
35
 
@@ -45,9 +41,7 @@ export const configVariableMeta = {
45
41
  group: { type: 'string' },
46
42
  name: { type: 'string', sortable: true, hideable: false },
47
43
  value: { type: 'string' },
48
- is_invisible: { type: 'boolean', renderer: 'boolean' },
49
- created_at: { type: 'string' },
50
- updated_at: { type: 'string' }
44
+ is_invisible: { type: 'boolean', renderer: 'boolean' }
51
45
  }
52
46
  } as const
53
47
 
@@ -55,16 +49,14 @@ export const domainMeta = {
55
49
  schemaName: 'DomainResource',
56
50
  fields: {
57
51
  id: { type: 'integer', renderer: 'number', hideable: false },
58
- name: { type: 'string', sortable: true, hideable: false },
59
52
  client: { type: 'ref', renderer: 'text', ref: 'ClientResource', labelKey: 'name' },
60
- client_id: { type: 'string' },
53
+ client_id: { type: 'integer', renderer: 'number' },
61
54
  is_active: { type: 'boolean', renderer: 'boolean' },
55
+ name: { type: 'string', sortable: true, hideable: false },
62
56
  protocol: { type: 'string' },
63
57
  host: { type: 'string' },
64
58
  port: { type: 'integer', renderer: 'number' },
65
- path: { type: 'string' },
66
- created_at: { type: 'string' },
67
- updated_at: { type: 'string' }
59
+ path: { type: 'string' }
68
60
  }
69
61
  } as const
70
62
 
@@ -72,12 +64,12 @@ export const emailTemplateMeta = {
72
64
  schemaName: 'EmailTemplateResource',
73
65
  fields: {
74
66
  id: { type: 'integer', renderer: 'number', hideable: false },
75
- name: { type: 'string', sortable: true, hideable: false },
76
- slug: { type: 'string' },
77
67
  client: { type: 'ref', renderer: 'text', ref: 'ClientResource', labelKey: 'name' },
78
68
  client_id: { type: 'integer', renderer: 'number' },
79
69
  language: { type: 'ref', renderer: 'text', ref: 'LanguageResource', labelKey: 'name' },
80
70
  language_id: { type: 'integer', renderer: 'number' },
71
+ name: { type: 'string', sortable: true, hideable: false },
72
+ slug: { type: 'string' },
81
73
  subject: { type: 'string' },
82
74
  body_text: { type: 'string' },
83
75
  body_html: { type: 'string' },
@@ -113,9 +105,7 @@ export const languageMeta = {
113
105
  id: { type: 'integer', renderer: 'number', hideable: false },
114
106
  iso_639_1: { type: 'string' },
115
107
  english_name: { type: 'string' },
116
- native_name: { type: 'string' },
117
- created_at: { type: 'string' },
118
- updated_at: { type: 'string' }
108
+ native_name: { type: 'string' }
119
109
  }
120
110
  } as const
121
111
 
@@ -124,11 +114,8 @@ export const permissionGroupMeta = {
124
114
  fields: {
125
115
  id: { type: 'integer', renderer: 'number', hideable: false },
126
116
  name: { type: 'string', sortable: true, hideable: false },
127
- sort_position: { type: 'string' },
128
- permission_names: { type: 'string' },
129
- permissions: { type: 'ref[]', renderer: 'list', ref: 'PermissionResource', labelKey: 'name' },
130
- created_at: { type: 'string' },
131
- updated_at: { type: 'string' }
117
+ sort_position: { type: 'integer', renderer: 'number' },
118
+ permissions: { type: 'ref[]', renderer: 'list', ref: 'PermissionResource', labelKey: 'name' }
132
119
  }
133
120
  } as const
134
121
 
@@ -138,9 +125,7 @@ export const permissionMeta = {
138
125
  id: { type: 'integer', renderer: 'number', hideable: false },
139
126
  name: { type: 'string', sortable: true, hideable: false },
140
127
  guard_name: { type: 'string' },
141
- permission_group: { type: 'ref', renderer: 'text', ref: 'PermissionGroupResource', labelKey: 'name' },
142
- created_at: { type: 'string' },
143
- updated_at: { type: 'string' }
128
+ permission_group: { type: 'ref', renderer: 'text', ref: 'PermissionGroupResource', labelKey: 'name' }
144
129
  }
145
130
  } as const
146
131
 
@@ -150,9 +135,7 @@ export const roleMeta = {
150
135
  id: { type: 'integer', renderer: 'number', hideable: false },
151
136
  name: { type: 'string', sortable: true, hideable: false },
152
137
  guard_name: { type: 'string' },
153
- permissions: { type: 'ref[]', renderer: 'list', ref: 'PermissionResource', labelKey: 'name' },
154
- created_at: { type: 'string' },
155
- updated_at: { type: 'string' }
138
+ permissions: { type: 'ref[]', renderer: 'list', ref: 'PermissionResource', labelKey: 'name' }
156
139
  }
157
140
  } as const
158
141
 
@@ -160,13 +143,12 @@ export const userMeta = {
160
143
  schemaName: 'UserResource',
161
144
  fields: {
162
145
  id: { type: 'integer', renderer: 'number', hideable: false },
163
- name: { type: 'string', sortable: true, hideable: false },
164
- email: { type: 'string', renderer: 'link' },
165
- avatar: { type: 'ref', renderer: 'avatar', ref: 'MediaResource' },
166
146
  clients: { type: 'ref[]', renderer: 'list', ref: 'ClientResource', labelKey: 'name' },
167
147
  roles: { type: 'ref[]', renderer: 'list', ref: 'RoleResource', labelKey: 'name' },
168
148
  permissions: { type: 'ref[]', renderer: 'list', ref: 'PermissionResource', labelKey: 'name' },
169
- created_at: { type: 'string' },
170
- updated_at: { type: 'string' }
149
+ name: { type: 'string', sortable: true, hideable: false },
150
+ email: { type: 'string', renderer: 'link' },
151
+ show_onboarding: { type: 'boolean', renderer: 'boolean' },
152
+ avatar: { type: 'ref', renderer: 'avatar', ref: 'MediaResource' }
171
153
  }
172
154
  } as const
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motor-cms/ui-admin",
3
- "version": "3.0.1",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -20,7 +20,7 @@
20
20
  "zod": "^4.0.0"
21
21
  },
22
22
  "peerDependencies": {
23
- "@motor-cms/ui-core": ">=3.0.1",
23
+ "@motor-cms/ui-core": ">=4.0.0",
24
24
  "nuxt": "^4.0.0",
25
25
  "vue": "^3.5.0"
26
26
  }