Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions src/features/signup-user-flow/model/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
ADMIN_COLLEGE_OPTIONS,
ADMIN_DEPARTMENT_OPTIONS,
} from "./data/adminOptions";
import { PARTNER_ADDRESS_OPTIONS } from "./data/partnerAddressOptions";
import type { SignupAdminFormState } from "./types";

export function findAdminCollegeOption(value: string | null) {
if (!value) {
return null;
}

return ADMIN_COLLEGE_OPTIONS.find((item) => item.value === value) ?? null;
}

export function findAdminDepartmentOption(value: string | null) {
if (!value) {
return null;
}

return ADMIN_DEPARTMENT_OPTIONS.find((item) => item.value === value) ?? null;
}

export function shouldShowAdminOfficeAddress(admin: SignupAdminFormState) {
return shouldShowAdminOfficeAddressBySelection(
admin.organizationType,
admin.collegeId,
admin.departmentId,
);
}

export function shouldShowAdminOfficeAddressBySelection(
organizationType: SignupAdminFormState["organizationType"],
collegeId: SignupAdminFormState["collegeId"],
departmentId: SignupAdminFormState["departmentId"],
) {
if (organizationType === "GENERAL_STUDENT_COUNCIL") {
return true;
}

if (organizationType === "COLLEGE_STUDENT_COUNCIL") {
return Boolean(collegeId);
}

if (organizationType === "DEPARTMENT_STUDENT_COUNCIL") {
return Boolean(collegeId && departmentId);
}

return false;
}

export function isAdminOrganizationInfoComplete(admin: SignupAdminFormState) {
if (!admin.organizationType || !shouldShowAdminOfficeAddress(admin)) {
return false;
}

if (admin.organizationType === "COLLEGE_STUDENT_COUNCIL") {
return (
Boolean(admin.collegeId) &&
Boolean(admin.officeAddressId) &&
admin.officeAddressDetail.length > 0
);
}

if (admin.organizationType === "DEPARTMENT_STUDENT_COUNCIL") {
return (
Boolean(admin.collegeId) &&
Boolean(admin.departmentId) &&
Boolean(admin.officeAddressId) &&
admin.officeAddressDetail.length > 0
);
}

return Boolean(admin.officeAddressId) && admin.officeAddressDetail.length > 0;
}

export function getAdminCompletionName(admin: SignupAdminFormState) {
if (admin.organizationType === "GENERAL_STUDENT_COUNCIL") {
return "숭실대학교 총학생회";
}

if (admin.organizationType === "COLLEGE_STUDENT_COUNCIL") {
const college = findAdminCollegeOption(admin.collegeId);
return college?.label ? `${college.label} 학생회` : "";
}

if (admin.organizationType === "DEPARTMENT_STUDENT_COUNCIL") {
const department = findAdminDepartmentOption(admin.departmentId);
return department?.label ? `${department.label} 학생회` : "";
}

return "";
}

export function findAddressOption(value: string | null) {
if (!value) {
return null;
}

return PARTNER_ADDRESS_OPTIONS.find((item) => item.id === value) ?? null;
}
Comment on lines +95 to +101
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The findAddressOption function in admin.ts currently uses PARTNER_ADDRESS_OPTIONS. If admin office addresses are distinct from partner addresses, this could lead to incorrect data being used for admin-related address searches. Please ensure that the correct data source is used for admin addresses.

12 changes: 12 additions & 0 deletions src/features/signup-user-flow/model/agreements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { SignupAgreementState } from "@/entities/signup";

export function getNextAgreementState(
current: SignupAgreementState,
partial: Partial<SignupAgreementState>,
): SignupAgreementState {
const next = { ...current, ...partial };
return {
...next,
agreeAll: next.agreePrivacy && next.agreeMarketing,
};
}
18 changes: 6 additions & 12 deletions src/features/signup-user-flow/model/constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import type { SignupStep } from "./types";

export const VERIFICATION_SUCCESS_CODE = "0502";

export const SIGNUP_PROGRESS_STEPS: SignupStep[] = [
"identity",
"role",
"school",
"studentInput1",
"studentInput2",
"studentInput3",
];
export {
getSignupFlowConfig,
getSignupFlowVariant,
SIGNUP_FLOW_CONFIG,
VERIFICATION_SUCCESS_CODE,
} from "./flowConfig";
24 changes: 24 additions & 0 deletions src/features/signup-user-flow/model/data/adminOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { SelectItem } from "@/shared/ui/select";

export const ADMIN_ORGANIZATION_TYPE_OPTIONS: SelectItem[] = [
{ label: "총학생회", value: "GENERAL_STUDENT_COUNCIL" },
{ label: "단과대학 학생회", value: "COLLEGE_STUDENT_COUNCIL" },
{ label: "학과/부 학생회", value: "DEPARTMENT_STUDENT_COUNCIL" },
];

export const ADMIN_COLLEGE_OPTIONS: SelectItem[] = [
{ label: "인문대학", value: "HUMANITIES" },
{ label: "자연과학대학", value: "NATURAL_SCIENCE" },
{ label: "IT대학", value: "IT" },
{ label: "공과대학", value: "ENGINEERING" },
{ label: "사회과학대학", value: "SOCIAL_SCIENCE" },
];

export const ADMIN_DEPARTMENT_OPTIONS: SelectItem[] = [
{ label: "컴퓨터학부", value: "COMPUTER" },
{ label: "소프트웨어학부", value: "SOFTWARE" },
{ label: "글로벌미디어학부", value: "GLOBAL_MEDIA" },
{ label: "전자정보공학부", value: "EE" },
{ label: "AI융합학과", value: "AI" },
];

11 changes: 11 additions & 0 deletions src/features/signup-user-flow/model/data/partnerAddressOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { AddressSearchItem } from "@/shared/ui/address-search/types";

export const PARTNER_ADDRESS_OPTIONS: AddressSearchItem[] = [
{ id: "sangdo-50", label: "서울시 동작구 상도로 50" },
{ id: "sangdo-360", label: "서울시 동작구 상도로 360" },
{ id: "sangdo-72", label: "서울시 동작구 상도로 72" },
{ id: "sangdo-72-1", label: "서울시 동작구 상도로 72-1" },
{ id: "sangdo-72-2", label: "서울시 동작구 상도로 72-2" },
{ id: "sangdo-72-3", label: "서울시 동작구 상도로 72-3" },
];

148 changes: 148 additions & 0 deletions src/features/signup-user-flow/model/flowConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { USER_TYPE } from "@/entities/user/model/types";
import type { UserType } from "@/entities/user/model/types";
import type { SignupFlowVariant, SignupStep } from "./types";

export const VERIFICATION_SUCCESS_CODE = "0502";

type SignupFlowConfig = {
stepOrder: SignupStep[];
progressSteps: SignupStep[];
progressByStep: Partial<Record<SignupStep, number>>;
buttonLabelByStep: Partial<Record<SignupStep, string>>;
};

const SHARED_BUTTON_LABELS: Partial<Record<SignupStep, string>> = {
identity: "인증완료",
role: "확인",
complete: "가입완료",
};

const CONTINUED_PROGRESS = {
identity: 16.67,
role: 50,
postRoleEntry: 66.67,
postRoleDetails: 83.33,
finalize: 100,
} as const;
Comment on lines +21 to +26
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The CONTINUED_PROGRESS values (e.g., 16.67, 50, 66.67) are magic numbers. It would improve readability and maintainability if these percentages were either calculated dynamically based on the total number of steps or defined as named constants that clearly describe their meaning within the flow.


export const SIGNUP_FLOW_CONFIG: Record<SignupFlowVariant, SignupFlowConfig> = {
student: {
stepOrder: [
"login1",
"loginForm",
"identity",
"role",
"school",
"studentInput1",
"studentInput2",
"studentInput3",
"complete",
],
progressSteps: [
"identity",
"role",
"school",
"studentInput1",
"studentInput2",
"studentInput3",
],
progressByStep: {
identity: CONTINUED_PROGRESS.identity,
role: CONTINUED_PROGRESS.role,
school: CONTINUED_PROGRESS.postRoleEntry,
studentInput1: CONTINUED_PROGRESS.postRoleDetails,
studentInput2: CONTINUED_PROGRESS.postRoleDetails,
studentInput3: CONTINUED_PROGRESS.postRoleDetails,
},
buttonLabelByStep: {
...SHARED_BUTTON_LABELS,
school: "완료",
studentInput2: "완료",
studentInput3: "완료",
},
},
partner: {
stepOrder: [
"login1",
"loginForm",
"identity",
"role",
"partnerCredentials",
"partnerCompanyInfo",
"partnerBusinessRegistration",
"complete",
],
progressSteps: [
"identity",
"role",
"partnerCredentials",
"partnerCompanyInfo",
"partnerBusinessRegistration",
],
progressByStep: {
identity: CONTINUED_PROGRESS.identity,
role: CONTINUED_PROGRESS.role,
partnerCredentials: CONTINUED_PROGRESS.postRoleEntry,
partnerCompanyInfo: CONTINUED_PROGRESS.postRoleDetails,
partnerBusinessRegistration: CONTINUED_PROGRESS.finalize,
},
buttonLabelByStep: {
...SHARED_BUTTON_LABELS,
partnerCredentials: "입력완료",
partnerCompanyInfo: "입력완료",
partnerBusinessRegistration: "완료",
},
},
admin: {
stepOrder: [
"login1",
"loginForm",
"identity",
"role",
"adminCredentials",
"adminOrganizationType",
"adminOrganizationInfo",
"adminSealRegistration",
"complete",
],
progressSteps: [
"identity",
"role",
"adminCredentials",
"adminOrganizationType",
"adminOrganizationInfo",
"adminSealRegistration",
],
progressByStep: {
identity: CONTINUED_PROGRESS.identity,
role: CONTINUED_PROGRESS.role,
adminCredentials: CONTINUED_PROGRESS.postRoleEntry,
adminOrganizationType: CONTINUED_PROGRESS.postRoleDetails,
adminOrganizationInfo: CONTINUED_PROGRESS.postRoleDetails,
adminSealRegistration: CONTINUED_PROGRESS.finalize,
},
buttonLabelByStep: {
...SHARED_BUTTON_LABELS,
adminCredentials: "입력완료",
adminOrganizationType: "입력완료",
adminOrganizationInfo: "입력완료",
adminSealRegistration: "완료",
},
},
};

export function getSignupFlowVariant(role: UserType | null): SignupFlowVariant {
if (role === USER_TYPE.PARTNER) {
return "partner";
}

if (role === USER_TYPE.ADMIN) {
return "admin";
}

return "student";
}

export function getSignupFlowConfig(variant: SignupFlowVariant) {
return SIGNUP_FLOW_CONFIG[variant];
}
54 changes: 41 additions & 13 deletions src/features/signup-user-flow/model/mock/signupUserFlow.mock.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
import type { SignupFormState } from "../types";

export const DEFAULT_SIGNUP_FORM_STATE: SignupFormState = {
email: "",
password: "",
phone: "",
verificationCode: "",
isCodeSent: false,
verificationAttempted: false,
auth: {
email: "",
password: "",
},
identity: {
phone: "",
verificationCode: "",
isCodeSent: false,
verificationAttempted: false,
},
role: null,
school: "숭실대학교",
major: "글로벌미디어학부",
studentId: "20231649",
agreeAll: false,
agreePrivacy: false,
agreeMarketing: false,
name: "김숭실",
student: {
school: "숭실대학교",
major: "글로벌미디어학부",
studentId: "20231649",
},
partner: {
email: "",
password: "",
companyName: "",
officeAddressId: null,
officeAddressDetail: "",
businessRegistrationFileName: "",
},
admin: {
email: "",
password: "",
organizationType: null,
collegeId: null,
departmentId: null,
officeAddressId: null,
officeAddressDetail: "",
sealFileName: "",
},
agreements: {
agreeAll: false,
agreePrivacy: false,
agreeMarketing: false,
},
profile: {
name: "김숭실",
},
};
Loading
Loading