From 47e7f18e6ac6f087e0cf5394fdaa2bd7b71db7ca Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Mon, 28 Aug 2023 23:39:42 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B8=B0=EB=B3=B8=20=EA=B3=A8?= =?UTF-8?q?=EA=B2=A9=20=EC=85=8B=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organizations/components/OrganizationForm.tsx | 13 +++++++++++++ .../views/ViewOrganizationCreate.tsx | 14 ++++++++++++++ .../views/ViewOrganizationEdit.tsx | 14 ++++++++++++++ src/pages/organizations/[organizationId]/edit.tsx | 7 +++++++ .../index.tsx} | 0 src/pages/organizations/create.tsx | 7 +++++++ 6 files changed, 55 insertions(+) create mode 100644 src/feature/organizations/components/OrganizationForm.tsx create mode 100644 src/feature/organizations/organizations.create/views/ViewOrganizationCreate.tsx create mode 100644 src/feature/organizations/organizations.edit/views/ViewOrganizationEdit.tsx create mode 100644 src/pages/organizations/[organizationId]/edit.tsx rename src/pages/organizations/{[organizationId].tsx => [organizationId]/index.tsx} (100%) create mode 100644 src/pages/organizations/create.tsx diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx new file mode 100644 index 0000000..4dc46c2 --- /dev/null +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -0,0 +1,13 @@ +import styled from "@emotion/styled"; + +interface Props { + isEditMode?: boolean; +} + +const OrganizationForm = ({ isEditMode = false }: Props) => { + return OrganizationForm; +}; + +export default OrganizationForm; + +const EmotionWrapper = styled.div``; diff --git a/src/feature/organizations/organizations.create/views/ViewOrganizationCreate.tsx b/src/feature/organizations/organizations.create/views/ViewOrganizationCreate.tsx new file mode 100644 index 0000000..37b63d9 --- /dev/null +++ b/src/feature/organizations/organizations.create/views/ViewOrganizationCreate.tsx @@ -0,0 +1,14 @@ +import styled from "@emotion/styled"; +import FormOrganization from "feature/organizations/components/OrganizationForm"; + +const ViewOrganizationCreate = () => { + return ( + + + + ); +}; + +export default ViewOrganizationCreate; + +const EmotionWrapper = styled.div``; diff --git a/src/feature/organizations/organizations.edit/views/ViewOrganizationEdit.tsx b/src/feature/organizations/organizations.edit/views/ViewOrganizationEdit.tsx new file mode 100644 index 0000000..185c6bc --- /dev/null +++ b/src/feature/organizations/organizations.edit/views/ViewOrganizationEdit.tsx @@ -0,0 +1,14 @@ +import styled from "@emotion/styled"; +import FormOrganization from "feature/organizations/components/OrganizationForm"; + +const ViewOrganizationEdit = () => { + return ( + + + + ); +}; + +export default ViewOrganizationEdit; + +const EmotionWrapper = styled.div``; diff --git a/src/pages/organizations/[organizationId]/edit.tsx b/src/pages/organizations/[organizationId]/edit.tsx new file mode 100644 index 0000000..585e310 --- /dev/null +++ b/src/pages/organizations/[organizationId]/edit.tsx @@ -0,0 +1,7 @@ +import ViewOrganizationEdit from "feature/organizations/organizations.edit/views/ViewOrganizationEdit"; + +const PageOrganizationEdit = () => { + return ; +}; + +export default PageOrganizationEdit; diff --git a/src/pages/organizations/[organizationId].tsx b/src/pages/organizations/[organizationId]/index.tsx similarity index 100% rename from src/pages/organizations/[organizationId].tsx rename to src/pages/organizations/[organizationId]/index.tsx diff --git a/src/pages/organizations/create.tsx b/src/pages/organizations/create.tsx new file mode 100644 index 0000000..5193338 --- /dev/null +++ b/src/pages/organizations/create.tsx @@ -0,0 +1,7 @@ +import ViewOrganizationCreate from "feature/organizations/organizations.create/views/ViewOrganizationCreate"; + +const PageOrganizationCreate = () => { + return ; +}; + +export default PageOrganizationCreate; From e57aeca19a0c75a3fa4eec8428a2fc7b21795e4e Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 14:05:36 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat(Checkbox):=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=B0=95=EC=8A=A4=20=EB=AA=A9=EB=A1=9D=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=A4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/checkbox/Checkbox.tsx | 9 ++ .../checkbox/items/CheckboxGroup.tsx | 39 +++++++ .../checkbox/items/CheckboxItem.tsx | 104 ++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 src/components/checkbox/Checkbox.tsx create mode 100644 src/components/checkbox/items/CheckboxGroup.tsx create mode 100644 src/components/checkbox/items/CheckboxItem.tsx diff --git a/src/components/checkbox/Checkbox.tsx b/src/components/checkbox/Checkbox.tsx new file mode 100644 index 0000000..1a20fe5 --- /dev/null +++ b/src/components/checkbox/Checkbox.tsx @@ -0,0 +1,9 @@ +import CheckboxGroup from "components/checkbox/items/CheckboxGroup"; +import CheckboxItem from "components/checkbox/items/CheckboxItem"; + +const Checkbox = () => {}; + +Checkbox.Group = CheckboxGroup; +Checkbox.Item = CheckboxItem; + +export default Checkbox; diff --git a/src/components/checkbox/items/CheckboxGroup.tsx b/src/components/checkbox/items/CheckboxGroup.tsx new file mode 100644 index 0000000..664df3d --- /dev/null +++ b/src/components/checkbox/items/CheckboxGroup.tsx @@ -0,0 +1,39 @@ +import styled from "@emotion/styled"; +import { Children, ReactNode, cloneElement, isValidElement } from "react"; + +export interface Props { + children: ReactNode; + checkedList: string[]; + setCheckedItem: (checkedList: string) => void; +} + +export interface CheckboxItemProps { + checked: boolean; + value: string; + label: string; + children: ReactNode; + setChecked: (value: string) => void; +} + +const CheckboxGroup = ({ children, checkedList, setCheckedItem }: Props) => { + const childrenWithProps = Children.map(children, (child) => { + if (isValidElement(child)) { + const checked = checkedList.includes(child.props.value); + const value = child.props.value; + + const setChecked = () => setCheckedItem(value); + + return cloneElement(child, { checked, setChecked, value }); + } + return child; + }); + + return {childrenWithProps}; +}; + +export default CheckboxGroup; + +const EmotionWrapper = styled.div` + display: flex; + flex-direction: column; +`; diff --git a/src/components/checkbox/items/CheckboxItem.tsx b/src/components/checkbox/items/CheckboxItem.tsx new file mode 100644 index 0000000..56062eb --- /dev/null +++ b/src/components/checkbox/items/CheckboxItem.tsx @@ -0,0 +1,104 @@ +import styled from "@emotion/styled"; +import { ReactNode } from "react"; + +export interface Props { + children: ReactNode; + checked?: boolean; + setChecked?: (checked: boolean | string) => void; + value: string; +} + +const CheckboxItem = ({ children, checked, setChecked, value }: Props) => { + const handleClickCheckbox = () => { + setChecked?.(!checked); + }; + + return ( + +
+ + + + ); +}; + +export default CheckboxItem; + +const EmotionWrapper = styled.button` + display: flex; + align-items: center; + padding: 4px 8px; + border: 1px solid ${({ theme }) => theme.color.gray200}; + border-radius: 4px; + margin: 4px 0; + cursor: pointer; + color: ${({ theme }) => theme.color.gray400}; + + &[data-checked="true"] { + border: 1px solid ${({ theme }) => theme.color.primary500}; + color: ${({ theme }) => theme.color.primary600}; + font-weight: 500; + } + + div { + width: 20px; + height: 20px; + border: 1px solid ${({ theme }) => theme.color.gray200}; + border-radius: 4px; + margin-right: 4px; + cursor: pointer; + + &[data-checked="true"] { + background: ${({ theme }) => theme.color.primary500}; + border: 1px solid ${({ theme }) => theme.color.primary600}; + + &::after { + content: ""; + display: block; + width: 6px; + height: 10px; + border: solid #fff; + border-width: 0 2px 2px 0; + transform: rotate(45deg); + position: relative; + top: 2px; + left: 5px; + + &:hover { + border: solid #fff; + border-width: 0 2px 2px 0; + + transform: rotate(45deg); + position: relative; + top: 2px; + left: 5px; + } + } + } + } + label { + padding: 10px 10px; + white-space: wrap; + word-break: break-all; + + /* &:hover { + border: 1px solid ${({ theme }) => theme.color.primary500}; + color: ${({ theme }) => theme.color.primary500}; + font-weight: 500; + } + + &[data-disabled="true"] { + border: 1px solid ${({ theme }) => theme.color.gray200}; + color: ${({ theme }) => theme.color.gray200}; + background-color: ${({ theme }) => theme.color.gray100}; + cursor: not-allowed; + } + + &[data-checked="true"] { + color: #fff; + background: ${({ theme }) => theme.color.primary500}; + border: 1px solid ${({ theme }) => theme.color.primary600}; + font-weight: 500; + } */ + } +`; From 5647565d1919fc77eeda6dda1d91547062a2c647 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 14:09:21 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=ED=8F=BC=EC=97=90=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=A2=85=EB=A5=98=20=EB=AC=BB=EB=8A=94=20=EC=A7=88=EB=AC=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/OrganizationForm.tsx | 45 ++++++++++++++++++- .../orgnizationTypeCheckboxItemList.ts | 20 +++++++++ .../types/TOrganizationFormValues.ts | 10 +++++ .../organizations/types/TOrganizationType.ts | 1 + .../types/TOrganizationTypeCheckboxItem.ts | 6 +++ 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 src/feature/organizations/constants/orgnizationTypeCheckboxItemList.ts create mode 100644 src/feature/organizations/types/TOrganizationFormValues.ts create mode 100644 src/feature/organizations/types/TOrganizationType.ts create mode 100644 src/feature/organizations/types/TOrganizationTypeCheckboxItem.ts diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index 4dc46c2..3aab202 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -1,13 +1,54 @@ import styled from "@emotion/styled"; +import Checkbox from "components/checkbox/Checkbox"; +import { ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST } from "feature/organizations/constants/orgnizationTypeCheckboxItemList"; +import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; +import { useState } from "react"; interface Props { isEditMode?: boolean; } const OrganizationForm = ({ isEditMode = false }: Props) => { - return OrganizationForm; + const [formValues, setFormValues] = useState({ + name: "", + type: "STUDENT_ORGANIZATION", + areaId: 0, + isPublic: true, + password: "", + nickname: "", + }); + + const handleSetFormValues = (key: keyof TOrganizationFormValues) => (value: unknown) => { + console.log(key, value); + setFormValues((prev) => ({ ...prev, [key]: value })); + }; + + const { type } = formValues; + + return ( + +

내 단체는 어떤 단체인가요?

+ + {ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST.map((item) => { + const { value, name } = item; + + return ( + + {name} + + ); + })} + +
+ ); }; export default OrganizationForm; -const EmotionWrapper = styled.div``; +const EmotionWrapper = styled.div` + p.text-question { + margin-bottom: 8px; + font-weight: 500; + font-size: 18px; + } +`; diff --git a/src/feature/organizations/constants/orgnizationTypeCheckboxItemList.ts b/src/feature/organizations/constants/orgnizationTypeCheckboxItemList.ts new file mode 100644 index 0000000..701a6fc --- /dev/null +++ b/src/feature/organizations/constants/orgnizationTypeCheckboxItemList.ts @@ -0,0 +1,20 @@ +import { TOrganizationTypeCheckboxItem } from "feature/organizations/types/TOrganizationTypeCheckboxItem"; + +export const ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST: TOrganizationTypeCheckboxItem[] = [ + { + name: "학생 단체", + value: "STUDENT_ORGANIZATION", + }, + { + name: "회사", + value: "CORPORATE", + }, + { + name: "가족", + value: "FAMILY", + }, + { + name: "기타", + value: "OTHERS", + }, +]; diff --git a/src/feature/organizations/types/TOrganizationFormValues.ts b/src/feature/organizations/types/TOrganizationFormValues.ts new file mode 100644 index 0000000..4e29897 --- /dev/null +++ b/src/feature/organizations/types/TOrganizationFormValues.ts @@ -0,0 +1,10 @@ +import { TOrganizationType } from "feature/organizations/types/TOrganizationType"; + +export type TOrganizationFormValues = { + name: string; // 단체명 + type: TOrganizationType; // 단체 종류 (학생 단체, 회사, 가족, 기타) + areaId: number; // 단체 활동 지역 + isPublic: boolean; // 직접 가입 가능 여부 (비밀번호 없이) + password?: string; // 직접 가입 시 필요한 비밀번호 + nickname: string; // 단체 소유자의 단체 내의 닉네임 +}; diff --git a/src/feature/organizations/types/TOrganizationType.ts b/src/feature/organizations/types/TOrganizationType.ts new file mode 100644 index 0000000..cbfa735 --- /dev/null +++ b/src/feature/organizations/types/TOrganizationType.ts @@ -0,0 +1 @@ +export type TOrganizationType = "STUDENT_ORGANIZATION" | "CORPORATE" | "FAMILY" | "OTHERS"; diff --git a/src/feature/organizations/types/TOrganizationTypeCheckboxItem.ts b/src/feature/organizations/types/TOrganizationTypeCheckboxItem.ts new file mode 100644 index 0000000..e25847a --- /dev/null +++ b/src/feature/organizations/types/TOrganizationTypeCheckboxItem.ts @@ -0,0 +1,6 @@ +import { TOrganizationType } from "feature/organizations/types/TOrganizationType"; + +export type TOrganizationTypeCheckboxItem = { + name: string; + value: TOrganizationType; +}; From ca0699e311fb89e6c66142a4f0f26b1adf63e476 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 14:53:52 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=ED=8F=BC=EC=97=90=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=EB=B3=84=20=EB=8B=89=EB=84=A4=EC=9E=84,=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=EC=9D=B4=EB=A6=84,=20=EB=8B=A8=EC=B2=B4=20=ED=99=9C=EB=8F=99?= =?UTF-8?q?=20=EC=A7=80=EC=97=AD=20=EB=AC=BB=EB=8A=94=20=EC=A7=88=EB=AC=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20,=20=EB=B0=8F=20=EC=A0=9C=EC=B6=9C=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../checkbox/items/CheckboxGroup.tsx | 10 +-- src/components/inputs/TextInput/TextInput.tsx | 4 +- .../components/OrganizationForm.tsx | 75 +++++++++++++++++-- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/components/checkbox/items/CheckboxGroup.tsx b/src/components/checkbox/items/CheckboxGroup.tsx index 664df3d..363ad8c 100644 --- a/src/components/checkbox/items/CheckboxGroup.tsx +++ b/src/components/checkbox/items/CheckboxGroup.tsx @@ -1,7 +1,7 @@ import styled from "@emotion/styled"; -import { Children, ReactNode, cloneElement, isValidElement } from "react"; +import { Children, HTMLAttributes, ReactNode, cloneElement, isValidElement } from "react"; -export interface Props { +export interface Props extends HTMLAttributes { children: ReactNode; checkedList: string[]; setCheckedItem: (checkedList: string) => void; @@ -15,7 +15,7 @@ export interface CheckboxItemProps { setChecked: (value: string) => void; } -const CheckboxGroup = ({ children, checkedList, setCheckedItem }: Props) => { +const CheckboxGroup = ({ children, checkedList, setCheckedItem, ...props }: Props) => { const childrenWithProps = Children.map(children, (child) => { if (isValidElement(child)) { const checked = checkedList.includes(child.props.value); @@ -28,12 +28,12 @@ const CheckboxGroup = ({ children, checkedList, setCheckedItem }: Props) => { return child; }); - return {childrenWithProps}; + return {childrenWithProps}; }; export default CheckboxGroup; -const EmotionWrapper = styled.div` +const EmotionWrapper = styled.ul` display: flex; flex-direction: column; `; diff --git a/src/components/inputs/TextInput/TextInput.tsx b/src/components/inputs/TextInput/TextInput.tsx index 33318fb..09a7182 100644 --- a/src/components/inputs/TextInput/TextInput.tsx +++ b/src/components/inputs/TextInput/TextInput.tsx @@ -13,6 +13,7 @@ interface Props { conditionCheckList?: TConditionCheck[]; multiline?: boolean; onTextChange?: (value: string, isValid: boolean) => void; + className?: string; } const TextInput: React.FC = ({ @@ -24,6 +25,7 @@ const TextInput: React.FC = ({ conditionCheckList, multiline = false, onTextChange, + className, }) => { const [status, setStatus] = useState(value === "" ? "default" : "success"); // default / success / invalid / focus const [enteredValue, setEnteredValue] = useState(value); @@ -76,7 +78,7 @@ const TextInput: React.FC = ({ }, [enteredValue, status, onTextChange]); return ( - + {label && {label}} {conditionList && (
diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index 3aab202..fa946ab 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -1,8 +1,11 @@ import styled from "@emotion/styled"; +import Button from "components/button/Button"; import Checkbox from "components/checkbox/Checkbox"; +import AreaInput from "components/inputs/AreaInput/AreaInput"; +import TextInput from "components/inputs/TextInput/TextInput"; import { ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST } from "feature/organizations/constants/orgnizationTypeCheckboxItemList"; import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; -import { useState } from "react"; +import { useCallback, useState } from "react"; interface Props { isEditMode?: boolean; @@ -18,17 +21,44 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { nickname: "", }); - const handleSetFormValues = (key: keyof TOrganizationFormValues) => (value: unknown) => { - console.log(key, value); - setFormValues((prev) => ({ ...prev, [key]: value })); + const handleClickSubmit = async () => { + console.log(formValues); }; + const handleSetFormValues = useCallback( + (key: keyof TOrganizationFormValues) => (value: unknown) => { + console.log(key, value); + setFormValues((prev) => ({ ...prev, [key]: value })); + }, + [] + ); + + const handleSetOrgName = useCallback( + (value: unknown, isValid: boolean) => handleSetFormValues("name")(value), + [] + ); + + const handleSetOrgNickname = useCallback( + (value: unknown, isValid: boolean) => handleSetFormValues("nickname")(value), + [] + ); + + const handleSetOrgAreaId = useCallback( + (value: unknown) => handleSetFormValues("areaId")(value), + [] + ); + const { type } = formValues; return ( -

내 단체는 어떤 단체인가요?

- +

{isEditMode ? "단체 정보 수정" : "단체 만들기"}

+

1. 내 단체는 어떤 단체인가요?

+ {ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST.map((item) => { const { value, name } = item; @@ -39,6 +69,26 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { ); })} +

2. 단체 이름을 지어보아요!

+ +

3. 나는 이 단체에서 이 이름을 쓸거에요!

+ +

4. 우리 단체는 이 지역에서 주로 활동합니다!

+ + +
); }; @@ -46,9 +96,20 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { export default OrganizationForm; const EmotionWrapper = styled.div` + h1 { + font-size: 24px; + font-weight: 700; + margin-bottom: 36px; + line-height: 1.5; + } + p.text-question { - margin-bottom: 8px; + margin-bottom: 16px; font-weight: 500; font-size: 18px; } + + .organization-form-item { + margin-bottom: 36px; + } `; From 43eeffd42bdb24d389daf3e4236f2a3b24968eeb Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 15:20:55 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=ED=8F=BC=EC=97=90=20=EA=B3=B5=EA=B0=9C?= =?UTF-8?q?,=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20=EB=8B=A8=EC=B2=B4=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=EC=9D=BC=20=EC=8B=9C=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=9E=85=EB=A0=A5=EA=B8=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/OrganizationForm.tsx | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index fa946ab..3298260 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -27,7 +27,6 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { const handleSetFormValues = useCallback( (key: keyof TOrganizationFormValues) => (value: unknown) => { - console.log(key, value); setFormValues((prev) => ({ ...prev, [key]: value })); }, [] @@ -48,7 +47,20 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { [] ); - const { type } = formValues; + const handleSetOrgPassword = useCallback( + (value: unknown, isValid: boolean) => handleSetFormValues("password")(value), + [] + ); + + const handleSetIsPublic = useCallback((value: unknown) => { + const isPublicBoolean = value === "true"; + setFormValues((prev) => ({ + ...prev, + ["isPublic" as keyof TOrganizationFormValues]: isPublicBoolean, + })); + }, []); + + const { type, isPublic } = formValues; return ( @@ -86,9 +98,36 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { className="organization-form-item" />

4. 우리 단체는 이 지역에서 주로 활동합니다!

- + - +

5. 단체를 비공개로 설정할 수 있어요!

+ handleSetIsPublic(value)} + className="organization-form-item" + > + 공개 + 비공개 + + + {true && ( +
+

6. 비밀번호를 설정해 보아요!

+ +
+ )} + +
); }; @@ -112,4 +151,18 @@ const EmotionWrapper = styled.div` .organization-form-item { margin-bottom: 36px; } + + .set-password-container { + max-height: 0; + overflow: hidden; + transition: max-height 0.5s ease-in-out; + + &[data-ispublic="false"] { + max-height: 1000px; + } + } + + button { + float: right; + } `; From 78443e2e35dfd7e21413660dc93c4afe01e934ef Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 15:34:16 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20textinput=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=9A=94=EC=86=8C=EC=97=90=20validation=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constant/limit.ts | 5 ++ .../components/OrganizationForm.tsx | 14 +++++ .../functions/validateOrganizationForm.ts | 56 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/constant/limit.ts create mode 100644 src/feature/organizations/functions/validateOrganizationForm.ts diff --git a/src/constant/limit.ts b/src/constant/limit.ts new file mode 100644 index 0000000..8cf148d --- /dev/null +++ b/src/constant/limit.ts @@ -0,0 +1,5 @@ +// 닉네임 최대 글자수 제한, 파일 업로드 최대 용량 제한 등 각종 제한 사항을 정의하는 파일입니다. +export const NICKNAME_MAX_LENGTH = 10; +export const ORGANIZATION_NAME_MAX_LENGTH = 15; +export const ORGANIZATION_PASSWORD_MIN_LENGTH = 4; +export const ORGANIZATION_PASSWORD_MAX_LENGTH = 15; diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index 3298260..efda5ef 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -4,6 +4,14 @@ import Checkbox from "components/checkbox/Checkbox"; import AreaInput from "components/inputs/AreaInput/AreaInput"; import TextInput from "components/inputs/TextInput/TextInput"; import { ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST } from "feature/organizations/constants/orgnizationTypeCheckboxItemList"; +import { + validateOrganizationNameEmpty, + validateOrganizationNameLength, + validateOrganizationNicknameEmpty, + validateOrganizationNicknameLength, + validateOrganizationPasswordEmpty, + validateOrganizationPasswordLength, +} from "feature/organizations/functions/validateOrganizationForm"; import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; import { useCallback, useState } from "react"; @@ -88,6 +96,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { name="name" onTextChange={handleSetOrgName} className="organization-form-item" + conditionCheckList={[validateOrganizationNameLength, validateOrganizationNameEmpty]} />

3. 나는 이 단체에서 이 이름을 쓸거에요!

{ name="nickname" onTextChange={handleSetOrgNickname} className="organization-form-item" + conditionCheckList={[validateOrganizationNicknameLength, validateOrganizationNicknameEmpty]} />

4. 우리 단체는 이 지역에서 주로 활동합니다!

{ name="password" onTextChange={handleSetOrgPassword} className="organization-form-item" + conditionCheckList={[ + validateOrganizationPasswordLength, + validateOrganizationPasswordEmpty, + ]} />
)} diff --git a/src/feature/organizations/functions/validateOrganizationForm.ts b/src/feature/organizations/functions/validateOrganizationForm.ts new file mode 100644 index 0000000..5f99392 --- /dev/null +++ b/src/feature/organizations/functions/validateOrganizationForm.ts @@ -0,0 +1,56 @@ +import { NICKNAME_MAX_LENGTH, ORGANIZATION_NAME_MAX_LENGTH } from "constant/limit"; + +// 단체명 유효성 검사 조건을 정의합니다. +export const validateOrganizationNameLength = { + condition: (value: string): boolean => { + if (value?.length > ORGANIZATION_NAME_MAX_LENGTH) return false; + return true; + }, + messageOnError: `단체명은 ${ORGANIZATION_NAME_MAX_LENGTH}자 이내로 입력해주세요.`, +}; + +export const validateOrganizationNameEmpty = { + condition: (value: string): boolean => { + if (!value) return false; + if (value?.length === 0) return false; + return true; + }, + messageOnError: "단체명을 입력해주세요.", +}; + +// 단체 내 닉네임 유효성 검사 조건을 정의합니다. +export const validateOrganizationNicknameLength = { + condition: (value: string): boolean => { + if (value?.length > NICKNAME_MAX_LENGTH) return false; + return true; + }, + messageOnError: `단체 내 닉네임은 ${NICKNAME_MAX_LENGTH}자 이내로 입력해주세요.`, +}; + +export const validateOrganizationNicknameEmpty = { + condition: (value: string): boolean => { + if (!value) return false; + if (value?.length === 0) return false; + return true; + }, + messageOnError: "단체 내 닉네임을 입력해주세요.", +}; + +// 단체 비밀번호 유효성 검사 조건을 정의합니다. +export const validateOrganizationPasswordLength = { + condition: (value: string): boolean => { + if (value?.length < 4 || value?.length > 15) return false; + return true; + }, + + messageOnError: "비밀번호는 4자 이상 15자 이하로 입력해주세요.", +}; + +export const validateOrganizationPasswordEmpty = { + condition: (value: string): boolean => { + if (!value) return false; + if (value?.length === 0) return false; + return true; + }, + messageOnError: "비밀번호를 입력해주세요.", +}; From 6ef738d82b609db61d6a5cac299958227cc0cd01 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 10 Sep 2023 16:10:10 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat(Organization):=20=EB=AA=A9=EC=97=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B4=88=EA=B8=B0=EA=B0=92=20=EB=B0=9B?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=9C=EB=AE=AC=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/OrganizationForm.tsx | 44 +++++++++++-------- .../mockups/mockupOrganiationInitialData.ts | 10 +++++ 2 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 src/feature/organizations/mockups/mockupOrganiationInitialData.ts diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index efda5ef..164197b 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -12,8 +12,9 @@ import { validateOrganizationPasswordEmpty, validateOrganizationPasswordLength, } from "feature/organizations/functions/validateOrganizationForm"; +import { MOCKUP_ORGANIZATION_INITIAL_DATA } from "feature/organizations/mockups/mockupOrganiationInitialData"; import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; interface Props { isEditMode?: boolean; @@ -68,7 +69,12 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { })); }, []); - const { type, isPublic } = formValues; + const { type, isPublic, name, password, nickname } = formValues; + + // 데이터 fetching 로직이 들어올 시 비동기적으로 form values 를 업데이트 해야함 + useEffect(() => { + if (isEditMode) setFormValues(MOCKUP_ORGANIZATION_INITIAL_DATA); + }, [isEditMode]); return ( @@ -97,6 +103,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { onTextChange={handleSetOrgName} className="organization-form-item" conditionCheckList={[validateOrganizationNameLength, validateOrganizationNameEmpty]} + value={name} />

3. 나는 이 단체에서 이 이름을 쓸거에요!

{ onTextChange={handleSetOrgNickname} className="organization-form-item" conditionCheckList={[validateOrganizationNicknameLength, validateOrganizationNicknameEmpty]} + value={nickname} />

4. 우리 단체는 이 지역에서 주로 활동합니다!

5. 단체를 비공개로 설정할 수 있어요!

@@ -124,22 +133,21 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { 비공개 - {true && ( -
-

6. 비밀번호를 설정해 보아요!

- -
- )} +
+

6. 비밀번호를 설정해 보아요!

+ +
diff --git a/src/feature/organizations/mockups/mockupOrganiationInitialData.ts b/src/feature/organizations/mockups/mockupOrganiationInitialData.ts new file mode 100644 index 0000000..88c5d2e --- /dev/null +++ b/src/feature/organizations/mockups/mockupOrganiationInitialData.ts @@ -0,0 +1,10 @@ +import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; + +export const MOCKUP_ORGANIZATION_INITIAL_DATA: TOrganizationFormValues = { + name: "우리가족", + type: "FAMILY", + areaId: 26170520, + isPublic: false, + password: "1234", + nickname: "아빠", +}; From 9a6fef78e8b769ae3025dbc493cc9665a3bfae95 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sat, 16 Sep 2023 22:13:38 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat(TextInput):=20=EB=B9=84=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=EC=A0=81=20value=20=EB=B3=80=EA=B2=BD=EC=97=90?= =?UTF-8?q?=EB=8F=84=20=EB=B0=98=EC=9D=91=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/inputs/TextInput/TextInput.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/inputs/TextInput/TextInput.tsx b/src/components/inputs/TextInput/TextInput.tsx index 09a7182..8cbfac7 100644 --- a/src/components/inputs/TextInput/TextInput.tsx +++ b/src/components/inputs/TextInput/TextInput.tsx @@ -77,6 +77,11 @@ const TextInput: React.FC = ({ } }, [enteredValue, status, onTextChange]); + // 비동기적인 value 변경에 대한 처리 (API 호출 시) + useEffect(() => { + setEnteredValue(value); + }, [value]); + return ( {label && {label}} From aabc44ea0c58f478cb5b775689852d086b9b4cb2 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:13:40 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix(AreaInput):=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EA=B0=92=20=EC=A3=BC=EC=96=B4=EC=A7=88=20=EB=95=8C=20=EB=AC=B4?= =?UTF-8?q?=ED=95=9C=20=EB=A6=AC=EB=A0=8C=EB=8D=94=EB=A7=81=20=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/inputs/AreaInput/AreaInput.tsx | 36 +++++++++++++------ .../components/OrganizationForm.tsx | 5 ++- src/hooks/.gitkeep | 0 src/hooks/usePrevious.ts | 10 ++++++ 4 files changed, 37 insertions(+), 14 deletions(-) delete mode 100644 src/hooks/.gitkeep create mode 100644 src/hooks/usePrevious.ts diff --git a/src/components/inputs/AreaInput/AreaInput.tsx b/src/components/inputs/AreaInput/AreaInput.tsx index e918f31..c6b82a9 100644 --- a/src/components/inputs/AreaInput/AreaInput.tsx +++ b/src/components/inputs/AreaInput/AreaInput.tsx @@ -3,18 +3,24 @@ import { useState, useEffect } from "react"; import AreaInputSido from "components/inputs/AreaInput/items/AreaInputSido"; import AreaInputSigungu from "components/inputs/AreaInput/items/AreaInputSigungu"; import AreaInputDong from "components/inputs/AreaInput/items/AreaInputDong"; +import usePrevious from "hooks/usePrevious"; interface Props { label?: string; value?: number; // 기존에 선택된 지역 id onSelectValueChange?: (value: number) => void; // 지역 id 콜백 함수 + className?: string; } -const AreaInput: React.FC = ({ label, value, onSelectValueChange }) => { +const AreaInput: React.FC = ({ label, value, onSelectValueChange, className }) => { const [sido, setSido] = useState(); const [sigungu, setSigungu] = useState(); const [dong, setDong] = useState(); + const previousSido = usePrevious(sido); + const previousSigungu = usePrevious(sigungu); + const previousDong = usePrevious(dong); + const handleSidoChange = (value: string | undefined) => { setSido(value); }; @@ -28,21 +34,29 @@ const AreaInput: React.FC = ({ label, value, onSelectValueChange }) => { }; useEffect(() => { - if (value && !sido && !sigungu && !dong) { - // 초기값이 존재하고, sido가 선택되지 않은 경우 + const areaId = `${sido ?? "NN"}${sigungu ?? "NNN"}${dong ?? "NNN"}`; + + onSelectValueChange?.(parseInt(areaId)); + }, [sido, sigungu, dong, onSelectValueChange]); + + useEffect(() => { + if (value) { const area = value.toString(); - setSido(area.slice(1, 3)); - setSigungu(area.slice(3, 6)); - setDong(area.slice(6)); - } - if (onSelectValueChange && dong) { - onSelectValueChange(parseInt(dong)); + const nextSido = area.slice(0, 2); + const nextSigungu = area.slice(2, 5); + const nextDong = area.slice(5); + // 무한 렌더링을 막기 위한 조치, 이전 값과 변경 예정인 값이 같을 경우 렌더링을 막는다. + if (nextSido === previousSido && nextSigungu === previousSigungu && nextDong === previousDong) + return; + setSido(nextSido); + setSigungu(nextSigungu); + setDong(nextDong); } - }, [value, sido, sigungu, dong, onSelectValueChange]); + }, [value, sido, sigungu, dong, previousSido, previousSigungu, previousDong]); return ( - + {label && {label}}
diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index 164197b..d1535e1 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -29,7 +29,6 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { password: "", nickname: "", }); - const handleClickSubmit = async () => { console.log(formValues); }; @@ -69,7 +68,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { })); }, []); - const { type, isPublic, name, password, nickname } = formValues; + const { type, isPublic, name, password, nickname, areaId } = formValues; // 데이터 fetching 로직이 들어올 시 비동기적으로 form values 를 업데이트 해야함 useEffect(() => { @@ -120,7 +119,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { label="활동 지역" onSelectValueChange={handleSetOrgAreaId} className="organization-form-item" - // value={areaId} + value={areaId} />

5. 단체를 비공개로 설정할 수 있어요!

diff --git a/src/hooks/.gitkeep b/src/hooks/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/hooks/usePrevious.ts b/src/hooks/usePrevious.ts new file mode 100644 index 0000000..8e7bc01 --- /dev/null +++ b/src/hooks/usePrevious.ts @@ -0,0 +1,10 @@ +import { useEffect, useRef } from "react"; + +// 바로 직전 상태의 state 값을 반환하는 hook +export default function usePrevious(value: T): T | undefined { + const ref = useRef(); + useEffect(() => { + ref.current = value; + }); + return ref.current; +} From 90545f6ca766448f16bd027f5097bbf867e1e6bd Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:25:03 +0900 Subject: [PATCH 10/14] =?UTF-8?q?fix(AreaInput):=20=EC=8B=9C=EB=8F=84,=20?= =?UTF-8?q?=EC=8B=9C=EA=B5=B0=EA=B5=AC,=20=EB=8F=99=20=EC=A4=91=20?= =?UTF-8?q?=EC=96=B4=EB=8A=90=20=ED=95=98=EB=82=98=EB=9D=BC=EB=8F=84=20?= =?UTF-8?q?=EC=97=86=EC=9C=BC=EB=A9=B4=20value=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=95=88=ED=95=98=EA=B2=8C=20=EC=A1=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/inputs/AreaInput/AreaInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/inputs/AreaInput/AreaInput.tsx b/src/components/inputs/AreaInput/AreaInput.tsx index c6b82a9..d1cee43 100644 --- a/src/components/inputs/AreaInput/AreaInput.tsx +++ b/src/components/inputs/AreaInput/AreaInput.tsx @@ -35,7 +35,7 @@ const AreaInput: React.FC = ({ label, value, onSelectValueChange, classNa useEffect(() => { const areaId = `${sido ?? "NN"}${sigungu ?? "NNN"}${dong ?? "NNN"}`; - + if (!sido || !sigungu || !dong) return; onSelectValueChange?.(parseInt(areaId)); }, [sido, sigungu, dong, onSelectValueChange]); @@ -46,7 +46,7 @@ const AreaInput: React.FC = ({ label, value, onSelectValueChange, classNa const nextSido = area.slice(0, 2); const nextSigungu = area.slice(2, 5); const nextDong = area.slice(5); - // 무한 렌더링을 막기 위한 조치, 이전 값과 변경 예정인 값이 같을 경우 렌더링을 막는다. + if (nextSido === previousSido && nextSigungu === previousSigungu && nextDong === previousDong) return; setSido(nextSido); From 7e7bb30e98d92f1d55462b55b152e2480e81e1f4 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:41:53 +0900 Subject: [PATCH 11/14] =?UTF-8?q?feat(FormOranization):=20=EB=8B=A8?= =?UTF-8?q?=EC=B2=B4=EC=97=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EB=8A=94=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/profile-image-default-org.png | Bin 0 -> 1331 bytes .../components/OrganizationForm.tsx | 27 +++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 public/images/profile-image-default-org.png diff --git a/public/images/profile-image-default-org.png b/public/images/profile-image-default-org.png new file mode 100644 index 0000000000000000000000000000000000000000..66944171fa1826370a08ca344859bbe2c65b250b GIT binary patch literal 1331 zcmZ`(Yg7^j6vonqc$_U?r3PWXB{L5`%Q`5CAfcjSnx;Xfsc4Fokz^}dhI*hP^%!AO zkomwx=#o-eVa-SLfnp<)PGm8ReB0(D8vX0f?z!LnzI*R?&i!|<#^BMGAO{ct0I_nu$1w002Zx zi}j;s#z3cYcj~aTx*KKv($G!nZM+u zbF9ay7d-;|ICrwi8&g3W?SV09J$XC79V)@O#PZfkFYMe)+?-T@o?+uz2Q=RMHgcVJ zFm*)@L*mTa2l#qkV4%*DVt#QSPgS*wR9>hD&o|1L@t$>?`^8F_#Cdo=f`NZ}!(A!b ztYJki4^EJSVW``5gA+~;$MlDDY6vV)T=zld4FWFiC?j82fW=;puhpPv$u)#CUbuqb zP$5QSv~Z%B37!=G6PrXi8N~>1_T?*<;BHTs<7?#+wCozqQ7@dRnw!kajq<{s2noH( z%PlYpmr$$fg;xF(cE}hRX4dT6R7eZJBD`AW|5~y!a<&$sTR{w{k1*q)(aM=wQwM|q zjrD&Zm2%la>h5K3K%St?&0DuLM{3K-La;1XAnUGW3}9R1nRR7}&ugM&oaEw) z7F@CaV|7J+q?h7ws;pWXAqcB%8gprKC}yz}SX>$*wzrW#wNBGD2il?{TdpbU9MgI^ zBX*DdNB0OwiKSAv%URhsuQ!g}$WT7&Ncbh{Fny^i(la83CZFk7*$TJEPw?g>?|W1q zxe%3Y37|7zrgh<}VNXyzHK%P;!et_Fsw0t_BmFA*Z6s(YzNrD2$(tIl2j7p2Eu#{M z%`9*}A+~IcQ22c8ZJHNjE^NT7Rc9{K+eQ^PJD!#r{j=o=^RjHi z^l?##a14i{hikJ%2Y|tq>iz1Wie-EU%%W{s0e8#Dbg!MunC`S{*z`(x?oG>9dg}wV z^c0i^c(cGQ-V1j_rL`CqPzKR{=j$l?Lko$DK40gAh}MjJ=qZ++?HW20$ zo4+*XIniPEO#Ehd7YdnAfB1A0M0P#e`HUyqWkDA=$L_f?zC=22|lr% z$y<8Y8nT+QQ?9+zcg)8xwPSw8Wyo&@GT601$raVShWGk+mD{IOtZ^HB*NHHGi7OQP e?N>4CyNVLCU288%;SFWduPY1+k8BT1zxW>osc_=} literal 0 HcmV?d00001 diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index d1535e1..e89aa80 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -2,6 +2,7 @@ import styled from "@emotion/styled"; import Button from "components/button/Button"; import Checkbox from "components/checkbox/Checkbox"; import AreaInput from "components/inputs/AreaInput/AreaInput"; +import ImageInput from "components/inputs/ImageInput/ImageInput"; import TextInput from "components/inputs/TextInput/TextInput"; import { ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST } from "feature/organizations/constants/orgnizationTypeCheckboxItemList"; import { @@ -16,6 +17,11 @@ import { MOCKUP_ORGANIZATION_INITIAL_DATA } from "feature/organizations/mockups/ import { TOrganizationFormValues } from "feature/organizations/types/TOrganizationFormValues"; import { useCallback, useEffect, useState } from "react"; +type TCustomFormTarget = { + imageInput: { files: FileList }; + imageUrlInput: { value: string }; +}; + interface Props { isEditMode?: boolean; } @@ -29,8 +35,16 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { password: "", nickname: "", }); - const handleClickSubmit = async () => { + + const handleClickSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + const target = e.target as typeof e.target & TCustomFormTarget; + + console.log("submit"); console.log(formValues); + + console.log(target.imageInput.files); + console.log(target.imageUrlInput.value); }; const handleSetFormValues = useCallback( @@ -69,6 +83,8 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { }, []); const { type, isPublic, name, password, nickname, areaId } = formValues; + const organizationImageSrc = null; // 서버에서 받아온 단체 프로필 이미지 + const existingImageUrlList = organizationImageSrc ? [organizationImageSrc] : []; // 데이터 fetching 로직이 들어올 시 비동기적으로 form values 를 업데이트 해야함 useEffect(() => { @@ -76,7 +92,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { }, [isEditMode]); return ( - +

{isEditMode ? "단체 정보 수정" : "단체 만들기"}

1. 내 단체는 어떤 단체인가요?

{ />
- +

7. 우리 단체를 대표하는 이미지를 설정해보세요!

+ + +
); }; export default OrganizationForm; -const EmotionWrapper = styled.div` +const EmotionWrapper = styled.form` h1 { font-size: 24px; font-weight: 700; From 840a315a12fcf9c5ddc836a8048a047199ba4a99 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:43:05 +0900 Subject: [PATCH 12/14] =?UTF-8?q?refactor(Organization):=20=EB=8B=A8?= =?UTF-8?q?=EC=B2=B4=20=EC=B5=9C=EB=8C=80=20=EC=97=86=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=20=EC=82=AC=EC=A7=84=20=EC=88=98=EB=A5=BC=20?= =?UTF-8?q?=EC=83=81=EC=88=98=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constant/limit.ts | 2 ++ src/feature/organizations/components/OrganizationForm.tsx | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/constant/limit.ts b/src/constant/limit.ts index 8cf148d..d8d1eeb 100644 --- a/src/constant/limit.ts +++ b/src/constant/limit.ts @@ -3,3 +3,5 @@ export const NICKNAME_MAX_LENGTH = 10; export const ORGANIZATION_NAME_MAX_LENGTH = 15; export const ORGANIZATION_PASSWORD_MIN_LENGTH = 4; export const ORGANIZATION_PASSWORD_MAX_LENGTH = 15; + +export const ORGANIZATION_IMAGE_MAX_COUNT = 1; diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index e89aa80..ee171f2 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -4,6 +4,7 @@ import Checkbox from "components/checkbox/Checkbox"; import AreaInput from "components/inputs/AreaInput/AreaInput"; import ImageInput from "components/inputs/ImageInput/ImageInput"; import TextInput from "components/inputs/TextInput/TextInput"; +import { ORGANIZATION_IMAGE_MAX_COUNT } from "constant/limit"; import { ORGANIZATION_TYPE_CHECKBOX_ITEM_LIST } from "feature/organizations/constants/orgnizationTypeCheckboxItemList"; import { validateOrganizationNameEmpty, @@ -165,7 +166,10 @@ const OrganizationForm = ({ isEditMode = false }: Props) => {

7. 우리 단체를 대표하는 이미지를 설정해보세요!

- +
From 2ebe424ecf59b1e96da503719e6f7da77ed4f639 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:45:16 +0900 Subject: [PATCH 13/14] =?UTF-8?q?feat(Organization):=20=EB=AC=B8=ED=95=AD?= =?UTF-8?q?=20=EC=88=9C=EC=84=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/inputs/ImageInput/ImageInput.tsx | 10 ++++++++-- .../components/OrganizationForm.tsx | 16 ++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/components/inputs/ImageInput/ImageInput.tsx b/src/components/inputs/ImageInput/ImageInput.tsx index a2af11d..1d7787e 100644 --- a/src/components/inputs/ImageInput/ImageInput.tsx +++ b/src/components/inputs/ImageInput/ImageInput.tsx @@ -8,9 +8,15 @@ interface Props { maxImageCount?: number; // 최대 입력할 수 있는 이미지 개수 existingImageUrlList?: string[]; // 이미 서버에 존재하던 이미지 URL 리스트 label?: string; // 이미지 인풋 레이블 + className?: string; } -const ImageInput: React.FC = ({ maxImageCount = 1, existingImageUrlList = [], label }) => { +const ImageInput: React.FC = ({ + maxImageCount = 1, + existingImageUrlList = [], + label, + className, +}) => { const imageUrlRef = useRef(null); // 이미 서버에 존재하던 이미지 URL 리스트 const fileInputRef = useRef(null); // 로컬에서 업로드한 이미지 리스트 const fileInputUniqueIndexCount = useRef(0); // 로컬 이미지 리스트의 uniqueIndex를 위한 카운트 @@ -89,7 +95,7 @@ const ImageInput: React.FC = ({ maxImageCount = 1, existingImageUrlList = }, [imageUrlListState]); return ( - +

최대 {maxImageCount}장 선택

diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index ee171f2..16ad092 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -138,8 +138,14 @@ const OrganizationForm = ({ isEditMode = false }: Props) => { className="organization-form-item" value={areaId} /> +

5. 우리 단체를 대표하는 이미지를 설정해보세요!

+ -

5. 단체를 비공개로 설정할 수 있어요!

+

6. 단체를 비공개로 설정할 수 있어요!

handleSetIsPublic(value)} @@ -150,7 +156,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => {
-

6. 비밀번호를 설정해 보아요!

+

7. 비밀번호를 설정해 보아요!

{ />
-

7. 우리 단체를 대표하는 이미지를 설정해보세요!

- - ); From 1656c360a92d1251c7af05c1bf93424afc97cd73 Mon Sep 17 00:00:00 2001 From: jaychang99 Date: Sun, 17 Sep 2023 11:47:13 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix(Organization):=20=EB=8B=A8=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20=ED=8F=BC=EC=97=90=EC=84=9C=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20input=20type=20password=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/inputs/TextInput/TextInput.tsx | 7 +++++-- src/feature/organizations/components/OrganizationForm.tsx | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/inputs/TextInput/TextInput.tsx b/src/components/inputs/TextInput/TextInput.tsx index 8cbfac7..24c76c3 100644 --- a/src/components/inputs/TextInput/TextInput.tsx +++ b/src/components/inputs/TextInput/TextInput.tsx @@ -1,10 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, InputHTMLAttributes } from "react"; import styled from "@emotion/styled"; import { css, Theme } from "@emotion/react"; import CheckIcon from "components/inputs/TextInput/CheckIcon"; import { TConditionCheck } from "./types/TConditionCheck"; -interface Props { +interface Props extends InputHTMLAttributes { name?: string; label?: string; value?: string; @@ -26,6 +26,7 @@ const TextInput: React.FC = ({ multiline = false, onTextChange, className, + ...props // input 태그의 나머지 속성들 }) => { const [status, setStatus] = useState(value === "" ? "default" : "success"); // default / success / invalid / focus const [enteredValue, setEnteredValue] = useState(value); @@ -104,6 +105,7 @@ const TextInput: React.FC = ({ onFocus={handleFocus} onBlur={handleBlur} data-status={status} + {...props} /> ) : ( = ({ onFocus={handleFocus} onBlur={handleBlur} data-status={status} + {...props} /> )} {status == "success" && ( diff --git a/src/feature/organizations/components/OrganizationForm.tsx b/src/feature/organizations/components/OrganizationForm.tsx index 16ad092..5deb0f6 100644 --- a/src/feature/organizations/components/OrganizationForm.tsx +++ b/src/feature/organizations/components/OrganizationForm.tsx @@ -158,6 +158,7 @@ const OrganizationForm = ({ isEditMode = false }: Props) => {

7. 비밀번호를 설정해 보아요!