@uniai-fe/uds-templates 0.0.9 → 0.0.11
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 +77 -1
- package/dist/styles.css +212 -267
- package/package.json +6 -4
- package/src/components/auth/index.tsx +11 -0
- package/src/components/auth/login/index.tsx +1 -1
- package/src/components/auth/login/markup/FormField.tsx +2 -2
- package/src/components/auth/login/types/props.ts +12 -12
- package/src/components/auth/login/types.ts +2 -2
- package/src/components/auth/signup/hooks/index.ts +3 -0
- package/src/components/auth/signup/hooks/useSignupAccountForm.ts +77 -0
- package/src/components/auth/signup/hooks/useSignupUserInfoForm.ts +81 -0
- package/src/components/auth/signup/hooks/useSignupVerificationForm.ts +77 -0
- package/src/components/auth/signup/index.ts +24 -0
- package/src/components/auth/signup/markup/AccountForm.tsx +124 -0
- package/src/components/auth/signup/markup/Complete.tsx +61 -0
- package/src/components/auth/signup/markup/UserInfoForm.tsx +97 -0
- package/src/components/auth/signup/markup/VerificationForm.tsx +155 -0
- package/src/components/auth/signup/markup/index.ts +4 -0
- package/src/components/auth/signup/styles/signup.scss +135 -0
- package/src/components/auth/signup/types/hooks.ts +85 -0
- package/src/components/auth/signup/types/index.ts +2 -0
- package/src/components/auth/signup/types/props.ts +105 -0
- package/src/components/auth/signup/utils/composeFieldProps.ts +50 -0
- package/src/components/modal/core/components/Container.tsx +41 -0
- package/src/components/modal/core/components/FooterButtons.tsx +132 -0
- package/src/components/modal/core/components/Provider.tsx +28 -0
- package/src/components/modal/core/components/Root.tsx +93 -0
- package/src/components/modal/core/hooks/useModal.ts +136 -0
- package/src/components/modal/core/jotai/atoms.ts +10 -0
- package/src/components/modal/index.scss +4 -0
- package/src/components/modal/index.tsx +16 -0
- package/src/components/modal/styles/animations.scss +24 -0
- package/src/components/modal/styles/base.scss +45 -0
- package/src/components/modal/styles/container.scss +138 -0
- package/src/components/modal/styles/dimmer.scss +23 -0
- package/src/components/modal/templates/Alert.tsx +104 -0
- package/src/components/modal/templates/Dialog.tsx +112 -0
- package/src/components/modal/types/footer.ts +36 -0
- package/src/components/modal/types/index.ts +21 -0
- package/src/components/modal/types/options.ts +6 -0
- package/src/components/modal/types/state.ts +31 -0
- package/src/components/modal/types/templates.ts +32 -0
- package/src/index.scss +1 -0
- package/src/index.tsx +1 -0
|
@@ -2,44 +2,44 @@ import React from "react";
|
|
|
2
2
|
import type { SubmitHandler } from "react-hook-form";
|
|
3
3
|
import type { AuthContainerProps } from "../../container";
|
|
4
4
|
import type {
|
|
5
|
-
|
|
5
|
+
InputFieldProps,
|
|
6
6
|
InputPasswordProps,
|
|
7
7
|
InputProps,
|
|
8
8
|
} from "@uniai-fe/uds-primitives";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* 로그인 입력 필드
|
|
12
|
-
* @typedef {
|
|
11
|
+
* 로그인 입력 필드 props
|
|
12
|
+
* @typedef {AuthLoginFieldProps}
|
|
13
13
|
* @template TProps
|
|
14
14
|
* @desc
|
|
15
|
-
* -
|
|
15
|
+
* - InputFieldProps 제네릭을 래핑해 로그인 요구사항과 연결한다.
|
|
16
16
|
* - props 제네릭으로 Input/PasswordInput 등 세부 props를 주입한다.
|
|
17
17
|
*/
|
|
18
|
-
export type
|
|
19
|
-
|
|
18
|
+
export type AuthLoginFieldProps<TProps extends InputProps = InputProps> =
|
|
19
|
+
InputFieldProps<TProps>;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* 로그인 필드 묶음
|
|
23
23
|
* @template TAdditional
|
|
24
24
|
* @desc
|
|
25
25
|
* - 필수 id/password 필드와 확장 필드를 포함한다.
|
|
26
|
-
* @property {
|
|
27
|
-
* @property {
|
|
26
|
+
* @property {AuthLoginFieldProps} id 아이디 필드 설정
|
|
27
|
+
* @property {AuthLoginFieldProps} password 비밀번호 필드 설정
|
|
28
28
|
*/
|
|
29
29
|
export type AuthLoginFields<
|
|
30
|
-
TAdditional extends Record<string,
|
|
30
|
+
TAdditional extends Record<string, AuthLoginFieldProps> = Record<
|
|
31
31
|
string,
|
|
32
|
-
|
|
32
|
+
AuthLoginFieldProps
|
|
33
33
|
>,
|
|
34
34
|
> = TAdditional & {
|
|
35
35
|
/**
|
|
36
36
|
* 아이디 필드 설정
|
|
37
37
|
*/
|
|
38
|
-
id:
|
|
38
|
+
id: AuthLoginFieldProps<InputProps>;
|
|
39
39
|
/**
|
|
40
40
|
* 비밀번호 필드 설정
|
|
41
41
|
*/
|
|
42
|
-
password:
|
|
42
|
+
password: AuthLoginFieldProps<InputPasswordProps>;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./types/props";
|
|
2
|
-
export * from "./types/hooks";
|
|
1
|
+
export type * from "./types/props";
|
|
2
|
+
export type * from "./types/hooks";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useWatch } from "react-hook-form";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type {
|
|
5
|
+
AuthSignupAccountFields,
|
|
6
|
+
AuthSignupAccountValues,
|
|
7
|
+
AuthSignupFieldProps,
|
|
8
|
+
UseSignupAccountFormOptions,
|
|
9
|
+
UseSignupAccountFormReturn,
|
|
10
|
+
} from "../types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 회원가입 Step3 훅; 계정 정보
|
|
14
|
+
* @hook
|
|
15
|
+
* @template TFields
|
|
16
|
+
* @param {UseSignupAccountFormOptions<TFields>} options
|
|
17
|
+
* @desc
|
|
18
|
+
* - 1) form init → 2) register merge → 3) helper/state → 4) submit 순서
|
|
19
|
+
*/
|
|
20
|
+
export function useSignupAccountForm<
|
|
21
|
+
TFields extends Record<string, AuthSignupFieldProps> =
|
|
22
|
+
AuthSignupAccountFields,
|
|
23
|
+
>({
|
|
24
|
+
fields,
|
|
25
|
+
form,
|
|
26
|
+
onSubmit,
|
|
27
|
+
}: UseSignupAccountFormOptions<TFields>): UseSignupAccountFormReturn<TFields> {
|
|
28
|
+
const values = useWatch({ control: form.control }) as
|
|
29
|
+
| AuthSignupAccountValues
|
|
30
|
+
| undefined;
|
|
31
|
+
|
|
32
|
+
const register = useMemo(() => {
|
|
33
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
34
|
+
(acc, fieldKey) => {
|
|
35
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
36
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
37
|
+
acc[fieldKey] = form.register(fieldName);
|
|
38
|
+
return acc;
|
|
39
|
+
},
|
|
40
|
+
{} as UseSignupAccountFormReturn<TFields>["register"],
|
|
41
|
+
);
|
|
42
|
+
}, [fields, form]);
|
|
43
|
+
|
|
44
|
+
const helpers = useMemo(() => {
|
|
45
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
46
|
+
(acc, fieldKey) => {
|
|
47
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
48
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
49
|
+
const state = form.getFieldState(fieldName);
|
|
50
|
+
acc[fieldKey] = {
|
|
51
|
+
text:
|
|
52
|
+
(state.error?.message as ReactNode | undefined) ?? config.helper,
|
|
53
|
+
state: state.invalid ? "error" : undefined,
|
|
54
|
+
};
|
|
55
|
+
return acc;
|
|
56
|
+
},
|
|
57
|
+
{} as UseSignupAccountFormReturn<TFields>["helpers"],
|
|
58
|
+
);
|
|
59
|
+
}, [fields, form]);
|
|
60
|
+
|
|
61
|
+
const trimmedFilled =
|
|
62
|
+
values &&
|
|
63
|
+
Object.values(values).every(value =>
|
|
64
|
+
typeof value === "string" ? value.trim().length > 0 : false,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const disabled = form.formState.isSubmitting || !trimmedFilled;
|
|
68
|
+
|
|
69
|
+
const onSubmitHandler = form.handleSubmit(onSubmit);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
register,
|
|
73
|
+
helpers,
|
|
74
|
+
disabled,
|
|
75
|
+
onSubmit: onSubmitHandler,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useWatch } from "react-hook-form";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type {
|
|
5
|
+
AuthSignupFieldProps,
|
|
6
|
+
AuthSignupUserInfoFields,
|
|
7
|
+
AuthSignupUserInfoValues,
|
|
8
|
+
UseSignupUserInfoFormOptions,
|
|
9
|
+
UseSignupUserInfoFormReturn,
|
|
10
|
+
} from "../types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 회원가입 기본 정보 훅 옵션
|
|
14
|
+
* @typedef {UseSignupUserInfoFormOptions}
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* 회원가입 Step1 훅; 사용자 기본 정보
|
|
18
|
+
* @hook
|
|
19
|
+
* @template TFields
|
|
20
|
+
* @param {UseSignupUserInfoFormOptions<TFields>} options 훅 옵션
|
|
21
|
+
* @desc
|
|
22
|
+
* - 1) form init → 2) register merge → 3) helper/state → 4) submit 순서
|
|
23
|
+
*/
|
|
24
|
+
export function useSignupUserInfoForm<
|
|
25
|
+
TFields extends Record<string, AuthSignupFieldProps> =
|
|
26
|
+
AuthSignupUserInfoFields,
|
|
27
|
+
>({
|
|
28
|
+
fields,
|
|
29
|
+
form,
|
|
30
|
+
onSubmit,
|
|
31
|
+
}: UseSignupUserInfoFormOptions<TFields>): UseSignupUserInfoFormReturn<TFields> {
|
|
32
|
+
const values = useWatch({ control: form.control }) as
|
|
33
|
+
| AuthSignupUserInfoValues
|
|
34
|
+
| undefined;
|
|
35
|
+
|
|
36
|
+
const register = useMemo(() => {
|
|
37
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
38
|
+
(acc, fieldKey) => {
|
|
39
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
40
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
41
|
+
acc[fieldKey] = form.register(fieldName);
|
|
42
|
+
return acc;
|
|
43
|
+
},
|
|
44
|
+
{} as UseSignupUserInfoFormReturn<TFields>["register"],
|
|
45
|
+
);
|
|
46
|
+
}, [fields, form]);
|
|
47
|
+
|
|
48
|
+
const helpers = useMemo(() => {
|
|
49
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
50
|
+
(acc, fieldKey) => {
|
|
51
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
52
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
53
|
+
const state = form.getFieldState(fieldName);
|
|
54
|
+
acc[fieldKey] = {
|
|
55
|
+
text:
|
|
56
|
+
(state.error?.message as ReactNode | undefined) ?? config.helper,
|
|
57
|
+
state: state.invalid ? "error" : undefined,
|
|
58
|
+
};
|
|
59
|
+
return acc;
|
|
60
|
+
},
|
|
61
|
+
{} as UseSignupUserInfoFormReturn<TFields>["helpers"],
|
|
62
|
+
);
|
|
63
|
+
}, [fields, form]);
|
|
64
|
+
|
|
65
|
+
const trimmedFilled =
|
|
66
|
+
values &&
|
|
67
|
+
Object.values(values).every(value =>
|
|
68
|
+
typeof value === "string" ? value.trim().length > 0 : false,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const disabled = form.formState.isSubmitting || !trimmedFilled;
|
|
72
|
+
|
|
73
|
+
const onSubmitHandler = form.handleSubmit(onSubmit);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
register,
|
|
77
|
+
helpers,
|
|
78
|
+
disabled,
|
|
79
|
+
onSubmit: onSubmitHandler,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useWatch } from "react-hook-form";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type {
|
|
5
|
+
AuthSignupFieldProps,
|
|
6
|
+
AuthSignupVerificationFields,
|
|
7
|
+
AuthSignupVerificationValues,
|
|
8
|
+
UseSignupVerificationFormOptions,
|
|
9
|
+
UseSignupVerificationFormReturn,
|
|
10
|
+
} from "../types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 회원가입 Step2 훅; 약관 + 이메일 인증
|
|
14
|
+
* @hook
|
|
15
|
+
* @template TFields
|
|
16
|
+
* @param {UseSignupVerificationFormOptions<TFields>} options
|
|
17
|
+
* @desc
|
|
18
|
+
* - 1) form init → 2) register merge → 3) helper/state → 4) submit 순서
|
|
19
|
+
*/
|
|
20
|
+
export function useSignupVerificationForm<
|
|
21
|
+
TFields extends Record<string, AuthSignupFieldProps> =
|
|
22
|
+
AuthSignupVerificationFields,
|
|
23
|
+
>({
|
|
24
|
+
fields,
|
|
25
|
+
form,
|
|
26
|
+
onSubmit,
|
|
27
|
+
}: UseSignupVerificationFormOptions<TFields>): UseSignupVerificationFormReturn<TFields> {
|
|
28
|
+
const values = useWatch({ control: form.control }) as
|
|
29
|
+
| AuthSignupVerificationValues
|
|
30
|
+
| undefined;
|
|
31
|
+
|
|
32
|
+
const register = useMemo(() => {
|
|
33
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
34
|
+
(acc, fieldKey) => {
|
|
35
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
36
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
37
|
+
acc[fieldKey] = form.register(fieldName);
|
|
38
|
+
return acc;
|
|
39
|
+
},
|
|
40
|
+
{} as UseSignupVerificationFormReturn<TFields>["register"],
|
|
41
|
+
);
|
|
42
|
+
}, [fields, form]);
|
|
43
|
+
|
|
44
|
+
const helpers = useMemo(() => {
|
|
45
|
+
return (Object.keys(fields) as Array<keyof TFields>).reduce(
|
|
46
|
+
(acc, fieldKey) => {
|
|
47
|
+
const config = fields[fieldKey] as AuthSignupFieldProps;
|
|
48
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
49
|
+
const state = form.getFieldState(fieldName);
|
|
50
|
+
acc[fieldKey] = {
|
|
51
|
+
text:
|
|
52
|
+
(state.error?.message as ReactNode | undefined) ?? config.helper,
|
|
53
|
+
state: state.invalid ? "error" : undefined,
|
|
54
|
+
};
|
|
55
|
+
return acc;
|
|
56
|
+
},
|
|
57
|
+
{} as UseSignupVerificationFormReturn<TFields>["helpers"],
|
|
58
|
+
);
|
|
59
|
+
}, [fields, form]);
|
|
60
|
+
|
|
61
|
+
const trimmedFilled =
|
|
62
|
+
values &&
|
|
63
|
+
Object.values(values).every(value =>
|
|
64
|
+
typeof value === "string" ? value.trim().length > 0 : false,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const disabled = form.formState.isSubmitting || !trimmedFilled;
|
|
68
|
+
|
|
69
|
+
const onSubmitHandler = form.handleSubmit(onSubmit);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
register,
|
|
73
|
+
helpers,
|
|
74
|
+
disabled,
|
|
75
|
+
onSubmit: onSubmitHandler,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthSignupUserInfoForm,
|
|
3
|
+
AuthSignupVerificationForm,
|
|
4
|
+
AuthSignupAccountForm,
|
|
5
|
+
AuthSignupComplete,
|
|
6
|
+
} from "./markup";
|
|
7
|
+
|
|
8
|
+
import "./styles/signup.scss";
|
|
9
|
+
|
|
10
|
+
export type * from "./types";
|
|
11
|
+
export * from "./hooks";
|
|
12
|
+
export {
|
|
13
|
+
AuthSignupUserInfoForm,
|
|
14
|
+
AuthSignupVerificationForm,
|
|
15
|
+
AuthSignupAccountForm,
|
|
16
|
+
AuthSignupComplete,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const AuthSignup = {
|
|
20
|
+
StepUserInfo: AuthSignupUserInfoForm,
|
|
21
|
+
StepVerification: AuthSignupVerificationForm,
|
|
22
|
+
StepAccount: AuthSignupAccountForm,
|
|
23
|
+
StepComplete: AuthSignupComplete,
|
|
24
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useForm } from "react-hook-form";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Input,
|
|
6
|
+
PasswordInput,
|
|
7
|
+
type InputPasswordProps,
|
|
8
|
+
type InputProps,
|
|
9
|
+
} from "@uniai-fe/uds-primitives";
|
|
10
|
+
import type { AuthSignupAccountProps, AuthSignupAccountValues } from "../types";
|
|
11
|
+
import { useSignupAccountForm } from "../hooks";
|
|
12
|
+
import { composeSignupFieldProps } from "../utils/composeFieldProps";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_SUBMIT_LABEL = "가입 요청";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 회원가입 Step3; 계정 정보 입력(아이디/비밀번호)
|
|
18
|
+
* @component
|
|
19
|
+
* @param {AuthSignupAccountProps} props account props
|
|
20
|
+
* @param {AuthSignupAccountProps["fields"]} props.fields 아이디/비밀번호 필드
|
|
21
|
+
* @param {AuthSignupPasswordRule[]} [props.passwordRules] 비밀번호 규칙 상태
|
|
22
|
+
* @param {React.FormHTMLAttributes<HTMLFormElement>} [props.formAttr] form attr
|
|
23
|
+
* @param {import("react").ReactNode} [props.submitLabel] CTA 라벨
|
|
24
|
+
*/
|
|
25
|
+
export function AuthSignupAccountForm({
|
|
26
|
+
fields,
|
|
27
|
+
passwordRules,
|
|
28
|
+
formAttr,
|
|
29
|
+
submitLabel,
|
|
30
|
+
onSubmit,
|
|
31
|
+
}: AuthSignupAccountProps) {
|
|
32
|
+
// 필드 구성에서 name attr을 추출해 RHF default 값을 정의한다.
|
|
33
|
+
const defaultValues = useMemo(
|
|
34
|
+
() =>
|
|
35
|
+
(Object.keys(fields) as Array<keyof typeof fields>).reduce(
|
|
36
|
+
(acc, fieldKey) => {
|
|
37
|
+
const config = fields[fieldKey];
|
|
38
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
39
|
+
acc[fieldName] = "";
|
|
40
|
+
return acc;
|
|
41
|
+
},
|
|
42
|
+
{} as AuthSignupAccountValues,
|
|
43
|
+
),
|
|
44
|
+
[fields],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const form = useForm<AuthSignupAccountValues>({
|
|
48
|
+
mode: "onChange",
|
|
49
|
+
defaultValues,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
register,
|
|
54
|
+
helpers,
|
|
55
|
+
disabled,
|
|
56
|
+
onSubmit: handleSubmit,
|
|
57
|
+
} = useSignupAccountForm({
|
|
58
|
+
fields,
|
|
59
|
+
form,
|
|
60
|
+
onSubmit,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<form
|
|
65
|
+
className="auth-signup-form auth-signup-form--account"
|
|
66
|
+
{...formAttr}
|
|
67
|
+
onSubmit={handleSubmit}
|
|
68
|
+
>
|
|
69
|
+
<div className="auth-signup-fields">
|
|
70
|
+
<Input
|
|
71
|
+
{...composeSignupFieldProps<InputProps>(
|
|
72
|
+
fields.accountId,
|
|
73
|
+
helpers.accountId,
|
|
74
|
+
)}
|
|
75
|
+
register={register.accountId}
|
|
76
|
+
/>
|
|
77
|
+
<PasswordInput
|
|
78
|
+
{...composeSignupFieldProps<InputPasswordProps>(
|
|
79
|
+
fields.password,
|
|
80
|
+
helpers.password,
|
|
81
|
+
)}
|
|
82
|
+
register={register.password}
|
|
83
|
+
/>
|
|
84
|
+
<PasswordInput
|
|
85
|
+
{...composeSignupFieldProps<InputPasswordProps>(
|
|
86
|
+
fields.confirmPassword,
|
|
87
|
+
helpers.confirmPassword,
|
|
88
|
+
)}
|
|
89
|
+
register={register.confirmPassword}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
{passwordRules?.length ? (
|
|
93
|
+
<ul className="auth-signup-password-rules">
|
|
94
|
+
{passwordRules.map(rule => (
|
|
95
|
+
<li
|
|
96
|
+
key={rule.id}
|
|
97
|
+
className="auth-signup-password-rule"
|
|
98
|
+
data-fulfilled={rule.fulfilled ? "true" : undefined}
|
|
99
|
+
>
|
|
100
|
+
<span
|
|
101
|
+
className="auth-signup-password-rule-indicator"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
/>
|
|
104
|
+
<span className="auth-signup-password-rule-text">
|
|
105
|
+
{rule.label}
|
|
106
|
+
</span>
|
|
107
|
+
</li>
|
|
108
|
+
))}
|
|
109
|
+
</ul>
|
|
110
|
+
) : null}
|
|
111
|
+
<Button.Default
|
|
112
|
+
type="submit"
|
|
113
|
+
scale="solid-xlarge"
|
|
114
|
+
priority="primary"
|
|
115
|
+
block
|
|
116
|
+
disabled={disabled}
|
|
117
|
+
>
|
|
118
|
+
{submitLabel ?? DEFAULT_SUBMIT_LABEL}
|
|
119
|
+
</Button.Default>
|
|
120
|
+
</form>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default AuthSignupAccountForm;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Button } from "@uniai-fe/uds-primitives";
|
|
2
|
+
import type { AuthSignupCompleteProps } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 회원가입 Step4; 가입 완료/승인 대기 안내
|
|
6
|
+
* @component
|
|
7
|
+
* @param {AuthSignupCompleteProps} props complete props
|
|
8
|
+
* @param {React.ReactNode} props.title 완료 메시지
|
|
9
|
+
* @param {React.ReactNode} [props.description] 부가 설명
|
|
10
|
+
* @param {React.ReactNode} [props.illustration] 일러스트 영역
|
|
11
|
+
* @param {AuthSignupCompleteAction} props.primaryAction 주요 CTA
|
|
12
|
+
* @param {AuthSignupCompleteAction} [props.secondaryAction] 보조 CTA
|
|
13
|
+
*/
|
|
14
|
+
export function AuthSignupComplete({
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
illustration,
|
|
18
|
+
primaryAction,
|
|
19
|
+
secondaryAction,
|
|
20
|
+
}: AuthSignupCompleteProps) {
|
|
21
|
+
// CTA 섹션은 primary/secondary 순으로 렌더해 사용자가 자연스럽게 진행하도록 안내한다.
|
|
22
|
+
return (
|
|
23
|
+
<section className="auth-signup-complete">
|
|
24
|
+
{illustration ? (
|
|
25
|
+
<div className="auth-signup-complete-illustration">{illustration}</div>
|
|
26
|
+
) : null}
|
|
27
|
+
<div className="auth-signup-complete-text">
|
|
28
|
+
<h2 className="auth-signup-complete-title">{title}</h2>
|
|
29
|
+
{description ? (
|
|
30
|
+
<p className="auth-signup-complete-description">{description}</p>
|
|
31
|
+
) : null}
|
|
32
|
+
</div>
|
|
33
|
+
<div className="auth-signup-complete-actions">
|
|
34
|
+
<Button.Default
|
|
35
|
+
type="button"
|
|
36
|
+
scale="solid-xlarge"
|
|
37
|
+
priority="primary"
|
|
38
|
+
block
|
|
39
|
+
disabled={primaryAction.disabled}
|
|
40
|
+
onClick={primaryAction.onClick}
|
|
41
|
+
>
|
|
42
|
+
{primaryAction.label}
|
|
43
|
+
</Button.Default>
|
|
44
|
+
{secondaryAction ? (
|
|
45
|
+
<Button.Default
|
|
46
|
+
type="button"
|
|
47
|
+
scale="solid-xlarge"
|
|
48
|
+
priority="secondary"
|
|
49
|
+
block
|
|
50
|
+
disabled={secondaryAction.disabled}
|
|
51
|
+
onClick={secondaryAction.onClick}
|
|
52
|
+
>
|
|
53
|
+
{secondaryAction.label}
|
|
54
|
+
</Button.Default>
|
|
55
|
+
) : null}
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default AuthSignupComplete;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useForm } from "react-hook-form";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Input,
|
|
6
|
+
PhoneInput,
|
|
7
|
+
type InputProps,
|
|
8
|
+
type PhoneInputProps,
|
|
9
|
+
} from "@uniai-fe/uds-primitives";
|
|
10
|
+
import type {
|
|
11
|
+
AuthSignupUserInfoProps,
|
|
12
|
+
AuthSignupUserInfoValues,
|
|
13
|
+
} from "../types";
|
|
14
|
+
import { useSignupUserInfoForm } from "../hooks";
|
|
15
|
+
import { composeSignupFieldProps } from "../utils/composeFieldProps";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_SUBMIT_LABEL = "다음";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 회원가입 Step1; 사용자 기본 정보(이름/휴대폰) 입력 폼
|
|
21
|
+
* @component
|
|
22
|
+
* @param {AuthSignupUserInfoProps} props form props
|
|
23
|
+
* @param {AuthSignupUserInfoFields} props.fields 필드 설정
|
|
24
|
+
* @param {React.FormHTMLAttributes<HTMLFormElement>} [props.formAttr] form attr
|
|
25
|
+
* @param {import("react").ReactNode} [props.submitLabel] CTA 라벨
|
|
26
|
+
* @param {SubmitHandler<AuthSignupUserInfoValues>} props.onSubmit 제출 핸들러
|
|
27
|
+
*/
|
|
28
|
+
export function AuthSignupUserInfoForm({
|
|
29
|
+
fields,
|
|
30
|
+
formAttr,
|
|
31
|
+
submitLabel,
|
|
32
|
+
onSubmit,
|
|
33
|
+
}: AuthSignupUserInfoProps) {
|
|
34
|
+
// 필드 name attr을 기준으로 RHF defaultValues를 생성한다.
|
|
35
|
+
const defaultValues = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
(Object.keys(fields) as Array<keyof typeof fields>).reduce(
|
|
38
|
+
(acc, fieldKey) => {
|
|
39
|
+
const config = fields[fieldKey];
|
|
40
|
+
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
41
|
+
acc[fieldName] = "";
|
|
42
|
+
return acc;
|
|
43
|
+
},
|
|
44
|
+
{} as AuthSignupUserInfoValues,
|
|
45
|
+
),
|
|
46
|
+
[fields],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const form = useForm<AuthSignupUserInfoValues>({
|
|
50
|
+
mode: "onChange",
|
|
51
|
+
defaultValues,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
register,
|
|
56
|
+
helpers,
|
|
57
|
+
disabled,
|
|
58
|
+
onSubmit: handleSubmit,
|
|
59
|
+
} = useSignupUserInfoForm({
|
|
60
|
+
fields,
|
|
61
|
+
form,
|
|
62
|
+
onSubmit,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<form
|
|
67
|
+
className="auth-signup-form auth-signup-form--user-info"
|
|
68
|
+
{...formAttr}
|
|
69
|
+
onSubmit={handleSubmit}
|
|
70
|
+
>
|
|
71
|
+
<div className="auth-signup-fields">
|
|
72
|
+
<Input
|
|
73
|
+
{...composeSignupFieldProps<InputProps>(fields.name, helpers.name)}
|
|
74
|
+
register={register.name}
|
|
75
|
+
/>
|
|
76
|
+
<PhoneInput
|
|
77
|
+
{...composeSignupFieldProps<PhoneInputProps>(
|
|
78
|
+
fields.phone,
|
|
79
|
+
helpers.phone,
|
|
80
|
+
)}
|
|
81
|
+
register={register.phone}
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
<Button.Default
|
|
85
|
+
type="submit"
|
|
86
|
+
scale="solid-xlarge"
|
|
87
|
+
priority="primary"
|
|
88
|
+
block
|
|
89
|
+
disabled={disabled}
|
|
90
|
+
>
|
|
91
|
+
{submitLabel ?? DEFAULT_SUBMIT_LABEL}
|
|
92
|
+
</Button.Default>
|
|
93
|
+
</form>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default AuthSignupUserInfoForm;
|