@byline/admin 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abilities.js +5 -24
- package/dist/index.js +8 -30
- package/dist/lib/assert-admin-actor.js +13 -74
- package/dist/lib/create-command.js +6 -16
- package/dist/modules/admin-account/commands.js +35 -24
- package/dist/modules/admin-account/components/change-password.d.ts +8 -0
- package/dist/modules/admin-account/components/change-password.js +192 -0
- package/dist/modules/admin-account/components/change-password.module.js +8 -0
- package/dist/modules/admin-account/components/change-password_module.css +27 -0
- package/dist/modules/admin-account/components/container.d.ts +29 -0
- package/dist/modules/admin-account/components/container.js +298 -0
- package/dist/modules/admin-account/components/container.module.js +28 -0
- package/dist/modules/admin-account/components/container_module.css +106 -0
- package/dist/modules/admin-account/components/update.d.ts +8 -0
- package/dist/modules/admin-account/components/update.js +207 -0
- package/dist/modules/admin-account/components/update.module.js +8 -0
- package/dist/modules/admin-account/components/update_module.css +27 -0
- package/dist/modules/admin-account/errors.js +14 -45
- package/dist/modules/admin-account/index.js +4 -34
- package/dist/modules/admin-account/schemas.js +25 -59
- package/dist/modules/admin-account/service.js +56 -61
- package/dist/modules/admin-permissions/abilities.js +6 -24
- package/dist/modules/admin-permissions/commands.js +42 -28
- package/dist/modules/admin-permissions/components/inspector.d.ts +4 -0
- package/dist/modules/admin-permissions/components/inspector.js +284 -0
- package/dist/modules/admin-permissions/components/inspector.module.js +56 -0
- package/dist/modules/admin-permissions/components/inspector_module.css +238 -0
- package/dist/modules/admin-permissions/dto.js +3 -16
- package/dist/modules/admin-permissions/errors.js +14 -27
- package/dist/modules/admin-permissions/index.js +6 -26
- package/dist/modules/admin-permissions/repository.js +1 -8
- package/dist/modules/admin-permissions/schemas.js +33 -70
- package/dist/modules/admin-permissions/service.js +88 -92
- package/dist/modules/admin-roles/abilities.js +8 -30
- package/dist/modules/admin-roles/commands.js +89 -55
- package/dist/modules/admin-roles/components/create.d.ts +7 -0
- package/dist/modules/admin-roles/components/create.js +177 -0
- package/dist/modules/admin-roles/components/create.module.js +8 -0
- package/dist/modules/admin-roles/components/create_module.css +27 -0
- package/dist/modules/admin-roles/components/permissions.d.ts +10 -0
- package/dist/modules/admin-roles/components/permissions.js +303 -0
- package/dist/modules/admin-roles/components/permissions.module.js +44 -0
- package/dist/modules/admin-roles/components/permissions_module.css +192 -0
- package/dist/modules/admin-roles/components/update.d.ts +8 -0
- package/dist/modules/admin-roles/components/update.js +166 -0
- package/dist/modules/admin-roles/components/update.module.js +8 -0
- package/dist/modules/admin-roles/components/update_module.css +27 -0
- package/dist/modules/admin-roles/dto.js +3 -16
- package/dist/modules/admin-roles/errors.js +16 -40
- package/dist/modules/admin-roles/index.js +6 -26
- package/dist/modules/admin-roles/repository.js +1 -8
- package/dist/modules/admin-roles/schemas.js +41 -71
- package/dist/modules/admin-roles/service.js +79 -82
- package/dist/modules/admin-users/abilities.js +9 -38
- package/dist/modules/admin-users/commands.js +92 -50
- package/dist/modules/admin-users/components/create.d.ts +8 -0
- package/dist/modules/admin-users/components/create.js +268 -0
- package/dist/modules/admin-users/components/create.module.js +10 -0
- package/dist/modules/admin-users/components/create_module.css +45 -0
- package/dist/modules/admin-users/components/roles.d.ts +11 -0
- package/dist/modules/admin-users/components/roles.js +148 -0
- package/dist/modules/admin-users/components/roles.module.js +18 -0
- package/dist/modules/admin-users/components/roles_module.css +75 -0
- package/dist/modules/admin-users/components/set-password.d.ts +8 -0
- package/dist/modules/admin-users/components/set-password.js +170 -0
- package/dist/modules/admin-users/components/set-password.module.js +9 -0
- package/dist/modules/admin-users/components/set-password_module.css +31 -0
- package/dist/modules/admin-users/components/update.d.ts +8 -0
- package/dist/modules/admin-users/components/update.js +254 -0
- package/dist/modules/admin-users/components/update.module.js +9 -0
- package/dist/modules/admin-users/components/update_module.css +34 -0
- package/dist/modules/admin-users/dto.js +3 -18
- package/dist/modules/admin-users/errors.js +17 -43
- package/dist/modules/admin-users/index.js +7 -27
- package/dist/modules/admin-users/repository.js +1 -8
- package/dist/modules/admin-users/schemas.js +44 -75
- package/dist/modules/admin-users/seed-super-admin.js +9 -34
- package/dist/modules/admin-users/service.js +76 -91
- package/dist/modules/auth/components/sign-in-form.d.ts +12 -0
- package/dist/modules/auth/components/sign-in-form.js +115 -0
- package/dist/modules/auth/components/sign-in-form.module.js +12 -0
- package/dist/modules/auth/components/sign-in-form_module.css +41 -0
- package/dist/modules/auth/index.js +3 -24
- package/dist/modules/auth/jwt-session-provider.js +179 -149
- package/dist/modules/auth/password.js +11 -53
- package/dist/modules/auth/phc.js +21 -54
- package/dist/modules/auth/refresh-tokens-repository.js +1 -8
- package/dist/modules/auth/resolve-actor.js +6 -28
- package/dist/services/admin-services-context.d.ts +16 -0
- package/dist/services/admin-services-context.js +13 -0
- package/dist/services/admin-services-types.d.ts +129 -0
- package/dist/services/admin-services-types.js +1 -0
- package/dist/store.js +1 -8
- package/dist/vendor/noble-argon2/_blake.js +277 -45
- package/dist/vendor/noble-argon2/_md.js +81 -136
- package/dist/vendor/noble-argon2/_u64.js +65 -67
- package/dist/vendor/noble-argon2/argon2.js +181 -342
- package/dist/vendor/noble-argon2/blake2.js +252 -327
- package/dist/vendor/noble-argon2/utils.js +110 -490
- package/dist/vendor/noble-argon2/utils.js.LICENSE.txt +1 -0
- package/package.json +89 -10
- package/src/abilities.ts +32 -0
- package/src/declarations.d.ts +4 -0
- package/src/index.ts +39 -0
- package/src/lib/assert-admin-actor.ts +90 -0
- package/src/lib/create-command.ts +109 -0
- package/src/modules/admin-account/commands.ts +76 -0
- package/src/modules/admin-account/components/change-password.module.css +40 -0
- package/src/modules/admin-account/components/change-password.tsx +232 -0
- package/src/modules/admin-account/components/container.module.css +158 -0
- package/src/modules/admin-account/components/container.tsx +229 -0
- package/src/modules/admin-account/components/update.module.css +40 -0
- package/src/modules/admin-account/components/update.tsx +263 -0
- package/src/modules/admin-account/errors.ts +75 -0
- package/src/modules/admin-account/index.ts +60 -0
- package/src/modules/admin-account/schemas.ts +84 -0
- package/src/modules/admin-account/service.ts +92 -0
- package/src/modules/admin-permissions/abilities.ts +46 -0
- package/src/modules/admin-permissions/commands.ts +103 -0
- package/src/modules/admin-permissions/components/inspector.module.css +326 -0
- package/src/modules/admin-permissions/components/inspector.tsx +298 -0
- package/src/modules/admin-permissions/dto.ts +28 -0
- package/src/modules/admin-permissions/errors.ts +57 -0
- package/src/modules/admin-permissions/index.ts +72 -0
- package/src/modules/admin-permissions/repository.ts +49 -0
- package/src/modules/admin-permissions/schemas.ts +128 -0
- package/src/modules/admin-permissions/service.ts +137 -0
- package/src/modules/admin-roles/abilities.ts +62 -0
- package/src/modules/admin-roles/commands.ts +161 -0
- package/src/modules/admin-roles/components/create.module.css +40 -0
- package/src/modules/admin-roles/components/create.tsx +218 -0
- package/src/modules/admin-roles/components/permissions.module.css +279 -0
- package/src/modules/admin-roles/components/permissions.tsx +396 -0
- package/src/modules/admin-roles/components/update.module.css +40 -0
- package/src/modules/admin-roles/components/update.tsx +218 -0
- package/src/modules/admin-roles/dto.ts +30 -0
- package/src/modules/admin-roles/errors.ts +76 -0
- package/src/modules/admin-roles/index.ts +81 -0
- package/src/modules/admin-roles/repository.ts +96 -0
- package/src/modules/admin-roles/schemas.ts +139 -0
- package/src/modules/admin-roles/service.ts +136 -0
- package/src/modules/admin-users/abilities.ts +76 -0
- package/src/modules/admin-users/commands.ts +157 -0
- package/src/modules/admin-users/components/create.module.css +63 -0
- package/src/modules/admin-users/components/create.tsx +323 -0
- package/src/modules/admin-users/components/roles.module.css +119 -0
- package/src/modules/admin-users/components/roles.tsx +172 -0
- package/src/modules/admin-users/components/set-password.module.css +46 -0
- package/src/modules/admin-users/components/set-password.tsx +199 -0
- package/src/modules/admin-users/components/update.module.css +49 -0
- package/src/modules/admin-users/components/update.tsx +328 -0
- package/src/modules/admin-users/dto.ts +39 -0
- package/src/modules/admin-users/errors.ts +84 -0
- package/src/modules/admin-users/index.ts +91 -0
- package/src/modules/admin-users/repository.ts +161 -0
- package/src/modules/admin-users/schemas.ts +168 -0
- package/src/modules/admin-users/seed-super-admin.ts +102 -0
- package/src/modules/admin-users/service.ts +166 -0
- package/src/modules/auth/components/sign-in-form.module.css +62 -0
- package/src/modules/auth/components/sign-in-form.tsx +132 -0
- package/src/modules/auth/index.ts +31 -0
- package/src/modules/auth/jwt-session-provider.ts +301 -0
- package/src/modules/auth/password.ts +94 -0
- package/src/modules/auth/phc.ts +121 -0
- package/src/modules/auth/refresh-tokens-repository.ts +74 -0
- package/src/modules/auth/resolve-actor.ts +42 -0
- package/src/services/admin-services-context.tsx +52 -0
- package/src/services/admin-services-types.ts +177 -0
- package/src/store.ts +32 -0
- package/src/vendor/noble-argon2/LICENSE +21 -0
- package/src/vendor/noble-argon2/README.md +87 -0
- package/src/vendor/noble-argon2/_blake.ts +58 -0
- package/src/vendor/noble-argon2/_md.ts +223 -0
- package/src/vendor/noble-argon2/_u64.ts +118 -0
- package/src/vendor/noble-argon2/argon2.ts +668 -0
- package/src/vendor/noble-argon2/blake2.ts +583 -0
- package/src/vendor/noble-argon2/utils.ts +849 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { revalidateLogic, useForm } from "@tanstack/react-form-start";
|
|
5
|
+
import { passwordSchema } from "@byline/core/validation";
|
|
6
|
+
import { Alert, Button, InputPassword, LoaderEllipsis } from "@byline/ui/react";
|
|
7
|
+
import classnames from "classnames";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { useBylineAdminServices } from "../../../services/admin-services-context.js";
|
|
10
|
+
import set_password_module from "./set-password.module.js";
|
|
11
|
+
const setPasswordFormSchema = z.object({
|
|
12
|
+
password: passwordSchema,
|
|
13
|
+
confirm: z.string({
|
|
14
|
+
message: 'Please confirm the password'
|
|
15
|
+
})
|
|
16
|
+
}).refine((v)=>v.password === v.confirm, {
|
|
17
|
+
message: 'Passwords do not match',
|
|
18
|
+
path: [
|
|
19
|
+
'confirm'
|
|
20
|
+
]
|
|
21
|
+
});
|
|
22
|
+
function SetPassword({ user, onClose, onSuccess }) {
|
|
23
|
+
const { setAdminUserPassword } = useBylineAdminServices();
|
|
24
|
+
const [formError, setFormError] = useState(null);
|
|
25
|
+
const [successMessage, setSuccessMessage] = useState(null);
|
|
26
|
+
const form = useForm({
|
|
27
|
+
defaultValues: {
|
|
28
|
+
password: '',
|
|
29
|
+
confirm: ''
|
|
30
|
+
},
|
|
31
|
+
validationLogic: revalidateLogic({
|
|
32
|
+
mode: 'blur',
|
|
33
|
+
modeAfterSubmission: 'change'
|
|
34
|
+
}),
|
|
35
|
+
validators: {
|
|
36
|
+
onDynamic: setPasswordFormSchema
|
|
37
|
+
},
|
|
38
|
+
onSubmit: async ({ value })=>{
|
|
39
|
+
setFormError(null);
|
|
40
|
+
setSuccessMessage(null);
|
|
41
|
+
try {
|
|
42
|
+
const updated = await setAdminUserPassword({
|
|
43
|
+
data: {
|
|
44
|
+
id: user.id,
|
|
45
|
+
vid: user.vid,
|
|
46
|
+
password: value.password
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
setSuccessMessage('Password updated.');
|
|
50
|
+
form.reset({
|
|
51
|
+
password: '',
|
|
52
|
+
confirm: ''
|
|
53
|
+
});
|
|
54
|
+
onSuccess?.(updated);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const code = getErrorCode(err);
|
|
57
|
+
if ('admin.users.versionConflict' === code) return void setFormError('This user has been modified elsewhere since you opened this form. Reload to refresh and try again.');
|
|
58
|
+
if ('admin.users.notFound' === code) return void setFormError('This user no longer exists.');
|
|
59
|
+
setFormError('Could not set the password. Please try again.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return /*#__PURE__*/ jsx("div", {
|
|
64
|
+
className: classnames('byline-user-set-password-wrap', set_password_module.wrap),
|
|
65
|
+
children: /*#__PURE__*/ jsxs("form", {
|
|
66
|
+
noValidate: true,
|
|
67
|
+
onSubmit: (event)=>{
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
form.handleSubmit();
|
|
71
|
+
},
|
|
72
|
+
className: classnames('byline-user-set-password-form', set_password_module.form),
|
|
73
|
+
children: [
|
|
74
|
+
formError ? /*#__PURE__*/ jsx(Alert, {
|
|
75
|
+
intent: "danger",
|
|
76
|
+
children: formError
|
|
77
|
+
}) : null,
|
|
78
|
+
successMessage ? /*#__PURE__*/ jsx(Alert, {
|
|
79
|
+
intent: "success",
|
|
80
|
+
children: successMessage
|
|
81
|
+
}) : null,
|
|
82
|
+
/*#__PURE__*/ jsxs("p", {
|
|
83
|
+
className: "muted",
|
|
84
|
+
children: [
|
|
85
|
+
"Sets a new password for",
|
|
86
|
+
' ',
|
|
87
|
+
/*#__PURE__*/ jsx("span", {
|
|
88
|
+
className: classnames('byline-user-set-password-target', set_password_module.target),
|
|
89
|
+
children: user.email
|
|
90
|
+
}),
|
|
91
|
+
". The user will need to sign in again with the new password."
|
|
92
|
+
]
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
95
|
+
name: "password",
|
|
96
|
+
children: (field)=>/*#__PURE__*/ jsx(InputPassword, {
|
|
97
|
+
label: "New password",
|
|
98
|
+
id: "password",
|
|
99
|
+
name: field.name,
|
|
100
|
+
value: field.state.value,
|
|
101
|
+
onBlur: field.handleBlur,
|
|
102
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
103
|
+
error: field.state.meta.errors.length > 0,
|
|
104
|
+
errorText: firstError(field.state.meta.errors),
|
|
105
|
+
autoComplete: "new-password",
|
|
106
|
+
required: true
|
|
107
|
+
})
|
|
108
|
+
}),
|
|
109
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
110
|
+
name: "confirm",
|
|
111
|
+
children: (field)=>/*#__PURE__*/ jsx(InputPassword, {
|
|
112
|
+
label: "Confirm new password",
|
|
113
|
+
id: "confirm",
|
|
114
|
+
name: field.name,
|
|
115
|
+
value: field.state.value,
|
|
116
|
+
onBlur: field.handleBlur,
|
|
117
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
118
|
+
error: field.state.meta.errors.length > 0,
|
|
119
|
+
errorText: firstError(field.state.meta.errors),
|
|
120
|
+
autoComplete: "new-password",
|
|
121
|
+
required: true
|
|
122
|
+
})
|
|
123
|
+
}),
|
|
124
|
+
/*#__PURE__*/ jsxs("div", {
|
|
125
|
+
className: classnames('byline-user-set-password-actions', set_password_module.actions),
|
|
126
|
+
children: [
|
|
127
|
+
/*#__PURE__*/ jsx(Button, {
|
|
128
|
+
type: "button",
|
|
129
|
+
intent: "secondary",
|
|
130
|
+
size: "sm",
|
|
131
|
+
onClick: onClose,
|
|
132
|
+
className: classnames('byline-user-set-password-action', set_password_module.action),
|
|
133
|
+
children: successMessage ? 'Close' : 'Cancel'
|
|
134
|
+
}),
|
|
135
|
+
/*#__PURE__*/ jsx(form.Subscribe, {
|
|
136
|
+
selector: (state)=>({
|
|
137
|
+
canSubmit: state.canSubmit,
|
|
138
|
+
isSubmitting: state.isSubmitting,
|
|
139
|
+
isDirty: state.isDirty
|
|
140
|
+
}),
|
|
141
|
+
children: ({ canSubmit, isSubmitting })=>/*#__PURE__*/ jsx(Button, {
|
|
142
|
+
size: "sm",
|
|
143
|
+
intent: "primary",
|
|
144
|
+
type: "submit",
|
|
145
|
+
disabled: !canSubmit || isSubmitting,
|
|
146
|
+
className: classnames('byline-user-set-password-action', set_password_module.action),
|
|
147
|
+
children: true === isSubmitting ? /*#__PURE__*/ jsx(LoaderEllipsis, {
|
|
148
|
+
size: 42
|
|
149
|
+
}) : 'Save'
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
})
|
|
154
|
+
]
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function firstError(errors) {
|
|
159
|
+
for (const err of errors){
|
|
160
|
+
if ('string' == typeof err) return err;
|
|
161
|
+
if (err && 'object' == typeof err && 'message' in err) {
|
|
162
|
+
const msg = err.message;
|
|
163
|
+
if ('string' == typeof msg) return msg;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function getErrorCode(err) {
|
|
168
|
+
return 'string' == typeof err?.code ? err.code : null;
|
|
169
|
+
}
|
|
170
|
+
export { SetPassword };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
:is(.wrap-SImk1V, .byline-user-set-password-wrap) {
|
|
2
|
+
gap: var(--spacing-8);
|
|
3
|
+
padding: var(--spacing-4);
|
|
4
|
+
margin-top: var(--spacing-4);
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
display: flex;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:is(.form-ZNsJjL, .byline-user-set-password-form) {
|
|
10
|
+
gap: var(--spacing-16);
|
|
11
|
+
padding-top: var(--spacing-8);
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
display: flex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:is(.target-NYVQqz, .byline-user-set-password-target) {
|
|
17
|
+
font-weight: var(--font-weight-semibold);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:is(.actions-hvHeN5, .byline-user-set-password-actions) {
|
|
21
|
+
justify-content: flex-end;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--spacing-8);
|
|
24
|
+
margin-top: var(--spacing-16);
|
|
25
|
+
display: flex;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:is(.action-nZwAQA, .byline-user-set-password-action) {
|
|
29
|
+
min-width: 4rem;
|
|
30
|
+
}
|
|
31
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AdminUserResponse } from '../index.js';
|
|
2
|
+
interface UpdateUserProps {
|
|
3
|
+
user: AdminUserResponse;
|
|
4
|
+
onClose?: () => void;
|
|
5
|
+
onSuccess?: (user: AdminUserResponse) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function UpdateUser({ user, onClose, onSuccess }: UpdateUserProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { revalidateLogic, useForm } from "@tanstack/react-form-start";
|
|
5
|
+
import { Alert, Button, Checkbox, Input, LoaderEllipsis } from "@byline/ui/react";
|
|
6
|
+
import classnames from "classnames";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { useBylineAdminServices } from "../../../services/admin-services-context.js";
|
|
9
|
+
import update_module from "./update.module.js";
|
|
10
|
+
const updateUserSchema = z.object({
|
|
11
|
+
given_name: z.string().max(100, 'Given name must not exceed 100 characters'),
|
|
12
|
+
family_name: z.string().max(100, 'Family name must not exceed 100 characters'),
|
|
13
|
+
username: z.string().max(100, 'Username must not exceed 100 characters'),
|
|
14
|
+
email: z.email({
|
|
15
|
+
message: 'Enter a valid email address'
|
|
16
|
+
}).min(3).max(254, 'Email must not exceed 254 characters'),
|
|
17
|
+
is_super_admin: z.boolean(),
|
|
18
|
+
is_enabled: z.boolean(),
|
|
19
|
+
is_email_verified: z.boolean()
|
|
20
|
+
});
|
|
21
|
+
function defaultsFrom(user) {
|
|
22
|
+
return {
|
|
23
|
+
given_name: user.given_name ?? '',
|
|
24
|
+
family_name: user.family_name ?? '',
|
|
25
|
+
username: user.username ?? '',
|
|
26
|
+
email: user.email,
|
|
27
|
+
is_super_admin: user.is_super_admin,
|
|
28
|
+
is_enabled: user.is_enabled,
|
|
29
|
+
is_email_verified: user.is_email_verified
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function buildPatch(values, user) {
|
|
33
|
+
const patch = {};
|
|
34
|
+
const normaliseText = (value)=>value.trim().length > 0 ? value : null;
|
|
35
|
+
const nextGiven = normaliseText(values.given_name);
|
|
36
|
+
const nextFamily = normaliseText(values.family_name);
|
|
37
|
+
const nextUsername = normaliseText(values.username);
|
|
38
|
+
if (nextGiven !== user.given_name) patch.given_name = nextGiven;
|
|
39
|
+
if (nextFamily !== user.family_name) patch.family_name = nextFamily;
|
|
40
|
+
if (nextUsername !== user.username) patch.username = nextUsername;
|
|
41
|
+
if (values.email !== user.email) patch.email = values.email;
|
|
42
|
+
if (values.is_super_admin !== user.is_super_admin) patch.is_super_admin = values.is_super_admin;
|
|
43
|
+
if (values.is_enabled !== user.is_enabled) patch.is_enabled = values.is_enabled;
|
|
44
|
+
if (values.is_email_verified !== user.is_email_verified) patch.is_email_verified = values.is_email_verified;
|
|
45
|
+
return patch;
|
|
46
|
+
}
|
|
47
|
+
function UpdateUser({ user, onClose, onSuccess }) {
|
|
48
|
+
const { updateAdminUser } = useBylineAdminServices();
|
|
49
|
+
const [formError, setFormError] = useState(null);
|
|
50
|
+
const [successMessage, setSuccessMessage] = useState(null);
|
|
51
|
+
const form = useForm({
|
|
52
|
+
defaultValues: defaultsFrom(user),
|
|
53
|
+
validationLogic: revalidateLogic({
|
|
54
|
+
mode: 'blur',
|
|
55
|
+
modeAfterSubmission: 'change'
|
|
56
|
+
}),
|
|
57
|
+
validators: {
|
|
58
|
+
onDynamic: updateUserSchema
|
|
59
|
+
},
|
|
60
|
+
onSubmit: async ({ value })=>{
|
|
61
|
+
setFormError(null);
|
|
62
|
+
setSuccessMessage(null);
|
|
63
|
+
const patch = buildPatch(value, user);
|
|
64
|
+
if (0 === Object.keys(patch).length) return void setSuccessMessage('No changes to save.');
|
|
65
|
+
try {
|
|
66
|
+
const updated = await updateAdminUser({
|
|
67
|
+
data: {
|
|
68
|
+
id: user.id,
|
|
69
|
+
vid: user.vid,
|
|
70
|
+
patch
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
setSuccessMessage('Saved.');
|
|
74
|
+
onSuccess?.(updated);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
const code = getErrorCode(err);
|
|
77
|
+
if ('admin.users.emailInUse' === code) return void form.setFieldMeta('email', (meta)=>({
|
|
78
|
+
...meta,
|
|
79
|
+
errorMap: {
|
|
80
|
+
...meta.errorMap,
|
|
81
|
+
onServer: 'This email is already in use.'
|
|
82
|
+
},
|
|
83
|
+
errors: [
|
|
84
|
+
'This email is already in use.'
|
|
85
|
+
]
|
|
86
|
+
}));
|
|
87
|
+
if ('admin.users.versionConflict' === code) return void setFormError('This user has been modified elsewhere since you opened this form. Reload to get the latest values and try again.');
|
|
88
|
+
if ('admin.users.notFound' === code) return void setFormError('This user no longer exists.');
|
|
89
|
+
setFormError('Could not save changes. Please try again.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return /*#__PURE__*/ jsx("div", {
|
|
94
|
+
className: classnames('byline-user-update-wrap', update_module.wrap),
|
|
95
|
+
children: /*#__PURE__*/ jsxs("form", {
|
|
96
|
+
noValidate: true,
|
|
97
|
+
onSubmit: (event)=>{
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
form.handleSubmit();
|
|
101
|
+
},
|
|
102
|
+
className: classnames('byline-user-update-form', update_module.form),
|
|
103
|
+
children: [
|
|
104
|
+
formError ? /*#__PURE__*/ jsx(Alert, {
|
|
105
|
+
intent: "danger",
|
|
106
|
+
children: formError
|
|
107
|
+
}) : null,
|
|
108
|
+
successMessage ? /*#__PURE__*/ jsx(Alert, {
|
|
109
|
+
intent: "success",
|
|
110
|
+
children: successMessage
|
|
111
|
+
}) : null,
|
|
112
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
113
|
+
name: "given_name",
|
|
114
|
+
children: (field)=>/*#__PURE__*/ jsx(Input, {
|
|
115
|
+
label: "Given name",
|
|
116
|
+
id: "given_name",
|
|
117
|
+
name: field.name,
|
|
118
|
+
value: field.state.value,
|
|
119
|
+
onBlur: field.handleBlur,
|
|
120
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
121
|
+
error: field.state.meta.errors.length > 0,
|
|
122
|
+
errorText: firstError(field.state.meta.errors),
|
|
123
|
+
autoComplete: "given-name"
|
|
124
|
+
})
|
|
125
|
+
}),
|
|
126
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
127
|
+
name: "family_name",
|
|
128
|
+
children: (field)=>/*#__PURE__*/ jsx(Input, {
|
|
129
|
+
label: "Family name",
|
|
130
|
+
id: "family_name",
|
|
131
|
+
name: field.name,
|
|
132
|
+
value: field.state.value,
|
|
133
|
+
onBlur: field.handleBlur,
|
|
134
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
135
|
+
error: field.state.meta.errors.length > 0,
|
|
136
|
+
errorText: firstError(field.state.meta.errors),
|
|
137
|
+
autoComplete: "family-name"
|
|
138
|
+
})
|
|
139
|
+
}),
|
|
140
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
141
|
+
name: "username",
|
|
142
|
+
children: (field)=>/*#__PURE__*/ jsx(Input, {
|
|
143
|
+
label: "Username",
|
|
144
|
+
id: "username",
|
|
145
|
+
name: field.name,
|
|
146
|
+
value: field.state.value,
|
|
147
|
+
onBlur: field.handleBlur,
|
|
148
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
149
|
+
error: field.state.meta.errors.length > 0,
|
|
150
|
+
errorText: firstError(field.state.meta.errors),
|
|
151
|
+
helpText: "Optional. Leave blank to clear.",
|
|
152
|
+
autoComplete: "username"
|
|
153
|
+
})
|
|
154
|
+
}),
|
|
155
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
156
|
+
name: "email",
|
|
157
|
+
children: (field)=>/*#__PURE__*/ jsx(Input, {
|
|
158
|
+
label: "Email",
|
|
159
|
+
id: "email",
|
|
160
|
+
name: field.name,
|
|
161
|
+
type: "email",
|
|
162
|
+
value: field.state.value,
|
|
163
|
+
onBlur: field.handleBlur,
|
|
164
|
+
onChange: (e)=>field.handleChange(e.currentTarget.value),
|
|
165
|
+
error: field.state.meta.errors.length > 0,
|
|
166
|
+
errorText: firstError(field.state.meta.errors),
|
|
167
|
+
autoComplete: "email",
|
|
168
|
+
required: true
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
/*#__PURE__*/ jsxs("div", {
|
|
172
|
+
className: classnames('byline-user-update-flags', update_module.flags),
|
|
173
|
+
children: [
|
|
174
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
175
|
+
name: "is_enabled",
|
|
176
|
+
children: (field)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
177
|
+
id: "is_enabled",
|
|
178
|
+
name: field.name,
|
|
179
|
+
label: "Enabled",
|
|
180
|
+
checked: field.state.value,
|
|
181
|
+
onCheckedChange: (checked)=>field.handleChange(true === checked),
|
|
182
|
+
helpText: "Disabled accounts cannot sign in."
|
|
183
|
+
})
|
|
184
|
+
}),
|
|
185
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
186
|
+
name: "is_email_verified",
|
|
187
|
+
children: (field)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
188
|
+
id: "is_email_verified",
|
|
189
|
+
name: field.name,
|
|
190
|
+
label: "Email verified",
|
|
191
|
+
checked: field.state.value,
|
|
192
|
+
onCheckedChange: (checked)=>field.handleChange(true === checked)
|
|
193
|
+
})
|
|
194
|
+
}),
|
|
195
|
+
/*#__PURE__*/ jsx(form.Field, {
|
|
196
|
+
name: "is_super_admin",
|
|
197
|
+
children: (field)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
198
|
+
id: "is_super_admin",
|
|
199
|
+
name: field.name,
|
|
200
|
+
label: "Super admin",
|
|
201
|
+
checked: field.state.value,
|
|
202
|
+
onCheckedChange: (checked)=>field.handleChange(true === checked),
|
|
203
|
+
helpText: "Super admins bypass every ability check — grant with care."
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
}),
|
|
208
|
+
/*#__PURE__*/ jsxs("div", {
|
|
209
|
+
className: classnames('byline-user-update-actions', update_module.actions),
|
|
210
|
+
children: [
|
|
211
|
+
/*#__PURE__*/ jsx(Button, {
|
|
212
|
+
type: "button",
|
|
213
|
+
intent: "secondary",
|
|
214
|
+
size: "sm",
|
|
215
|
+
onClick: onClose,
|
|
216
|
+
className: classnames('byline-user-update-action', update_module.action),
|
|
217
|
+
children: successMessage ? 'Close' : 'Cancel'
|
|
218
|
+
}),
|
|
219
|
+
/*#__PURE__*/ jsx(form.Subscribe, {
|
|
220
|
+
selector: (state)=>({
|
|
221
|
+
canSubmit: state.canSubmit,
|
|
222
|
+
isSubmitting: state.isSubmitting,
|
|
223
|
+
isDirty: state.isDirty
|
|
224
|
+
}),
|
|
225
|
+
children: ({ canSubmit, isSubmitting })=>/*#__PURE__*/ jsx(Button, {
|
|
226
|
+
size: "sm",
|
|
227
|
+
intent: "primary",
|
|
228
|
+
type: "submit",
|
|
229
|
+
disabled: !canSubmit || isSubmitting,
|
|
230
|
+
className: classnames('byline-user-update-action', update_module.action),
|
|
231
|
+
children: true === isSubmitting ? /*#__PURE__*/ jsx(LoaderEllipsis, {
|
|
232
|
+
size: 42
|
|
233
|
+
}) : 'Save'
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
]
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
})
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function firstError(errors) {
|
|
243
|
+
for (const err of errors){
|
|
244
|
+
if ('string' == typeof err) return err;
|
|
245
|
+
if (err && 'object' == typeof err && 'message' in err) {
|
|
246
|
+
const msg = err.message;
|
|
247
|
+
if ('string' == typeof msg) return msg;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function getErrorCode(err) {
|
|
252
|
+
return 'string' == typeof err?.code ? err.code : null;
|
|
253
|
+
}
|
|
254
|
+
export { UpdateUser };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
:is(.wrap-nz82Of, .byline-user-update-wrap) {
|
|
2
|
+
gap: var(--spacing-8);
|
|
3
|
+
padding: var(--spacing-4);
|
|
4
|
+
margin-top: var(--spacing-4);
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
display: flex;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:is(.form-jCTlkq, .byline-user-update-form) {
|
|
10
|
+
gap: var(--spacing-16);
|
|
11
|
+
padding-top: var(--spacing-8);
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
display: flex;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:is(.flags-Drwubn, .byline-user-update-flags) {
|
|
17
|
+
gap: var(--spacing-8);
|
|
18
|
+
padding: var(--spacing-4);
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
display: flex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:is(.actions-yzizIs, .byline-user-update-actions) {
|
|
24
|
+
justify-content: flex-end;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: var(--spacing-8);
|
|
27
|
+
margin-top: var(--spacing-16);
|
|
28
|
+
display: flex;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:is(.action-peiHA8, .byline-user-update-action) {
|
|
32
|
+
min-width: 4rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
-
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) Infonomic Company Limited
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Shape an `AdminUserRow` into its public `AdminUserResponse` form.
|
|
10
|
-
*
|
|
11
|
-
* The row type from the repository already omits `password_hash`, so
|
|
12
|
-
* this is effectively an identity map — the indirection exists so that
|
|
13
|
-
* if internal fields ever get added to the row (e.g. tenant id,
|
|
14
|
-
* soft-delete timestamp), they are explicitly opted out of the public
|
|
15
|
-
* shape here rather than leaking by default.
|
|
16
|
-
*/
|
|
17
|
-
export function toAdminUser(row) {
|
|
1
|
+
function toAdminUser(row) {
|
|
18
2
|
return {
|
|
19
3
|
id: row.id,
|
|
20
4
|
vid: row.vid,
|
|
@@ -30,6 +14,7 @@ export function toAdminUser(row) {
|
|
|
30
14
|
is_enabled: row.is_enabled,
|
|
31
15
|
is_email_verified: row.is_email_verified,
|
|
32
16
|
created_at: row.created_at,
|
|
33
|
-
updated_at: row.updated_at
|
|
17
|
+
updated_at: row.updated_at
|
|
34
18
|
};
|
|
35
19
|
}
|
|
20
|
+
export { toAdminUser };
|
|
@@ -1,52 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
-
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) Infonomic Company Limited
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Module-local error codes for admin-users.
|
|
10
|
-
*
|
|
11
|
-
* Follows the same `code + factory` shape used by `AuthError` in
|
|
12
|
-
* `@byline/auth`, but with its own class so consumers can distinguish
|
|
13
|
-
* admin-users-specific failures from generic auth failures (e.g. to
|
|
14
|
-
* translate `EMAIL_IN_USE` into a 409 at a transport boundary while
|
|
15
|
-
* `FORBIDDEN` maps to 403).
|
|
16
|
-
*
|
|
17
|
-
* The codes are intentionally prefixed — `admin.users.*` — so they sort
|
|
18
|
-
* alongside the matching ability keys in logs and admin UI messages.
|
|
19
|
-
*/
|
|
20
|
-
export const AdminUsersErrorCodes = {
|
|
1
|
+
const AdminUsersErrorCodes = {
|
|
21
2
|
NOT_FOUND: 'admin.users.notFound',
|
|
22
3
|
EMAIL_IN_USE: 'admin.users.emailInUse',
|
|
23
4
|
SELF_DELETE_FORBIDDEN: 'admin.users.selfDeleteForbidden',
|
|
24
5
|
SELF_DISABLE_FORBIDDEN: 'admin.users.selfDisableForbidden',
|
|
25
|
-
VERSION_CONFLICT: 'admin.users.versionConflict'
|
|
6
|
+
VERSION_CONFLICT: 'admin.users.versionConflict'
|
|
26
7
|
};
|
|
27
|
-
|
|
28
|
-
code
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
class AdminUsersError extends Error {
|
|
9
|
+
constructor(code, options){
|
|
10
|
+
super(options.message, null != options.cause ? {
|
|
11
|
+
cause: options.cause
|
|
12
|
+
} : void 0);
|
|
31
13
|
this.name = 'AdminUsersError';
|
|
32
14
|
this.code = code;
|
|
33
15
|
}
|
|
34
16
|
}
|
|
35
|
-
const make = (code, defaultMessage)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export
|
|
45
|
-
/** The actor attempted to disable their own admin-user row. */
|
|
46
|
-
export const ERR_ADMIN_USER_SELF_DISABLE = make(AdminUsersErrorCodes.SELF_DISABLE_FORBIDDEN, 'cannot disable your own admin account');
|
|
47
|
-
/**
|
|
48
|
-
* The stored `vid` does not match the client-supplied `expectedVid` —
|
|
49
|
-
* the caller is holding a stale version of the row. Typical admin-UI
|
|
50
|
-
* response is to reload the edit form with the current values.
|
|
51
|
-
*/
|
|
52
|
-
export const ERR_ADMIN_USER_VERSION_CONFLICT = make(AdminUsersErrorCodes.VERSION_CONFLICT, 'admin user has been modified elsewhere — please reload and try again');
|
|
17
|
+
const make = (code, defaultMessage)=>(options)=>new AdminUsersError(code, {
|
|
18
|
+
message: options?.message ?? defaultMessage,
|
|
19
|
+
cause: options?.cause
|
|
20
|
+
});
|
|
21
|
+
const ERR_ADMIN_USER_NOT_FOUND = make(AdminUsersErrorCodes.NOT_FOUND, 'admin user not found');
|
|
22
|
+
const ERR_ADMIN_USER_EMAIL_IN_USE = make(AdminUsersErrorCodes.EMAIL_IN_USE, 'email already in use');
|
|
23
|
+
const ERR_ADMIN_USER_SELF_DELETE = make(AdminUsersErrorCodes.SELF_DELETE_FORBIDDEN, 'cannot delete your own admin account');
|
|
24
|
+
const ERR_ADMIN_USER_SELF_DISABLE = make(AdminUsersErrorCodes.SELF_DISABLE_FORBIDDEN, 'cannot disable your own admin account');
|
|
25
|
+
const ERR_ADMIN_USER_VERSION_CONFLICT = make(AdminUsersErrorCodes.VERSION_CONFLICT, 'admin user has been modified elsewhere — please reload and try again');
|
|
26
|
+
export { AdminUsersError, AdminUsersErrorCodes, ERR_ADMIN_USER_EMAIL_IN_USE, ERR_ADMIN_USER_NOT_FOUND, ERR_ADMIN_USER_SELF_DELETE, ERR_ADMIN_USER_SELF_DISABLE, ERR_ADMIN_USER_VERSION_CONFLICT };
|
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* `@byline/admin/admin-users` — admin user CRUD.
|
|
10
|
-
*
|
|
11
|
-
* Exports the adapter-facing `AdminUsersRepository` contract, ability
|
|
12
|
-
* keys, transport-agnostic commands, the `AdminUsersService`, the seed
|
|
13
|
-
* helper, and the module's error types. Commands are the recommended
|
|
14
|
-
* entry point for any caller; the service is exposed for internal uses
|
|
15
|
-
* (seeds, other services) that want to skip Zod/ability overhead.
|
|
16
|
-
*
|
|
17
|
-
* Password hashing is owned by `@byline/admin/auth`; this module takes
|
|
18
|
-
* pre-hashed `password_hash` strings on the repository boundary so the
|
|
19
|
-
* adapter never sees plaintext.
|
|
20
|
-
*/
|
|
21
|
-
export { ADMIN_USERS_ABILITIES, registerAdminUsersAbilities, } from './abilities.js';
|
|
22
|
-
export { createAdminUserCommand, deleteAdminUserCommand, disableAdminUserCommand, enableAdminUserCommand, getAdminUserCommand, listAdminUsersCommand, setAdminUserPasswordCommand, updateAdminUserCommand, } from './commands.js';
|
|
23
|
-
export { toAdminUser } from './dto.js';
|
|
24
|
-
export { AdminUsersError, AdminUsersErrorCodes, ERR_ADMIN_USER_EMAIL_IN_USE, ERR_ADMIN_USER_NOT_FOUND, ERR_ADMIN_USER_SELF_DELETE, ERR_ADMIN_USER_SELF_DISABLE, ERR_ADMIN_USER_VERSION_CONFLICT, } from './errors.js';
|
|
25
|
-
export { adminUserListResponseSchema, adminUserResponseSchema, createAdminUserRequestSchema, deleteAdminUserRequestSchema, disableAdminUserRequestSchema, enableAdminUserRequestSchema, getAdminUserRequestSchema, listAdminUsersRequestSchema, okResponseSchema, setAdminUserPasswordRequestSchema, updateAdminUserRequestSchema, } from './schemas.js';
|
|
26
|
-
export { seedSuperAdmin, } from './seed-super-admin.js';
|
|
27
|
-
export { AdminUsersService } from './service.js';
|
|
1
|
+
export { ADMIN_USERS_ABILITIES, registerAdminUsersAbilities } from "./abilities.js";
|
|
2
|
+
export { createAdminUserCommand, deleteAdminUserCommand, disableAdminUserCommand, enableAdminUserCommand, getAdminUserCommand, listAdminUsersCommand, setAdminUserPasswordCommand, updateAdminUserCommand } from "./commands.js";
|
|
3
|
+
export { toAdminUser } from "./dto.js";
|
|
4
|
+
export { AdminUsersError, AdminUsersErrorCodes, ERR_ADMIN_USER_EMAIL_IN_USE, ERR_ADMIN_USER_NOT_FOUND, ERR_ADMIN_USER_SELF_DELETE, ERR_ADMIN_USER_SELF_DISABLE, ERR_ADMIN_USER_VERSION_CONFLICT } from "./errors.js";
|
|
5
|
+
export { adminUserListResponseSchema, adminUserResponseSchema, createAdminUserRequestSchema, deleteAdminUserRequestSchema, disableAdminUserRequestSchema, enableAdminUserRequestSchema, getAdminUserRequestSchema, listAdminUsersRequestSchema, okResponseSchema, setAdminUserPasswordRequestSchema, updateAdminUserRequestSchema } from "./schemas.js";
|
|
6
|
+
export { seedSuperAdmin } from "./seed-super-admin.js";
|
|
7
|
+
export { AdminUsersService } from "./service.js";
|