@abpjs/account 0.7.6

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/index.mjs ADDED
@@ -0,0 +1,552 @@
1
+ // src/providers/AccountProvider.tsx
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var DEFAULT_OPTIONS = {
5
+ redirectUrl: "/"
6
+ };
7
+ var AccountContext = createContext(null);
8
+ function AccountProvider({ children, options }) {
9
+ const mergedOptions = useMemo(
10
+ () => ({
11
+ ...DEFAULT_OPTIONS,
12
+ ...options
13
+ }),
14
+ [options]
15
+ );
16
+ const value = useMemo(
17
+ () => ({
18
+ options: mergedOptions
19
+ }),
20
+ [mergedOptions]
21
+ );
22
+ return /* @__PURE__ */ jsx(AccountContext.Provider, { value, children });
23
+ }
24
+ function useAccountContext() {
25
+ const context = useContext(AccountContext);
26
+ if (!context) {
27
+ throw new Error("useAccountContext must be used within an AccountProvider");
28
+ }
29
+ return context;
30
+ }
31
+ function useAccountOptions() {
32
+ const context = useContext(AccountContext);
33
+ return context?.options ?? DEFAULT_OPTIONS;
34
+ }
35
+
36
+ // src/hooks/usePasswordFlow.ts
37
+ import { useState, useCallback } from "react";
38
+ import { useAbp, useConfig, configActions } from "@abpjs/core";
39
+ import { useNavigate } from "react-router-dom";
40
+ function usePasswordFlow() {
41
+ const { store, axiosInstance, applicationConfigurationService, userManager } = useAbp();
42
+ const config = useConfig();
43
+ const options = useAccountOptions();
44
+ const navigate = useNavigate();
45
+ const [isLoading, setIsLoading] = useState(false);
46
+ const [error, setError] = useState(null);
47
+ const getTokenEndpoint = useCallback(() => {
48
+ const oAuthConfig = config.environment.oAuthConfig;
49
+ if (oAuthConfig?.authority) {
50
+ const authority = oAuthConfig.authority.replace(/\/+$/, "");
51
+ return `${authority}/connect/token`;
52
+ }
53
+ throw new Error("OAuth authority not configured");
54
+ }, [config.environment.oAuthConfig]);
55
+ const login = useCallback(
56
+ async (username, password, flowOptions) => {
57
+ setIsLoading(true);
58
+ setError(null);
59
+ try {
60
+ const oAuthConfig = config.environment.oAuthConfig;
61
+ if (!oAuthConfig?.client_id) {
62
+ throw new Error("OAuth client_id not configured");
63
+ }
64
+ const tokenEndpoint = getTokenEndpoint();
65
+ const formData = new URLSearchParams();
66
+ formData.append("grant_type", "password");
67
+ formData.append("username", username);
68
+ formData.append("password", password);
69
+ formData.append("client_id", oAuthConfig.client_id);
70
+ if (oAuthConfig.scope) {
71
+ formData.append("scope", oAuthConfig.scope);
72
+ }
73
+ const response = await axiosInstance.post(
74
+ tokenEndpoint,
75
+ formData.toString(),
76
+ {
77
+ headers: {
78
+ "Content-Type": "application/x-www-form-urlencoded"
79
+ }
80
+ }
81
+ );
82
+ const tokenData = response.data;
83
+ const storage = flowOptions?.remember ? localStorage : sessionStorage;
84
+ const storageKey = `oidc.user:${oAuthConfig.authority}:${oAuthConfig.client_id}`;
85
+ const userData = {
86
+ access_token: tokenData.access_token,
87
+ token_type: tokenData.token_type,
88
+ expires_at: Math.floor(Date.now() / 1e3) + tokenData.expires_in,
89
+ refresh_token: tokenData.refresh_token,
90
+ scope: tokenData.scope,
91
+ profile: {}
92
+ // Will be filled after config fetch
93
+ };
94
+ storage.setItem(storageKey, JSON.stringify(userData));
95
+ const oidcKey = `oidc.user:${oAuthConfig.authority}:${oAuthConfig.client_id}`;
96
+ storage.setItem(oidcKey, JSON.stringify(userData));
97
+ if (userManager) {
98
+ const loadedUser = await userManager.getUser();
99
+ if (loadedUser) {
100
+ userManager.events.load(loadedUser);
101
+ }
102
+ }
103
+ const appConfig = await applicationConfigurationService.getConfiguration();
104
+ store.dispatch(configActions.setApplicationConfiguration(appConfig));
105
+ const redirectUrl = window.history.state?.redirectUrl || options.redirectUrl;
106
+ navigate(redirectUrl);
107
+ setIsLoading(false);
108
+ return { success: true };
109
+ } catch (err) {
110
+ const errorMessage = err.response?.data?.error_description || err.response?.data?.error || err.message || "Login failed";
111
+ setError(errorMessage);
112
+ setIsLoading(false);
113
+ console.error("Password flow error:", errorMessage);
114
+ return { success: false, error: errorMessage };
115
+ }
116
+ },
117
+ [
118
+ config.environment.oAuthConfig,
119
+ getTokenEndpoint,
120
+ axiosInstance,
121
+ applicationConfigurationService,
122
+ store,
123
+ options.redirectUrl,
124
+ navigate,
125
+ userManager
126
+ ]
127
+ );
128
+ const clearError = useCallback(() => {
129
+ setError(null);
130
+ }, []);
131
+ return {
132
+ login,
133
+ isLoading,
134
+ error,
135
+ clearError
136
+ };
137
+ }
138
+
139
+ // src/components/TenantBox/TenantBox.tsx
140
+ import { useState as useState2, useCallback as useCallback2 } from "react";
141
+ import { useForm } from "react-hook-form";
142
+ import { useLocalization } from "@abpjs/core";
143
+ import { Modal, Button, FormField } from "@abpjs/theme-shared";
144
+ import { Box, Text, Link, Input, VStack } from "@chakra-ui/react";
145
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
146
+ function TenantBox({ containerStyle }) {
147
+ const { t } = useLocalization();
148
+ const [selected, setSelected] = useState2(null);
149
+ const [modalVisible, setModalVisible] = useState2(false);
150
+ const { register, handleSubmit, reset } = useForm({
151
+ defaultValues: {
152
+ name: ""
153
+ }
154
+ });
155
+ const openModal = useCallback2(() => {
156
+ reset({ name: selected?.name || "" });
157
+ setModalVisible(true);
158
+ }, [selected, reset]);
159
+ const onSwitch = useCallback2(() => {
160
+ setSelected(null);
161
+ openModal();
162
+ }, [openModal]);
163
+ const onSave = useCallback2((data) => {
164
+ setSelected(data.name ? data : null);
165
+ setModalVisible(false);
166
+ }, []);
167
+ const onClose = useCallback2(() => {
168
+ setModalVisible(false);
169
+ }, []);
170
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
171
+ /* @__PURE__ */ jsxs(
172
+ Box,
173
+ {
174
+ className: "tenant-switch-box",
175
+ bg: "gray.100",
176
+ mb: 5,
177
+ color: "black",
178
+ p: 2.5,
179
+ textAlign: "center",
180
+ borderRadius: "md",
181
+ style: containerStyle,
182
+ children: [
183
+ /* @__PURE__ */ jsxs(Text, { as: "span", color: "gray.600", children: [
184
+ t("AbpUiMultiTenancy::Tenant"),
185
+ ":",
186
+ " "
187
+ ] }),
188
+ /* @__PURE__ */ jsx2(Text, { as: "strong", children: /* @__PURE__ */ jsx2(Text, { as: "i", children: selected?.name || t("AbpUiMultiTenancy::NotSelected") }) }),
189
+ " (",
190
+ /* @__PURE__ */ jsx2(
191
+ Link,
192
+ {
193
+ id: "abp-tenant-switch-link",
194
+ color: "gray.700",
195
+ cursor: "pointer",
196
+ onClick: onSwitch,
197
+ _hover: { textDecoration: "underline" },
198
+ children: t("AbpUiMultiTenancy::Switch")
199
+ }
200
+ ),
201
+ ")"
202
+ ]
203
+ }
204
+ ),
205
+ /* @__PURE__ */ jsx2(
206
+ Modal,
207
+ {
208
+ visible: modalVisible,
209
+ onVisibleChange: setModalVisible,
210
+ size: "md",
211
+ header: t("AbpUiMultiTenancy::SwitchTenant") || "Switch Tenant",
212
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
213
+ /* @__PURE__ */ jsx2(Button, { variant: "ghost", colorPalette: "gray", onClick: onClose, children: t("AbpTenantManagement::Cancel") }),
214
+ /* @__PURE__ */ jsxs(Button, { colorPalette: "blue", onClick: handleSubmit(onSave), children: [
215
+ /* @__PURE__ */ jsx2(CheckIcon, {}),
216
+ t("AbpTenantManagement::Save")
217
+ ] })
218
+ ] }),
219
+ children: /* @__PURE__ */ jsx2("form", { onSubmit: handleSubmit(onSave), children: /* @__PURE__ */ jsxs(VStack, { gap: 4, align: "stretch", children: [
220
+ /* @__PURE__ */ jsx2(FormField, { label: t("AbpUiMultiTenancy::Name"), htmlFor: "tenant-name", children: /* @__PURE__ */ jsx2(Input, { id: "tenant-name", type: "text", ...register("name") }) }),
221
+ /* @__PURE__ */ jsx2(Text, { fontSize: "sm", color: "gray.600", children: t("AbpUiMultiTenancy::SwitchTenantHint") })
222
+ ] }) })
223
+ }
224
+ )
225
+ ] });
226
+ }
227
+ function CheckIcon() {
228
+ return /* @__PURE__ */ jsx2(
229
+ "svg",
230
+ {
231
+ xmlns: "http://www.w3.org/2000/svg",
232
+ width: "16",
233
+ height: "16",
234
+ viewBox: "0 0 24 24",
235
+ fill: "none",
236
+ stroke: "currentColor",
237
+ strokeWidth: "2",
238
+ strokeLinecap: "round",
239
+ strokeLinejoin: "round",
240
+ children: /* @__PURE__ */ jsx2("polyline", { points: "20 6 9 17 4 12" })
241
+ }
242
+ );
243
+ }
244
+
245
+ // src/components/LoginForm/LoginForm.tsx
246
+ import { useForm as useForm2 } from "react-hook-form";
247
+ import { zodResolver } from "@hookform/resolvers/zod";
248
+ import { z } from "zod";
249
+ import { Link as RouterLink } from "react-router-dom";
250
+ import { useLocalization as useLocalization2 } from "@abpjs/core";
251
+ import { Alert, Button as Button2, Checkbox } from "@abpjs/theme-shared";
252
+ import { Box as Box2, Heading, Input as Input2, Link as Link2, HStack, Show } from "@chakra-ui/react";
253
+ import {
254
+ Card,
255
+ Container,
256
+ Field,
257
+ Flex,
258
+ InputGroup,
259
+ Stack,
260
+ Text as Text2
261
+ } from "@chakra-ui/react";
262
+ import { LuLock, LuMail } from "react-icons/lu";
263
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
264
+ var loginSchema = z.object({
265
+ username: z.string().min(1, "Username is required").max(255, "Username must be at most 255 characters"),
266
+ password: z.string().min(1, "Password is required").max(32, "Password must be at most 32 characters"),
267
+ remember: z.boolean().default(false)
268
+ });
269
+ function LoginForm({
270
+ showTenantBox = true,
271
+ showRegisterLink = true,
272
+ registerUrl = "/account/register",
273
+ onLoginSuccess,
274
+ onLoginError
275
+ }) {
276
+ const { t } = useLocalization2();
277
+ const { login, isLoading, error, clearError } = usePasswordFlow();
278
+ const {
279
+ register,
280
+ handleSubmit,
281
+ formState: { errors, isSubmitting }
282
+ } = useForm2({
283
+ resolver: zodResolver(loginSchema),
284
+ defaultValues: {
285
+ username: "",
286
+ password: "",
287
+ remember: false
288
+ }
289
+ });
290
+ const onSubmit = async (data) => {
291
+ clearError();
292
+ const result = await login(data.username, data.password, {
293
+ remember: data.remember
294
+ });
295
+ if (result.success) {
296
+ onLoginSuccess?.();
297
+ } else if (result.error) {
298
+ onLoginError?.(result.error);
299
+ }
300
+ };
301
+ const isFormLoading = isLoading || isSubmitting;
302
+ return /* @__PURE__ */ jsx3(Flex, { height: "full", flex: "1", children: /* @__PURE__ */ jsx3(Box2, { flex: "1.5", py: { base: "24", md: "32" }, children: /* @__PURE__ */ jsx3(Container, { maxW: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: "8", children: [
303
+ /* @__PURE__ */ jsx3(Show, { when: showTenantBox, children: /* @__PURE__ */ jsx3(TenantBox, {}) }),
304
+ /* @__PURE__ */ jsx3(Stack, { gap: { base: "2", md: "3" }, textAlign: "center", children: /* @__PURE__ */ jsx3(Heading, { size: { base: "2xl", md: "3xl" }, children: t("AbpAccount::Login") }) }),
305
+ error && /* @__PURE__ */ jsx3(Alert, { status: "error", children: error }),
306
+ /* @__PURE__ */ jsx3("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ jsxs2(Stack, { gap: "6", children: [
307
+ /* @__PURE__ */ jsxs2(Stack, { gap: "5", children: [
308
+ /* @__PURE__ */ jsxs2(Field.Root, { invalid: !!errors.username, children: [
309
+ /* @__PURE__ */ jsx3(Field.Label, { children: t("AbpAccount::UserNameOrEmailAddress") }),
310
+ /* @__PURE__ */ jsx3(InputGroup, { startElement: /* @__PURE__ */ jsx3(LuMail, {}), width: "full", children: /* @__PURE__ */ jsx3(
311
+ Input2,
312
+ {
313
+ id: "login-input-user-name-or-email-address",
314
+ type: "text",
315
+ autoComplete: "username",
316
+ placeholder: "me@example.com",
317
+ ...register("username")
318
+ }
319
+ ) }),
320
+ errors.username && /* @__PURE__ */ jsx3(Field.ErrorText, { children: errors.username.message })
321
+ ] }),
322
+ /* @__PURE__ */ jsxs2(Field.Root, { invalid: !!errors.password, children: [
323
+ /* @__PURE__ */ jsx3(Field.Label, { children: t("AbpAccount::Password") }),
324
+ /* @__PURE__ */ jsx3(InputGroup, { startElement: /* @__PURE__ */ jsx3(LuLock, {}), width: "full", children: /* @__PURE__ */ jsx3(
325
+ Input2,
326
+ {
327
+ id: "login-input-password",
328
+ type: "password",
329
+ autoComplete: "current-password",
330
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
331
+ ...register("password")
332
+ }
333
+ ) }),
334
+ errors.password && /* @__PURE__ */ jsx3(Field.ErrorText, { children: errors.password.message })
335
+ ] }),
336
+ /* @__PURE__ */ jsx3(Checkbox, { id: "login-input-remember-me", ...register("remember"), children: t("AbpAccount::RememberMe") }),
337
+ /* @__PURE__ */ jsx3(
338
+ Button2,
339
+ {
340
+ type: "submit",
341
+ colorPalette: "blue",
342
+ loading: isFormLoading,
343
+ loadingText: t("AbpAccount::Login"),
344
+ children: t("AbpAccount::Login")
345
+ }
346
+ ),
347
+ /* @__PURE__ */ jsx3(Link2, { variant: "plain", children: t("AbpAccount::ForgotPassword") })
348
+ ] }),
349
+ /* @__PURE__ */ jsx3(Show, { when: showRegisterLink, children: /* @__PURE__ */ jsx3(Card.Root, { size: "sm", mt: "10", children: /* @__PURE__ */ jsx3(Card.Body, { children: /* @__PURE__ */ jsxs2(HStack, { textStyle: "sm", children: [
350
+ /* @__PURE__ */ jsx3(Text2, { children: t("AbpAccount::AreYouANewUser") }),
351
+ /* @__PURE__ */ jsx3(Link2, { asChild: true, variant: "underline", fontWeight: "semibold", children: /* @__PURE__ */ jsx3(RouterLink, { to: registerUrl, children: t("AbpAccount::Register") }) })
352
+ ] }) }) }) })
353
+ ] }) })
354
+ ] }) }) }) });
355
+ }
356
+
357
+ // src/components/RegisterForm/RegisterForm.tsx
358
+ import { useForm as useForm3 } from "react-hook-form";
359
+ import { zodResolver as zodResolver2 } from "@hookform/resolvers/zod";
360
+ 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";
364
+ import { Box as Box3, Heading as Heading2, Input as Input3, Link as Link3, HStack as HStack2, Show as Show2 } from "@chakra-ui/react";
365
+ import {
366
+ Card as Card2,
367
+ Container as Container2,
368
+ Field as Field2,
369
+ Flex as Flex2,
370
+ InputGroup as InputGroup2,
371
+ Stack as Stack2,
372
+ Text as Text3
373
+ } from "@chakra-ui/react";
374
+ import { LuLock as LuLock2, LuMail as LuMail2, LuUser } from "react-icons/lu";
375
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
376
+ var passwordValidation = {
377
+ hasLowercase: /[a-z]/,
378
+ hasUppercase: /[A-Z]/,
379
+ hasNumber: /[0-9]/,
380
+ hasSpecial: /[!@#$%^&*(),.?":{}|<>]/
381
+ };
382
+ var registerSchema = z2.object({
383
+ username: z2.string().min(1, "Username is required").max(255, "Username must be at most 255 characters"),
384
+ email: z2.string().min(1, "Email is required").email("Please enter a valid email address"),
385
+ password: z2.string().min(6, "Password must be at least 6 characters").max(32, "Password must be at most 32 characters").refine(
386
+ (val) => passwordValidation.hasLowercase.test(val),
387
+ "Password must contain at least one lowercase letter"
388
+ ).refine(
389
+ (val) => passwordValidation.hasUppercase.test(val),
390
+ "Password must contain at least one uppercase letter"
391
+ ).refine(
392
+ (val) => passwordValidation.hasNumber.test(val),
393
+ "Password must contain at least one number"
394
+ ).refine(
395
+ (val) => passwordValidation.hasSpecial.test(val),
396
+ "Password must contain at least one special character"
397
+ )
398
+ });
399
+ function RegisterForm({
400
+ showTenantBox = true,
401
+ showLoginLink = true,
402
+ loginUrl = "/account/login",
403
+ onRegisterSuccess,
404
+ // Note: onRegisterError will be used when registration API is implemented
405
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
406
+ onRegisterError: _onRegisterError
407
+ }) {
408
+ const { t } = useLocalization3();
409
+ const {
410
+ register,
411
+ handleSubmit,
412
+ formState: { errors, isSubmitting }
413
+ } = useForm3({
414
+ resolver: zodResolver2(registerSchema),
415
+ defaultValues: {
416
+ username: "",
417
+ email: "",
418
+ password: ""
419
+ }
420
+ });
421
+ const onSubmit = async (data) => {
422
+ console.log("Register form submitted (no API call in v0.7.6):", data);
423
+ onRegisterSuccess?.();
424
+ };
425
+ 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
+ /* @__PURE__ */ jsx4(Show2, { when: showTenantBox, children: /* @__PURE__ */ jsx4(TenantBox, {}) }),
427
+ /* @__PURE__ */ jsx4(Stack2, { gap: { base: "2", md: "3" }, textAlign: "center", children: /* @__PURE__ */ jsx4(Heading2, { size: { base: "2xl", md: "3xl" }, children: t("AbpAccount::Register") }) }),
428
+ /* @__PURE__ */ jsx4("form", { onSubmit: handleSubmit(onSubmit), noValidate: true, children: /* @__PURE__ */ jsxs3(Stack2, { gap: "6", children: [
429
+ /* @__PURE__ */ jsxs3(Stack2, { gap: "5", children: [
430
+ /* @__PURE__ */ jsxs3(Field2.Root, { invalid: !!errors.username, children: [
431
+ /* @__PURE__ */ jsx4(Field2.Label, { children: t("AbpAccount::UserName") }),
432
+ /* @__PURE__ */ jsx4(InputGroup2, { startElement: /* @__PURE__ */ jsx4(LuUser, {}), width: "full", children: /* @__PURE__ */ jsx4(
433
+ Input3,
434
+ {
435
+ id: "input-user-name",
436
+ type: "text",
437
+ autoFocus: true,
438
+ autoComplete: "username",
439
+ placeholder: "johndoe",
440
+ ...register("username")
441
+ }
442
+ ) }),
443
+ errors.username && /* @__PURE__ */ jsx4(Field2.ErrorText, { children: errors.username.message })
444
+ ] }),
445
+ /* @__PURE__ */ jsxs3(Field2.Root, { invalid: !!errors.email, children: [
446
+ /* @__PURE__ */ jsx4(Field2.Label, { children: t("AbpAccount::EmailAddress") }),
447
+ /* @__PURE__ */ jsx4(InputGroup2, { startElement: /* @__PURE__ */ jsx4(LuMail2, {}), width: "full", children: /* @__PURE__ */ jsx4(
448
+ Input3,
449
+ {
450
+ id: "input-email-address",
451
+ type: "email",
452
+ autoComplete: "email",
453
+ placeholder: "me@example.com",
454
+ ...register("email")
455
+ }
456
+ ) }),
457
+ errors.email && /* @__PURE__ */ jsx4(Field2.ErrorText, { children: errors.email.message })
458
+ ] }),
459
+ /* @__PURE__ */ jsxs3(Field2.Root, { invalid: !!errors.password, children: [
460
+ /* @__PURE__ */ jsx4(Field2.Label, { children: t("AbpAccount::Password") }),
461
+ /* @__PURE__ */ jsx4(InputGroup2, { startElement: /* @__PURE__ */ jsx4(LuLock2, {}), width: "full", children: /* @__PURE__ */ jsx4(
462
+ Input3,
463
+ {
464
+ id: "input-password",
465
+ type: "password",
466
+ autoComplete: "new-password",
467
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
468
+ ...register("password")
469
+ }
470
+ ) }),
471
+ errors.password && /* @__PURE__ */ jsx4(Field2.ErrorText, { children: errors.password.message })
472
+ ] }),
473
+ /* @__PURE__ */ jsx4(
474
+ Button3,
475
+ {
476
+ type: "submit",
477
+ colorPalette: "blue",
478
+ loading: isSubmitting,
479
+ loadingText: t("AbpAccount::Register"),
480
+ children: t("AbpAccount::Register")
481
+ }
482
+ )
483
+ ] }),
484
+ /* @__PURE__ */ jsx4(Show2, { when: showLoginLink, children: /* @__PURE__ */ jsx4(Card2.Root, { size: "sm", mt: "10", children: /* @__PURE__ */ jsx4(Card2.Body, { children: /* @__PURE__ */ jsxs3(HStack2, { textStyle: "sm", children: [
485
+ /* @__PURE__ */ jsx4(Text3, { children: t("AbpAccount::AlreadyRegistered") }),
486
+ /* @__PURE__ */ jsx4(Link3, { asChild: true, variant: "underline", fontWeight: "semibold", children: /* @__PURE__ */ jsx4(RouterLink2, { to: loginUrl, children: t("AbpAccount::Login") }) })
487
+ ] }) }) }) })
488
+ ] }) })
489
+ ] }) }) }) });
490
+ }
491
+
492
+ // src/pages/LoginPage.tsx
493
+ import { Container as Container3, Center } from "@chakra-ui/react";
494
+ import { jsx as jsx5 } from "react/jsx-runtime";
495
+ function LoginPage({
496
+ maxWidth = "container.sm",
497
+ ...loginFormProps
498
+ }) {
499
+ return /* @__PURE__ */ jsx5(Container3, { maxW: maxWidth, py: 10, children: /* @__PURE__ */ jsx5(Center, { children: /* @__PURE__ */ jsx5(LoginForm, { ...loginFormProps }) }) });
500
+ }
501
+
502
+ // src/pages/RegisterPage.tsx
503
+ import { Container as Container4, Center as Center2 } from "@chakra-ui/react";
504
+ import { jsx as jsx6 } from "react/jsx-runtime";
505
+ function RegisterPage({
506
+ maxWidth = "container.sm",
507
+ ...registerFormProps
508
+ }) {
509
+ return /* @__PURE__ */ jsx6(Container4, { maxW: maxWidth, py: 10, children: /* @__PURE__ */ jsx6(Center2, { children: /* @__PURE__ */ jsx6(RegisterForm, { ...registerFormProps }) }) });
510
+ }
511
+
512
+ // src/routes/index.ts
513
+ import { eLayoutType } from "@abpjs/core";
514
+ var ACCOUNT_ROUTES = [
515
+ {
516
+ name: "Account",
517
+ path: "account",
518
+ invisible: true,
519
+ layout: eLayoutType.application,
520
+ children: [
521
+ {
522
+ path: "login",
523
+ name: "Login",
524
+ order: 1
525
+ },
526
+ {
527
+ path: "register",
528
+ name: "Register",
529
+ order: 2
530
+ }
531
+ ]
532
+ }
533
+ ];
534
+ var DEFAULT_REDIRECT_URL = "/";
535
+ var ACCOUNT_PATHS = {
536
+ login: "/account/login",
537
+ register: "/account/register"
538
+ };
539
+ export {
540
+ ACCOUNT_PATHS,
541
+ ACCOUNT_ROUTES,
542
+ AccountProvider,
543
+ DEFAULT_REDIRECT_URL,
544
+ LoginForm,
545
+ LoginPage,
546
+ RegisterForm,
547
+ RegisterPage,
548
+ TenantBox,
549
+ useAccountContext,
550
+ useAccountOptions,
551
+ usePasswordFlow
552
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Account module type definitions
3
+ */
4
+ /**
5
+ * Account module configuration options
6
+ */
7
+ export interface AccountOptions {
8
+ /**
9
+ * URL to redirect to after successful login
10
+ * @default '/'
11
+ */
12
+ redirectUrl?: string;
13
+ }
14
+ /**
15
+ * Login form data structure
16
+ */
17
+ export interface LoginFormData {
18
+ username: string;
19
+ password: string;
20
+ remember: boolean;
21
+ }
22
+ /**
23
+ * Register form data structure
24
+ */
25
+ export interface RegisterFormData {
26
+ username: string;
27
+ email: string;
28
+ password: string;
29
+ }
30
+ /**
31
+ * Tenant selection data
32
+ */
33
+ export interface TenantInfo {
34
+ name: string;
35
+ }
36
+ /**
37
+ * Password flow result
38
+ */
39
+ export interface PasswordFlowResult {
40
+ success: boolean;
41
+ error?: string;
42
+ }
@@ -0,0 +1,25 @@
1
+ import { type LoginFormProps } from '../components';
2
+ /**
3
+ * Props for LoginPage component
4
+ */
5
+ export interface LoginPageProps extends LoginFormProps {
6
+ /**
7
+ * Maximum width of the container
8
+ * @default 'container.sm'
9
+ */
10
+ maxWidth?: string;
11
+ }
12
+ /**
13
+ * LoginPage - Page wrapper for the login form
14
+ *
15
+ * This component wraps LoginForm with proper layout and spacing.
16
+ * It's designed to be used as a route component.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // In your routes configuration
21
+ * <Route path="/account/login" element={<LoginPage />} />
22
+ * ```
23
+ */
24
+ export declare function LoginPage({ maxWidth, ...loginFormProps }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
25
+ export default LoginPage;
@@ -0,0 +1,25 @@
1
+ import { type RegisterFormProps } from '../components';
2
+ /**
3
+ * Props for RegisterPage component
4
+ */
5
+ export interface RegisterPageProps extends RegisterFormProps {
6
+ /**
7
+ * Maximum width of the container
8
+ * @default 'container.sm'
9
+ */
10
+ maxWidth?: string;
11
+ }
12
+ /**
13
+ * RegisterPage - Page wrapper for the registration form
14
+ *
15
+ * This component wraps RegisterForm with proper layout and spacing.
16
+ * It's designed to be used as a route component.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // In your routes configuration
21
+ * <Route path="/account/register" element={<RegisterPage />} />
22
+ * ```
23
+ */
24
+ export declare function RegisterPage({ maxWidth, ...registerFormProps }: RegisterPageProps): import("react/jsx-runtime").JSX.Element;
25
+ export default RegisterPage;
@@ -0,0 +1,2 @@
1
+ export { LoginPage, type LoginPageProps } from './LoginPage';
2
+ export { RegisterPage, type RegisterPageProps } from './RegisterPage';
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import type { AccountOptions } from '../models';
3
+ /**
4
+ * Account context value
5
+ */
6
+ export interface AccountContextValue {
7
+ options: Required<AccountOptions>;
8
+ }
9
+ /**
10
+ * Props for AccountProvider
11
+ */
12
+ export interface AccountProviderProps {
13
+ children: React.ReactNode;
14
+ options?: AccountOptions;
15
+ }
16
+ /**
17
+ * AccountProvider - Provides account configuration to the component tree
18
+ *
19
+ * This is the React equivalent of Angular's AccountModule.forRoot(options)
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <AccountProvider options={{ redirectUrl: '/dashboard' }}>
24
+ * <App />
25
+ * </AccountProvider>
26
+ * ```
27
+ */
28
+ export declare function AccountProvider({ children, options }: AccountProviderProps): import("react/jsx-runtime").JSX.Element;
29
+ /**
30
+ * Hook to access the account context
31
+ *
32
+ * @throws Error if used outside of AccountProvider
33
+ */
34
+ export declare function useAccountContext(): AccountContextValue;
35
+ /**
36
+ * Hook to access account options
37
+ *
38
+ * Returns default options if not within AccountProvider (for standalone usage)
39
+ */
40
+ export declare function useAccountOptions(): Required<AccountOptions>;
@@ -0,0 +1 @@
1
+ export { AccountProvider, useAccountContext, useAccountOptions, type AccountProviderProps, type AccountContextValue, } from './AccountProvider';