@abpjs/account 0.8.0 → 0.9.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/components/RegisterForm/RegisterForm.d.ts +5 -3
- package/dist/components/TenantBox/TenantBox.d.ts +4 -2
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useAccountService.d.ts +27 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +267 -123
- package/dist/index.mjs +208 -66
- package/dist/models/index.d.ts +41 -0
- package/dist/routes/index.d.ts +5 -1
- package/dist/services/account.service.d.ts +28 -0
- package/dist/services/index.d.ts +1 -0
- package/package.json +5 -4
|
@@ -31,8 +31,10 @@ export interface RegisterFormProps {
|
|
|
31
31
|
*
|
|
32
32
|
* This is the React equivalent of Angular's RegisterComponent.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* In v0.9.0, this component now makes API calls for registration using
|
|
35
|
+
* AccountService and automatically logs in the user after successful registration.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.9.0 - Now uses AccountService for registration
|
|
36
38
|
*
|
|
37
39
|
* @example
|
|
38
40
|
* ```tsx
|
|
@@ -46,5 +48,5 @@ export interface RegisterFormProps {
|
|
|
46
48
|
* }
|
|
47
49
|
* ```
|
|
48
50
|
*/
|
|
49
|
-
export declare function RegisterForm({ showTenantBox, showLoginLink, loginUrl, onRegisterSuccess, onRegisterError
|
|
51
|
+
export declare function RegisterForm({ showTenantBox, showLoginLink, loginUrl, onRegisterSuccess, onRegisterError, }: RegisterFormProps): import("react/jsx-runtime").JSX.Element;
|
|
50
52
|
export default RegisterForm;
|
|
@@ -14,8 +14,10 @@ export interface TenantBoxProps {
|
|
|
14
14
|
* Displays the currently selected tenant and allows users to switch tenants.
|
|
15
15
|
* This is a direct translation of Angular's TenantBoxComponent.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* and
|
|
17
|
+
* In v0.9.0, this component integrates with the backend tenant resolution API
|
|
18
|
+
* and properly updates the session state.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.9.0 - Now uses AccountService for tenant lookup
|
|
19
21
|
*/
|
|
20
22
|
export declare function TenantBox({ containerStyle }: TenantBoxProps): import("react/jsx-runtime").JSX.Element;
|
|
21
23
|
export default TenantBox;
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AccountService } from '../services/account.service';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to access the AccountService
|
|
4
|
+
*
|
|
5
|
+
* Provides account-related API operations like tenant lookup and registration.
|
|
6
|
+
*
|
|
7
|
+
* @returns AccountService instance
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* function MyComponent() {
|
|
12
|
+
* const accountService = useAccountService();
|
|
13
|
+
*
|
|
14
|
+
* const handleFindTenant = async (name: string) => {
|
|
15
|
+
* const result = await accountService.findTenant(name);
|
|
16
|
+
* if (result.success) {
|
|
17
|
+
* console.log('Found tenant:', result.tenantId);
|
|
18
|
+
* }
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* return <button onClick={() => handleFindTenant('myTenant')}>Find</button>;
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @since 0.9.0
|
|
26
|
+
*/
|
|
27
|
+
export declare function useAccountService(): AccountService;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @abpjs/account
|
|
3
3
|
* ABP Framework Account module for React
|
|
4
|
-
* Translated from @abp/ng.account v0.
|
|
4
|
+
* Translated from @abp/ng.account v0.9.0
|
|
5
5
|
*/
|
|
6
6
|
export * from './models';
|
|
7
|
+
export * from './services';
|
|
7
8
|
export * from './providers';
|
|
8
9
|
export * from './hooks';
|
|
9
10
|
export * from './components';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
ACCOUNT_PATHS: () => ACCOUNT_PATHS,
|
|
24
24
|
ACCOUNT_ROUTES: () => ACCOUNT_ROUTES,
|
|
25
25
|
AccountProvider: () => AccountProvider,
|
|
26
|
+
AccountService: () => AccountService,
|
|
26
27
|
DEFAULT_REDIRECT_URL: () => DEFAULT_REDIRECT_URL,
|
|
27
28
|
LoginForm: () => LoginForm,
|
|
28
29
|
LoginPage: () => LoginPage,
|
|
@@ -31,10 +32,42 @@ __export(index_exports, {
|
|
|
31
32
|
TenantBox: () => TenantBox,
|
|
32
33
|
useAccountContext: () => useAccountContext,
|
|
33
34
|
useAccountOptions: () => useAccountOptions,
|
|
35
|
+
useAccountService: () => useAccountService,
|
|
34
36
|
usePasswordFlow: () => usePasswordFlow
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(index_exports);
|
|
37
39
|
|
|
40
|
+
// src/services/account.service.ts
|
|
41
|
+
var AccountService = class {
|
|
42
|
+
constructor(rest) {
|
|
43
|
+
this.rest = rest;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Find a tenant by name
|
|
47
|
+
*
|
|
48
|
+
* @param tenantName - The name of the tenant to find
|
|
49
|
+
* @returns Promise resolving to TenantIdResponse
|
|
50
|
+
*/
|
|
51
|
+
findTenant(tenantName) {
|
|
52
|
+
return this.rest.get(
|
|
53
|
+
`/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Register a new user
|
|
58
|
+
*
|
|
59
|
+
* @param body - The registration request data
|
|
60
|
+
* @returns Promise resolving to RegisterResponse
|
|
61
|
+
*/
|
|
62
|
+
register(body) {
|
|
63
|
+
return this.rest.post(
|
|
64
|
+
"/api/account/register",
|
|
65
|
+
body,
|
|
66
|
+
{ skipHandleError: true }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
38
71
|
// src/providers/AccountProvider.tsx
|
|
39
72
|
var import_react = require("react");
|
|
40
73
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -173,40 +206,80 @@ function usePasswordFlow() {
|
|
|
173
206
|
};
|
|
174
207
|
}
|
|
175
208
|
|
|
176
|
-
// src/
|
|
209
|
+
// src/hooks/useAccountService.ts
|
|
177
210
|
var import_react3 = require("react");
|
|
178
|
-
var import_react_hook_form = require("react-hook-form");
|
|
179
211
|
var import_core2 = require("@abpjs/core");
|
|
212
|
+
function useAccountService() {
|
|
213
|
+
const restService = (0, import_core2.useRestService)();
|
|
214
|
+
return (0, import_react3.useMemo)(() => new AccountService(restService), [restService]);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/components/TenantBox/TenantBox.tsx
|
|
218
|
+
var import_react4 = require("react");
|
|
219
|
+
var import_react_redux = require("react-redux");
|
|
220
|
+
var import_core3 = require("@abpjs/core");
|
|
180
221
|
var import_theme_shared = require("@abpjs/theme-shared");
|
|
181
|
-
var
|
|
222
|
+
var import_react5 = require("@chakra-ui/react");
|
|
182
223
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
183
224
|
function TenantBox({ containerStyle }) {
|
|
184
|
-
const { t } = (0,
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
setSelected(null);
|
|
198
|
-
openModal();
|
|
199
|
-
}, [openModal]);
|
|
200
|
-
const onSave = (0, import_react3.useCallback)((data) => {
|
|
201
|
-
setSelected(data.name ? data : null);
|
|
202
|
-
setModalVisible(false);
|
|
203
|
-
}, []);
|
|
204
|
-
const onClose = (0, import_react3.useCallback)(() => {
|
|
205
|
-
setModalVisible(false);
|
|
225
|
+
const { t } = (0, import_core3.useLocalization)();
|
|
226
|
+
const dispatch = (0, import_react_redux.useDispatch)();
|
|
227
|
+
const accountService = useAccountService();
|
|
228
|
+
const toaster = (0, import_theme_shared.useToaster)();
|
|
229
|
+
const currentTenant = (0, import_react_redux.useSelector)(import_core3.selectTenant);
|
|
230
|
+
const [tenantName, setTenantName] = (0, import_react4.useState)("");
|
|
231
|
+
const [isModalVisible, setIsModalVisible] = (0, import_react4.useState)(false);
|
|
232
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
233
|
+
(0, import_react4.useEffect)(() => {
|
|
234
|
+
setTenantName(currentTenant?.name || "");
|
|
235
|
+
}, [currentTenant]);
|
|
236
|
+
const onSwitch = (0, import_react4.useCallback)(() => {
|
|
237
|
+
setIsModalVisible(true);
|
|
206
238
|
}, []);
|
|
239
|
+
const save = (0, import_react4.useCallback)(async () => {
|
|
240
|
+
if (tenantName) {
|
|
241
|
+
setIsLoading(true);
|
|
242
|
+
try {
|
|
243
|
+
const { success, tenantId } = await accountService.findTenant(tenantName);
|
|
244
|
+
if (success) {
|
|
245
|
+
const newTenant = {
|
|
246
|
+
id: tenantId,
|
|
247
|
+
name: tenantName
|
|
248
|
+
};
|
|
249
|
+
dispatch(import_core3.sessionActions.setTenant(newTenant));
|
|
250
|
+
setIsModalVisible(false);
|
|
251
|
+
} else {
|
|
252
|
+
toaster.error(
|
|
253
|
+
t("AbpUiMultiTenancy::GivenTenantIsNotAvailable", tenantName) || `Tenant "${tenantName}" is not available`,
|
|
254
|
+
t("AbpUi::Error") || "Error"
|
|
255
|
+
);
|
|
256
|
+
setTenantName("");
|
|
257
|
+
}
|
|
258
|
+
} catch (err) {
|
|
259
|
+
const errorMessage = err?.error?.error_description || err?.error?.error?.message || t("AbpUi::DefaultErrorMessage") || "An error occurred";
|
|
260
|
+
toaster.error(errorMessage, t("AbpUi::Error") || "Error");
|
|
261
|
+
} finally {
|
|
262
|
+
setIsLoading(false);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
dispatch(import_core3.sessionActions.setTenant({ id: "", name: "" }));
|
|
266
|
+
setIsModalVisible(false);
|
|
267
|
+
}
|
|
268
|
+
}, [tenantName, accountService, dispatch, toaster, t]);
|
|
269
|
+
const onClose = (0, import_react4.useCallback)(() => {
|
|
270
|
+
setTenantName(currentTenant?.name || "");
|
|
271
|
+
setIsModalVisible(false);
|
|
272
|
+
}, [currentTenant]);
|
|
273
|
+
const handleSubmit = (0, import_react4.useCallback)(
|
|
274
|
+
(e) => {
|
|
275
|
+
e.preventDefault();
|
|
276
|
+
save();
|
|
277
|
+
},
|
|
278
|
+
[save]
|
|
279
|
+
);
|
|
207
280
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
208
281
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
209
|
-
|
|
282
|
+
import_react5.Box,
|
|
210
283
|
{
|
|
211
284
|
className: "tenant-switch-box",
|
|
212
285
|
bg: "gray.100",
|
|
@@ -217,15 +290,15 @@ function TenantBox({ containerStyle }) {
|
|
|
217
290
|
borderRadius: "md",
|
|
218
291
|
style: containerStyle,
|
|
219
292
|
children: [
|
|
220
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
293
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react5.Text, { as: "span", color: "gray.600", children: [
|
|
221
294
|
t("AbpUiMultiTenancy::Tenant"),
|
|
222
295
|
":",
|
|
223
296
|
" "
|
|
224
297
|
] }),
|
|
225
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
298
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react5.Text, { as: "strong", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react5.Text, { as: "i", children: currentTenant?.name || t("AbpUiMultiTenancy::NotSelected") }) }),
|
|
226
299
|
" (",
|
|
227
300
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
228
|
-
|
|
301
|
+
import_react5.Link,
|
|
229
302
|
{
|
|
230
303
|
id: "abp-tenant-switch-link",
|
|
231
304
|
color: "gray.700",
|
|
@@ -242,20 +315,41 @@ function TenantBox({ containerStyle }) {
|
|
|
242
315
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
243
316
|
import_theme_shared.Modal,
|
|
244
317
|
{
|
|
245
|
-
visible:
|
|
246
|
-
onVisibleChange:
|
|
318
|
+
visible: isModalVisible,
|
|
319
|
+
onVisibleChange: setIsModalVisible,
|
|
247
320
|
size: "md",
|
|
248
321
|
header: t("AbpUiMultiTenancy::SwitchTenant") || "Switch Tenant",
|
|
249
322
|
footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
250
323
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared.Button, { variant: "ghost", colorPalette: "gray", onClick: onClose, children: t("AbpTenantManagement::Cancel") }),
|
|
251
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
324
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
325
|
+
import_theme_shared.Button,
|
|
326
|
+
{
|
|
327
|
+
colorPalette: "blue",
|
|
328
|
+
onClick: save,
|
|
329
|
+
loading: isLoading,
|
|
330
|
+
loadingText: t("AbpTenantManagement::Save"),
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CheckIcon, {}),
|
|
333
|
+
t("AbpTenantManagement::Save")
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
)
|
|
255
337
|
] }),
|
|
256
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { onSubmit: handleSubmit
|
|
257
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
258
|
-
|
|
338
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react5.VStack, { gap: 4, align: "stretch", children: [
|
|
339
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react5.Box, { children: [
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react5.Text, { as: "label", mb: 1, display: "block", children: t("AbpUiMultiTenancy::Name") }),
|
|
341
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
342
|
+
import_react5.Input,
|
|
343
|
+
{
|
|
344
|
+
id: "tenant-name",
|
|
345
|
+
type: "text",
|
|
346
|
+
value: tenantName,
|
|
347
|
+
onChange: (e) => setTenantName(e.target.value),
|
|
348
|
+
autoFocus: true
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react5.Text, { fontSize: "sm", color: "gray.600", children: t("AbpUiMultiTenancy::SwitchTenantHint") })
|
|
259
353
|
] }) })
|
|
260
354
|
}
|
|
261
355
|
)
|
|
@@ -280,14 +374,14 @@ function CheckIcon() {
|
|
|
280
374
|
}
|
|
281
375
|
|
|
282
376
|
// src/components/LoginForm/LoginForm.tsx
|
|
283
|
-
var
|
|
377
|
+
var import_react_hook_form = require("react-hook-form");
|
|
284
378
|
var import_zod = require("@hookform/resolvers/zod");
|
|
285
379
|
var import_zod2 = require("zod");
|
|
286
380
|
var import_react_router_dom2 = require("react-router-dom");
|
|
287
|
-
var
|
|
381
|
+
var import_core4 = require("@abpjs/core");
|
|
288
382
|
var import_theme_shared2 = require("@abpjs/theme-shared");
|
|
289
|
-
var import_react5 = require("@chakra-ui/react");
|
|
290
383
|
var import_react6 = require("@chakra-ui/react");
|
|
384
|
+
var import_react7 = require("@chakra-ui/react");
|
|
291
385
|
var import_lu = require("react-icons/lu");
|
|
292
386
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
293
387
|
var loginSchema = import_zod2.z.object({
|
|
@@ -302,13 +396,13 @@ function LoginForm({
|
|
|
302
396
|
onLoginSuccess,
|
|
303
397
|
onLoginError
|
|
304
398
|
}) {
|
|
305
|
-
const { t } = (0,
|
|
399
|
+
const { t } = (0, import_core4.useLocalization)();
|
|
306
400
|
const { login, isLoading, error, clearError } = usePasswordFlow();
|
|
307
401
|
const {
|
|
308
402
|
register,
|
|
309
403
|
handleSubmit,
|
|
310
404
|
formState: { errors, isSubmitting }
|
|
311
|
-
} = (0,
|
|
405
|
+
} = (0, import_react_hook_form.useForm)({
|
|
312
406
|
resolver: (0, import_zod.zodResolver)(loginSchema),
|
|
313
407
|
defaultValues: {
|
|
314
408
|
username: "",
|
|
@@ -328,16 +422,16 @@ function LoginForm({
|
|
|
328
422
|
}
|
|
329
423
|
};
|
|
330
424
|
const isFormLoading = isLoading || isSubmitting;
|
|
331
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
332
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
333
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Flex, { height: "full", flex: "1", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Box, { flex: "1.5", py: { base: "24", md: "32" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Container, { maxW: "md", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react7.Stack, { gap: "8", children: [
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Show, { when: showTenantBox, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TenantBox, {}) }),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Stack, { gap: { base: "2", md: "3" }, textAlign: "center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Heading, { size: { base: "2xl", md: "3xl" }, children: t("AbpAccount::Login") }) }),
|
|
334
428
|
error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_theme_shared2.Alert, { status: "error", children: error }),
|
|
335
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
336
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
337
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
338
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
339
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
340
|
-
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react7.Stack, { gap: "6", children: [
|
|
430
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react7.Stack, { gap: "5", children: [
|
|
431
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react7.Field.Root, { invalid: !!errors.username, children: [
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Field.Label, { children: t("AbpAccount::UserNameOrEmailAddress") }),
|
|
433
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.InputGroup, { startElement: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lu.LuMail, {}), width: "full", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
434
|
+
import_react6.Input,
|
|
341
435
|
{
|
|
342
436
|
id: "login-input-user-name-or-email-address",
|
|
343
437
|
type: "text",
|
|
@@ -346,12 +440,12 @@ function LoginForm({
|
|
|
346
440
|
...register("username")
|
|
347
441
|
}
|
|
348
442
|
) }),
|
|
349
|
-
errors.username && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
443
|
+
errors.username && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Field.ErrorText, { children: errors.username.message })
|
|
350
444
|
] }),
|
|
351
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
352
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
353
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
354
|
-
|
|
445
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react7.Field.Root, { invalid: !!errors.password, children: [
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Field.Label, { children: t("AbpAccount::Password") }),
|
|
447
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.InputGroup, { startElement: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lu.LuLock, {}), width: "full", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
448
|
+
import_react6.Input,
|
|
355
449
|
{
|
|
356
450
|
id: "login-input-password",
|
|
357
451
|
type: "password",
|
|
@@ -360,7 +454,7 @@ function LoginForm({
|
|
|
360
454
|
...register("password")
|
|
361
455
|
}
|
|
362
456
|
) }),
|
|
363
|
-
errors.password && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
457
|
+
errors.password && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Field.ErrorText, { children: errors.password.message })
|
|
364
458
|
] }),
|
|
365
459
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_theme_shared2.Checkbox, { id: "login-input-remember-me", ...register("remember"), children: t("AbpAccount::RememberMe") }),
|
|
366
460
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -373,25 +467,26 @@ function LoginForm({
|
|
|
373
467
|
children: t("AbpAccount::Login")
|
|
374
468
|
}
|
|
375
469
|
),
|
|
376
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Link, { variant: "plain", children: t("AbpAccount::ForgotPassword") })
|
|
377
471
|
] }),
|
|
378
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
379
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
380
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Show, { when: showRegisterLink, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Card.Root, { size: "sm", mt: "10", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Card.Body, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react6.HStack, { textStyle: "sm", children: [
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Text, { children: t("AbpAccount::AreYouANewUser") }),
|
|
474
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react6.Link, { asChild: true, variant: "underline", fontWeight: "semibold", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_router_dom2.Link, { to: registerUrl, children: t("AbpAccount::Register") }) })
|
|
381
475
|
] }) }) }) })
|
|
382
476
|
] }) })
|
|
383
477
|
] }) }) }) });
|
|
384
478
|
}
|
|
385
479
|
|
|
386
480
|
// src/components/RegisterForm/RegisterForm.tsx
|
|
387
|
-
var
|
|
481
|
+
var import_react8 = require("react");
|
|
482
|
+
var import_react_hook_form2 = require("react-hook-form");
|
|
388
483
|
var import_zod3 = require("@hookform/resolvers/zod");
|
|
389
484
|
var import_zod4 = require("zod");
|
|
390
485
|
var import_react_router_dom3 = require("react-router-dom");
|
|
391
|
-
var
|
|
486
|
+
var import_core5 = require("@abpjs/core");
|
|
392
487
|
var import_theme_shared3 = require("@abpjs/theme-shared");
|
|
393
|
-
var
|
|
394
|
-
var
|
|
488
|
+
var import_react9 = require("@chakra-ui/react");
|
|
489
|
+
var import_react10 = require("@chakra-ui/react");
|
|
395
490
|
var import_lu2 = require("react-icons/lu");
|
|
396
491
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
397
492
|
var passwordValidation = {
|
|
@@ -422,16 +517,20 @@ function RegisterForm({
|
|
|
422
517
|
showLoginLink = true,
|
|
423
518
|
loginUrl = "/account/login",
|
|
424
519
|
onRegisterSuccess,
|
|
425
|
-
|
|
426
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
427
|
-
onRegisterError: _onRegisterError
|
|
520
|
+
onRegisterError
|
|
428
521
|
}) {
|
|
429
|
-
const { t } = (0,
|
|
522
|
+
const { t } = (0, import_core5.useLocalization)();
|
|
523
|
+
const navigate = (0, import_react_router_dom3.useNavigate)();
|
|
524
|
+
const accountService = useAccountService();
|
|
525
|
+
const toaster = (0, import_theme_shared3.useToaster)();
|
|
526
|
+
const userManager = (0, import_core5.useUserManager)();
|
|
527
|
+
const { store, applicationConfigurationService } = (0, import_core5.useAbp)();
|
|
528
|
+
const [inProgress, setInProgress] = (0, import_react8.useState)(false);
|
|
430
529
|
const {
|
|
431
530
|
register,
|
|
432
531
|
handleSubmit,
|
|
433
|
-
formState: { errors
|
|
434
|
-
} = (0,
|
|
532
|
+
formState: { errors }
|
|
533
|
+
} = (0, import_react_hook_form2.useForm)({
|
|
435
534
|
resolver: (0, import_zod3.zodResolver)(registerSchema),
|
|
436
535
|
defaultValues: {
|
|
437
536
|
username: "",
|
|
@@ -440,18 +539,59 @@ function RegisterForm({
|
|
|
440
539
|
}
|
|
441
540
|
});
|
|
442
541
|
const onSubmit = async (data) => {
|
|
443
|
-
|
|
444
|
-
|
|
542
|
+
setInProgress(true);
|
|
543
|
+
const newUser = {
|
|
544
|
+
userName: data.username,
|
|
545
|
+
password: data.password,
|
|
546
|
+
emailAddress: data.email,
|
|
547
|
+
appName: "React"
|
|
548
|
+
};
|
|
549
|
+
try {
|
|
550
|
+
await accountService.register(newUser);
|
|
551
|
+
if (userManager) {
|
|
552
|
+
try {
|
|
553
|
+
await userManager.signinResourceOwnerCredentials({
|
|
554
|
+
username: newUser.userName,
|
|
555
|
+
password: newUser.password
|
|
556
|
+
});
|
|
557
|
+
const config = await applicationConfigurationService.getConfiguration();
|
|
558
|
+
store.dispatch(import_core5.configActions.setApplicationConfiguration(config));
|
|
559
|
+
navigate("/");
|
|
560
|
+
onRegisterSuccess?.();
|
|
561
|
+
} catch (loginErr) {
|
|
562
|
+
console.warn("Auto-login failed after registration:", loginErr);
|
|
563
|
+
toaster.success(
|
|
564
|
+
t("AbpAccount::SuccessfullyRegistered") || "Successfully registered! Please log in.",
|
|
565
|
+
t("AbpAccount::Success") || "Success"
|
|
566
|
+
);
|
|
567
|
+
navigate(loginUrl);
|
|
568
|
+
onRegisterSuccess?.();
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
toaster.success(
|
|
572
|
+
t("AbpAccount::SuccessfullyRegistered") || "Successfully registered! Please log in.",
|
|
573
|
+
t("AbpAccount::Success") || "Success"
|
|
574
|
+
);
|
|
575
|
+
navigate(loginUrl);
|
|
576
|
+
onRegisterSuccess?.();
|
|
577
|
+
}
|
|
578
|
+
} catch (err) {
|
|
579
|
+
const errorMessage = err?.error?.error_description || err?.error?.error?.message || t("AbpAccount::DefaultErrorMessage") || "An error occurred";
|
|
580
|
+
toaster.error(errorMessage, t("AbpUi::Error") || "Error", { life: 7e3 });
|
|
581
|
+
onRegisterError?.(errorMessage);
|
|
582
|
+
} finally {
|
|
583
|
+
setInProgress(false);
|
|
584
|
+
}
|
|
445
585
|
};
|
|
446
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
447
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
448
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
449
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
450
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
451
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
452
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
453
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
454
|
-
|
|
586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Flex, { height: "full", flex: "1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react9.Box, { flex: "1.5", py: { base: "24", md: "32" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Container, { maxW: "md", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Stack, { gap: "8", children: [
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react9.Show, { when: showTenantBox, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TenantBox, {}) }),
|
|
588
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Stack, { gap: { base: "2", md: "3" }, textAlign: "center", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react9.Heading, { size: { base: "2xl", md: "3xl" }, children: t("AbpAccount::Register") }) }),
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Stack, { gap: "6", children: [
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Stack, { gap: "5", children: [
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Field.Root, { invalid: !!errors.username, children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.Label, { children: t("AbpAccount::UserName") }),
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.InputGroup, { startElement: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lu2.LuUser, {}), width: "full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
594
|
+
import_react9.Input,
|
|
455
595
|
{
|
|
456
596
|
id: "input-user-name",
|
|
457
597
|
type: "text",
|
|
@@ -461,12 +601,12 @@ function RegisterForm({
|
|
|
461
601
|
...register("username")
|
|
462
602
|
}
|
|
463
603
|
) }),
|
|
464
|
-
errors.username && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
604
|
+
errors.username && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.ErrorText, { children: errors.username.message })
|
|
465
605
|
] }),
|
|
466
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
467
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
468
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
469
|
-
|
|
606
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Field.Root, { invalid: !!errors.email, children: [
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.Label, { children: t("AbpAccount::EmailAddress") }),
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.InputGroup, { startElement: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lu2.LuMail, {}), width: "full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
609
|
+
import_react9.Input,
|
|
470
610
|
{
|
|
471
611
|
id: "input-email-address",
|
|
472
612
|
type: "email",
|
|
@@ -475,12 +615,12 @@ function RegisterForm({
|
|
|
475
615
|
...register("email")
|
|
476
616
|
}
|
|
477
617
|
) }),
|
|
478
|
-
errors.email && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
618
|
+
errors.email && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.ErrorText, { children: errors.email.message })
|
|
479
619
|
] }),
|
|
480
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
481
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
482
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
483
|
-
|
|
620
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react10.Field.Root, { invalid: !!errors.password, children: [
|
|
621
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.Label, { children: t("AbpAccount::Password") }),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.InputGroup, { startElement: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lu2.LuLock, {}), width: "full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
623
|
+
import_react9.Input,
|
|
484
624
|
{
|
|
485
625
|
id: "input-password",
|
|
486
626
|
type: "password",
|
|
@@ -489,69 +629,71 @@ function RegisterForm({
|
|
|
489
629
|
...register("password")
|
|
490
630
|
}
|
|
491
631
|
) }),
|
|
492
|
-
errors.password && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
632
|
+
errors.password && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Field.ErrorText, { children: errors.password.message })
|
|
493
633
|
] }),
|
|
494
634
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
495
635
|
import_theme_shared3.Button,
|
|
496
636
|
{
|
|
497
637
|
type: "submit",
|
|
498
638
|
colorPalette: "blue",
|
|
499
|
-
loading:
|
|
639
|
+
loading: inProgress,
|
|
500
640
|
loadingText: t("AbpAccount::Register"),
|
|
501
641
|
children: t("AbpAccount::Register")
|
|
502
642
|
}
|
|
503
643
|
)
|
|
504
644
|
] }),
|
|
505
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
506
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
507
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react9.Show, { when: showLoginLink, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Card.Root, { size: "sm", mt: "10", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Card.Body, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react9.HStack, { textStyle: "sm", children: [
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react10.Text, { children: t("AbpAccount::AlreadyRegistered") }),
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react9.Link, { asChild: true, variant: "underline", fontWeight: "semibold", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_router_dom3.Link, { to: loginUrl, children: t("AbpAccount::Login") }) })
|
|
508
648
|
] }) }) }) })
|
|
509
649
|
] }) })
|
|
510
650
|
] }) }) }) });
|
|
511
651
|
}
|
|
512
652
|
|
|
513
653
|
// src/pages/LoginPage.tsx
|
|
514
|
-
var
|
|
654
|
+
var import_react11 = require("@chakra-ui/react");
|
|
515
655
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
516
656
|
function LoginPage({
|
|
517
657
|
maxWidth = "container.sm",
|
|
518
658
|
...loginFormProps
|
|
519
659
|
}) {
|
|
520
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
660
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react11.Container, { maxW: maxWidth, py: 10, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react11.Center, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginForm, { ...loginFormProps }) }) });
|
|
521
661
|
}
|
|
522
662
|
|
|
523
663
|
// src/pages/RegisterPage.tsx
|
|
524
|
-
var
|
|
664
|
+
var import_react12 = require("@chakra-ui/react");
|
|
525
665
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
526
666
|
function RegisterPage({
|
|
527
667
|
maxWidth = "container.sm",
|
|
528
668
|
...registerFormProps
|
|
529
669
|
}) {
|
|
530
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react12.Container, { maxW: maxWidth, py: 10, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react12.Center, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RegisterForm, { ...registerFormProps }) }) });
|
|
531
671
|
}
|
|
532
672
|
|
|
533
673
|
// src/routes/index.ts
|
|
534
|
-
var
|
|
535
|
-
var ACCOUNT_ROUTES =
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
674
|
+
var import_core6 = require("@abpjs/core");
|
|
675
|
+
var ACCOUNT_ROUTES = {
|
|
676
|
+
routes: [
|
|
677
|
+
{
|
|
678
|
+
name: "Account",
|
|
679
|
+
path: "account",
|
|
680
|
+
invisible: true,
|
|
681
|
+
layout: import_core6.eLayoutType.application,
|
|
682
|
+
children: [
|
|
683
|
+
{
|
|
684
|
+
path: "login",
|
|
685
|
+
name: "Login",
|
|
686
|
+
order: 1
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
path: "register",
|
|
690
|
+
name: "Register",
|
|
691
|
+
order: 2
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
}
|
|
695
|
+
]
|
|
696
|
+
};
|
|
555
697
|
var DEFAULT_REDIRECT_URL = "/";
|
|
556
698
|
var ACCOUNT_PATHS = {
|
|
557
699
|
login: "/account/login",
|
|
@@ -562,6 +704,7 @@ var ACCOUNT_PATHS = {
|
|
|
562
704
|
ACCOUNT_PATHS,
|
|
563
705
|
ACCOUNT_ROUTES,
|
|
564
706
|
AccountProvider,
|
|
707
|
+
AccountService,
|
|
565
708
|
DEFAULT_REDIRECT_URL,
|
|
566
709
|
LoginForm,
|
|
567
710
|
LoginPage,
|
|
@@ -570,5 +713,6 @@ var ACCOUNT_PATHS = {
|
|
|
570
713
|
TenantBox,
|
|
571
714
|
useAccountContext,
|
|
572
715
|
useAccountOptions,
|
|
716
|
+
useAccountService,
|
|
573
717
|
usePasswordFlow
|
|
574
718
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
// src/services/account.service.ts
|
|
2
|
+
var AccountService = class {
|
|
3
|
+
constructor(rest) {
|
|
4
|
+
this.rest = rest;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Find a tenant by name
|
|
8
|
+
*
|
|
9
|
+
* @param tenantName - The name of the tenant to find
|
|
10
|
+
* @returns Promise resolving to TenantIdResponse
|
|
11
|
+
*/
|
|
12
|
+
findTenant(tenantName) {
|
|
13
|
+
return this.rest.get(
|
|
14
|
+
`/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Register a new user
|
|
19
|
+
*
|
|
20
|
+
* @param body - The registration request data
|
|
21
|
+
* @returns Promise resolving to RegisterResponse
|
|
22
|
+
*/
|
|
23
|
+
register(body) {
|
|
24
|
+
return this.rest.post(
|
|
25
|
+
"/api/account/register",
|
|
26
|
+
body,
|
|
27
|
+
{ skipHandleError: true }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
1
32
|
// src/providers/AccountProvider.tsx
|
|
2
33
|
import { createContext, useContext, useMemo } from "react";
|
|
3
34
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -136,37 +167,77 @@ function usePasswordFlow() {
|
|
|
136
167
|
};
|
|
137
168
|
}
|
|
138
169
|
|
|
170
|
+
// src/hooks/useAccountService.ts
|
|
171
|
+
import { useMemo as useMemo2 } from "react";
|
|
172
|
+
import { useRestService } from "@abpjs/core";
|
|
173
|
+
function useAccountService() {
|
|
174
|
+
const restService = useRestService();
|
|
175
|
+
return useMemo2(() => new AccountService(restService), [restService]);
|
|
176
|
+
}
|
|
177
|
+
|
|
139
178
|
// src/components/TenantBox/TenantBox.tsx
|
|
140
|
-
import { useState as useState2, useCallback as useCallback2 } from "react";
|
|
141
|
-
import {
|
|
142
|
-
import { useLocalization } from "@abpjs/core";
|
|
143
|
-
import { Modal, Button,
|
|
179
|
+
import { useState as useState2, useCallback as useCallback2, useEffect } from "react";
|
|
180
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
181
|
+
import { useLocalization, sessionActions, selectTenant } from "@abpjs/core";
|
|
182
|
+
import { Modal, Button, useToaster } from "@abpjs/theme-shared";
|
|
144
183
|
import { Box, Text, Link, Input, VStack } from "@chakra-ui/react";
|
|
145
184
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
146
185
|
function TenantBox({ containerStyle }) {
|
|
147
186
|
const { t } = useLocalization();
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}, [selected, reset]);
|
|
187
|
+
const dispatch = useDispatch();
|
|
188
|
+
const accountService = useAccountService();
|
|
189
|
+
const toaster = useToaster();
|
|
190
|
+
const currentTenant = useSelector(selectTenant);
|
|
191
|
+
const [tenantName, setTenantName] = useState2("");
|
|
192
|
+
const [isModalVisible, setIsModalVisible] = useState2(false);
|
|
193
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
setTenantName(currentTenant?.name || "");
|
|
196
|
+
}, [currentTenant]);
|
|
159
197
|
const onSwitch = useCallback2(() => {
|
|
160
|
-
|
|
161
|
-
openModal();
|
|
162
|
-
}, [openModal]);
|
|
163
|
-
const onSave = useCallback2((data) => {
|
|
164
|
-
setSelected(data.name ? data : null);
|
|
165
|
-
setModalVisible(false);
|
|
198
|
+
setIsModalVisible(true);
|
|
166
199
|
}, []);
|
|
200
|
+
const save = useCallback2(async () => {
|
|
201
|
+
if (tenantName) {
|
|
202
|
+
setIsLoading(true);
|
|
203
|
+
try {
|
|
204
|
+
const { success, tenantId } = await accountService.findTenant(tenantName);
|
|
205
|
+
if (success) {
|
|
206
|
+
const newTenant = {
|
|
207
|
+
id: tenantId,
|
|
208
|
+
name: tenantName
|
|
209
|
+
};
|
|
210
|
+
dispatch(sessionActions.setTenant(newTenant));
|
|
211
|
+
setIsModalVisible(false);
|
|
212
|
+
} else {
|
|
213
|
+
toaster.error(
|
|
214
|
+
t("AbpUiMultiTenancy::GivenTenantIsNotAvailable", tenantName) || `Tenant "${tenantName}" is not available`,
|
|
215
|
+
t("AbpUi::Error") || "Error"
|
|
216
|
+
);
|
|
217
|
+
setTenantName("");
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
const errorMessage = err?.error?.error_description || err?.error?.error?.message || t("AbpUi::DefaultErrorMessage") || "An error occurred";
|
|
221
|
+
toaster.error(errorMessage, t("AbpUi::Error") || "Error");
|
|
222
|
+
} finally {
|
|
223
|
+
setIsLoading(false);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
dispatch(sessionActions.setTenant({ id: "", name: "" }));
|
|
227
|
+
setIsModalVisible(false);
|
|
228
|
+
}
|
|
229
|
+
}, [tenantName, accountService, dispatch, toaster, t]);
|
|
167
230
|
const onClose = useCallback2(() => {
|
|
168
|
-
|
|
169
|
-
|
|
231
|
+
setTenantName(currentTenant?.name || "");
|
|
232
|
+
setIsModalVisible(false);
|
|
233
|
+
}, [currentTenant]);
|
|
234
|
+
const handleSubmit = useCallback2(
|
|
235
|
+
(e) => {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
save();
|
|
238
|
+
},
|
|
239
|
+
[save]
|
|
240
|
+
);
|
|
170
241
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
171
242
|
/* @__PURE__ */ jsxs(
|
|
172
243
|
Box,
|
|
@@ -185,7 +256,7 @@ function TenantBox({ containerStyle }) {
|
|
|
185
256
|
":",
|
|
186
257
|
" "
|
|
187
258
|
] }),
|
|
188
|
-
/* @__PURE__ */ jsx2(Text, { as: "strong", children: /* @__PURE__ */ jsx2(Text, { as: "i", children:
|
|
259
|
+
/* @__PURE__ */ jsx2(Text, { as: "strong", children: /* @__PURE__ */ jsx2(Text, { as: "i", children: currentTenant?.name || t("AbpUiMultiTenancy::NotSelected") }) }),
|
|
189
260
|
" (",
|
|
190
261
|
/* @__PURE__ */ jsx2(
|
|
191
262
|
Link,
|
|
@@ -205,19 +276,40 @@ function TenantBox({ containerStyle }) {
|
|
|
205
276
|
/* @__PURE__ */ jsx2(
|
|
206
277
|
Modal,
|
|
207
278
|
{
|
|
208
|
-
visible:
|
|
209
|
-
onVisibleChange:
|
|
279
|
+
visible: isModalVisible,
|
|
280
|
+
onVisibleChange: setIsModalVisible,
|
|
210
281
|
size: "md",
|
|
211
282
|
header: t("AbpUiMultiTenancy::SwitchTenant") || "Switch Tenant",
|
|
212
283
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
213
284
|
/* @__PURE__ */ jsx2(Button, { variant: "ghost", colorPalette: "gray", onClick: onClose, children: t("AbpTenantManagement::Cancel") }),
|
|
214
|
-
/* @__PURE__ */ jsxs(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
285
|
+
/* @__PURE__ */ jsxs(
|
|
286
|
+
Button,
|
|
287
|
+
{
|
|
288
|
+
colorPalette: "blue",
|
|
289
|
+
onClick: save,
|
|
290
|
+
loading: isLoading,
|
|
291
|
+
loadingText: t("AbpTenantManagement::Save"),
|
|
292
|
+
children: [
|
|
293
|
+
/* @__PURE__ */ jsx2(CheckIcon, {}),
|
|
294
|
+
t("AbpTenantManagement::Save")
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
)
|
|
218
298
|
] }),
|
|
219
|
-
children: /* @__PURE__ */ jsx2("form", { onSubmit: handleSubmit
|
|
220
|
-
/* @__PURE__ */
|
|
299
|
+
children: /* @__PURE__ */ jsx2("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(VStack, { gap: 4, align: "stretch", children: [
|
|
300
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
301
|
+
/* @__PURE__ */ jsx2(Text, { as: "label", mb: 1, display: "block", children: t("AbpUiMultiTenancy::Name") }),
|
|
302
|
+
/* @__PURE__ */ jsx2(
|
|
303
|
+
Input,
|
|
304
|
+
{
|
|
305
|
+
id: "tenant-name",
|
|
306
|
+
type: "text",
|
|
307
|
+
value: tenantName,
|
|
308
|
+
onChange: (e) => setTenantName(e.target.value),
|
|
309
|
+
autoFocus: true
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
] }),
|
|
221
313
|
/* @__PURE__ */ jsx2(Text, { fontSize: "sm", color: "gray.600", children: t("AbpUiMultiTenancy::SwitchTenantHint") })
|
|
222
314
|
] }) })
|
|
223
315
|
}
|
|
@@ -243,7 +335,7 @@ function CheckIcon() {
|
|
|
243
335
|
}
|
|
244
336
|
|
|
245
337
|
// src/components/LoginForm/LoginForm.tsx
|
|
246
|
-
import { useForm
|
|
338
|
+
import { useForm } from "react-hook-form";
|
|
247
339
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
248
340
|
import { z } from "zod";
|
|
249
341
|
import { Link as RouterLink } from "react-router-dom";
|
|
@@ -279,7 +371,7 @@ function LoginForm({
|
|
|
279
371
|
register,
|
|
280
372
|
handleSubmit,
|
|
281
373
|
formState: { errors, isSubmitting }
|
|
282
|
-
} =
|
|
374
|
+
} = useForm({
|
|
283
375
|
resolver: zodResolver(loginSchema),
|
|
284
376
|
defaultValues: {
|
|
285
377
|
username: "",
|
|
@@ -355,12 +447,13 @@ function LoginForm({
|
|
|
355
447
|
}
|
|
356
448
|
|
|
357
449
|
// src/components/RegisterForm/RegisterForm.tsx
|
|
358
|
-
import {
|
|
450
|
+
import { useState as useState3 } from "react";
|
|
451
|
+
import { useForm as useForm2 } from "react-hook-form";
|
|
359
452
|
import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
|
|
360
453
|
import { z as z2 } from "zod";
|
|
361
|
-
import { Link as RouterLink2 } from "react-router-dom";
|
|
362
|
-
import { useLocalization as useLocalization3 } from "@abpjs/core";
|
|
363
|
-
import { Button as Button3 } from "@abpjs/theme-shared";
|
|
454
|
+
import { Link as RouterLink2, useNavigate as useNavigate2 } from "react-router-dom";
|
|
455
|
+
import { useLocalization as useLocalization3, useUserManager, useAbp as useAbp2, configActions as configActions2 } from "@abpjs/core";
|
|
456
|
+
import { Button as Button3, useToaster as useToaster2 } from "@abpjs/theme-shared";
|
|
364
457
|
import { Box as Box3, Heading as Heading2, Input as Input3, Link as Link3, HStack as HStack2, Show as Show2 } from "@chakra-ui/react";
|
|
365
458
|
import {
|
|
366
459
|
Card as Card2,
|
|
@@ -401,16 +494,20 @@ function RegisterForm({
|
|
|
401
494
|
showLoginLink = true,
|
|
402
495
|
loginUrl = "/account/login",
|
|
403
496
|
onRegisterSuccess,
|
|
404
|
-
|
|
405
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
406
|
-
onRegisterError: _onRegisterError
|
|
497
|
+
onRegisterError
|
|
407
498
|
}) {
|
|
408
499
|
const { t } = useLocalization3();
|
|
500
|
+
const navigate = useNavigate2();
|
|
501
|
+
const accountService = useAccountService();
|
|
502
|
+
const toaster = useToaster2();
|
|
503
|
+
const userManager = useUserManager();
|
|
504
|
+
const { store, applicationConfigurationService } = useAbp2();
|
|
505
|
+
const [inProgress, setInProgress] = useState3(false);
|
|
409
506
|
const {
|
|
410
507
|
register,
|
|
411
508
|
handleSubmit,
|
|
412
|
-
formState: { errors
|
|
413
|
-
} =
|
|
509
|
+
formState: { errors }
|
|
510
|
+
} = useForm2({
|
|
414
511
|
resolver: zodResolver2(registerSchema),
|
|
415
512
|
defaultValues: {
|
|
416
513
|
username: "",
|
|
@@ -419,8 +516,49 @@ function RegisterForm({
|
|
|
419
516
|
}
|
|
420
517
|
});
|
|
421
518
|
const onSubmit = async (data) => {
|
|
422
|
-
|
|
423
|
-
|
|
519
|
+
setInProgress(true);
|
|
520
|
+
const newUser = {
|
|
521
|
+
userName: data.username,
|
|
522
|
+
password: data.password,
|
|
523
|
+
emailAddress: data.email,
|
|
524
|
+
appName: "React"
|
|
525
|
+
};
|
|
526
|
+
try {
|
|
527
|
+
await accountService.register(newUser);
|
|
528
|
+
if (userManager) {
|
|
529
|
+
try {
|
|
530
|
+
await userManager.signinResourceOwnerCredentials({
|
|
531
|
+
username: newUser.userName,
|
|
532
|
+
password: newUser.password
|
|
533
|
+
});
|
|
534
|
+
const config = await applicationConfigurationService.getConfiguration();
|
|
535
|
+
store.dispatch(configActions2.setApplicationConfiguration(config));
|
|
536
|
+
navigate("/");
|
|
537
|
+
onRegisterSuccess?.();
|
|
538
|
+
} catch (loginErr) {
|
|
539
|
+
console.warn("Auto-login failed after registration:", loginErr);
|
|
540
|
+
toaster.success(
|
|
541
|
+
t("AbpAccount::SuccessfullyRegistered") || "Successfully registered! Please log in.",
|
|
542
|
+
t("AbpAccount::Success") || "Success"
|
|
543
|
+
);
|
|
544
|
+
navigate(loginUrl);
|
|
545
|
+
onRegisterSuccess?.();
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
toaster.success(
|
|
549
|
+
t("AbpAccount::SuccessfullyRegistered") || "Successfully registered! Please log in.",
|
|
550
|
+
t("AbpAccount::Success") || "Success"
|
|
551
|
+
);
|
|
552
|
+
navigate(loginUrl);
|
|
553
|
+
onRegisterSuccess?.();
|
|
554
|
+
}
|
|
555
|
+
} catch (err) {
|
|
556
|
+
const errorMessage = err?.error?.error_description || err?.error?.error?.message || t("AbpAccount::DefaultErrorMessage") || "An error occurred";
|
|
557
|
+
toaster.error(errorMessage, t("AbpUi::Error") || "Error", { life: 7e3 });
|
|
558
|
+
onRegisterError?.(errorMessage);
|
|
559
|
+
} finally {
|
|
560
|
+
setInProgress(false);
|
|
561
|
+
}
|
|
424
562
|
};
|
|
425
563
|
return /* @__PURE__ */ jsx4(Flex2, { height: "full", flex: "1", children: /* @__PURE__ */ jsx4(Box3, { flex: "1.5", py: { base: "24", md: "32" }, children: /* @__PURE__ */ jsx4(Container2, { maxW: "md", children: /* @__PURE__ */ jsxs3(Stack2, { gap: "8", children: [
|
|
426
564
|
/* @__PURE__ */ jsx4(Show2, { when: showTenantBox, children: /* @__PURE__ */ jsx4(TenantBox, {}) }),
|
|
@@ -475,7 +613,7 @@ function RegisterForm({
|
|
|
475
613
|
{
|
|
476
614
|
type: "submit",
|
|
477
615
|
colorPalette: "blue",
|
|
478
|
-
loading:
|
|
616
|
+
loading: inProgress,
|
|
479
617
|
loadingText: t("AbpAccount::Register"),
|
|
480
618
|
children: t("AbpAccount::Register")
|
|
481
619
|
}
|
|
@@ -511,26 +649,28 @@ function RegisterPage({
|
|
|
511
649
|
|
|
512
650
|
// src/routes/index.ts
|
|
513
651
|
import { eLayoutType } from "@abpjs/core";
|
|
514
|
-
var ACCOUNT_ROUTES =
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
652
|
+
var ACCOUNT_ROUTES = {
|
|
653
|
+
routes: [
|
|
654
|
+
{
|
|
655
|
+
name: "Account",
|
|
656
|
+
path: "account",
|
|
657
|
+
invisible: true,
|
|
658
|
+
layout: eLayoutType.application,
|
|
659
|
+
children: [
|
|
660
|
+
{
|
|
661
|
+
path: "login",
|
|
662
|
+
name: "Login",
|
|
663
|
+
order: 1
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
path: "register",
|
|
667
|
+
name: "Register",
|
|
668
|
+
order: 2
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
]
|
|
673
|
+
};
|
|
534
674
|
var DEFAULT_REDIRECT_URL = "/";
|
|
535
675
|
var ACCOUNT_PATHS = {
|
|
536
676
|
login: "/account/login",
|
|
@@ -540,6 +680,7 @@ export {
|
|
|
540
680
|
ACCOUNT_PATHS,
|
|
541
681
|
ACCOUNT_ROUTES,
|
|
542
682
|
AccountProvider,
|
|
683
|
+
AccountService,
|
|
543
684
|
DEFAULT_REDIRECT_URL,
|
|
544
685
|
LoginForm,
|
|
545
686
|
LoginPage,
|
|
@@ -548,5 +689,6 @@ export {
|
|
|
548
689
|
TenantBox,
|
|
549
690
|
useAccountContext,
|
|
550
691
|
useAccountOptions,
|
|
692
|
+
useAccountService,
|
|
551
693
|
usePasswordFlow
|
|
552
694
|
};
|
package/dist/models/index.d.ts
CHANGED
|
@@ -40,3 +40,44 @@ export interface PasswordFlowResult {
|
|
|
40
40
|
success: boolean;
|
|
41
41
|
error?: string;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Request body for user registration
|
|
45
|
+
*/
|
|
46
|
+
export interface RegisterRequest {
|
|
47
|
+
userName: string;
|
|
48
|
+
emailAddress: string;
|
|
49
|
+
password: string;
|
|
50
|
+
appName?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Response from user registration API
|
|
54
|
+
*/
|
|
55
|
+
export interface RegisterResponse {
|
|
56
|
+
tenantId: string;
|
|
57
|
+
userName: string;
|
|
58
|
+
name: string;
|
|
59
|
+
surname: string;
|
|
60
|
+
email: string;
|
|
61
|
+
emailConfirmed: boolean;
|
|
62
|
+
phoneNumber: string;
|
|
63
|
+
phoneNumberConfirmed: boolean;
|
|
64
|
+
twoFactorEnabled: boolean;
|
|
65
|
+
lockoutEnabled: boolean;
|
|
66
|
+
lockoutEnd: string;
|
|
67
|
+
concurrencyStamp: string;
|
|
68
|
+
isDeleted: boolean;
|
|
69
|
+
deleterId: string;
|
|
70
|
+
deletionTime: string;
|
|
71
|
+
lastModificationTime: string;
|
|
72
|
+
lastModifierId: string;
|
|
73
|
+
creationTime: string;
|
|
74
|
+
creatorId: string;
|
|
75
|
+
id: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Response from tenant lookup API
|
|
79
|
+
*/
|
|
80
|
+
export interface TenantIdResponse {
|
|
81
|
+
success: boolean;
|
|
82
|
+
tenantId: string;
|
|
83
|
+
}
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -9,8 +9,12 @@ import { type ABP } from '@abpjs/core';
|
|
|
9
9
|
* - /account (invisible, uses account layout)
|
|
10
10
|
* - /account/login
|
|
11
11
|
* - /account/register
|
|
12
|
+
*
|
|
13
|
+
* @since 0.9.0 - Changed from array to object with `routes` property
|
|
12
14
|
*/
|
|
13
|
-
export declare const ACCOUNT_ROUTES:
|
|
15
|
+
export declare const ACCOUNT_ROUTES: {
|
|
16
|
+
routes: ABP.FullRoute[];
|
|
17
|
+
};
|
|
14
18
|
/**
|
|
15
19
|
* Default redirect path after login
|
|
16
20
|
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RestService } from '@abpjs/core';
|
|
2
|
+
import type { RegisterRequest, RegisterResponse, TenantIdResponse } from '../models';
|
|
3
|
+
/**
|
|
4
|
+
* AccountService - Service for account-related API operations
|
|
5
|
+
*
|
|
6
|
+
* This is the React equivalent of Angular's AccountService.
|
|
7
|
+
* Provides methods for tenant lookup and user registration.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.9.0
|
|
10
|
+
*/
|
|
11
|
+
export declare class AccountService {
|
|
12
|
+
private rest;
|
|
13
|
+
constructor(rest: RestService);
|
|
14
|
+
/**
|
|
15
|
+
* Find a tenant by name
|
|
16
|
+
*
|
|
17
|
+
* @param tenantName - The name of the tenant to find
|
|
18
|
+
* @returns Promise resolving to TenantIdResponse
|
|
19
|
+
*/
|
|
20
|
+
findTenant(tenantName: string): Promise<TenantIdResponse>;
|
|
21
|
+
/**
|
|
22
|
+
* Register a new user
|
|
23
|
+
*
|
|
24
|
+
* @param body - The registration request data
|
|
25
|
+
* @returns Promise resolving to RegisterResponse
|
|
26
|
+
*/
|
|
27
|
+
register(body: RegisterRequest): Promise<RegisterResponse>;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './account.service';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abpjs/account",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "ABP Framework Account module for React - Translation of @abp/ng.account",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -49,13 +49,14 @@
|
|
|
49
49
|
"@emotion/react": "^11.11.0",
|
|
50
50
|
"@hookform/resolvers": "^3.3.0",
|
|
51
51
|
"react-hook-form": "^7.48.0",
|
|
52
|
+
"react-redux": "^9.0.0",
|
|
52
53
|
"zod": "^3.22.0",
|
|
53
54
|
"react-icons": "^5.0.0",
|
|
54
|
-
"@abpjs/
|
|
55
|
-
"@abpjs/
|
|
55
|
+
"@abpjs/theme-shared": "0.9.0",
|
|
56
|
+
"@abpjs/core": "0.9.0"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
|
-
"@abp/ng.account": "0.
|
|
59
|
+
"@abp/ng.account": "0.9.0"
|
|
59
60
|
},
|
|
60
61
|
"scripts": {
|
|
61
62
|
"build": "tsup src/index.ts --format cjs,esm --clean && tsc -p tsconfig.build.json",
|