@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.
Files changed (122) hide show
  1. package/README.md +11 -0
  2. package/dist/styles.css +916 -1074
  3. package/package.json +3 -3
  4. package/src/auth/common/complete/Template.tsx +47 -0
  5. package/src/auth/common/complete/img/circle-check-complete.svg +4 -0
  6. package/src/auth/common/complete/index.scss +38 -0
  7. package/src/auth/common/complete/types.ts +15 -0
  8. package/src/auth/common/container/header/StageHeader.tsx +61 -0
  9. package/src/auth/common/container/header/index.tsx +5 -0
  10. package/src/auth/common/container/header/stage-header.scss +50 -0
  11. package/src/{components/auth → auth/common}/container/index.tsx +2 -0
  12. package/src/auth/common/find/hooks/useFindAccountForm.ts +79 -0
  13. package/src/auth/common/find/markup/CodeStep.tsx +166 -0
  14. package/src/auth/common/find/markup/Header.tsx +46 -0
  15. package/src/auth/common/find/markup/InfoStep.tsx +109 -0
  16. package/src/auth/common/find/styles/email.scss +55 -0
  17. package/src/auth/common/find/styles/find-account.scss +4 -0
  18. package/src/auth/common/find/styles/layout.scss +19 -0
  19. package/src/auth/common/find/styles/password.scss +39 -0
  20. package/src/auth/common/find/styles/result.scss +78 -0
  21. package/src/auth/common/find/types/forms.ts +30 -0
  22. package/src/auth/common/find/types/index.ts +121 -0
  23. package/src/auth/common/find/utils/composeFieldProps.ts +45 -0
  24. package/src/auth/common/password/constants.ts +19 -0
  25. package/src/auth/common/password/hooks/useCheckPassword.ts +133 -0
  26. package/src/auth/common/password/img/check-password.svg +3 -0
  27. package/src/auth/common/password/markup/PasswordSetField.tsx +250 -0
  28. package/src/auth/common/password/styles/password-set-field.scss +49 -0
  29. package/src/auth/common/password/types.ts +142 -0
  30. package/src/auth/common/password/utils/composePasswordFieldProps.ts +44 -0
  31. package/src/auth/find-account.ts +28 -0
  32. package/src/auth/find-id/hooks/index.ts +1 -0
  33. package/src/auth/find-id/index.scss +1 -0
  34. package/src/auth/find-id/index.ts +23 -0
  35. package/src/auth/find-id/markup/StepComplete.tsx +58 -0
  36. package/src/auth/find-id/markup/StepIdentify.tsx +46 -0
  37. package/src/auth/find-id/markup/StepVerifyCode.tsx +48 -0
  38. package/src/auth/find-id/types/index.ts +66 -0
  39. package/src/auth/find-password/index.scss +1 -0
  40. package/src/auth/find-password/index.ts +30 -0
  41. package/src/auth/find-password/markup/StepComplete.tsx +30 -0
  42. package/src/auth/find-password/markup/StepIdentify.tsx +45 -0
  43. package/src/auth/find-password/markup/StepResetPassword.tsx +150 -0
  44. package/src/auth/find-password/markup/StepVerifyCode.tsx +48 -0
  45. package/src/auth/index.tsx +41 -0
  46. package/src/{components/auth → auth}/login/markup/Container.tsx +1 -1
  47. package/src/{components/auth → auth}/login/types/props.ts +1 -1
  48. package/src/{components/auth → auth}/signup/hooks/useSignupAccountForm.ts +26 -2
  49. package/src/{components/auth → auth}/signup/hooks/useSignupUserInfoForm.ts +10 -3
  50. package/src/auth/signup/img/check-agree.svg +3 -0
  51. package/src/auth/signup/img/chevron-open-detail.svg +3 -0
  52. package/src/{components/auth → auth}/signup/index.ts +3 -0
  53. package/src/auth/signup/markup/AccountForm.tsx +113 -0
  54. package/src/auth/signup/markup/Complete.tsx +59 -0
  55. package/src/auth/signup/markup/Template.tsx +110 -0
  56. package/src/{components/auth → auth}/signup/markup/UserInfoForm.tsx +23 -13
  57. package/src/auth/signup/markup/VerificationForm.tsx +285 -0
  58. package/src/{components/auth → auth}/signup/markup/index.ts +1 -0
  59. package/src/auth/signup/styles/signup.scss +187 -0
  60. package/src/{components/auth → auth}/signup/types/hooks.ts +1 -0
  61. package/src/{components/auth → auth}/signup/types/props.ts +49 -9
  62. package/src/auth/signup/utils/getSignupFieldDefaultValue.ts +40 -0
  63. package/src/index.scss +5 -4
  64. package/src/index.tsx +3 -3
  65. package/src/page-frame/mobile/header/PageFrameMobileHeader.tsx +52 -0
  66. package/src/page-frame/mobile/header/index.ts +4 -0
  67. package/src/page-frame/mobile/header/page-frame-mobile-header.scss +48 -0
  68. package/src/page-frame/mobile/img/chevron-backward.svg +3 -0
  69. package/src/components/auth/index.tsx +0 -20
  70. package/src/components/auth/signup/markup/AccountForm.tsx +0 -124
  71. package/src/components/auth/signup/markup/Complete.tsx +0 -61
  72. package/src/components/auth/signup/markup/VerificationForm.tsx +0 -155
  73. package/src/components/auth/signup/styles/signup.scss +0 -135
  74. /package/src/{components/auth → auth/common}/container/AuthContainer.tsx +0 -0
  75. /package/src/{components/auth → auth/common}/container/index.scss +0 -0
  76. /package/src/{components/auth → auth/common}/container/types.ts +0 -0
  77. /package/src/{components/auth → auth}/login/data/valid-options.ts +0 -0
  78. /package/src/{components/auth → auth}/login/hooks/index.ts +0 -0
  79. /package/src/{components/auth → auth}/login/hooks/useAuthLoginForm.ts +0 -0
  80. /package/src/{components/auth → auth}/login/index.scss +0 -0
  81. /package/src/{components/auth → auth}/login/index.tsx +0 -0
  82. /package/src/{components/auth → auth}/login/markup/FormField.tsx +0 -0
  83. /package/src/{components/auth → auth}/login/markup/LinkButtons.tsx +0 -0
  84. /package/src/{components/auth → auth}/login/styles/login.scss +0 -0
  85. /package/src/{components/auth → auth}/login/types/form.ts +0 -0
  86. /package/src/{components/auth → auth}/login/types/hooks.ts +0 -0
  87. /package/src/{components/auth → auth}/login/types.ts +0 -0
  88. /package/src/{components/auth → auth}/signup/hooks/index.ts +0 -0
  89. /package/src/{components/auth → auth}/signup/hooks/useSignupVerificationForm.ts +0 -0
  90. /package/src/{components/auth → auth}/signup/types/index.ts +0 -0
  91. /package/src/{components/auth → auth}/signup/utils/composeFieldProps.ts +0 -0
  92. /package/src/{components/modal → modal}/core/components/Container.tsx +0 -0
  93. /package/src/{components/modal → modal}/core/components/FooterButtons.tsx +0 -0
  94. /package/src/{components/modal → modal}/core/components/Provider.tsx +0 -0
  95. /package/src/{components/modal → modal}/core/components/Root.tsx +0 -0
  96. /package/src/{components/modal → modal}/core/hooks/useModal.ts +0 -0
  97. /package/src/{components/modal → modal}/core/jotai/atoms.ts +0 -0
  98. /package/src/{components/modal → modal}/index.scss +0 -0
  99. /package/src/{components/modal → modal}/index.tsx +0 -0
  100. /package/src/{components/modal → modal}/styles/animations.scss +0 -0
  101. /package/src/{components/modal → modal}/styles/base.scss +0 -0
  102. /package/src/{components/modal → modal}/styles/container.scss +0 -0
  103. /package/src/{components/modal → modal}/styles/dimmer.scss +0 -0
  104. /package/src/{components/modal → modal}/templates/Alert.tsx +0 -0
  105. /package/src/{components/modal → modal}/templates/Dialog.tsx +0 -0
  106. /package/src/{components/modal → modal}/types/footer.ts +0 -0
  107. /package/src/{components/modal → modal}/types/index.ts +0 -0
  108. /package/src/{components/modal → modal}/types/options.ts +0 -0
  109. /package/src/{components/modal → modal}/types/state.ts +0 -0
  110. /package/src/{components/modal → modal}/types/templates.ts +0 -0
  111. /package/src/{components/page-frame → page-frame}/container/PageFrameContainer.tsx +0 -0
  112. /package/src/{components/page-frame → page-frame}/container/index.scss +0 -0
  113. /package/src/{components/page-frame → page-frame}/container/index.tsx +0 -0
  114. /package/src/{components/page-frame → page-frame}/container/types.ts +0 -0
  115. /package/src/{components/page-frame → page-frame}/index.tsx +0 -0
  116. /package/src/{components/page-frame → page-frame}/mobile/PageFrameMobile.tsx +0 -0
  117. /package/src/{components/page-frame → page-frame}/mobile/index.scss +0 -0
  118. /package/src/{components/page-frame → page-frame}/mobile/index.tsx +0 -0
  119. /package/src/{components/page-frame → page-frame}/mobile/types.ts +0 -0
  120. /package/src/{components/page-frame → page-frame}/navigation/PageFrameNavigation.tsx +0 -0
  121. /package/src/{components/page-frame → page-frame}/navigation/index.scss +0 -0
  122. /package/src/{components/page-frame → page-frame}/navigation/index.tsx +0 -0
@@ -0,0 +1,66 @@
1
+ import type React from "react";
2
+ import type { AuthContainerProps } from "../../common/container";
3
+ import type { SubmitHandler } from "react-hook-form";
4
+ import type {
5
+ InputFieldProps,
6
+ InputPasswordProps,
7
+ } from "@uniai-fe/uds-primitives";
8
+ import type { FindAccountCTAProps } from "../../common/find/types";
9
+ import type { AuthPasswordRule } from "../../common/password/types";
10
+
11
+ export type * from "../../common/find/types";
12
+
13
+ export type FindAccountIdSummaryProps = {
14
+ label?: React.ReactNode;
15
+ userId: React.ReactNode;
16
+ userIdLabel?: React.ReactNode;
17
+ registeredAt?: React.ReactNode;
18
+ registeredAtLabel?: React.ReactNode;
19
+ helper?: React.ReactNode;
20
+ };
21
+
22
+ export type FindAccountIdCompleteProps = Omit<
23
+ AuthContainerProps,
24
+ "children"
25
+ > & {
26
+ title?: React.ReactNode;
27
+ summary: FindAccountIdSummaryProps;
28
+ description?: React.ReactNode;
29
+ cta?: FindAccountCTAProps;
30
+ };
31
+
32
+ export type FindAccountPasswordValues = Record<string, string>;
33
+
34
+ export type FindAccountPasswordFields = {
35
+ password: InputFieldProps<InputPasswordProps>;
36
+ confirm: InputFieldProps<InputPasswordProps>;
37
+ };
38
+
39
+ export type FindAccountPasswordFieldOptions<
40
+ TFields extends FindAccountPasswordFields = FindAccountPasswordFields,
41
+ > = {
42
+ fields: TFields;
43
+ formAttr?: React.FormHTMLAttributes<HTMLFormElement>;
44
+ onSubmit: SubmitHandler<FindAccountPasswordValues>;
45
+ isSubmittable?: (values?: FindAccountPasswordValues) => boolean;
46
+ };
47
+
48
+ export type FindAccountPasswordRule = AuthPasswordRule;
49
+
50
+ export type FindAccountPasswordFormProps<
51
+ TFields extends FindAccountPasswordFields = FindAccountPasswordFields,
52
+ > = Omit<AuthContainerProps, "children"> & {
53
+ fieldOptions: FindAccountPasswordFieldOptions<TFields>;
54
+ rules?: FindAccountPasswordRule[];
55
+ helper?: React.ReactNode;
56
+ cta?: FindAccountCTAProps;
57
+ };
58
+
59
+ export type FindAccountPasswordCompleteProps = Omit<
60
+ AuthContainerProps,
61
+ "children"
62
+ > & {
63
+ title?: React.ReactNode;
64
+ description?: React.ReactNode;
65
+ cta?: FindAccountCTAProps;
66
+ };
@@ -0,0 +1 @@
1
+ @use "../common/find/styles/find-account.scss";
@@ -0,0 +1,30 @@
1
+ import "./index.scss";
2
+ import FindPasswordStepIdentify from "./markup/StepIdentify";
3
+ import FindPasswordStepVerifyCode from "./markup/StepVerifyCode";
4
+ import FindPasswordStepReset from "./markup/StepResetPassword";
5
+ import FindPasswordStepComplete from "./markup/StepComplete";
6
+
7
+ export type {
8
+ FindAccountPasswordFormProps,
9
+ FindAccountPasswordValues,
10
+ FindAccountPasswordCompleteProps,
11
+ } from "../find-id/types";
12
+
13
+ export const FindAccountPasswordForm = FindPasswordStepReset;
14
+ export const FindAccountPasswordComplete = FindPasswordStepComplete;
15
+
16
+ export {
17
+ FindPasswordStepIdentify,
18
+ FindPasswordStepVerifyCode,
19
+ FindPasswordStepReset,
20
+ FindPasswordStepComplete,
21
+ };
22
+
23
+ export const FindPassword = {
24
+ StepIdentify: FindPasswordStepIdentify,
25
+ StepVerifyCode: FindPasswordStepVerifyCode,
26
+ StepReset: FindPasswordStepReset,
27
+ StepComplete: FindPasswordStepComplete,
28
+ Form: FindAccountPasswordForm,
29
+ Complete: FindAccountPasswordComplete,
30
+ };
@@ -0,0 +1,30 @@
1
+ import clsx from "clsx";
2
+ import type { FindAccountPasswordCompleteProps } from "../../find-id/types";
3
+ import AuthCompleteTemplate from "../../common/complete/Template";
4
+
5
+ const DEFAULT_TITLE = "비밀번호 변경 완료";
6
+ const DEFAULT_DESCRIPTION = "로그인 페이지로 이동하여 로그인해 주세요.";
7
+ const DEFAULT_CTA_LABEL = "로그인하기";
8
+
9
+ /**
10
+ * 비밀번호 변경 완료 화면
11
+ * @component
12
+ */
13
+ export default function FindPasswordStepComplete({
14
+ className,
15
+ title,
16
+ description,
17
+ cta,
18
+ }: FindAccountPasswordCompleteProps) {
19
+ return (
20
+ <AuthCompleteTemplate
21
+ className={clsx("auth-find-account-container", className)}
22
+ title={title ?? DEFAULT_TITLE}
23
+ description={description ?? DEFAULT_DESCRIPTION}
24
+ cta={{
25
+ label: cta?.label ?? DEFAULT_CTA_LABEL,
26
+ buttonProps: cta?.buttonProps,
27
+ }}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,45 @@
1
+ import FindAccountInfoStep from "../../common/find/markup/InfoStep";
2
+ import type { FindAccountInfoStepProps } from "../../common/find/types";
3
+
4
+ const DEFAULT_NAVIGATION = {
5
+ title: "비밀번호 찾기",
6
+ };
7
+
8
+ const DEFAULT_HEADLINE = {
9
+ progress: { total: 3, current: 1 },
10
+ title: (
11
+ <>
12
+ 가입한 이름과 메일 주소를
13
+ <br />
14
+ 입력해 주세요
15
+ </>
16
+ ),
17
+ };
18
+
19
+ const DEFAULT_CTA_LABEL = "인증코드 요청";
20
+
21
+ export type FindPasswordStepIdentifyProps = FindAccountInfoStepProps;
22
+
23
+ /**
24
+ * Find Password Step1 — 이름/메일 입력
25
+ * @component
26
+ */
27
+ export default function FindPasswordStepIdentify({
28
+ navigation,
29
+ headline,
30
+ cta,
31
+ ...rest
32
+ }: FindPasswordStepIdentifyProps) {
33
+ const resolvedNavigation = navigation ?? DEFAULT_NAVIGATION;
34
+ const resolvedHeadline = headline ?? DEFAULT_HEADLINE;
35
+ const resolvedCTA = cta ?? { label: DEFAULT_CTA_LABEL };
36
+
37
+ return (
38
+ <FindAccountInfoStep
39
+ {...rest}
40
+ navigation={resolvedNavigation}
41
+ headline={resolvedHeadline}
42
+ cta={resolvedCTA}
43
+ />
44
+ );
45
+ }
@@ -0,0 +1,150 @@
1
+ import clsx from "clsx";
2
+ import { useMemo } from "react";
3
+ import { FormProvider, useForm } from "react-hook-form";
4
+ import { AuthContainer } from "../../common/container";
5
+ import { AuthStageHeader } from "../../common/container/header";
6
+ import { Button, type InputPasswordProps } from "@uniai-fe/uds-primitives";
7
+ import AuthPasswordSetField from "../../common/password/markup/PasswordSetField";
8
+ import { DEFAULT_PASSWORD_RULES } from "../../common/password/constants";
9
+ import type {
10
+ FindAccountPasswordFormProps,
11
+ FindAccountPasswordValues,
12
+ } from "../../find-id/types";
13
+
14
+ const DEFAULT_CTA_LABEL = "비밀번호 변경";
15
+ const DEFAULT_HEADER = (
16
+ <AuthStageHeader
17
+ className="auth-find-account-header"
18
+ navigationTitle="비밀번호 찾기"
19
+ headline={
20
+ <>
21
+ 새로운 비밀번호를
22
+ <br />
23
+ 입력해 주세요
24
+ </>
25
+ }
26
+ indicator={{ total: 3, current: 3 }}
27
+ />
28
+ );
29
+
30
+ /**
31
+ * 새 비밀번호 입력 화면
32
+ * @component
33
+ */
34
+ export default function FindPasswordStepReset({
35
+ className,
36
+ header,
37
+ footer,
38
+ fieldOptions,
39
+ rules,
40
+ helper,
41
+ cta,
42
+ }: FindAccountPasswordFormProps) {
43
+ const { fields, formAttr, onSubmit, isSubmittable } = fieldOptions;
44
+
45
+ // 1) 필드 정의를 순회해 RHF 초기값을 만든다.
46
+ const defaultValues = useMemo(
47
+ () =>
48
+ (Object.keys(fields) as Array<keyof typeof fields>).reduce((acc, key) => {
49
+ const config = fields[key];
50
+ const fieldName = config.attr?.name ?? String(key);
51
+ acc[fieldName] = "";
52
+ return acc;
53
+ }, {} as FindAccountPasswordValues),
54
+ [fields],
55
+ );
56
+
57
+ // 2) RHF form 인스턴스를 초기화한다.
58
+ const form = useForm<FindAccountPasswordValues>({
59
+ mode: "onChange",
60
+ defaultValues,
61
+ });
62
+
63
+ const watchedValues = form.watch();
64
+ const normalizedValues: FindAccountPasswordValues = {
65
+ ...defaultValues,
66
+ ...watchedValues,
67
+ };
68
+ const baseFilled =
69
+ normalizedValues &&
70
+ Object.values(normalizedValues).every(value =>
71
+ typeof value === "string" ? value.trim().length > 0 : Boolean(value),
72
+ );
73
+ const resolvedFilled = isSubmittable
74
+ ? isSubmittable(normalizedValues)
75
+ : Boolean(baseFilled);
76
+ const disabled = form.formState.isSubmitting || !resolvedFilled;
77
+ const handleSubmit = form.handleSubmit(onSubmit);
78
+
79
+ const buttonProps = {
80
+ type: "submit" as const,
81
+ scale: "solid-xlarge" as const,
82
+ priority: "primary" as const,
83
+ block: true,
84
+ ...cta?.buttonProps,
85
+ disabled: cta?.buttonProps?.disabled ?? disabled,
86
+ };
87
+ const passwordFieldConfig = useMemo(() => {
88
+ const priority = (fields.password.props?.priority ??
89
+ "secondary") as InputPasswordProps["priority"];
90
+ const size = (fields.password.props?.size ??
91
+ "large") as InputPasswordProps["size"];
92
+ return {
93
+ ...fields.password,
94
+ props: {
95
+ ...fields.password.props,
96
+ priority,
97
+ size,
98
+ },
99
+ };
100
+ }, [fields.password]);
101
+ const confirmFieldConfig = useMemo(() => {
102
+ const priority = (fields.confirm.props?.priority ??
103
+ "secondary") as InputPasswordProps["priority"];
104
+ const size = (fields.confirm.props?.size ??
105
+ "large") as InputPasswordProps["size"];
106
+ return {
107
+ ...fields.confirm,
108
+ props: {
109
+ ...fields.confirm.props,
110
+ priority,
111
+ size,
112
+ },
113
+ };
114
+ }, [fields.confirm]);
115
+
116
+ const resolvedPasswordRules =
117
+ rules && rules.length > 0 ? rules : DEFAULT_PASSWORD_RULES;
118
+
119
+ return (
120
+ <AuthContainer
121
+ className={clsx("auth-find-account-container", className)}
122
+ header={header ?? DEFAULT_HEADER}
123
+ footer={footer}
124
+ >
125
+ <FormProvider {...form}>
126
+ <form
127
+ className="auth-find-account-form auth-find-account-form--password"
128
+ {...formAttr}
129
+ onSubmit={handleSubmit}
130
+ >
131
+ <AuthPasswordSetField
132
+ passwordField={passwordFieldConfig}
133
+ confirmPasswordField={confirmFieldConfig}
134
+ passwordRules={resolvedPasswordRules}
135
+ messages={{
136
+ missing: "새 비밀번호를 먼저 입력해 주세요.",
137
+ mismatch: "비밀번호가 일치하지 않습니다.",
138
+ }}
139
+ />
140
+ {helper ? (
141
+ <p className="auth-find-account-password-helper">{helper}</p>
142
+ ) : null}
143
+ <Button.Default {...buttonProps}>
144
+ {cta?.label ?? DEFAULT_CTA_LABEL}
145
+ </Button.Default>
146
+ </form>
147
+ </FormProvider>
148
+ </AuthContainer>
149
+ );
150
+ }
@@ -0,0 +1,48 @@
1
+ import FindAccountCodeStep from "../../common/find/markup/CodeStep";
2
+ import type { FindAccountCodeStepProps } from "../../common/find/types";
3
+
4
+ const DEFAULT_NAVIGATION = {
5
+ title: "비밀번호 찾기",
6
+ };
7
+
8
+ const DEFAULT_HEADLINE = {
9
+ progress: { total: 3, current: 2 },
10
+ title: (
11
+ <>
12
+ 메일 주소로
13
+ <br />
14
+ 인증을 진행해 주세요
15
+ </>
16
+ ),
17
+ };
18
+
19
+ const DEFAULT_EMAIL_LABEL = "메일";
20
+
21
+ export type FindPasswordStepVerifyCodeProps = FindAccountCodeStepProps;
22
+
23
+ /**
24
+ * Find Password Step2 — 인증코드 입력
25
+ * @component
26
+ */
27
+ export default function FindPasswordStepVerifyCode({
28
+ navigation,
29
+ headline,
30
+ emailDisplay,
31
+ ...rest
32
+ }: FindPasswordStepVerifyCodeProps) {
33
+ const resolvedNavigation = navigation ?? DEFAULT_NAVIGATION;
34
+ const resolvedHeadline = headline ?? DEFAULT_HEADLINE;
35
+ const resolvedEmailDisplay = {
36
+ label: DEFAULT_EMAIL_LABEL,
37
+ ...emailDisplay,
38
+ };
39
+
40
+ return (
41
+ <FindAccountCodeStep
42
+ {...rest}
43
+ navigation={resolvedNavigation}
44
+ headline={resolvedHeadline}
45
+ emailDisplay={resolvedEmailDisplay}
46
+ />
47
+ );
48
+ }
@@ -0,0 +1,41 @@
1
+ import "./login/index.scss";
2
+
3
+ import { AuthContainer } from "./common/container";
4
+ import AuthCompleteTemplate from "./common/complete/Template";
5
+ import AuthPasswordSetField from "./common/password/markup/PasswordSetField";
6
+ import { AuthLogin } from "./login";
7
+ import { AuthSignup } from "./signup";
8
+ import { FindAccount } from "./find-account";
9
+ import { FindId } from "./find-id";
10
+ import { FindPassword } from "./find-password";
11
+
12
+ export const Auth = {
13
+ Container: AuthContainer,
14
+ Common: {
15
+ Container: AuthContainer,
16
+ Complete: AuthCompleteTemplate,
17
+ PasswordSetField: AuthPasswordSetField,
18
+ },
19
+ Login: AuthLogin,
20
+ Signup: AuthSignup,
21
+ FindAccount,
22
+ FindId,
23
+ FindPassword,
24
+ };
25
+
26
+ export {
27
+ useSignupUserInfoForm,
28
+ useSignupVerificationForm,
29
+ useSignupAccountForm,
30
+ } from "./signup";
31
+
32
+ export { useCheckPassword } from "./common/password/hooks/useCheckPassword";
33
+ export { DEFAULT_PASSWORD_RULES } from "./common/password/constants";
34
+ export { FindAccount, useFindAccountForm } from "./find-account";
35
+ export * from "./find-id";
36
+ export * from "./find-password";
37
+ export { default as AuthCompleteTemplate } from "./common/complete/Template";
38
+ export type * from "./common/password/types";
39
+ export type * from "./login";
40
+ export type * from "./signup";
41
+ export type * from "./find-account";
@@ -1,5 +1,5 @@
1
1
  import clsx from "clsx";
2
- import { AuthContainer } from "../../container";
2
+ import { AuthContainer } from "../../common/container";
3
3
  import AuthLoginFormField from "./FormField";
4
4
  import AuthLoginLinkButtons from "./LinkButtons";
5
5
  import type { AuthLoginProps } from "../types";
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import type { SubmitHandler } from "react-hook-form";
3
- import type { AuthContainerProps } from "../../container";
3
+ import type { AuthContainerProps } from "../../common/container";
4
4
  import type {
5
5
  InputFieldProps,
6
6
  InputPasswordProps,
@@ -29,23 +29,45 @@ export function useSignupAccountForm<
29
29
  | AuthSignupAccountValues
30
30
  | undefined;
31
31
 
32
+ const passwordFieldName = useMemo(() => {
33
+ const config = (fields as Partial<AuthSignupAccountFields>).password;
34
+ return (
35
+ (config?.attr?.name as keyof AuthSignupAccountValues | undefined) ??
36
+ ("password" as keyof AuthSignupAccountValues)
37
+ );
38
+ }, [fields]);
39
+
40
+ const confirmFieldName = useMemo(() => {
41
+ const config = (fields as Partial<AuthSignupAccountFields>).confirmPassword;
42
+ return (
43
+ (config?.attr?.name as keyof AuthSignupAccountValues | undefined) ??
44
+ ("confirmPassword" as keyof AuthSignupAccountValues)
45
+ );
46
+ }, [fields]);
47
+
32
48
  const register = useMemo(() => {
33
49
  return (Object.keys(fields) as Array<keyof TFields>).reduce(
34
50
  (acc, fieldKey) => {
35
51
  const config = fields[fieldKey] as AuthSignupFieldProps;
36
52
  const fieldName = config.attr?.name ?? String(fieldKey);
53
+ if (fieldName === passwordFieldName || fieldName === confirmFieldName) {
54
+ return acc;
55
+ }
37
56
  acc[fieldKey] = form.register(fieldName);
38
57
  return acc;
39
58
  },
40
59
  {} as UseSignupAccountFormReturn<TFields>["register"],
41
60
  );
42
- }, [fields, form]);
61
+ }, [confirmFieldName, fields, form, passwordFieldName]);
43
62
 
44
63
  const helpers = useMemo(() => {
45
64
  return (Object.keys(fields) as Array<keyof TFields>).reduce(
46
65
  (acc, fieldKey) => {
47
66
  const config = fields[fieldKey] as AuthSignupFieldProps;
48
67
  const fieldName = config.attr?.name ?? String(fieldKey);
68
+ if (fieldName === passwordFieldName || fieldName === confirmFieldName) {
69
+ return acc;
70
+ }
49
71
  const state = form.getFieldState(fieldName);
50
72
  acc[fieldKey] = {
51
73
  text:
@@ -56,7 +78,7 @@ export function useSignupAccountForm<
56
78
  },
57
79
  {} as UseSignupAccountFormReturn<TFields>["helpers"],
58
80
  );
59
- }, [fields, form]);
81
+ }, [confirmFieldName, fields, form, passwordFieldName]);
60
82
 
61
83
  const trimmedFilled =
62
84
  values &&
@@ -75,3 +97,5 @@ export function useSignupAccountForm<
75
97
  onSubmit: onSubmitHandler,
76
98
  };
77
99
  }
100
+
101
+ export default useSignupAccountForm;
@@ -28,6 +28,7 @@ export function useSignupUserInfoForm<
28
28
  fields,
29
29
  form,
30
30
  onSubmit,
31
+ activeFields,
31
32
  }: UseSignupUserInfoFormOptions<TFields>): UseSignupUserInfoFormReturn<TFields> {
32
33
  const values = useWatch({ control: form.control }) as
33
34
  | AuthSignupUserInfoValues
@@ -62,11 +63,17 @@ export function useSignupUserInfoForm<
62
63
  );
63
64
  }, [fields, form]);
64
65
 
66
+ const targetKeys =
67
+ activeFields ?? (Object.keys(fields) as Array<keyof TFields>);
68
+
65
69
  const trimmedFilled =
66
70
  values &&
67
- Object.values(values).every(value =>
68
- typeof value === "string" ? value.trim().length > 0 : false,
69
- );
71
+ targetKeys.every(fieldKey => {
72
+ const fieldName = (fields[fieldKey].attr?.name ??
73
+ String(fieldKey)) as keyof AuthSignupUserInfoValues;
74
+ const value = values[fieldName];
75
+ return typeof value === "string" ? value.trim().length > 0 : false;
76
+ });
70
77
 
71
78
  const disabled = form.formState.isSubmitting || !trimmedFilled;
72
79
 
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5 9.36395L8.53553 12.8995L14.8995 6.53552" stroke="#94989E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -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="M5.87377 3.74906L10.1164 7.9917L5.87377 12.2343C5.56135 12.5468 5.56135 13.0533 5.87377 13.3657C6.18619 13.6781 6.69272 13.6781 7.00514 13.3657L11.8135 8.55739C12.1259 8.24497 12.1259 7.73843 11.8135 7.42601L7.00514 2.61769C6.69272 2.30527 6.18619 2.30527 5.87377 2.61769C5.56135 2.93011 5.56135 3.43664 5.87377 3.74906Z" fill="#CACBCE"/>
3
+ </svg>
@@ -3,6 +3,7 @@ import {
3
3
  AuthSignupVerificationForm,
4
4
  AuthSignupAccountForm,
5
5
  AuthSignupComplete,
6
+ AuthSignupTemplate,
6
7
  } from "./markup";
7
8
 
8
9
  import "./styles/signup.scss";
@@ -14,9 +15,11 @@ export {
14
15
  AuthSignupVerificationForm,
15
16
  AuthSignupAccountForm,
16
17
  AuthSignupComplete,
18
+ AuthSignupTemplate,
17
19
  };
18
20
 
19
21
  export const AuthSignup = {
22
+ Template: AuthSignupTemplate,
20
23
  StepUserInfo: AuthSignupUserInfoForm,
21
24
  StepVerification: AuthSignupVerificationForm,
22
25
  StepAccount: AuthSignupAccountForm,
@@ -0,0 +1,113 @@
1
+ import { useMemo } from "react";
2
+ import { FormProvider, useForm } from "react-hook-form";
3
+ import { Button, Input, type InputProps } from "@uniai-fe/uds-primitives";
4
+ import type { AuthSignupAccountProps, AuthSignupAccountValues } from "../types";
5
+ import { useSignupAccountForm } from "../hooks";
6
+ import { composeSignupFieldProps } from "../utils/composeFieldProps";
7
+ import { getSignupFieldDefaultValue } from "../utils/getSignupFieldDefaultValue";
8
+ import AuthPasswordSetField from "../../common/password/markup/PasswordSetField";
9
+ import { DEFAULT_PASSWORD_RULES } from "../../common/password/constants";
10
+
11
+ const DEFAULT_SUBMIT_LABEL = "가입 요청";
12
+ /**
13
+ * 회원가입 Step3; 계정 정보 입력(아이디/비밀번호)
14
+ * @component
15
+ * @param {AuthSignupAccountProps} props account props
16
+ * @param {AuthSignupAccountProps["fields"]} props.fields 아이디/비밀번호 필드
17
+ * @param {AuthSignupPasswordRule[]} [props.passwordRules] 비밀번호 규칙 상태
18
+ * @param {React.FormHTMLAttributes<HTMLFormElement>} [props.formAttr] form attr
19
+ * @param {import("react").ReactNode} [props.submitLabel] CTA 라벨
20
+ */
21
+ export function AuthSignupAccountForm({
22
+ fields,
23
+ passwordRules,
24
+ formAttr,
25
+ submitLabel,
26
+ onSubmit,
27
+ }: AuthSignupAccountProps) {
28
+ // 필드 구성에서 name attr을 추출해 RHF default 값을 정의한다.
29
+ const defaultValues = useMemo(
30
+ () =>
31
+ (Object.keys(fields) as Array<keyof typeof fields>).reduce(
32
+ (acc, fieldKey) => {
33
+ const config = fields[fieldKey];
34
+ const fieldName = config.attr?.name ?? String(fieldKey);
35
+ acc[fieldName] = getSignupFieldDefaultValue(config);
36
+ return acc;
37
+ },
38
+ {} as AuthSignupAccountValues,
39
+ ),
40
+ [fields],
41
+ );
42
+
43
+ const form = useForm<AuthSignupAccountValues>({
44
+ mode: "onChange",
45
+ defaultValues,
46
+ });
47
+
48
+ const {
49
+ register,
50
+ helpers,
51
+ disabled,
52
+ onSubmit: handleSubmit,
53
+ } = useSignupAccountForm({
54
+ fields,
55
+ form,
56
+ onSubmit,
57
+ });
58
+
59
+ const passwordFieldName =
60
+ (fields.password.attr?.name as keyof AuthSignupAccountValues | undefined) ??
61
+ ("password" as keyof AuthSignupAccountValues);
62
+ const confirmFieldName =
63
+ (fields.confirmPassword.attr?.name as
64
+ | keyof AuthSignupAccountValues
65
+ | undefined) ?? ("confirmPassword" as keyof AuthSignupAccountValues);
66
+
67
+ const resolvedPasswordRules =
68
+ passwordRules && passwordRules.length > 0
69
+ ? passwordRules
70
+ : DEFAULT_PASSWORD_RULES;
71
+
72
+ return (
73
+ <FormProvider {...form}>
74
+ <form
75
+ className="auth-signup-form auth-signup-form--account"
76
+ {...formAttr}
77
+ onSubmit={handleSubmit}
78
+ >
79
+ <div className="auth-signup-fields">
80
+ <Input
81
+ {...composeSignupFieldProps<InputProps>(
82
+ fields.accountId,
83
+ helpers.accountId,
84
+ )}
85
+ register={register.accountId}
86
+ />
87
+ <AuthPasswordSetField
88
+ passwordField={fields.password}
89
+ confirmPasswordField={fields.confirmPassword}
90
+ passwordFieldName={passwordFieldName}
91
+ confirmPasswordFieldName={confirmFieldName}
92
+ passwordRules={resolvedPasswordRules}
93
+ messages={{
94
+ missing: "비밀번호를 먼저 입력해 주세요.",
95
+ mismatch: "비밀번호 불일치",
96
+ }}
97
+ />
98
+ </div>
99
+ <Button.Default
100
+ type="submit"
101
+ scale="solid-xlarge"
102
+ priority="primary"
103
+ block
104
+ disabled={disabled}
105
+ >
106
+ {submitLabel ?? DEFAULT_SUBMIT_LABEL}
107
+ </Button.Default>
108
+ </form>
109
+ </FormProvider>
110
+ );
111
+ }
112
+
113
+ export default AuthSignupAccountForm;