@motor-cms/ui-admin 1.1.0-alpha.4 → 1.1.0-alpha.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.
Files changed (73) hide show
  1. package/app/assets/css/v-onboarding.css +64 -0
  2. package/app/components/OnboardingStep.vue +42 -0
  3. package/app/components/UsersOnboarding.vue +84 -0
  4. package/app/components/client/FooterSlotCard.vue +313 -0
  5. package/app/components/client/GlobalComponentsSection.vue +65 -0
  6. package/app/components/dashboard/DashboardActivity.vue +71 -0
  7. package/app/components/dashboard/DashboardActivityItem.vue +96 -0
  8. package/app/components/dashboard/DashboardAnnouncementModal.vue +327 -0
  9. package/app/components/dashboard/DashboardAnnouncements.vue +93 -0
  10. package/app/components/dashboard/DashboardOnboarding.vue +285 -0
  11. package/app/components/dashboard/DashboardPublishingQueue.vue +47 -0
  12. package/app/components/dashboard/DashboardQuickActions.vue +44 -0
  13. package/app/components/dashboard/DashboardStats.vue +63 -0
  14. package/app/components/form/inputs/CategoryTreePicker.vue +265 -109
  15. package/app/components/form/inputs/EntityConfigurationsPanel.vue +235 -0
  16. package/app/composables/useClientFormExtensions.ts +89 -0
  17. package/app/composables/useClientLanguages.ts +81 -0
  18. package/app/composables/useDashboardData.ts +169 -0
  19. package/app/composables/useOnboardingState.ts +151 -0
  20. package/app/data/footerTemplate.ts +283 -0
  21. package/app/lang/de/motor-admin/ai_system_prompts.json +1 -0
  22. package/app/lang/de/motor-admin/categories.json +1 -0
  23. package/app/lang/de/motor-admin/category_trees.json +2 -1
  24. package/app/lang/de/motor-admin/clients.json +17 -1
  25. package/app/lang/de/motor-admin/config_variables.json +1 -0
  26. package/app/lang/de/motor-admin/dashboard.json +83 -0
  27. package/app/lang/de/motor-admin/domains.json +6 -1
  28. package/app/lang/de/motor-admin/email_templates.json +1 -0
  29. package/app/lang/de/motor-admin/entity_configurations.json +12 -0
  30. package/app/lang/de/motor-admin/languages.json +1 -0
  31. package/app/lang/de/motor-admin/onboarding.json +60 -0
  32. package/app/lang/de/motor-admin/permissions.json +1 -0
  33. package/app/lang/de/motor-admin/roles.json +1 -0
  34. package/app/lang/de/motor-admin/users.json +1 -0
  35. package/app/lang/en/motor-admin/ai_system_prompts.json +1 -0
  36. package/app/lang/en/motor-admin/categories.json +1 -0
  37. package/app/lang/en/motor-admin/category_trees.json +2 -1
  38. package/app/lang/en/motor-admin/clients.json +17 -1
  39. package/app/lang/en/motor-admin/config_variables.json +1 -0
  40. package/app/lang/en/motor-admin/dashboard.json +83 -0
  41. package/app/lang/en/motor-admin/domains.json +6 -1
  42. package/app/lang/en/motor-admin/email_templates.json +1 -0
  43. package/app/lang/en/motor-admin/entity_configurations.json +12 -0
  44. package/app/lang/en/motor-admin/languages.json +1 -0
  45. package/app/lang/en/motor-admin/onboarding.json +60 -0
  46. package/app/lang/en/motor-admin/permissions.json +1 -0
  47. package/app/lang/en/motor-admin/roles.json +1 -0
  48. package/app/lang/en/motor-admin/users.json +1 -0
  49. package/app/pages/index.vue +119 -22
  50. package/app/pages/login.vue +6 -0
  51. package/app/pages/motor-admin/ai-system-prompts/[id]/edit.vue +4 -4
  52. package/app/pages/motor-admin/category-trees/[id]/categories/[categoryId]/edit.vue +4 -3
  53. package/app/pages/motor-admin/category-trees/[id]/edit.vue +4 -4
  54. package/app/pages/motor-admin/clients/[id]/edit.vue +146 -6
  55. package/app/pages/motor-admin/clients/create.vue +34 -2
  56. package/app/pages/motor-admin/config-variables/[id]/edit.vue +4 -4
  57. package/app/pages/motor-admin/domains/[id]/edit.vue +18 -5
  58. package/app/pages/motor-admin/email-templates/[id]/edit.vue +4 -4
  59. package/app/pages/motor-admin/email-templates/index.vue +36 -25
  60. package/app/pages/motor-admin/languages/[id]/edit.vue +17 -4
  61. package/app/pages/motor-admin/languages/create.vue +13 -0
  62. package/app/pages/motor-admin/permission-groups/[id]/edit.vue +4 -4
  63. package/app/pages/motor-admin/roles/[id]/edit.vue +4 -4
  64. package/app/pages/motor-admin/roles/create.vue +4 -1
  65. package/app/pages/motor-admin/users/[id]/edit.vue +4 -3
  66. package/app/pages/motor-admin/users/index.vue +1 -0
  67. package/app/pages/profile.vue +47 -1
  68. package/app/pages/search.vue +13 -3
  69. package/app/types/generated/form-meta.ts +24 -20
  70. package/app/types/generated/grid-meta.ts +5 -3
  71. package/nuxt.config.ts +15 -1
  72. package/package.json +6 -2
  73. package/app/pages/dashboard.vue +0 -5
@@ -3,11 +3,10 @@
3
3
  import { categoryTreeFormMeta } from '@motor-cms/ui-core/app/types/generated/form-meta'
4
4
  import { categoryTreeEditFormConfig } from '@motor-cms/ui-core/app/types/config/category-tree'
5
5
 
6
- definePageMeta({ layout: 'default', permission: 'category-trees.write' })
6
+ definePageMeta({ layout: 'default', permission: 'category-trees.read' })
7
7
 
8
8
  const route = useRoute()
9
- const { t } = useI18n()
10
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
9
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
11
10
  apiEndpoint: '/api/v2/category-trees',
12
11
  routePrefix: '/motor-admin/category-trees',
13
12
  translationPrefix: 'motor-admin.category_trees',
@@ -20,13 +19,14 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, o
20
19
 
21
20
  <template>
22
21
  <FormPage
23
- :title="t('motor-admin.category_trees.edit_title')"
22
+ :title="pageTitle"
24
23
  back-route="/motor-admin/category-trees"
25
24
  :loading="fetching"
26
25
  :error="fetchError"
27
26
  >
28
27
  <FormBase
29
28
  ref="formRef"
29
+ :disabled="!canWrite"
30
30
  v-model:state="state"
31
31
  :fields="fields"
32
32
  :schema="schema"
@@ -2,31 +2,142 @@
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 { useClientLanguages } from '../../../../composables/useClientLanguages'
5
6
 
6
- definePageMeta({ layout: 'default', permission: 'clients.write' })
7
+ definePageMeta({ layout: 'default', permission: 'clients.read' })
7
8
 
8
- const route = useRoute()
9
9
  const { t } = useI18n()
10
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
+ const { error: notifyError } = useNotify()
11
+ const route = useRoute()
12
+ const clientId = route.params.id as string
13
+
14
+ const isFrontendConfigEnabled
15
+ = useRuntimeConfig().public.featureClientFrontendConfig === true
16
+
17
+ const { extensions, allExtensionsValid, validateAll, getAllSubmitData } = useClientFormExtensions()
18
+
19
+ const {
20
+ fields,
21
+ schema,
22
+ groups,
23
+ state,
24
+ loading,
25
+ fetching,
26
+ fetchError,
27
+ canWrite,
28
+ pageTitle,
29
+ formRef,
30
+ onSubmit,
31
+ onSaveAndContinue,
32
+ onSaveAndNew,
33
+ deleteRecord,
34
+ deleting
35
+ } = await useEntityForm({
11
36
  apiEndpoint: '/api/v2/clients',
12
37
  routePrefix: '/motor-admin/clients',
13
38
  translationPrefix: 'motor-admin.clients',
14
39
  formMeta: clientFormMeta,
15
40
  formConfig: clientFormConfig,
16
41
  mode: 'edit',
17
- id: route.params.id as string
42
+ id: clientId,
43
+ beforeSubmit: async (data) => {
44
+ if (!isFrontendConfigEnabled || extensions.value.length === 0) return
45
+ const valid = await validateAll()
46
+ if (!valid) {
47
+ throw new Error(t('motor-core.global.validation_failed'))
48
+ }
49
+ data.frontend_config = {
50
+ ...getAllSubmitData(),
51
+ globalComponents: (data.frontend_config as Record<string, unknown>)?.globalComponents
52
+ ?? (clientRecord.value?.data?.frontend_config as Record<string, unknown>)?.globalComponents,
53
+ }
54
+ }
55
+ })
56
+
57
+ const { data: clientRecord } = useNuxtData<{ data: Record<string, unknown> }>(
58
+ `entity-form-/api/v2/clients-${clientId}`
59
+ )
60
+
61
+ const clientIdRef = computed(() =>
62
+ isFrontendConfigEnabled ? (route.params.id as string) : ''
63
+ )
64
+ const { languages, isMultiLanguage, loading: languagesLoading } = useClientLanguages(clientIdRef)
65
+
66
+ const footerMap = computed(() => {
67
+ const fc = clientRecord.value?.data?.frontend_config as Record<string, unknown> | undefined
68
+ const gc = fc?.globalComponents as Record<string, unknown> | undefined
69
+ return gc?.footer as Record<string, string> | undefined
18
70
  })
71
+
72
+ const sanctumClient = useSanctumClient()
73
+
74
+ async function onFooterLinked(languageId: number, uuid: string, _pageId: number) {
75
+ try {
76
+ const freshClient = await sanctumClient<{ data: Record<string, unknown> }>(
77
+ `/api/v2/clients/${clientId}`
78
+ )
79
+ const freshConfig = (freshClient.data.frontend_config as Record<string, unknown>) ?? {}
80
+ const freshGc = (freshConfig.globalComponents as Record<string, unknown>) ?? {}
81
+ const freshFooter = { ...(freshGc.footer as Record<string, string>) ?? {} }
82
+ freshFooter[String(languageId)] = uuid
83
+
84
+ await sanctumClient(`/api/v2/clients/${clientId}`, {
85
+ method: 'PATCH',
86
+ body: {
87
+ ...freshClient.data,
88
+ frontend_config: {
89
+ ...freshConfig,
90
+ globalComponents: { ...freshGc, footer: freshFooter }
91
+ }
92
+ }
93
+ })
94
+
95
+ await refreshNuxtData(`entity-form-/api/v2/clients-${clientId}`)
96
+ } catch (err: unknown) {
97
+ const message = err instanceof Error ? err.message : t('motor-core.errors.update_failed')
98
+ notifyError(t('motor-admin.clients.edit_title'), message)
99
+ }
100
+ }
101
+
102
+ async function onFooterUnlinked(languageId: number) {
103
+ try {
104
+ const freshClient = await sanctumClient<{ data: Record<string, unknown> }>(
105
+ `/api/v2/clients/${clientId}`
106
+ )
107
+ const freshConfig = (freshClient.data.frontend_config as Record<string, unknown>) ?? {}
108
+ const freshGc = (freshConfig.globalComponents as Record<string, unknown>) ?? {}
109
+ const freshFooter = { ...(freshGc.footer as Record<string, string>) ?? {} }
110
+ delete freshFooter[String(languageId)]
111
+
112
+ await sanctumClient(`/api/v2/clients/${clientId}`, {
113
+ method: 'PATCH',
114
+ body: {
115
+ ...freshClient.data,
116
+ frontend_config: {
117
+ ...freshConfig,
118
+ globalComponents: { ...freshGc, footer: freshFooter }
119
+ }
120
+ }
121
+ })
122
+
123
+ await refreshNuxtData(`entity-form-/api/v2/clients-${clientId}`)
124
+ } catch (err: unknown) {
125
+ const message = err instanceof Error ? err.message : t('motor-core.errors.update_failed')
126
+ notifyError(t('motor-admin.clients.edit_title'), message)
127
+ }
128
+ }
19
129
  </script>
20
130
 
21
131
  <template>
22
132
  <FormPage
23
- :title="t('motor-admin.clients.edit_title')"
133
+ :title="pageTitle"
24
134
  back-route="/motor-admin/clients"
25
135
  :loading="fetching"
26
136
  :error="fetchError"
27
137
  >
28
138
  <FormBase
29
139
  ref="formRef"
140
+ :disabled="!canWrite"
30
141
  v-model:state="state"
31
142
  :fields="fields"
32
143
  :schema="schema"
@@ -40,6 +151,35 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, o
40
151
  @submit="onSubmit"
41
152
  @save-and-continue="onSaveAndContinue"
42
153
  @save-and-new="onSaveAndNew"
43
- />
154
+ >
155
+ <template
156
+ v-if="isFrontendConfigEnabled && extensions.length > 0"
157
+ #after-fields
158
+ >
159
+ <h2 class="text-lg font-semibold text-highlighted mt-2">
160
+ {{ t('motor-admin.clients.frontend_config_title') }}
161
+ </h2>
162
+ <component
163
+ v-for="ext in extensions"
164
+ :key="ext.key"
165
+ :is="ext.component"
166
+ :client-id="clientId"
167
+ :client-record="clientRecord"
168
+ :disabled="!canWrite"
169
+ mode="edit"
170
+ />
171
+ <ClientGlobalComponentsSection
172
+ :client-id="route.params.id"
173
+ :client-name="(state.name as string) ?? ''"
174
+ :footer-map="footerMap"
175
+ :languages="languages"
176
+ :is-multi-language="isMultiLanguage"
177
+ :languages-loading="languagesLoading"
178
+ :disabled="!canWrite || !allExtensionsValid"
179
+ @footer-linked="onFooterLinked"
180
+ @footer-unlinked="onFooterUnlinked"
181
+ />
182
+ </template>
183
+ </FormBase>
44
184
  </FormPage>
45
185
  </template>
@@ -6,13 +6,27 @@ import { clientFormConfig } from '@motor-cms/ui-core/app/types/config/client'
6
6
  definePageMeta({ layout: 'default', permission: 'clients.write' })
7
7
 
8
8
  const { t } = useI18n()
9
+
10
+ const isFrontendConfigEnabled
11
+ = useRuntimeConfig().public.featureClientFrontendConfig === true
12
+
13
+ const { extensions, validateAll, getAllSubmitData } = useClientFormExtensions()
14
+
9
15
  const { fields, schema, groups, state, loading, formRef, onSubmit, onSaveAndNew } = await useEntityForm({
10
16
  apiEndpoint: '/api/v2/clients',
11
17
  routePrefix: '/motor-admin/clients',
12
18
  translationPrefix: 'motor-admin.clients',
13
19
  formMeta: clientFormMeta,
14
20
  formConfig: clientFormConfig,
15
- mode: 'create'
21
+ mode: 'create',
22
+ beforeSubmit: async (data) => {
23
+ if (!isFrontendConfigEnabled || extensions.value.length === 0) return
24
+ const valid = await validateAll()
25
+ if (!valid) {
26
+ throw new Error(t('motor-core.global.validation_failed'))
27
+ }
28
+ data.frontend_config = getAllSubmitData()
29
+ }
16
30
  })
17
31
  </script>
18
32
 
@@ -32,6 +46,24 @@ const { fields, schema, groups, state, loading, formRef, onSubmit, onSaveAndNew
32
46
  show-save-and-new
33
47
  @submit="onSubmit"
34
48
  @save-and-new="onSaveAndNew"
35
- />
49
+ >
50
+ <template
51
+ v-if="isFrontendConfigEnabled && extensions.length > 0"
52
+ #after-fields
53
+ >
54
+ <h2 class="text-lg font-semibold text-highlighted mt-2">
55
+ {{ t('motor-admin.clients.frontend_config_title') }}
56
+ </h2>
57
+ <component
58
+ v-for="ext in extensions"
59
+ :key="ext.key"
60
+ :is="ext.component"
61
+ :client-id="undefined"
62
+ :client-record="null"
63
+ :disabled="false"
64
+ mode="create"
65
+ />
66
+ </template>
67
+ </FormBase>
36
68
  </FormPage>
37
69
  </template>
@@ -2,11 +2,10 @@
2
2
  import { configVariableFormMeta } from '../../../../types/generated/form-meta'
3
3
  import { configVariableFormConfig } from '@motor-cms/ui-core/app/types/config/config-variable'
4
4
 
5
- definePageMeta({ layout: 'default', permission: 'config-variables.write' })
5
+ definePageMeta({ layout: 'default', permission: 'config-variables.read' })
6
6
 
7
7
  const route = useRoute()
8
- const { t } = useI18n()
9
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
8
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
9
  apiEndpoint: '/api/v2/config-variables',
11
10
  routePrefix: '/motor-admin/config-variables',
12
11
  translationPrefix: 'motor-admin.config_variables',
@@ -19,13 +18,14 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, o
19
18
 
20
19
  <template>
21
20
  <FormPage
22
- :title="t('motor-admin.config_variables.edit_title')"
21
+ :title="pageTitle"
23
22
  back-route="/motor-admin/config-variables"
24
23
  :loading="fetching"
25
24
  :error="fetchError"
26
25
  >
27
26
  <FormBase
28
27
  ref="formRef"
28
+ :disabled="!canWrite"
29
29
  v-model:state="state"
30
30
  :fields="fields"
31
31
  :schema="schema"
@@ -3,12 +3,11 @@
3
3
  import { domainFormMeta } from '../../../../types/generated/form-meta'
4
4
  import { domainFormConfig, domainSelectOptionConfigs, domainProtocolOptions } from '@motor-cms/ui-core/app/types/config/domain'
5
5
 
6
- definePageMeta({ layout: 'default', permission: 'domains.write' })
6
+ definePageMeta({ layout: 'default', permission: 'domains.read' })
7
7
 
8
8
  const route = useRoute()
9
- const { t } = useI18n()
10
9
 
11
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
12
11
  apiEndpoint: '/api/v2/domains',
13
12
  routePrefix: '/motor-admin/domains',
14
13
  translationPrefix: 'motor-admin.domains',
@@ -23,17 +22,21 @@ const mergedSelectOptions = computed(() => ({
23
22
  ...selectOptions?.value,
24
23
  protocol: domainProtocolOptions
25
24
  }))
25
+
26
+ const { can } = usePermissions()
27
+ const showEntityConfigs = computed(() => can('entity-configurations.read'))
26
28
  </script>
27
29
 
28
30
  <template>
29
31
  <FormPage
30
- :title="t('motor-admin.domains.edit_title')"
32
+ :title="pageTitle"
31
33
  back-route="/motor-admin/domains"
32
34
  :loading="fetching"
33
35
  :error="fetchError"
34
36
  >
35
37
  <FormBase
36
38
  ref="formRef"
39
+ :disabled="!canWrite"
37
40
  v-model:state="state"
38
41
  :fields="fields"
39
42
  :schema="schema"
@@ -49,6 +52,16 @@ const mergedSelectOptions = computed(() => ({
49
52
  @submit="onSubmit"
50
53
  @save-and-continue="onSaveAndContinue"
51
54
  @save-and-new="onSaveAndNew"
52
- />
55
+ >
56
+ <template
57
+ v-if="showEntityConfigs"
58
+ #after-fields
59
+ >
60
+ <FormInputsEntityConfigurationsPanel
61
+ configurable-type="Motor\Admin\Models\Domain"
62
+ :configurable-id="Number(route.params.id)"
63
+ />
64
+ </template>
65
+ </FormBase>
53
66
  </FormPage>
54
67
  </template>
@@ -3,11 +3,10 @@
3
3
  import { emailTemplateFormMeta } from '../../../../types/generated/form-meta'
4
4
  import { emailTemplateFormConfig, emailTemplateSelectOptionConfigs } from '@motor-cms/ui-core/app/types/config/email-template'
5
5
 
6
- definePageMeta({ layout: 'default', permission: 'email-templates.write' })
6
+ definePageMeta({ layout: 'default', permission: 'email-templates.read' })
7
7
 
8
8
  const route = useRoute()
9
- const { t } = useI18n()
10
- const { fields: rawFields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
9
+ const { fields: rawFields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
11
10
  apiEndpoint: '/api/v2/email-templates',
12
11
  routePrefix: '/motor-admin/email-templates',
13
12
  translationPrefix: 'motor-admin.email_templates',
@@ -29,13 +28,14 @@ if (bodyHtmlField) {
29
28
 
30
29
  <template>
31
30
  <FormPage
32
- :title="t('motor-admin.email_templates.edit_title')"
31
+ :title="pageTitle"
33
32
  back-route="/motor-admin/email-templates"
34
33
  :loading="fetching"
35
34
  :error="fetchError"
36
35
  >
37
36
  <FormBase
38
37
  ref="formRef"
38
+ :disabled="!canWrite"
39
39
  v-model:state="state"
40
40
  :fields="fields"
41
41
  :schema="schema"
@@ -10,6 +10,7 @@ definePageMeta({ permission: 'email-templates.read' })
10
10
  type EmailTemplate = components['schemas']['EmailTemplateResource']
11
11
 
12
12
  const { t } = useI18n()
13
+ const client = useSanctumClient()
13
14
 
14
15
  const usageModalOpen = ref(false)
15
16
  const usageEndpoint = ref('')
@@ -28,6 +29,18 @@ const rowActions: RowActionDef<EmailTemplate>[] = [
28
29
  usageEndpoint.value = `/api/v2/email-templates/${row.id}/usage`
29
30
  usageModalOpen.value = true
30
31
  }
32
+ },
33
+ {
34
+ key: 'duplicate',
35
+ label: t('motor-core.grid.duplicate'),
36
+ icon: 'i-lucide-copy',
37
+ handler: async (row: EmailTemplate) => {
38
+ const response = await client<{ data: EmailTemplate }>(
39
+ `/api/v2/email-templates/${row.id}/duplicate`,
40
+ { method: 'POST' }
41
+ )
42
+ await navigateTo(`/motor-admin/email-templates/${response.data.id}/edit`)
43
+ }
31
44
  }
32
45
  ]
33
46
 
@@ -37,31 +50,29 @@ const fetchEmailTemplates = useGridFetch<EmailTemplate>('/api/v2/email-templates
37
50
  </script>
38
51
 
39
52
  <template>
40
- <div>
41
- <GridPage
42
- :title="t('motor-admin.email_templates.title')"
43
- :subtitle="t('motor-admin.email_templates.subtitle')"
44
- add-route="/motor-admin/email-templates/create"
45
- :add-label="t('motor-admin.email_templates.add')"
53
+ <GridPage
54
+ :title="t('motor-admin.email_templates.title')"
55
+ :subtitle="t('motor-admin.email_templates.subtitle')"
56
+ add-route="/motor-admin/email-templates/create"
57
+ :add-label="t('motor-admin.email_templates.add')"
58
+ write-permission="email-templates.write"
59
+ >
60
+ <GridBase
61
+ id="email-templates-grid"
62
+ :fetch="fetchEmailTemplates"
63
+ :columns="columns"
64
+ :filters="filters"
65
+ :row-actions="rowActions"
66
+ base-path="/motor-admin/email-templates"
67
+ :row-click-to="(row: any) => `/motor-admin/email-templates/${row.id}/edit`"
46
68
  write-permission="email-templates.write"
47
- >
48
- <GridBase
49
- id="email-templates-grid"
50
- :fetch="fetchEmailTemplates"
51
- :columns="columns"
52
- :filters="filters"
53
- :row-actions="rowActions"
54
- base-path="/motor-admin/email-templates"
55
- :row-click-to="(row: any) => `/motor-admin/email-templates/${row.id}/edit`"
56
- write-permission="email-templates.write"
57
- delete-permission="email-templates.delete"
58
- />
59
- </GridPage>
60
-
61
- <EntityUsageModal
62
- v-model:open="usageModalOpen"
63
- :endpoint="usageEndpoint"
64
- :title="t('motor-admin.email_templates.usage_title')"
69
+ delete-permission="email-templates.delete"
65
70
  />
66
- </div>
71
+ </GridPage>
72
+
73
+ <EntityUsageModal
74
+ v-model:open="usageModalOpen"
75
+ :endpoint="usageEndpoint"
76
+ :title="t('motor-admin.email_templates.usage_title')"
77
+ />
67
78
  </template>
@@ -2,11 +2,10 @@
2
2
  import { languageFormMeta } from '../../../../types/generated/form-meta'
3
3
  import { languageFormConfig } from '@motor-cms/ui-core/app/types/config/language'
4
4
 
5
- definePageMeta({ layout: 'default', permission: 'languages.write' })
5
+ definePageMeta({ layout: 'default', permission: 'languages.read' })
6
6
 
7
7
  const route = useRoute()
8
- const { t } = useI18n()
9
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
8
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
9
  apiEndpoint: '/api/v2/languages',
11
10
  routePrefix: '/motor-admin/languages',
12
11
  translationPrefix: 'motor-admin.languages',
@@ -15,17 +14,31 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, o
15
14
  mode: 'edit',
16
15
  id: route.params.id as string
17
16
  })
17
+
18
+ const isoField = fields.find(f => f.key === 'iso_639_1')
19
+ watch(() => state.iso_639_1, (code) => {
20
+ const opt = isoField?.staticOptions?.find(o => o.value === code)
21
+ if (opt) {
22
+ const match = opt.label.match(/^(.+?)\s*—\s*(.+?)\s*\(/)
23
+ state.english_name = match?.[1]?.trim() ?? ''
24
+ state.native_name = match?.[2]?.trim() ?? ''
25
+ } else {
26
+ state.english_name = ''
27
+ state.native_name = ''
28
+ }
29
+ })
18
30
  </script>
19
31
 
20
32
  <template>
21
33
  <FormPage
22
- :title="t('motor-admin.languages.edit_title')"
34
+ :title="pageTitle"
23
35
  back-route="/motor-admin/languages"
24
36
  :loading="fetching"
25
37
  :error="fetchError"
26
38
  >
27
39
  <FormBase
28
40
  ref="formRef"
41
+ :disabled="!canWrite"
29
42
  v-model:state="state"
30
43
  :fields="fields"
31
44
  :schema="schema"
@@ -13,6 +13,19 @@ const { fields, schema, groups, state, loading, formRef, onSubmit, onSaveAndNew
13
13
  formConfig: languageFormConfig,
14
14
  mode: 'create'
15
15
  })
16
+
17
+ const isoField = fields.find(f => f.key === 'iso_639_1')
18
+ watch(() => state.iso_639_1, (code) => {
19
+ const opt = isoField?.staticOptions?.find(o => o.value === code)
20
+ if (opt) {
21
+ const match = opt.label.match(/^(.+?)\s*—\s*(.+?)\s*\(/)
22
+ state.english_name = match?.[1]?.trim() ?? ''
23
+ state.native_name = match?.[2]?.trim() ?? ''
24
+ } else {
25
+ state.english_name = ''
26
+ state.native_name = ''
27
+ }
28
+ })
16
29
  </script>
17
30
 
18
31
  <template>
@@ -2,11 +2,10 @@
2
2
  import { permissionGroupFormMeta } from '../../../../types/generated/form-meta'
3
3
  import { permissionGroupFormConfig, permissionGroupSelectOptionConfigs, permissionGroupExtraFields } from '@motor-cms/ui-core/app/types/config/permission-group'
4
4
 
5
- definePageMeta({ layout: 'default', permission: 'permission-groups.write' })
5
+ definePageMeta({ layout: 'default', permission: 'permission-groups.read' })
6
6
 
7
7
  const route = useRoute()
8
- const { t } = useI18n()
9
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
8
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
9
  apiEndpoint: '/api/v2/permission-groups',
11
10
  routePrefix: '/motor-admin/permission-groups',
12
11
  translationPrefix: 'motor-admin.permissions',
@@ -22,13 +21,14 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, s
22
21
 
23
22
  <template>
24
23
  <FormPage
25
- :title="t('motor-admin.permissions.edit_title')"
24
+ :title="pageTitle"
26
25
  back-route="/motor-admin/permission-groups"
27
26
  :loading="fetching"
28
27
  :error="fetchError"
29
28
  >
30
29
  <FormBase
31
30
  ref="formRef"
31
+ :disabled="!canWrite"
32
32
  v-model:state="state"
33
33
  :fields="fields"
34
34
  :schema="schema"
@@ -2,11 +2,10 @@
2
2
  import { roleFormMeta } from '../../../../types/generated/form-meta'
3
3
  import { roleFormConfig, roleSelectOptionConfigs } from '@motor-cms/ui-core/app/types/config/role'
4
4
 
5
- definePageMeta({ layout: 'default', permission: 'roles.write' })
5
+ definePageMeta({ layout: 'default', permission: 'roles.read' })
6
6
 
7
7
  const route = useRoute()
8
- const { t } = useI18n()
9
- const { fields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
8
+ const { fields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
10
9
  apiEndpoint: '/api/v2/roles',
11
10
  routePrefix: '/motor-admin/roles',
12
11
  translationPrefix: 'motor-admin.roles',
@@ -20,13 +19,14 @@ const { fields, schema, groups, state, loading, fetching, fetchError, formRef, s
20
19
 
21
20
  <template>
22
21
  <FormPage
23
- :title="t('motor-admin.roles.edit_title')"
22
+ :title="pageTitle"
24
23
  back-route="/motor-admin/roles"
25
24
  :loading="fetching"
26
25
  :error="fetchError"
27
26
  >
28
27
  <FormBase
29
28
  ref="formRef"
29
+ :disabled="!canWrite"
30
30
  v-model:state="state"
31
31
  :fields="fields"
32
32
  :schema="schema"
@@ -13,7 +13,10 @@ const { fields, schema, groups, state, loading, selectOptions, selectOptionsLoad
13
13
  formConfig: roleFormConfig,
14
14
  mode: 'create',
15
15
  selectOptionConfigs: roleSelectOptionConfigs,
16
- extraState: { guard_name: 'web' }
16
+ extraState: { guard_name: 'web' },
17
+ beforeSubmit: (data, formState) => {
18
+ data.guard_name = formState.guard_name
19
+ }
17
20
  })
18
21
  </script>
19
22
 
@@ -3,12 +3,12 @@
3
3
  import { userFormMeta } from '../../../../types/generated/form-meta'
4
4
  import { userEditFormConfig, userSelectOptionConfigs, userEditExtraFields } from '@motor-cms/ui-core/app/types/config/user'
5
5
 
6
- definePageMeta({ layout: 'default', permission: 'users.write' })
6
+ definePageMeta({ layout: 'default', permission: 'users.read' })
7
7
 
8
8
  const route = useRoute()
9
9
  const { t } = useI18n()
10
10
 
11
- const { fields: rawFields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
11
+ const { fields: rawFields, schema, groups, state, loading, fetching, fetchError, canWrite, pageTitle, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
12
12
  apiEndpoint: '/api/v2/users',
13
13
  routePrefix: '/motor-admin/users',
14
14
  translationPrefix: 'motor-admin.users',
@@ -56,13 +56,14 @@ watchEffect(() => {
56
56
 
57
57
  <template>
58
58
  <FormPage
59
- :title="t('motor-admin.users.edit_title')"
59
+ :title="pageTitle"
60
60
  back-route="/motor-admin/users"
61
61
  :loading="fetching"
62
62
  :error="fetchError"
63
63
  >
64
64
  <FormBase
65
65
  ref="formRef"
66
+ :disabled="!canWrite"
66
67
  v-model:state="state"
67
68
  :fields="fields"
68
69
  :schema="refinedSchema"
@@ -44,6 +44,7 @@ const { fetch: fetchUsers } = useGridData<User>('/api/v2/users')
44
44
  </script>
45
45
 
46
46
  <template>
47
+ <UsersOnboarding />
47
48
  <GridPage
48
49
  :title="t('motor-admin.users.title')"
49
50
  :subtitle="t('motor-admin.users.subtitle')"