@byline/admin 2.5.1 → 2.6.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.
- package/dist/fields/array/array-field.d.ts +14 -0
- package/dist/fields/array/array-field.js +177 -0
- package/dist/fields/array/array-field.module.js +11 -0
- package/dist/fields/array/array-field_module.css +32 -0
- package/dist/fields/blocks/blocks-field.d.ts +13 -0
- package/dist/fields/blocks/blocks-field.js +245 -0
- package/dist/fields/blocks/blocks-field.module.js +26 -0
- package/dist/fields/blocks/blocks-field_module.css +107 -0
- package/dist/fields/checkbox/checkbox-field.d.ts +16 -0
- package/dist/fields/checkbox/checkbox-field.js +28 -0
- package/dist/fields/checkbox/checkbox-field.module.js +6 -0
- package/dist/fields/checkbox/checkbox-field_module.css +4 -0
- package/dist/fields/column-formatter.d.ts +20 -0
- package/dist/fields/column-formatter.js +15 -0
- package/dist/fields/date-time-formatter.d.ts +16 -0
- package/dist/fields/date-time-formatter.js +8 -0
- package/dist/fields/datetime/datetime-field.d.ts +16 -0
- package/dist/fields/datetime/datetime-field.js +37 -0
- package/dist/fields/datetime/datetime-field.module.js +5 -0
- package/dist/fields/datetime/datetime-field_module.css +4 -0
- package/dist/fields/draggable-context-menu.d.ts +6 -0
- package/dist/fields/draggable-context-menu.js +85 -0
- package/dist/fields/draggable-context-menu.module.js +15 -0
- package/dist/fields/draggable-context-menu_module.css +91 -0
- package/dist/fields/field-helpers.d.ts +26 -0
- package/dist/fields/field-helpers.js +50 -0
- package/dist/fields/field-renderer.d.ts +37 -0
- package/dist/fields/field-renderer.js +206 -0
- package/dist/fields/field-renderer.module.js +8 -0
- package/dist/fields/field-renderer_module.css +11 -0
- package/dist/fields/field-services-context.d.ts +16 -0
- package/dist/fields/field-services-context.js +13 -0
- package/dist/fields/field-services-types.d.ts +63 -0
- package/dist/fields/field-services-types.js +1 -0
- package/dist/fields/file/file-field.d.ts +19 -0
- package/dist/fields/file/file-field.js +225 -0
- package/dist/fields/file/file-field.module.js +18 -0
- package/dist/fields/file/file-field_module.css +131 -0
- package/dist/fields/file/file-upload-field.d.ts +21 -0
- package/dist/fields/file/file-upload-field.js +130 -0
- package/dist/fields/file/file-upload-field.module.js +15 -0
- package/dist/fields/file/file-upload-field_module.css +74 -0
- package/dist/fields/group/group-field.d.ts +15 -0
- package/dist/fields/group/group-field.js +59 -0
- package/dist/fields/group/group-field.module.js +9 -0
- package/dist/fields/group/group-field_module.css +27 -0
- package/dist/fields/image/image-field.d.ts +19 -0
- package/dist/fields/image/image-field.js +241 -0
- package/dist/fields/image/image-field.module.js +22 -0
- package/dist/fields/image/image-field_module.css +121 -0
- package/dist/fields/image/image-upload-field.d.ts +21 -0
- package/dist/fields/image/image-upload-field.js +190 -0
- package/dist/fields/image/image-upload-field.module.js +19 -0
- package/dist/fields/image/image-upload-field_module.css +92 -0
- package/dist/fields/local-date-time.d.ts +27 -0
- package/dist/fields/local-date-time.js +49 -0
- package/dist/fields/locale-badge.d.ts +18 -0
- package/dist/fields/locale-badge.js +10 -0
- package/dist/fields/locale-badge.module.js +5 -0
- package/dist/fields/locale-badge_module.css +27 -0
- package/dist/fields/numerical/numerical-field.d.ts +18 -0
- package/dist/fields/numerical/numerical-field.js +74 -0
- package/dist/fields/relation/relation-display.d.ts +40 -0
- package/dist/fields/relation/relation-display.js +58 -0
- package/dist/fields/relation/relation-display.module.js +9 -0
- package/dist/fields/relation/relation-display_module.css +21 -0
- package/dist/fields/relation/relation-field.d.ts +18 -0
- package/dist/fields/relation/relation-field.js +138 -0
- package/dist/fields/relation/relation-field.module.js +13 -0
- package/dist/fields/relation/relation-field_module.css +62 -0
- package/dist/fields/relation/relation-picker.d.ts +49 -0
- package/dist/fields/relation/relation-picker.js +236 -0
- package/dist/fields/relation/relation-picker.module.js +26 -0
- package/dist/fields/relation/relation-picker_module.css +124 -0
- package/dist/fields/relation/relation-summary.d.ts +31 -0
- package/dist/fields/relation/relation-summary.js +50 -0
- package/dist/fields/relation/relation-summary.module.js +11 -0
- package/dist/fields/relation/relation-summary_module.css +37 -0
- package/dist/fields/select/select-field.d.ts +16 -0
- package/dist/fields/select/select-field.js +50 -0
- package/dist/fields/select/select-field.module.js +5 -0
- package/dist/fields/select/select-field_module.css +4 -0
- package/dist/fields/sortable-item.d.ts +15 -0
- package/dist/fields/sortable-item.js +81 -0
- package/dist/fields/sortable-item.module.js +22 -0
- package/dist/fields/sortable-item_module.css +124 -0
- package/dist/fields/text/text-field.d.ts +20 -0
- package/dist/fields/text/text-field.js +104 -0
- package/dist/fields/text/text-field.module.js +6 -0
- package/dist/fields/text/text-field_module.css +5 -0
- package/dist/fields/text-area/text-area-field.d.ts +20 -0
- package/dist/fields/text-area/text-area-field.js +105 -0
- package/dist/fields/text-area/text-area-field.module.js +6 -0
- package/dist/fields/text-area/text-area-field_module.css +5 -0
- package/dist/fields/use-field-change-handler.d.ts +23 -0
- package/dist/fields/use-field-change-handler.js +52 -0
- package/dist/forms/document-actions.d.ts +48 -0
- package/dist/forms/document-actions.js +475 -0
- package/dist/forms/document-actions.module.js +34 -0
- package/dist/forms/document-actions_module.css +118 -0
- package/dist/forms/form-context.d.ts +89 -0
- package/dist/forms/form-context.js +466 -0
- package/dist/forms/form-renderer.d.ts +98 -0
- package/dist/forms/form-renderer.js +597 -0
- package/dist/forms/form-renderer.module.js +46 -0
- package/dist/forms/form-renderer_module.css +245 -0
- package/dist/forms/navigation-guard.d.ts +54 -0
- package/dist/forms/navigation-guard.js +22 -0
- package/dist/forms/path-widget.d.ts +36 -0
- package/dist/forms/path-widget.js +116 -0
- package/dist/forms/path-widget.module.js +8 -0
- package/dist/forms/path-widget_module.css +29 -0
- package/dist/forms/upload-executor.d.ts +57 -0
- package/dist/forms/upload-executor.js +94 -0
- package/dist/lib/translate-validation-error.d.ts +36 -0
- package/dist/lib/translate-validation-error.js +11 -0
- package/dist/modules/admin-account/commands.d.ts +2 -1
- package/dist/modules/admin-account/commands.js +13 -2
- package/dist/modules/admin-account/components/change-password.js +45 -36
- package/dist/modules/admin-account/components/container.js +185 -134
- package/dist/modules/admin-account/components/preferences.d.ts +8 -0
- package/dist/modules/admin-account/components/preferences.js +152 -0
- package/dist/modules/admin-account/components/preferences.module.js +11 -0
- package/dist/modules/admin-account/components/preferences_module.css +41 -0
- package/dist/modules/admin-account/components/update.js +50 -31
- package/dist/modules/admin-account/index.d.ts +3 -3
- package/dist/modules/admin-account/index.js +2 -2
- package/dist/modules/admin-account/schemas.d.ts +4 -0
- package/dist/modules/admin-account/schemas.js +4 -1
- package/dist/modules/admin-account/service.d.ts +1 -0
- package/dist/modules/admin-account/service.js +8 -0
- package/dist/modules/admin-permissions/components/inspector.js +31 -41
- package/dist/modules/admin-roles/components/create.js +43 -26
- package/dist/modules/admin-roles/components/permissions.js +26 -35
- package/dist/modules/admin-roles/components/update.js +26 -16
- package/dist/modules/admin-users/components/create.js +60 -40
- package/dist/modules/admin-users/components/roles.js +9 -15
- package/dist/modules/admin-users/components/set-password.js +30 -31
- package/dist/modules/admin-users/components/update.js +58 -39
- package/dist/modules/admin-users/dto.js +1 -0
- package/dist/modules/admin-users/repository.d.ts +17 -0
- package/dist/modules/admin-users/schemas.d.ts +4 -0
- package/dist/modules/admin-users/schemas.js +6 -2
- package/dist/modules/auth/components/sign-in-form.js +10 -8
- package/dist/presentation/group.d.ts +27 -0
- package/dist/presentation/group.js +14 -0
- package/dist/presentation/group.module.js +6 -0
- package/dist/presentation/group_module.css +19 -0
- package/dist/presentation/row.d.ts +25 -0
- package/dist/presentation/row.js +8 -0
- package/dist/presentation/row.module.js +5 -0
- package/dist/presentation/row_module.css +18 -0
- package/dist/presentation/tabs.d.ts +25 -0
- package/dist/presentation/tabs.js +39 -0
- package/dist/presentation/tabs.module.js +10 -0
- package/dist/presentation/tabs_module.css +68 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +36 -0
- package/dist/services/admin-services-types.d.ts +16 -0
- package/dist/widgets/diff-viewer/diff-modal.d.ts +22 -0
- package/dist/widgets/diff-viewer/diff-modal.js +149 -0
- package/dist/widgets/diff-viewer/diff-modal.module.js +14 -0
- package/dist/widgets/diff-viewer/diff-modal_module.css +56 -0
- package/dist/widgets/status-badge/status-badge.d.ts +25 -0
- package/dist/widgets/status-badge/status-badge.js +37 -0
- package/dist/widgets/status-badge/status-badge.module.js +7 -0
- package/dist/widgets/status-badge/status-badge_module.css +20 -0
- package/package.json +14 -4
- package/src/fields/array/array-field.module.css +48 -0
- package/src/fields/array/array-field.tsx +267 -0
- package/src/fields/blocks/blocks-field.module.css +148 -0
- package/src/fields/blocks/blocks-field.tsx +323 -0
- package/src/fields/checkbox/checkbox-field.module.css +4 -0
- package/src/fields/checkbox/checkbox-field.tsx +54 -0
- package/src/fields/column-formatter.tsx +31 -0
- package/src/fields/date-time-formatter.tsx +22 -0
- package/src/fields/datetime/datetime-field.module.css +13 -0
- package/src/fields/datetime/datetime-field.tsx +54 -0
- package/src/fields/draggable-context-menu.module.css +127 -0
- package/src/fields/draggable-context-menu.tsx +87 -0
- package/src/fields/field-helpers.ts +69 -0
- package/src/fields/field-renderer.module.css +22 -0
- package/src/fields/field-renderer.tsx +288 -0
- package/src/fields/field-services-context.tsx +35 -0
- package/src/fields/field-services-types.ts +68 -0
- package/src/fields/file/file-field.module.css +153 -0
- package/src/fields/file/file-field.tsx +286 -0
- package/src/fields/file/file-upload-field.module.css +101 -0
- package/src/fields/file/file-upload-field.tsx +187 -0
- package/src/fields/group/group-field.module.css +43 -0
- package/src/fields/group/group-field.tsx +84 -0
- package/src/fields/image/image-field.module.css +155 -0
- package/src/fields/image/image-field.tsx +306 -0
- package/src/fields/image/image-upload-field.module.css +123 -0
- package/src/fields/image/image-upload-field.tsx +276 -0
- package/src/fields/local-date-time.tsx +88 -0
- package/src/fields/locale-badge.module.css +37 -0
- package/src/fields/locale-badge.tsx +32 -0
- package/src/fields/numerical/numerical-field.tsx +114 -0
- package/src/fields/relation/relation-display.module.css +36 -0
- package/src/fields/relation/relation-display.tsx +130 -0
- package/src/fields/relation/relation-field.module.css +83 -0
- package/src/fields/relation/relation-field.tsx +211 -0
- package/src/fields/relation/relation-picker.module.css +168 -0
- package/src/fields/relation/relation-picker.tsx +326 -0
- package/src/fields/relation/relation-summary.module.css +55 -0
- package/src/fields/relation/relation-summary.tsx +123 -0
- package/src/fields/select/select-field.module.css +13 -0
- package/src/fields/select/select-field.tsx +61 -0
- package/src/fields/sortable-item.module.css +167 -0
- package/src/fields/sortable-item.tsx +106 -0
- package/src/fields/text/text-field.module.css +13 -0
- package/src/fields/text/text-field.tsx +146 -0
- package/src/fields/text-area/text-area-field.module.css +13 -0
- package/src/fields/text-area/text-area-field.tsx +147 -0
- package/src/fields/use-field-change-handler.ts +112 -0
- package/src/forms/document-actions.module.css +160 -0
- package/src/forms/document-actions.tsx +482 -0
- package/src/forms/form-context.tsx +704 -0
- package/src/forms/form-renderer.module.css +321 -0
- package/src/forms/form-renderer.tsx +891 -0
- package/src/forms/navigation-guard.tsx +98 -0
- package/src/forms/path-widget.module.css +41 -0
- package/src/forms/path-widget.test.tsx +217 -0
- package/src/forms/path-widget.tsx +183 -0
- package/src/forms/upload-executor.ts +192 -0
- package/src/lib/translate-validation-error.ts +56 -0
- package/src/modules/admin-account/commands.ts +13 -0
- package/src/modules/admin-account/components/change-password.tsx +46 -31
- package/src/modules/admin-account/components/container.tsx +83 -38
- package/src/modules/admin-account/components/preferences.module.css +60 -0
- package/src/modules/admin-account/components/preferences.tsx +203 -0
- package/src/modules/admin-account/components/update.tsx +53 -27
- package/src/modules/admin-account/index.ts +3 -0
- package/src/modules/admin-account/schemas.ts +13 -0
- package/src/modules/admin-account/service.ts +12 -0
- package/src/modules/admin-permissions/components/inspector.tsx +22 -14
- package/src/modules/admin-roles/components/create.tsx +51 -23
- package/src/modules/admin-roles/components/permissions.tsx +25 -21
- package/src/modules/admin-roles/components/update.tsx +37 -19
- package/src/modules/admin-users/components/create.tsx +63 -34
- package/src/modules/admin-users/components/roles.tsx +9 -8
- package/src/modules/admin-users/components/set-password.tsx +34 -28
- package/src/modules/admin-users/components/update.tsx +58 -36
- package/src/modules/admin-users/dto.ts +1 -0
- package/src/modules/admin-users/repository.ts +17 -0
- package/src/modules/admin-users/schemas.ts +12 -0
- package/src/modules/auth/components/sign-in-form.tsx +14 -8
- package/src/presentation/group.module.css +41 -0
- package/src/presentation/group.tsx +40 -0
- package/src/presentation/row.module.css +32 -0
- package/src/presentation/row.tsx +33 -0
- package/src/presentation/tabs.module.css +107 -0
- package/src/presentation/tabs.tsx +84 -0
- package/src/react.ts +84 -0
- package/src/services/admin-services-types.ts +18 -0
- package/src/widgets/diff-viewer/diff-modal.module.css +79 -0
- package/src/widgets/diff-viewer/diff-modal.tsx +186 -0
- package/src/widgets/status-badge/status-badge.module.css +31 -0
- package/src/widgets/status-badge/status-badge.tsx +71 -0
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* time and immutable thereafter.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { useState } from 'react'
|
|
20
|
+
import { useMemo, useState } from 'react'
|
|
21
21
|
import { revalidateLogic, useForm } from '@tanstack/react-form-start'
|
|
22
22
|
|
|
23
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
23
24
|
import { Alert, Button, Input, LoaderEllipsis, TextArea } from '@byline/ui/react'
|
|
24
25
|
import cx from 'classnames'
|
|
25
26
|
import { z } from 'zod'
|
|
@@ -29,12 +30,13 @@ import styles from './update.module.css'
|
|
|
29
30
|
import type { UpdateAdminRoleInput } from '../../../services/admin-services-types.js'
|
|
30
31
|
import type { AdminRoleResponse } from '../index.js'
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
description: z.string().max(2000, 'Description must not exceed 2000 characters'),
|
|
35
|
-
})
|
|
33
|
+
const MAX_NAME = 128
|
|
34
|
+
const MAX_DESCRIPTION = 2000
|
|
36
35
|
|
|
37
|
-
type UpdateRoleValues =
|
|
36
|
+
type UpdateRoleValues = {
|
|
37
|
+
name: string
|
|
38
|
+
description: string
|
|
39
|
+
}
|
|
38
40
|
|
|
39
41
|
function defaultsFrom(role: AdminRoleResponse): UpdateRoleValues {
|
|
40
42
|
return {
|
|
@@ -66,9 +68,27 @@ interface UpdateRoleProps {
|
|
|
66
68
|
|
|
67
69
|
export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
68
70
|
const { updateAdminRole } = useBylineAdminServices()
|
|
71
|
+
const { t } = useTranslation('byline-admin')
|
|
69
72
|
const [formError, setFormError] = useState<string | null>(null)
|
|
70
73
|
const [successMessage, setSuccessMessage] = useState<string | null>(null)
|
|
71
74
|
|
|
75
|
+
const updateRoleSchema = useMemo(
|
|
76
|
+
() =>
|
|
77
|
+
z.object({
|
|
78
|
+
name: z
|
|
79
|
+
.string()
|
|
80
|
+
.min(1, t('adminRoles.create.errors.nameRequired'))
|
|
81
|
+
.max(MAX_NAME, t('adminRoles.create.errors.nameTooLong', { max: MAX_NAME })),
|
|
82
|
+
description: z
|
|
83
|
+
.string()
|
|
84
|
+
.max(
|
|
85
|
+
MAX_DESCRIPTION,
|
|
86
|
+
t('adminRoles.create.errors.descriptionTooLong', { max: MAX_DESCRIPTION })
|
|
87
|
+
),
|
|
88
|
+
}),
|
|
89
|
+
[t]
|
|
90
|
+
)
|
|
91
|
+
|
|
72
92
|
const form = useForm({
|
|
73
93
|
defaultValues: defaultsFrom(role),
|
|
74
94
|
validationLogic: revalidateLogic({
|
|
@@ -83,7 +103,7 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
83
103
|
setSuccessMessage(null)
|
|
84
104
|
const patch = buildPatch(value, role)
|
|
85
105
|
if (Object.keys(patch).length === 0) {
|
|
86
|
-
setSuccessMessage('
|
|
106
|
+
setSuccessMessage(t('common.feedback.noChanges'))
|
|
87
107
|
return
|
|
88
108
|
}
|
|
89
109
|
|
|
@@ -91,21 +111,19 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
91
111
|
const updated = await updateAdminRole({
|
|
92
112
|
data: { id: role.id, vid: role.vid, patch },
|
|
93
113
|
})
|
|
94
|
-
setSuccessMessage('
|
|
114
|
+
setSuccessMessage(t('common.feedback.saved'))
|
|
95
115
|
onSuccess?.(updated)
|
|
96
116
|
} catch (err) {
|
|
97
117
|
const code = getErrorCode(err)
|
|
98
118
|
if (code === 'admin.roles.versionConflict') {
|
|
99
|
-
setFormError(
|
|
100
|
-
'This role has been modified elsewhere since you opened this form. Reload to get the latest values and try again.'
|
|
101
|
-
)
|
|
119
|
+
setFormError(t('adminRoles.update.errors.versionConflict'))
|
|
102
120
|
return
|
|
103
121
|
}
|
|
104
122
|
if (code === 'admin.roles.notFound') {
|
|
105
|
-
setFormError('
|
|
123
|
+
setFormError(t('adminRoles.update.errors.notFound'))
|
|
106
124
|
return
|
|
107
125
|
}
|
|
108
|
-
setFormError('
|
|
126
|
+
setFormError(t('common.errors.couldNotSave'))
|
|
109
127
|
}
|
|
110
128
|
},
|
|
111
129
|
})
|
|
@@ -127,7 +145,7 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
127
145
|
<form.Field name="name">
|
|
128
146
|
{(field) => (
|
|
129
147
|
<Input
|
|
130
|
-
label=
|
|
148
|
+
label={t('adminRoles.fields.name')}
|
|
131
149
|
id="role-name"
|
|
132
150
|
name={field.name}
|
|
133
151
|
value={field.state.value}
|
|
@@ -141,19 +159,19 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
141
159
|
</form.Field>
|
|
142
160
|
|
|
143
161
|
<Input
|
|
144
|
-
label=
|
|
162
|
+
label={t('adminRoles.fields.machineName')}
|
|
145
163
|
id="role-machine-name"
|
|
146
164
|
name="machine_name"
|
|
147
165
|
value={role.machine_name}
|
|
148
166
|
readOnly
|
|
149
167
|
disabled
|
|
150
|
-
helpText=
|
|
168
|
+
helpText={t('adminRoles.update.fields.machineNameHelp')}
|
|
151
169
|
/>
|
|
152
170
|
|
|
153
171
|
<form.Field name="description">
|
|
154
172
|
{(field) => (
|
|
155
173
|
<TextArea
|
|
156
|
-
label=
|
|
174
|
+
label={t('adminRoles.fields.description')}
|
|
157
175
|
id="role-description"
|
|
158
176
|
name={field.name}
|
|
159
177
|
value={field.state.value}
|
|
@@ -174,7 +192,7 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
174
192
|
onClick={onClose}
|
|
175
193
|
className={cx('byline-role-update-action', styles.action)}
|
|
176
194
|
>
|
|
177
|
-
{successMessage ? '
|
|
195
|
+
{successMessage ? t('common.actions.close') : t('common.actions.cancel')}
|
|
178
196
|
</Button>
|
|
179
197
|
<form.Subscribe
|
|
180
198
|
selector={(state) => ({
|
|
@@ -190,7 +208,7 @@ export function UpdateRole({ role, onClose, onSuccess }: UpdateRoleProps) {
|
|
|
190
208
|
disabled={!canSubmit || isSubmitting}
|
|
191
209
|
className={cx('byline-role-update-action', styles.action)}
|
|
192
210
|
>
|
|
193
|
-
{isSubmitting === true ? <LoaderEllipsis size={42} /> : '
|
|
211
|
+
{isSubmitting === true ? <LoaderEllipsis size={42} /> : t('common.actions.save')}
|
|
194
212
|
</Button>
|
|
195
213
|
)}
|
|
196
214
|
</form.Subscribe>
|
|
@@ -23,33 +23,34 @@
|
|
|
23
23
|
* change in the loader and a prop here.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { useState } from 'react'
|
|
26
|
+
import { useMemo, useState } from 'react'
|
|
27
27
|
import { revalidateLogic, useForm } from '@tanstack/react-form-start'
|
|
28
28
|
|
|
29
29
|
import { passwordSchema } from '@byline/core/validation'
|
|
30
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
30
31
|
import { Alert, Button, Checkbox, Input, LoaderEllipsis } from '@byline/ui/react'
|
|
31
32
|
import cx from 'classnames'
|
|
32
33
|
import { z } from 'zod'
|
|
33
34
|
|
|
35
|
+
import { translateValidationError } from '../../../lib/translate-validation-error.js'
|
|
34
36
|
import { useBylineAdminServices } from '../../../services/admin-services-context.js'
|
|
35
37
|
import styles from './create.module.css'
|
|
36
38
|
import type { AdminUserResponse } from '../index.js'
|
|
37
39
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.min(3)
|
|
42
|
-
.max(254, 'Email must not exceed 254 characters'),
|
|
43
|
-
password: passwordSchema,
|
|
44
|
-
given_name: z.string().max(100, 'Given name must not exceed 100 characters'),
|
|
45
|
-
family_name: z.string().max(100, 'Family name must not exceed 100 characters'),
|
|
46
|
-
username: z.string().max(100, 'Username must not exceed 100 characters'),
|
|
47
|
-
is_super_admin: z.boolean(),
|
|
48
|
-
is_enabled: z.boolean(),
|
|
49
|
-
is_email_verified: z.boolean(),
|
|
50
|
-
})
|
|
40
|
+
const MAX_NAME = 100
|
|
41
|
+
const MAX_USERNAME = 100
|
|
42
|
+
const MAX_EMAIL = 254
|
|
51
43
|
|
|
52
|
-
type CreateAdminUserValues =
|
|
44
|
+
type CreateAdminUserValues = {
|
|
45
|
+
email: string
|
|
46
|
+
password: string
|
|
47
|
+
given_name: string
|
|
48
|
+
family_name: string
|
|
49
|
+
username: string
|
|
50
|
+
is_super_admin: boolean
|
|
51
|
+
is_enabled: boolean
|
|
52
|
+
is_email_verified: boolean
|
|
53
|
+
}
|
|
53
54
|
|
|
54
55
|
const initialValues: CreateAdminUserValues = {
|
|
55
56
|
email: '',
|
|
@@ -77,8 +78,35 @@ interface CreateAdminUserProps {
|
|
|
77
78
|
|
|
78
79
|
export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
79
80
|
const { createAdminUser } = useBylineAdminServices()
|
|
81
|
+
const { t } = useTranslation('byline-admin')
|
|
80
82
|
const [formError, setFormError] = useState<string | null>(null)
|
|
81
83
|
|
|
84
|
+
// Schema rebuilt per-render so error messages reflect the active
|
|
85
|
+
// locale; wrapped in useMemo([t]) to keep validator identity stable.
|
|
86
|
+
const createAdminUserFormSchema = useMemo(
|
|
87
|
+
() =>
|
|
88
|
+
z.object({
|
|
89
|
+
email: z
|
|
90
|
+
.email({ message: t('account.update.errors.invalidEmail') })
|
|
91
|
+
.min(3)
|
|
92
|
+
.max(MAX_EMAIL, t('account.update.errors.emailTooLong', { max: MAX_EMAIL })),
|
|
93
|
+
password: passwordSchema,
|
|
94
|
+
given_name: z
|
|
95
|
+
.string()
|
|
96
|
+
.max(MAX_NAME, t('account.update.errors.givenNameTooLong', { max: MAX_NAME })),
|
|
97
|
+
family_name: z
|
|
98
|
+
.string()
|
|
99
|
+
.max(MAX_NAME, t('account.update.errors.familyNameTooLong', { max: MAX_NAME })),
|
|
100
|
+
username: z
|
|
101
|
+
.string()
|
|
102
|
+
.max(MAX_USERNAME, t('account.update.errors.usernameTooLong', { max: MAX_USERNAME })),
|
|
103
|
+
is_super_admin: z.boolean(),
|
|
104
|
+
is_enabled: z.boolean(),
|
|
105
|
+
is_email_verified: z.boolean(),
|
|
106
|
+
}),
|
|
107
|
+
[t]
|
|
108
|
+
)
|
|
109
|
+
|
|
82
110
|
const form = useForm({
|
|
83
111
|
defaultValues: initialValues,
|
|
84
112
|
validationLogic: revalidateLogic({
|
|
@@ -111,14 +139,15 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
111
139
|
} catch (err) {
|
|
112
140
|
const code = getErrorCode(err)
|
|
113
141
|
if (code === 'admin.users.emailInUse') {
|
|
142
|
+
const message = t('account.update.errors.emailInUse')
|
|
114
143
|
form.setFieldMeta('email', (meta) => ({
|
|
115
144
|
...meta,
|
|
116
|
-
errorMap: { ...meta.errorMap, onServer:
|
|
117
|
-
errors: [
|
|
145
|
+
errorMap: { ...meta.errorMap, onServer: message },
|
|
146
|
+
errors: [message],
|
|
118
147
|
}))
|
|
119
148
|
return
|
|
120
149
|
}
|
|
121
|
-
setFormError('
|
|
150
|
+
setFormError(t('adminUsers.create.errors.fallback'))
|
|
122
151
|
}
|
|
123
152
|
},
|
|
124
153
|
})
|
|
@@ -140,7 +169,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
140
169
|
<form.Field name="given_name">
|
|
141
170
|
{(field) => (
|
|
142
171
|
<Input
|
|
143
|
-
label=
|
|
172
|
+
label={t('account.update.fields.givenName')}
|
|
144
173
|
id="new-given-name"
|
|
145
174
|
name={field.name}
|
|
146
175
|
value={field.state.value}
|
|
@@ -156,7 +185,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
156
185
|
<form.Field name="family_name">
|
|
157
186
|
{(field) => (
|
|
158
187
|
<Input
|
|
159
|
-
label=
|
|
188
|
+
label={t('account.update.fields.familyName')}
|
|
160
189
|
id="new-family-name"
|
|
161
190
|
name={field.name}
|
|
162
191
|
value={field.state.value}
|
|
@@ -173,7 +202,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
173
202
|
<form.Field name="username">
|
|
174
203
|
{(field) => (
|
|
175
204
|
<Input
|
|
176
|
-
label=
|
|
205
|
+
label={t('account.update.fields.username')}
|
|
177
206
|
id="new-username"
|
|
178
207
|
name={field.name}
|
|
179
208
|
value={field.state.value}
|
|
@@ -181,7 +210,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
181
210
|
onChange={(e) => field.handleChange(e.currentTarget.value)}
|
|
182
211
|
error={field.state.meta.errors.length > 0}
|
|
183
212
|
errorText={firstError(field.state.meta.errors)}
|
|
184
|
-
helpText=
|
|
213
|
+
helpText={t('adminUsers.create.fields.usernameHelp')}
|
|
185
214
|
autoComplete="username"
|
|
186
215
|
/>
|
|
187
216
|
)}
|
|
@@ -190,7 +219,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
190
219
|
<form.Field name="email">
|
|
191
220
|
{(field) => (
|
|
192
221
|
<Input
|
|
193
|
-
label=
|
|
222
|
+
label={t('common.fields.email')}
|
|
194
223
|
id="new-email"
|
|
195
224
|
name={field.name}
|
|
196
225
|
type="email"
|
|
@@ -208,7 +237,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
208
237
|
<form.Field name="password">
|
|
209
238
|
{(field) => (
|
|
210
239
|
<Input
|
|
211
|
-
label=
|
|
240
|
+
label={t('adminUsers.create.fields.password')}
|
|
212
241
|
id="new-password"
|
|
213
242
|
name={field.name}
|
|
214
243
|
type="password"
|
|
@@ -216,8 +245,8 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
216
245
|
onBlur={field.handleBlur}
|
|
217
246
|
onChange={(e) => field.handleChange(e.currentTarget.value)}
|
|
218
247
|
error={field.state.meta.errors.length > 0}
|
|
219
|
-
errorText={firstError(field.state.meta.errors)}
|
|
220
|
-
helpText=
|
|
248
|
+
errorText={translateValidationError(t, firstError(field.state.meta.errors))}
|
|
249
|
+
helpText={t('adminUsers.create.fields.passwordHelp')}
|
|
221
250
|
autoComplete="new-password"
|
|
222
251
|
required
|
|
223
252
|
/>
|
|
@@ -230,10 +259,10 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
230
259
|
<Checkbox
|
|
231
260
|
id="new-is-enabled"
|
|
232
261
|
name={field.name}
|
|
233
|
-
label=
|
|
262
|
+
label={t('adminUsers.create.flags.enabledLabel')}
|
|
234
263
|
checked={field.state.value}
|
|
235
264
|
onCheckedChange={(checked) => field.handleChange(checked === true)}
|
|
236
|
-
helpText=
|
|
265
|
+
helpText={t('adminUsers.create.flags.enabledHelp')}
|
|
237
266
|
/>
|
|
238
267
|
)}
|
|
239
268
|
</form.Field>
|
|
@@ -243,10 +272,10 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
243
272
|
<Checkbox
|
|
244
273
|
id="new-is-email-verified"
|
|
245
274
|
name={field.name}
|
|
246
|
-
label=
|
|
275
|
+
label={t('adminUsers.create.flags.emailVerifiedLabel')}
|
|
247
276
|
checked={field.state.value}
|
|
248
277
|
onCheckedChange={(checked) => field.handleChange(checked === true)}
|
|
249
|
-
helpText=
|
|
278
|
+
helpText={t('adminUsers.create.flags.emailVerifiedHelp')}
|
|
250
279
|
/>
|
|
251
280
|
)}
|
|
252
281
|
</form.Field>
|
|
@@ -256,10 +285,10 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
256
285
|
<Checkbox
|
|
257
286
|
id="new-is-super-admin"
|
|
258
287
|
name={field.name}
|
|
259
|
-
label=
|
|
288
|
+
label={t('adminUsers.create.flags.superAdminLabel')}
|
|
260
289
|
checked={field.state.value}
|
|
261
290
|
onCheckedChange={(checked) => field.handleChange(checked === true)}
|
|
262
|
-
helpText=
|
|
291
|
+
helpText={t('adminUsers.create.flags.superAdminHelp')}
|
|
263
292
|
/>
|
|
264
293
|
)}
|
|
265
294
|
</form.Field>
|
|
@@ -273,7 +302,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
273
302
|
onClick={onClose}
|
|
274
303
|
className={cx('byline-user-create-action', styles.action)}
|
|
275
304
|
>
|
|
276
|
-
|
|
305
|
+
{t('common.actions.cancel')}
|
|
277
306
|
</Button>
|
|
278
307
|
<form.Subscribe
|
|
279
308
|
selector={(state) => ({
|
|
@@ -289,7 +318,7 @@ export function CreateAdminUser({ onClose, onSuccess }: CreateAdminUserProps) {
|
|
|
289
318
|
disabled={!canSubmit || isSubmitting}
|
|
290
319
|
className={cx('byline-user-create-action', styles.action)}
|
|
291
320
|
>
|
|
292
|
-
{isSubmitting === true ? <LoaderEllipsis size={42} /> : '
|
|
321
|
+
{isSubmitting === true ? <LoaderEllipsis size={42} /> : t('common.actions.save')}
|
|
293
322
|
</Button>
|
|
294
323
|
)}
|
|
295
324
|
</form.Subscribe>
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { useState } from 'react'
|
|
27
27
|
|
|
28
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
28
29
|
import { Alert, Button, Checkbox, LoaderEllipsis } from '@byline/ui/react'
|
|
29
30
|
import cx from 'classnames'
|
|
30
31
|
|
|
@@ -49,6 +50,7 @@ function setsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
|
|
49
50
|
|
|
50
51
|
export function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }: UserRolesProps) {
|
|
51
52
|
const { setUserRoles } = useBylineAdminServices()
|
|
53
|
+
const { t } = useTranslation('byline-admin')
|
|
52
54
|
const [initialSet, setInitialSet] = useState<ReadonlySet<string>>(() => new Set(initialRoleIds))
|
|
53
55
|
const [selected, setSelected] = useState<Set<string>>(() => new Set(initialRoleIds))
|
|
54
56
|
const [saving, setSaving] = useState(false)
|
|
@@ -79,16 +81,16 @@ export function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }:
|
|
|
79
81
|
const storedSet = new Set(response.roles.map((r) => r.id))
|
|
80
82
|
setInitialSet(storedSet)
|
|
81
83
|
setSelected(new Set(storedSet))
|
|
82
|
-
setSuccessMessage('
|
|
84
|
+
setSuccessMessage(t('common.feedback.saved'))
|
|
83
85
|
onSaved?.(response)
|
|
84
86
|
} catch (err) {
|
|
85
87
|
const code = getErrorCode(err)
|
|
86
88
|
if (code === 'admin.roles.userNotFound') {
|
|
87
|
-
setError('
|
|
89
|
+
setError(t('adminUsers.roles.errors.userNotFound'))
|
|
88
90
|
} else if (code === 'admin.roles.notFound') {
|
|
89
|
-
setError('
|
|
91
|
+
setError(t('adminUsers.roles.errors.roleNotFound'))
|
|
90
92
|
} else {
|
|
91
|
-
setError('
|
|
93
|
+
setError(t('adminUsers.roles.errors.fallback'))
|
|
92
94
|
}
|
|
93
95
|
} finally {
|
|
94
96
|
setSaving(false)
|
|
@@ -102,8 +104,7 @@ export function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }:
|
|
|
102
104
|
|
|
103
105
|
{allRoles.length === 0 ? (
|
|
104
106
|
<p className={cx('muted', 'byline-user-roles-empty', styles.empty)}>
|
|
105
|
-
|
|
106
|
-
<span className="muted">/admin/roles</span> first.
|
|
107
|
+
{t('adminUsers.roles.emptyCatalog')}
|
|
107
108
|
</p>
|
|
108
109
|
) : (
|
|
109
110
|
<div className={cx('byline-user-roles-list', styles.list)}>
|
|
@@ -148,7 +149,7 @@ export function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }:
|
|
|
148
149
|
disabled={saving}
|
|
149
150
|
className={cx('byline-user-roles-action', styles.action)}
|
|
150
151
|
>
|
|
151
|
-
{successMessage ? '
|
|
152
|
+
{successMessage ? t('common.actions.close') : t('common.actions.cancel')}
|
|
152
153
|
</Button>
|
|
153
154
|
<Button
|
|
154
155
|
type="button"
|
|
@@ -158,7 +159,7 @@ export function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }:
|
|
|
158
159
|
disabled={saving || !isDirty}
|
|
159
160
|
className={cx('byline-user-roles-action', styles.action)}
|
|
160
161
|
>
|
|
161
|
-
{saving ? <LoaderEllipsis size={30} /> : '
|
|
162
|
+
{saving ? <LoaderEllipsis size={30} /> : t('common.actions.save')}
|
|
162
163
|
</Button>
|
|
163
164
|
</div>
|
|
164
165
|
</div>
|
|
@@ -21,29 +21,24 @@
|
|
|
21
21
|
* `vid` back into the container; the drawer doesn't need to re-fetch.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { useState } from 'react'
|
|
24
|
+
import { useMemo, useState } from 'react'
|
|
25
25
|
import { revalidateLogic, useForm } from '@tanstack/react-form-start'
|
|
26
26
|
|
|
27
27
|
import { passwordSchema } from '@byline/core/validation'
|
|
28
|
+
import { useTranslation } from '@byline/i18n/react'
|
|
28
29
|
import { Alert, Button, InputPassword, LoaderEllipsis } from '@byline/ui/react'
|
|
29
30
|
import cx from 'classnames'
|
|
30
31
|
import { z } from 'zod'
|
|
31
32
|
|
|
33
|
+
import { translateValidationError } from '../../../lib/translate-validation-error.js'
|
|
32
34
|
import { useBylineAdminServices } from '../../../services/admin-services-context.js'
|
|
33
35
|
import styles from './set-password.module.css'
|
|
34
36
|
import type { AdminUserResponse } from '../index.js'
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
41
|
-
.refine((v) => v.password === v.confirm, {
|
|
42
|
-
message: 'Passwords do not match',
|
|
43
|
-
path: ['confirm'],
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
type SetPasswordValues = z.infer<typeof setPasswordFormSchema>
|
|
38
|
+
type SetPasswordValues = {
|
|
39
|
+
password: string
|
|
40
|
+
confirm: string
|
|
41
|
+
}
|
|
47
42
|
|
|
48
43
|
interface SetPasswordProps {
|
|
49
44
|
user: AdminUserResponse
|
|
@@ -53,9 +48,26 @@ interface SetPasswordProps {
|
|
|
53
48
|
|
|
54
49
|
export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
55
50
|
const { setAdminUserPassword } = useBylineAdminServices()
|
|
51
|
+
const { t } = useTranslation('byline-admin')
|
|
56
52
|
const [formError, setFormError] = useState<string | null>(null)
|
|
57
53
|
const [successMessage, setSuccessMessage] = useState<string | null>(null)
|
|
58
54
|
|
|
55
|
+
const setPasswordFormSchema = useMemo(
|
|
56
|
+
() =>
|
|
57
|
+
z
|
|
58
|
+
.object({
|
|
59
|
+
password: passwordSchema,
|
|
60
|
+
confirm: z.string({
|
|
61
|
+
message: t('account.changePassword.errors.confirmRequired'),
|
|
62
|
+
}),
|
|
63
|
+
})
|
|
64
|
+
.refine((v) => v.password === v.confirm, {
|
|
65
|
+
message: t('account.changePassword.errors.mismatch'),
|
|
66
|
+
path: ['confirm'],
|
|
67
|
+
}),
|
|
68
|
+
[t]
|
|
69
|
+
)
|
|
70
|
+
|
|
59
71
|
const form = useForm({
|
|
60
72
|
defaultValues: { password: '', confirm: '' } as SetPasswordValues,
|
|
61
73
|
validationLogic: revalidateLogic({
|
|
@@ -72,22 +84,20 @@ export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
|
72
84
|
const updated = await setAdminUserPassword({
|
|
73
85
|
data: { id: user.id, vid: user.vid, password: value.password },
|
|
74
86
|
})
|
|
75
|
-
setSuccessMessage('
|
|
87
|
+
setSuccessMessage(t('account.changePassword.feedback.updated'))
|
|
76
88
|
form.reset({ password: '', confirm: '' })
|
|
77
89
|
onSuccess?.(updated)
|
|
78
90
|
} catch (err) {
|
|
79
91
|
const code = getErrorCode(err)
|
|
80
92
|
if (code === 'admin.users.versionConflict') {
|
|
81
|
-
setFormError(
|
|
82
|
-
'This user has been modified elsewhere since you opened this form. Reload to refresh and try again.'
|
|
83
|
-
)
|
|
93
|
+
setFormError(t('adminUsers.update.errors.versionConflict'))
|
|
84
94
|
return
|
|
85
95
|
}
|
|
86
96
|
if (code === 'admin.users.notFound') {
|
|
87
|
-
setFormError('
|
|
97
|
+
setFormError(t('adminUsers.update.errors.notFound'))
|
|
88
98
|
return
|
|
89
99
|
}
|
|
90
|
-
setFormError('
|
|
100
|
+
setFormError(t('adminUsers.setPassword.errors.fallback'))
|
|
91
101
|
}
|
|
92
102
|
},
|
|
93
103
|
})
|
|
@@ -106,23 +116,19 @@ export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
|
106
116
|
{formError ? <Alert intent="danger">{formError}</Alert> : null}
|
|
107
117
|
{successMessage ? <Alert intent="success">{successMessage}</Alert> : null}
|
|
108
118
|
|
|
109
|
-
<p className="muted">
|
|
110
|
-
Sets a new password for{' '}
|
|
111
|
-
<span className={cx('byline-user-set-password-target', styles.target)}>{user.email}</span>
|
|
112
|
-
. The user will need to sign in again with the new password.
|
|
113
|
-
</p>
|
|
119
|
+
<p className="muted">{t('adminUsers.setPassword.intro', { email: user.email })}</p>
|
|
114
120
|
|
|
115
121
|
<form.Field name="password">
|
|
116
122
|
{(field) => (
|
|
117
123
|
<InputPassword
|
|
118
|
-
label=
|
|
124
|
+
label={t('account.changePassword.fields.new')}
|
|
119
125
|
id="password"
|
|
120
126
|
name={field.name}
|
|
121
127
|
value={field.state.value}
|
|
122
128
|
onBlur={field.handleBlur}
|
|
123
129
|
onChange={(e) => field.handleChange(e.currentTarget.value)}
|
|
124
130
|
error={field.state.meta.errors.length > 0}
|
|
125
|
-
errorText={firstError(field.state.meta.errors)}
|
|
131
|
+
errorText={translateValidationError(t, firstError(field.state.meta.errors))}
|
|
126
132
|
autoComplete="new-password"
|
|
127
133
|
required
|
|
128
134
|
/>
|
|
@@ -132,7 +138,7 @@ export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
|
132
138
|
<form.Field name="confirm">
|
|
133
139
|
{(field) => (
|
|
134
140
|
<InputPassword
|
|
135
|
-
label=
|
|
141
|
+
label={t('account.changePassword.fields.confirm')}
|
|
136
142
|
id="confirm"
|
|
137
143
|
name={field.name}
|
|
138
144
|
value={field.state.value}
|
|
@@ -154,7 +160,7 @@ export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
|
154
160
|
onClick={onClose}
|
|
155
161
|
className={cx('byline-user-set-password-action', styles.action)}
|
|
156
162
|
>
|
|
157
|
-
{successMessage ? '
|
|
163
|
+
{successMessage ? t('common.actions.close') : t('common.actions.cancel')}
|
|
158
164
|
</Button>
|
|
159
165
|
<form.Subscribe
|
|
160
166
|
selector={(state) => ({
|
|
@@ -171,7 +177,7 @@ export function SetPassword({ user, onClose, onSuccess }: SetPasswordProps) {
|
|
|
171
177
|
disabled={!canSubmit || isSubmitting}
|
|
172
178
|
className={cx('byline-user-set-password-action', styles.action)}
|
|
173
179
|
>
|
|
174
|
-
{isSubmitting === true ? <LoaderEllipsis size={42} /> : '
|
|
180
|
+
{isSubmitting === true ? <LoaderEllipsis size={42} /> : t('common.actions.save')}
|
|
175
181
|
</Button>
|
|
176
182
|
)}
|
|
177
183
|
</form.Subscribe>
|