@uniai-fe/uds-templates 0.0.11 → 0.0.13
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/README.md +11 -0
- package/dist/styles.css +916 -1074
- package/package.json +3 -3
- package/src/auth/common/complete/Template.tsx +47 -0
- package/src/auth/common/complete/img/circle-check-complete.svg +4 -0
- package/src/auth/common/complete/index.scss +38 -0
- package/src/auth/common/complete/types.ts +15 -0
- package/src/auth/common/container/header/StageHeader.tsx +61 -0
- package/src/auth/common/container/header/index.tsx +5 -0
- package/src/auth/common/container/header/stage-header.scss +50 -0
- package/src/{components/auth → auth/common}/container/index.tsx +2 -0
- package/src/auth/common/find/hooks/useFindAccountForm.ts +79 -0
- package/src/auth/common/find/markup/CodeStep.tsx +166 -0
- package/src/auth/common/find/markup/Header.tsx +46 -0
- package/src/auth/common/find/markup/InfoStep.tsx +109 -0
- package/src/auth/common/find/styles/email.scss +55 -0
- package/src/auth/common/find/styles/find-account.scss +4 -0
- package/src/auth/common/find/styles/layout.scss +19 -0
- package/src/auth/common/find/styles/password.scss +39 -0
- package/src/auth/common/find/styles/result.scss +78 -0
- package/src/auth/common/find/types/forms.ts +30 -0
- package/src/auth/common/find/types/index.ts +121 -0
- package/src/auth/common/find/utils/composeFieldProps.ts +45 -0
- package/src/auth/common/password/constants.ts +19 -0
- package/src/auth/common/password/hooks/useCheckPassword.ts +133 -0
- package/src/auth/common/password/img/check-password.svg +3 -0
- package/src/auth/common/password/markup/PasswordSetField.tsx +250 -0
- package/src/auth/common/password/styles/password-set-field.scss +49 -0
- package/src/auth/common/password/types.ts +142 -0
- package/src/auth/common/password/utils/composePasswordFieldProps.ts +44 -0
- package/src/auth/find-account.ts +28 -0
- package/src/auth/find-id/hooks/index.ts +1 -0
- package/src/auth/find-id/index.scss +1 -0
- package/src/auth/find-id/index.ts +23 -0
- package/src/auth/find-id/markup/StepComplete.tsx +58 -0
- package/src/auth/find-id/markup/StepIdentify.tsx +46 -0
- package/src/auth/find-id/markup/StepVerifyCode.tsx +48 -0
- package/src/auth/find-id/types/index.ts +66 -0
- package/src/auth/find-password/index.scss +1 -0
- package/src/auth/find-password/index.ts +30 -0
- package/src/auth/find-password/markup/StepComplete.tsx +30 -0
- package/src/auth/find-password/markup/StepIdentify.tsx +45 -0
- package/src/auth/find-password/markup/StepResetPassword.tsx +150 -0
- package/src/auth/find-password/markup/StepVerifyCode.tsx +48 -0
- package/src/auth/index.tsx +41 -0
- package/src/{components/auth → auth}/login/markup/Container.tsx +1 -1
- package/src/{components/auth → auth}/login/types/props.ts +1 -1
- package/src/{components/auth → auth}/signup/hooks/useSignupAccountForm.ts +26 -2
- package/src/{components/auth → auth}/signup/hooks/useSignupUserInfoForm.ts +10 -3
- package/src/auth/signup/img/check-agree.svg +3 -0
- package/src/auth/signup/img/chevron-open-detail.svg +3 -0
- package/src/{components/auth → auth}/signup/index.ts +3 -0
- package/src/auth/signup/markup/AccountForm.tsx +113 -0
- package/src/auth/signup/markup/Complete.tsx +59 -0
- package/src/auth/signup/markup/Template.tsx +110 -0
- package/src/{components/auth → auth}/signup/markup/UserInfoForm.tsx +23 -13
- package/src/auth/signup/markup/VerificationForm.tsx +285 -0
- package/src/{components/auth → auth}/signup/markup/index.ts +1 -0
- package/src/auth/signup/styles/signup.scss +187 -0
- package/src/{components/auth → auth}/signup/types/hooks.ts +1 -0
- package/src/{components/auth → auth}/signup/types/props.ts +49 -9
- package/src/auth/signup/utils/getSignupFieldDefaultValue.ts +40 -0
- package/src/index.scss +5 -4
- package/src/index.tsx +3 -3
- package/src/page-frame/mobile/header/PageFrameMobileHeader.tsx +52 -0
- package/src/page-frame/mobile/header/index.ts +4 -0
- package/src/page-frame/mobile/header/page-frame-mobile-header.scss +48 -0
- package/src/page-frame/mobile/img/chevron-backward.svg +3 -0
- package/src/components/auth/index.tsx +0 -20
- package/src/components/auth/signup/markup/AccountForm.tsx +0 -124
- package/src/components/auth/signup/markup/Complete.tsx +0 -61
- package/src/components/auth/signup/markup/VerificationForm.tsx +0 -155
- package/src/components/auth/signup/styles/signup.scss +0 -135
- /package/src/{components/auth → auth/common}/container/AuthContainer.tsx +0 -0
- /package/src/{components/auth → auth/common}/container/index.scss +0 -0
- /package/src/{components/auth → auth/common}/container/types.ts +0 -0
- /package/src/{components/auth → auth}/login/data/valid-options.ts +0 -0
- /package/src/{components/auth → auth}/login/hooks/index.ts +0 -0
- /package/src/{components/auth → auth}/login/hooks/useAuthLoginForm.ts +0 -0
- /package/src/{components/auth → auth}/login/index.scss +0 -0
- /package/src/{components/auth → auth}/login/index.tsx +0 -0
- /package/src/{components/auth → auth}/login/markup/FormField.tsx +0 -0
- /package/src/{components/auth → auth}/login/markup/LinkButtons.tsx +0 -0
- /package/src/{components/auth → auth}/login/styles/login.scss +0 -0
- /package/src/{components/auth → auth}/login/types/form.ts +0 -0
- /package/src/{components/auth → auth}/login/types/hooks.ts +0 -0
- /package/src/{components/auth → auth}/login/types.ts +0 -0
- /package/src/{components/auth → auth}/signup/hooks/index.ts +0 -0
- /package/src/{components/auth → auth}/signup/hooks/useSignupVerificationForm.ts +0 -0
- /package/src/{components/auth → auth}/signup/types/index.ts +0 -0
- /package/src/{components/auth → auth}/signup/utils/composeFieldProps.ts +0 -0
- /package/src/{components/modal → modal}/core/components/Container.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/FooterButtons.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/Provider.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/Root.tsx +0 -0
- /package/src/{components/modal → modal}/core/hooks/useModal.ts +0 -0
- /package/src/{components/modal → modal}/core/jotai/atoms.ts +0 -0
- /package/src/{components/modal → modal}/index.scss +0 -0
- /package/src/{components/modal → modal}/index.tsx +0 -0
- /package/src/{components/modal → modal}/styles/animations.scss +0 -0
- /package/src/{components/modal → modal}/styles/base.scss +0 -0
- /package/src/{components/modal → modal}/styles/container.scss +0 -0
- /package/src/{components/modal → modal}/styles/dimmer.scss +0 -0
- /package/src/{components/modal → modal}/templates/Alert.tsx +0 -0
- /package/src/{components/modal → modal}/templates/Dialog.tsx +0 -0
- /package/src/{components/modal → modal}/types/footer.ts +0 -0
- /package/src/{components/modal → modal}/types/index.ts +0 -0
- /package/src/{components/modal → modal}/types/options.ts +0 -0
- /package/src/{components/modal → modal}/types/state.ts +0 -0
- /package/src/{components/modal → modal}/types/templates.ts +0 -0
- /package/src/{components/page-frame → page-frame}/container/PageFrameContainer.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/container/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/container/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/container/types.ts +0 -0
- /package/src/{components/page-frame → page-frame}/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/PageFrameMobile.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/types.ts +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/PageFrameNavigation.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/index.tsx +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
.auth-find-account-email-block {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--spacing-padding-3, 12px);
|
|
5
|
+
margin-bottom: var(--spacing-padding-7, 28px);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.auth-find-account-email-label {
|
|
9
|
+
color: var(--color-label-standard, var(--primitive-coolgray-20, #3d3f43));
|
|
10
|
+
font-size: var(--font-label-small-size, 13px);
|
|
11
|
+
line-height: 1.5;
|
|
12
|
+
margin: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.auth-find-account-email-field {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
border-bottom: 1px solid
|
|
20
|
+
var(--color-border-standard, var(--primitive-coolgray-90, #e4e5e7));
|
|
21
|
+
min-height: var(--spacing-padding-14, 56px);
|
|
22
|
+
padding-bottom: var(--spacing-padding-2, 8px);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.auth-find-account-email-value {
|
|
26
|
+
font-size: var(--font-body-large-size, 19px);
|
|
27
|
+
color: var(--color-label-strong, var(--primitive-coolgray-10, #18191b));
|
|
28
|
+
line-height: 1.5;
|
|
29
|
+
word-break: break-word;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.auth-find-account-email-helper {
|
|
33
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
34
|
+
color: var(--color-label-alternative, var(--primitive-coolgray-50, #afb1b6));
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.auth-find-account-meta {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
gap: var(--spacing-padding-4, 16px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.auth-find-account-timer-text {
|
|
46
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
47
|
+
color: var(--color-primary-50, #2563eb);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.auth-find-account-timer-helper,
|
|
51
|
+
.auth-find-account-helper {
|
|
52
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
53
|
+
color: var(--color-label-neutral, var(--primitive-coolgray-40, #797e86));
|
|
54
|
+
margin: 0;
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.auth-find-account-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.auth-find-account-form {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--spacing-padding-6, 24px);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.auth-find-account-fields {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--spacing-padding-5, 20px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.auth-find-account-form--code .auth-find-account-fields {
|
|
18
|
+
margin-bottom: var(--spacing-padding-4, 16px);
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.auth-find-account-password-rules {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--spacing-padding-2, 8px);
|
|
5
|
+
padding: 0;
|
|
6
|
+
margin: 0;
|
|
7
|
+
list-style: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.auth-find-account-password-rule {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--spacing-padding-3, 12px);
|
|
14
|
+
font-size: var(--font-body-medium-size, 15px);
|
|
15
|
+
color: var(--color-label-assistive);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.auth-find-account-password-rule::before {
|
|
19
|
+
content: "";
|
|
20
|
+
width: 8px;
|
|
21
|
+
height: 8px;
|
|
22
|
+
border-radius: 50%;
|
|
23
|
+
background: var(--color-border-standard);
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.auth-find-account-password-rule[data-satisfied="true"] {
|
|
28
|
+
color: var(--color-label-positive);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.auth-find-account-password-rule[data-satisfied="true"]::before {
|
|
32
|
+
background: var(--color-label-positive);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.auth-find-account-password-helper {
|
|
36
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
37
|
+
color: var(--color-label-neutral);
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
.auth-find-account-result-card {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--spacing-padding-2, 8px);
|
|
5
|
+
padding: var(--spacing-padding-7, 28px);
|
|
6
|
+
border-radius: var(--shape-rounded-3, 16px);
|
|
7
|
+
background: var(--color-background-subtle);
|
|
8
|
+
margin-bottom: var(--spacing-padding-6, 24px);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.auth-find-account-result-label {
|
|
12
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
13
|
+
color: var(--color-label-assistive);
|
|
14
|
+
margin: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.auth-find-account-result-value {
|
|
18
|
+
font-size: var(--font-heading-medium-size, 21px);
|
|
19
|
+
font-weight: 700;
|
|
20
|
+
color: var(--color-label-strong);
|
|
21
|
+
margin: 0;
|
|
22
|
+
word-break: break-word;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.auth-find-account-result-sub {
|
|
26
|
+
font-size: var(--font-body-medium-size, 15px);
|
|
27
|
+
color: var(--color-label-standard);
|
|
28
|
+
margin: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.auth-find-account-result-helper {
|
|
32
|
+
font-size: var(--font-caption-medium-size, 12px);
|
|
33
|
+
color: var(--color-label-assistive);
|
|
34
|
+
margin: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.auth-find-account-id-summary {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--spacing-padding-4, 16px);
|
|
42
|
+
width: 100%;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.auth-find-account-id-summary-card {
|
|
46
|
+
width: fit-content;
|
|
47
|
+
min-width: 230px;
|
|
48
|
+
padding: 0 var(--spacing-padding-6, 16px);
|
|
49
|
+
border-radius: var(--theme-radius-medium-3, 8px);
|
|
50
|
+
background: var(
|
|
51
|
+
--color-background-alternative-cool_gray,
|
|
52
|
+
var(--colot-cool-gray-95, #f2f2f3)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.auth-find-account-id-summary-row {
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: space-between;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: var(--spacing-padding-5, 20px);
|
|
61
|
+
margin: var(--spacing-gap-5, 12px) 0;
|
|
62
|
+
|
|
63
|
+
.auth-find-account-id-summary-text {
|
|
64
|
+
font-size: 0;
|
|
65
|
+
span {
|
|
66
|
+
font-size: var(--font-caption-large-size);
|
|
67
|
+
line-height: var(--font-caption-large-line-height);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
dt {
|
|
72
|
+
color: var(--color-label-neutral);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
dd {
|
|
76
|
+
color: var(--color-label-strong);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type {
|
|
3
|
+
FieldValues,
|
|
4
|
+
SubmitHandler,
|
|
5
|
+
UseFormReturn,
|
|
6
|
+
} from "react-hook-form";
|
|
7
|
+
import type { InputFieldProps } from "@uniai-fe/uds-primitives";
|
|
8
|
+
import type { FindAccountFieldHelper } from "./index";
|
|
9
|
+
|
|
10
|
+
export type UseFindAccountFormOptions<
|
|
11
|
+
TFields extends Record<string, InputFieldProps>,
|
|
12
|
+
TValues extends FieldValues,
|
|
13
|
+
> = {
|
|
14
|
+
fields: TFields;
|
|
15
|
+
form: UseFormReturn<TValues>;
|
|
16
|
+
onSubmit: SubmitHandler<TValues>;
|
|
17
|
+
isSubmittable?: (values?: TValues) => boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type UseFindAccountFormReturn<
|
|
21
|
+
TFields extends Record<string, InputFieldProps>,
|
|
22
|
+
TValues extends FieldValues,
|
|
23
|
+
> = {
|
|
24
|
+
register: {
|
|
25
|
+
[K in keyof TFields]: ReturnType<UseFormReturn<TValues>["register"]>;
|
|
26
|
+
};
|
|
27
|
+
helpers: Record<keyof TFields, FindAccountFieldHelper>;
|
|
28
|
+
disabled: boolean;
|
|
29
|
+
onSubmit: React.FormEventHandler<HTMLFormElement>;
|
|
30
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { SubmitHandler } from "react-hook-form";
|
|
3
|
+
import type { AuthContainerProps } from "../../container";
|
|
4
|
+
import type {
|
|
5
|
+
AuthCodeInputProps,
|
|
6
|
+
ButtonProps,
|
|
7
|
+
InputFieldProps,
|
|
8
|
+
InputProps,
|
|
9
|
+
} from "@uniai-fe/uds-primitives";
|
|
10
|
+
|
|
11
|
+
export type FindAccountNavigationProps = {
|
|
12
|
+
title?: React.ReactNode;
|
|
13
|
+
backLabel?: React.ReactNode;
|
|
14
|
+
backIcon?: React.ReactNode;
|
|
15
|
+
onBack?: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type FindAccountProgressProps = {
|
|
19
|
+
total: number;
|
|
20
|
+
current: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type FindAccountHeadlineProps = {
|
|
24
|
+
title?: React.ReactNode;
|
|
25
|
+
description?: React.ReactNode;
|
|
26
|
+
progress?: FindAccountProgressProps;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type FindAccountCTAProps = {
|
|
30
|
+
label: React.ReactNode;
|
|
31
|
+
buttonProps?: Omit<ButtonProps, "children">;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type FindAccountFieldHelper = {
|
|
35
|
+
text?: React.ReactNode;
|
|
36
|
+
state?: "error" | "success" | "warning";
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type FindAccountInfoValues = Record<string, string>;
|
|
40
|
+
|
|
41
|
+
export type FindAccountInfoFields = {
|
|
42
|
+
name: InputFieldProps<InputProps>;
|
|
43
|
+
email: InputFieldProps<InputProps>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type FindAccountInfoFieldOptions<
|
|
47
|
+
TFields extends FindAccountInfoFields = FindAccountInfoFields,
|
|
48
|
+
> = {
|
|
49
|
+
fields: TFields;
|
|
50
|
+
formAttr?: React.FormHTMLAttributes<HTMLFormElement>;
|
|
51
|
+
onSubmit: SubmitHandler<FindAccountInfoValues>;
|
|
52
|
+
isSubmittable?: (values?: FindAccountInfoValues) => boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type FindAccountInfoStepProps<
|
|
56
|
+
TFields extends FindAccountInfoFields = FindAccountInfoFields,
|
|
57
|
+
> = Omit<AuthContainerProps, "children"> & {
|
|
58
|
+
fieldOptions: FindAccountInfoFieldOptions<TFields>;
|
|
59
|
+
cta?: FindAccountCTAProps;
|
|
60
|
+
navigation?: FindAccountNavigationProps;
|
|
61
|
+
headline?: FindAccountHeadlineProps;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type FindAccountInfoScreenProps<
|
|
65
|
+
TFields extends FindAccountInfoFields = FindAccountInfoFields,
|
|
66
|
+
> = FindAccountInfoStepProps<TFields>;
|
|
67
|
+
|
|
68
|
+
export type FindAccountCodeValues = Record<string, string>;
|
|
69
|
+
|
|
70
|
+
export type FindAccountCodeField = {
|
|
71
|
+
attr?: {
|
|
72
|
+
name?: string;
|
|
73
|
+
};
|
|
74
|
+
label?: React.ReactNode;
|
|
75
|
+
helper?: React.ReactNode;
|
|
76
|
+
length?: number;
|
|
77
|
+
props?: Omit<AuthCodeInputProps, "label" | "helper" | "length">;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type FindAccountCodeFields = {
|
|
81
|
+
code: FindAccountCodeField;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type FindAccountCodeFieldOptions<
|
|
85
|
+
TFields extends FindAccountCodeFields = FindAccountCodeFields,
|
|
86
|
+
> = {
|
|
87
|
+
fields: TFields;
|
|
88
|
+
formAttr?: React.FormHTMLAttributes<HTMLFormElement>;
|
|
89
|
+
onSubmit: SubmitHandler<FindAccountCodeValues>;
|
|
90
|
+
isSubmittable?: (values?: FindAccountCodeValues) => boolean;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type FindAccountEmailDisplayProps = {
|
|
94
|
+
label?: React.ReactNode;
|
|
95
|
+
value: React.ReactNode;
|
|
96
|
+
helper?: React.ReactNode;
|
|
97
|
+
resend?: FindAccountCTAProps;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type FindAccountTimerProps = {
|
|
101
|
+
text: React.ReactNode;
|
|
102
|
+
helper?: React.ReactNode;
|
|
103
|
+
extend?: FindAccountCTAProps;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type FindAccountCodeStepProps<
|
|
107
|
+
TFields extends FindAccountCodeFields = FindAccountCodeFields,
|
|
108
|
+
> = Omit<AuthContainerProps, "children"> & {
|
|
109
|
+
emailDisplay: FindAccountEmailDisplayProps;
|
|
110
|
+
timer?: FindAccountTimerProps;
|
|
111
|
+
fieldOptions: FindAccountCodeFieldOptions<TFields>;
|
|
112
|
+
cta?: FindAccountCTAProps;
|
|
113
|
+
navigation?: FindAccountNavigationProps;
|
|
114
|
+
headline?: FindAccountHeadlineProps;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type FindAccountCodeScreenProps<
|
|
118
|
+
TFields extends FindAccountCodeFields = FindAccountCodeFields,
|
|
119
|
+
> = FindAccountCodeStepProps<TFields>;
|
|
120
|
+
|
|
121
|
+
export type * from "./forms";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { InputFieldProps, InputProps } from "@uniai-fe/uds-primitives";
|
|
2
|
+
import type { FindAccountFieldHelper } from "../types";
|
|
3
|
+
|
|
4
|
+
export const composeFindAccountFieldProps = <
|
|
5
|
+
TProps extends InputProps = InputProps,
|
|
6
|
+
>(
|
|
7
|
+
config: InputFieldProps<TProps>,
|
|
8
|
+
helper?: FindAccountFieldHelper,
|
|
9
|
+
): TProps => {
|
|
10
|
+
const baseProps = {
|
|
11
|
+
...(config.attr ?? {}),
|
|
12
|
+
...(config.props ?? ({} as TProps)),
|
|
13
|
+
} as TProps;
|
|
14
|
+
|
|
15
|
+
const mergedLabelProps = config.labelClassName
|
|
16
|
+
? {
|
|
17
|
+
...baseProps.labelProps,
|
|
18
|
+
className: config.labelClassName,
|
|
19
|
+
}
|
|
20
|
+
: baseProps.labelProps;
|
|
21
|
+
|
|
22
|
+
const mergedHelperProps = config.helperClassName
|
|
23
|
+
? {
|
|
24
|
+
...baseProps.helperProps,
|
|
25
|
+
className: config.helperClassName,
|
|
26
|
+
}
|
|
27
|
+
: baseProps.helperProps;
|
|
28
|
+
|
|
29
|
+
const resolvedPriority =
|
|
30
|
+
baseProps.priority ?? ("secondary" as TProps["priority"]);
|
|
31
|
+
const resolvedSize = baseProps.size ?? ("large" as TProps["size"]);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
...baseProps,
|
|
35
|
+
label: config.label ?? baseProps.label,
|
|
36
|
+
helper: helper?.text ?? config.helper ?? baseProps.helper,
|
|
37
|
+
state: helper?.state ?? baseProps.state,
|
|
38
|
+
priority: resolvedPriority,
|
|
39
|
+
size: resolvedSize,
|
|
40
|
+
block: config.block ?? baseProps.block ?? true,
|
|
41
|
+
className: config.className ?? baseProps.className,
|
|
42
|
+
labelProps: mergedLabelProps,
|
|
43
|
+
helperProps: mergedHelperProps,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AuthPasswordRule } from "./types";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_PASSWORD_RULES: AuthPasswordRule[] = [
|
|
4
|
+
{
|
|
5
|
+
id: "letters",
|
|
6
|
+
label: "영문",
|
|
7
|
+
predicate: (value: string) => /[A-Za-z]/.test(value),
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "numbers",
|
|
11
|
+
label: "숫자",
|
|
12
|
+
predicate: (value: string) => /[0-9]/.test(value),
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "length",
|
|
16
|
+
label: "8자 이상",
|
|
17
|
+
predicate: (value: string) => value.length >= 8,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { useWatch } from "react-hook-form";
|
|
4
|
+
import type { FieldPath, FieldValues, UseFormReturn } from "react-hook-form";
|
|
5
|
+
import type { AuthPasswordHelperState } from "../types";
|
|
6
|
+
|
|
7
|
+
interface UseCheckPasswordMessages {
|
|
8
|
+
missing?: string;
|
|
9
|
+
mismatch?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface UseCheckPasswordOptions<TValues extends FieldValues> {
|
|
13
|
+
form: UseFormReturn<TValues>;
|
|
14
|
+
passwordField: FieldPath<TValues>;
|
|
15
|
+
confirmField: FieldPath<TValues>;
|
|
16
|
+
helperText?: ReactNode;
|
|
17
|
+
messages?: UseCheckPasswordMessages;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UseCheckPasswordReturn {
|
|
21
|
+
passwordValue?: string;
|
|
22
|
+
confirmPasswordValue?: string;
|
|
23
|
+
validator: (value: string) => true | string;
|
|
24
|
+
helper: AuthPasswordHelperState;
|
|
25
|
+
isMatched: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Password 확인 훅; 비밀번호/재확인 값의 일치 여부와 helper 상태를 계산한다.
|
|
30
|
+
* @hook
|
|
31
|
+
* @param {UseCheckPasswordOptions<TValues>} options 훅 옵션
|
|
32
|
+
* @param {UseFormReturn<TValues>} options.form react-hook-form 반환값
|
|
33
|
+
* @param {FieldPath<TValues>} options.passwordField 비밀번호 필드 이름
|
|
34
|
+
* @param {FieldPath<TValues>} options.confirmField 재확인 필드 이름
|
|
35
|
+
* @param {ReactNode} [options.helperText] 기본 helper 텍스트
|
|
36
|
+
* @param {UseCheckPasswordMessages} [options.messages] 검증 메시지
|
|
37
|
+
* @returns {UseCheckPasswordReturn} validator/helper/입력값 집합
|
|
38
|
+
*/
|
|
39
|
+
export function useCheckPassword<TValues extends FieldValues>({
|
|
40
|
+
form,
|
|
41
|
+
passwordField,
|
|
42
|
+
confirmField,
|
|
43
|
+
helperText,
|
|
44
|
+
messages,
|
|
45
|
+
}: UseCheckPasswordOptions<TValues>): UseCheckPasswordReturn {
|
|
46
|
+
const missingMessage = messages?.missing ?? "비밀번호를 먼저 입력해 주세요.";
|
|
47
|
+
const mismatchMessage = messages?.mismatch ?? "비밀번호 불일치";
|
|
48
|
+
const passwordValue = useWatch({
|
|
49
|
+
control: form.control,
|
|
50
|
+
name: passwordField,
|
|
51
|
+
}) as string | undefined;
|
|
52
|
+
const confirmPasswordValue = useWatch({
|
|
53
|
+
control: form.control,
|
|
54
|
+
name: confirmField,
|
|
55
|
+
}) as string | undefined;
|
|
56
|
+
|
|
57
|
+
const validator = useCallback(
|
|
58
|
+
(value: string) => {
|
|
59
|
+
if (!value) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const latestPassword = form.getValues(passwordField) as
|
|
63
|
+
| string
|
|
64
|
+
| undefined;
|
|
65
|
+
if (!latestPassword || latestPassword.length === 0) {
|
|
66
|
+
return missingMessage;
|
|
67
|
+
}
|
|
68
|
+
return latestPassword === value || mismatchMessage;
|
|
69
|
+
},
|
|
70
|
+
[form, mismatchMessage, missingMessage, passwordField],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const helper: AuthPasswordHelperState = useMemo(() => {
|
|
74
|
+
if (!confirmPasswordValue || confirmPasswordValue.length === 0) {
|
|
75
|
+
return {
|
|
76
|
+
text: helperText ?? undefined,
|
|
77
|
+
state: undefined,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!passwordValue || passwordValue.length === 0) {
|
|
82
|
+
return {
|
|
83
|
+
text: missingMessage,
|
|
84
|
+
state: "error",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (confirmPasswordValue !== passwordValue) {
|
|
89
|
+
return {
|
|
90
|
+
text: mismatchMessage,
|
|
91
|
+
state: "error",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
text: helperText ?? undefined,
|
|
97
|
+
state: undefined,
|
|
98
|
+
};
|
|
99
|
+
}, [
|
|
100
|
+
confirmPasswordValue,
|
|
101
|
+
helperText,
|
|
102
|
+
mismatchMessage,
|
|
103
|
+
missingMessage,
|
|
104
|
+
passwordValue,
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const isMatched =
|
|
108
|
+
typeof passwordValue === "string" &&
|
|
109
|
+
passwordValue.length > 0 &&
|
|
110
|
+
typeof confirmPasswordValue === "string" &&
|
|
111
|
+
confirmPasswordValue.length > 0 &&
|
|
112
|
+
confirmPasswordValue === passwordValue;
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!isMatched) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const fieldState = form.getFieldState(confirmField);
|
|
119
|
+
if (fieldState.error) {
|
|
120
|
+
form.clearErrors(confirmField);
|
|
121
|
+
}
|
|
122
|
+
}, [confirmField, form, isMatched]);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
passwordValue,
|
|
126
|
+
confirmPasswordValue,
|
|
127
|
+
validator,
|
|
128
|
+
helper,
|
|
129
|
+
isMatched,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default useCheckPassword;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M8 2C11.3137 2 14 4.68629 14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2ZM10.9268 6.44141C10.7185 6.23363 10.381 6.23351 10.1729 6.44141L7.48535 9.12891L5.71094 7.35352C5.50266 7.14524 5.16433 7.14524 4.95605 7.35352C4.74799 7.56167 4.74821 7.89914 4.95605 8.10742L7.1084 10.2598C7.31665 10.468 7.655 10.4689 7.86328 10.2607L10.9277 7.19629C11.1358 6.98799 11.135 6.6496 10.9268 6.44141Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|