From ff8198bdace6bff566937c86c8d6a3f88ebcafa4 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Wed, 30 Oct 2024 16:11:11 +0900 Subject: [PATCH 01/52] =?UTF-8?q?FE-Refactor:=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=ED=98=B8=EC=B6=9C=20=EC=8B=9C=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=EC=A0=95=EB=B3=B4=20populate=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/controllers/reservationControllers.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/api/src/controllers/reservationControllers.ts b/apps/api/src/controllers/reservationControllers.ts index a747d16a..77594ed5 100644 --- a/apps/api/src/controllers/reservationControllers.ts +++ b/apps/api/src/controllers/reservationControllers.ts @@ -48,10 +48,7 @@ export const getUserReservations = async ( }) .populate("user", "name email") .populate("attendees", "name email") - .populate({ - path: "item", - select: "name itemType", - }) + .populate("item", "name itemType") .sort({ itemType: 1, startAt: 1 }); if (userReservations.length === 0) { @@ -98,7 +95,7 @@ export const getReservationsByTypeAndDate = async ( const reservations: IReservation[] = await Reservation.find(query) .populate("user", "name email") .populate("attendees", "name email") - .populate({ path: "item", select: "name", model: itemTypeToModel[itemType] }) + .populate("item", "name") .sort({ status: 1, startAt: 1 }); res.status(200).json(reservations); From dae58fc682e6ca27ea475c7c20f3ccdebd6fea03 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Fri, 1 Nov 2024 13:58:30 +0900 Subject: [PATCH 02/52] =?UTF-8?q?FE-Feat:=20dropdown,=20modal=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/(seats)/_components/SeatGrid.tsx | 2 +- .../_components/CategoryEditDropdown.tsx | 33 ++++++++++ .../(items)/_components/CategoryListItem.tsx | 66 ++++--------------- .../(items)/_components/ConfirmationModal.tsx | 33 ++++++++++ 4 files changed, 81 insertions(+), 53 deletions(-) create mode 100644 apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx create mode 100644 apps/web/app/admin/(items)/_components/ConfirmationModal.tsx diff --git a/apps/web/app/(seats)/_components/SeatGrid.tsx b/apps/web/app/(seats)/_components/SeatGrid.tsx index c7a53984..47de0c35 100644 --- a/apps/web/app/(seats)/_components/SeatGrid.tsx +++ b/apps/web/app/(seats)/_components/SeatGrid.tsx @@ -4,9 +4,9 @@ import { SEAT_GRID } from "@ui/src/utils/constants/seats"; import useSeatStatus from "@ui/src/hooks/useSeatStatus"; import { useQuery } from "@tanstack/react-query"; import { type ISeat } from "@repo/types"; +import { getAllSeats } from "@/api/seats"; import { SeatProvider } from "../context/SeatContext"; import SeatBlock from "./SeatBlock"; -import { getAllSeats } from "@/api/seats"; export default function SeatGrid(): JSX.Element { // 탠스택쿼리로 바꿀 예정 (data / Loading) diff --git a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx new file mode 100644 index 00000000..1bf3cd43 --- /dev/null +++ b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx @@ -0,0 +1,33 @@ +import { Modal } from "@ui/index"; +import Dropdown from "@ui/src/components/common/Dropdown"; +import { type Dispatch } from "react"; + +interface CategoryEditDropdownProps { + isModifying: boolean; + setIsModifying: Dispatch>; +} +export default function CategoryEditDropdown({ isModifying, setIsModifying }: CategoryEditDropdownProps): JSX.Element { + return ( + { + if (value === "수정") { + setIsModifying(true); + } + }} + size="sm" + > + + + + 이름 편집 + + + + 삭제 + + + + + ); +} diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 45f878b9..76a83576 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -1,39 +1,40 @@ "use client"; -import { Modal } from "@ui/index"; -import Dropdown from "@ui/src/components/common/Dropdown"; import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useRef, useState } from "react"; +import CategoryEditDropdown from "./CategoryEditDropdown"; +import ConfirmationModal from "./ConfirmationModal"; const MOCK_TITLE = "회의실"; export default function CategoryListItem(): JSX.Element { - const [isModify, setIsModify] = useState(false); - const [changeName, setChangeName] = useState(""); + const [isModifying, setIsModifying] = useState(false); + const [inputValue, setInputValue] = useState(""); const inputRef = useRef(null); useOnClickOutside(inputRef, () => { - if (isModify) { - setIsModify(false); + if (isModifying) { + setIsModifying(false); } }); + return ( - {isModify ? ( + {isModifying ? ( { - setChangeName(e.target.value); + setInputValue(e.target.value); }} onKeyDown={(e) => { if (e.key === "Enter") { // TODO: input 데이터 patch // eslint-disable-next-line no-console - console.log(changeName); + console.log(inputValue); } }} /> @@ -41,48 +42,9 @@ export default function CategoryListItem(): JSX.Element { MOCK_TITLE )} - - { - if (value === "수정") { - setIsModify(true); - } - }} - size="sm" - > - - - - 이름 편집 - - - - 삭제 - - - - - - -

{MOCK_TITLE}

-

해당 카테고리를 삭제하시겠습니까?

-
- -

삭제된 카테고리는 복구할 수 없습니다.

-

카테고리 하위의 아이템들도 함께 삭제됩니다.

-
- { - // TODO: 삭제 로직 작성 - }} - confirmText="확인" - cancelText="취소" - > - 예 - -
-
+ + +
); } diff --git a/apps/web/app/admin/(items)/_components/ConfirmationModal.tsx b/apps/web/app/admin/(items)/_components/ConfirmationModal.tsx new file mode 100644 index 00000000..0e41c6de --- /dev/null +++ b/apps/web/app/admin/(items)/_components/ConfirmationModal.tsx @@ -0,0 +1,33 @@ +import { Modal } from "@ui/index"; +import { type PropsWithChildren } from "react"; + +interface ConfirmationModalProps extends PropsWithChildren { + Title: string; +} + +export default function ConfirmationModal({ Title, children }: ConfirmationModalProps): JSX.Element { + return ( + + {children} + + +

{Title}

+

해당 카테고리를 삭제하시겠습니까?

+
+ +

삭제된 카테고리는 복구할 수 없습니다.

+

카테고리 하위의 아이템들도 함께 삭제됩니다.

+
+ { + // TODO: 삭제 로직 작성 + }} + confirmText="확인" + cancelText="취소" + > + 예 + +
+
+ ); +} From beb3c72cdf6019b8e22ed6bf3d90d2efe6db311d Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sun, 3 Nov 2024 19:23:52 +0900 Subject: [PATCH 03/52] =?UTF-8?q?FE-Feat:=20=ED=9A=8C=EC=9D=98=EC=8B=A4=20?= =?UTF-8?q?=ED=95=98=EC=9C=84=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/CategoryList.tsx | 5 +- .../(items)/_components/CategoryListItem.tsx | 78 ++++++++++++------- .../_components/CategoryListSubItem.tsx | 52 +++++++++++++ 3 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx diff --git a/apps/web/app/admin/(items)/_components/CategoryList.tsx b/apps/web/app/admin/(items)/_components/CategoryList.tsx index ef6a52fb..dd97eb76 100644 --- a/apps/web/app/admin/(items)/_components/CategoryList.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryList.tsx @@ -1,9 +1,12 @@ +"use client"; + import CategoryListItem from "./CategoryListItem"; export default function CategoryList(): JSX.Element { return (
- + {/* todo 데이터 받아서 mapping */} +
); } diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 76a83576..b9d0f24d 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -3,14 +3,19 @@ import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useRef, useState } from "react"; +import { motion } from "framer-motion"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; +import CategoryListSubItem from "./CategoryListSubItem"; -const MOCK_TITLE = "회의실"; +interface CategoryListItemProps { + title: string; +} -export default function CategoryListItem(): JSX.Element { +export default function CategoryListItem({ title }: CategoryListItemProps): JSX.Element { const [isModifying, setIsModifying] = useState(false); const [inputValue, setInputValue] = useState(""); + const [isOpen, setIsOpen] = useState(false); const inputRef = useRef(null); useOnClickOutside(inputRef, () => { @@ -19,32 +24,49 @@ export default function CategoryListItem(): JSX.Element { } }); + const toggleListItem = (): void => { + setIsOpen(!isOpen); + }; + return ( - - - {isModifying ? ( - { - setInputValue(e.target.value); - }} - onKeyDown={(e) => { - if (e.key === "Enter") { - // TODO: input 데이터 patch - // eslint-disable-next-line no-console - console.log(inputValue); - } - }} - /> - ) : ( - MOCK_TITLE - )} - - - - - + <> + + + {isModifying ? ( + { + setInputValue(e.target.value); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + // TODO: input 데이터 patch + // eslint-disable-next-line no-console + console.log(inputValue); + } + }} + /> + ) : ( + title + )} + + + + + + {isOpen ? ( + + + + ) : null} + ); } diff --git a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx new file mode 100644 index 00000000..76fe93bb --- /dev/null +++ b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx @@ -0,0 +1,52 @@ +"use client"; + +import ListItem from "@ui/src/components/common/ListItem"; +import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; +import { useRef, useState } from "react"; +import CategoryEditDropdown from "./CategoryEditDropdown"; +import ConfirmationModal from "./ConfirmationModal"; + +interface CategoryListSubItemProps { + title: string; +} + +export default function CategoryListSubItem({ title }: CategoryListSubItemProps): JSX.Element { + const [isModifying, setIsModifying] = useState(false); + const [inputValue, setInputValue] = useState(""); + const inputRef = useRef(null); + + useOnClickOutside(inputRef, () => { + if (isModifying) { + setIsModifying(false); + } + }); + + return ( + + + {isModifying ? ( + { + setInputValue(e.target.value); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + // TODO: input 데이터 patch + // eslint-disable-next-line no-console + console.log(inputValue); + } + }} + /> + ) : ( + title + )} + + + + + + ); +} From d4e0cda10e3a6fe9476e4c6431cb1e71a2b0dda5 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sun, 3 Nov 2024 21:04:45 +0900 Subject: [PATCH 04/52] =?UTF-8?q?FE-Feat:=20=EC=95=84=EC=9D=B4=ED=85=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B2=84=ED=8A=BC=20,=20=EC=95=84?= =?UTF-8?q?=EC=BD=94=EB=94=94=EC=96=B8=20=ED=8E=BC=EC=B9=98=EA=B8=B0?= =?UTF-8?q?=EC=9A=A9=20caret=20=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 --- .../(items)/_components/CategoryListItem.tsx | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index b9d0f24d..76d1251b 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -4,6 +4,7 @@ import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useRef, useState } from "react"; import { motion } from "framer-motion"; +import { PlusIcon, TriangleIcon } from "@ui/public"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; import CategoryListSubItem from "./CategoryListSubItem"; @@ -30,7 +31,7 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. return ( <> - + {isModifying ? ( - - - +
+ + + + +
+
{isOpen ? ( Date: Wed, 6 Nov 2024 20:02:22 +0900 Subject: [PATCH 05/52] =?UTF-8?q?FE-Feat:=20=ED=9A=8C=EC=9D=98=EC=8B=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=93=9C=EB=A1=9C=EC=9B=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddItemButton.tsx | 32 +++++++++++++++++++ .../admin/(items)/_components/AddItemForm.tsx | 0 .../(items)/_components/CategoryListItem.tsx | 10 ++---- 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 apps/web/app/admin/(items)/_components/AddItemButton.tsx create mode 100644 apps/web/app/admin/(items)/_components/AddItemForm.tsx diff --git a/apps/web/app/admin/(items)/_components/AddItemButton.tsx b/apps/web/app/admin/(items)/_components/AddItemButton.tsx new file mode 100644 index 00000000..887818aa --- /dev/null +++ b/apps/web/app/admin/(items)/_components/AddItemButton.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { PlusIcon } from "@ui/public"; +import { useState } from "react"; +import Sidebar from "@/components/common/Sidebar"; + +export default function AddItemButton(): JSX.Element { + const [isOpen, setIsOpen] = useState(false); + + const openDrawer = (): void => { + setIsOpen(true); + }; + + const closeDrawer = (): void => { + setIsOpen(false); + }; + return ( + <> + + + + hi + + + ); +} diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx new file mode 100644 index 00000000..e69de29b diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 76d1251b..237e7ec5 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -4,10 +4,11 @@ import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useRef, useState } from "react"; import { motion } from "framer-motion"; -import { PlusIcon, TriangleIcon } from "@ui/public"; +import { TriangleIcon } from "@ui/public"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; import CategoryListSubItem from "./CategoryListSubItem"; +import AddItemButton from "./AddItemButton"; interface CategoryListItemProps { title: string; @@ -54,12 +55,7 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. )}
- + From b304cad3107c964c45c805d9b79f3ca74d7586b5 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sun, 10 Nov 2024 15:05:01 +0900 Subject: [PATCH 06/52] =?UTF-8?q?FE-Feat:=20=ED=9A=8C=EC=9D=98=EC=8B=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=8F=BC=20=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/api/teams.ts | 2 +- .../(items)/_components/AddItemButton.tsx | 18 ++++--- .../admin/(items)/_components/AddItemForm.tsx | 52 +++++++++++++++++++ 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/apps/web/api/teams.ts b/apps/web/api/teams.ts index 5c1c5f7d..c572ff7e 100644 --- a/apps/web/api/teams.ts +++ b/apps/web/api/teams.ts @@ -54,5 +54,5 @@ export const updateTeam = async ({ teamId, newName }: UpdateRequest): Promise { - setIsOpen(true); + const openPanel = (): void => { + setIsPanelOpen(true); }; - const closeDrawer = (): void => { - setIsOpen(false); + const closePanel = (): void => { + setIsPanelOpen(false); }; return ( <> - - hi + +

회의실 추가

+
); diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index e69de29b..f750a3a5 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -0,0 +1,52 @@ +"use client"; + +import { Input, Radio } from "@ui/index"; +import Dropdown from "@ui/src/components/common/Dropdown"; +import { useState } from "react"; + +export default function AddItemForm(): JSX.Element { + const [category, setCategory] = useState("카테고리 선택"); + + return ( +
+
+ + 사용 가능 + 사용 중 + 점검 중 + +
+ + +
+ { + if (typeof value === "string") { + setCategory(value); + } + }} + isError={false} + errorMessage="Error" + > + 카테고리 + + Option 1 + Option 2 + Option 3 + + +
+ + +
+ ); +} + +// "name": "string", +// "description": "string", +// "status": "available", +// "imageUrl": "string", +// "category": "string", +// "capacity": 0, +// "location": "string" From 26e31eb0ae3138930f7c01294c34e9aba9b824d2 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sun, 10 Nov 2024 17:45:01 +0900 Subject: [PATCH 07/52] =?UTF-8?q?FE-Feat:=20=EC=82=AC=EC=9D=B4=EB=93=9C?= =?UTF-8?q?=ED=8C=A8=EB=84=90=20listItem=20=EB=8E=81=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99,=20=EC=B6=94=EA=B0=80/=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=8B=9C=20=EB=8F=99=EC=9D=BC=ED=95=9C=20=ED=8F=BC=20=EC=9E=AC?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B2=84=ED=8A=BC=20click=EC=8B=9C=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddItemButton.tsx | 37 +++++---------- .../_components/CategoryEditDropdown.tsx | 4 +- .../(items)/_components/CategoryListItem.tsx | 46 +++++++++++++++---- .../_components/CategoryListSubItem.tsx | 39 ++-------------- apps/web/app/admin/(items)/meetings/page.tsx | 7 +-- 5 files changed, 57 insertions(+), 76 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddItemButton.tsx b/apps/web/app/admin/(items)/_components/AddItemButton.tsx index ae575bfd..b22a262c 100644 --- a/apps/web/app/admin/(items)/_components/AddItemButton.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemButton.tsx @@ -1,34 +1,19 @@ "use client"; import { PlusIcon } from "@ui/public"; -import { useState } from "react"; -import Sidebar from "@/components/common/Sidebar"; -import AddItemForm from "./AddItemForm"; -export default function AddItemButton(): JSX.Element { - const [isPanelOpen, setIsPanelOpen] = useState(true); - - const openPanel = (): void => { - setIsPanelOpen(true); - }; +interface AddItemButtonProps { + onClick: () => void; +} - const closePanel = (): void => { - setIsPanelOpen(false); - }; +export default function AddItemButton({ onClick }: AddItemButtonProps): JSX.Element { return ( - <> - - - -

회의실 추가

- -
- + ); } diff --git a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx index 1bf3cd43..71f53dd6 100644 --- a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx @@ -3,7 +3,7 @@ import Dropdown from "@ui/src/components/common/Dropdown"; import { type Dispatch } from "react"; interface CategoryEditDropdownProps { - isModifying: boolean; + isModifying?: boolean; setIsModifying: Dispatch>; } export default function CategoryEditDropdown({ isModifying, setIsModifying }: CategoryEditDropdownProps): JSX.Element { @@ -20,7 +20,7 @@ export default function CategoryEditDropdown({ isModifying, setIsModifying }: Ca - 이름 편집 + 수정 diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 237e7ec5..466a5823 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -5,24 +5,50 @@ import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useRef, useState } from "react"; import { motion } from "framer-motion"; import { TriangleIcon } from "@ui/public"; +import Sidebar from "@/components/common/Sidebar"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; import CategoryListSubItem from "./CategoryListSubItem"; import AddItemButton from "./AddItemButton"; +import AddItemForm from "./AddItemForm"; interface CategoryListItemProps { title: string; } export default function CategoryListItem({ title }: CategoryListItemProps): JSX.Element { - const [isModifying, setIsModifying] = useState(false); + const [isModifyingCategoryName, setIsModifyingCategoryName] = useState(false); const [inputValue, setInputValue] = useState(""); + const [isOpen, setIsOpen] = useState(false); + const [isPanelOpen, setIsPanelOpen] = useState(true); + const [panelState, setPanelState] = useState(""); + + const openPanelToAdd = (): void => { + if (!isPanelOpen) { + setIsPanelOpen(true); + setPanelState("add"); + } + }; + + const openPanelToEdit = (): void => { + if (!isPanelOpen) { + setIsPanelOpen(true); + setPanelState("edit"); + } + }; + + const closePanel = (): void => { + if (isPanelOpen) { + setIsPanelOpen(false); + } + }; + const inputRef = useRef(null); useOnClickOutside(inputRef, () => { - if (isModifying) { - setIsModifying(false); + if (isModifyingCategoryName) { + setIsModifyingCategoryName(false); } }); @@ -34,10 +60,10 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. <> - {isModifying ? ( + {isModifyingCategoryName ? ( { setInputValue(e.target.value); @@ -55,9 +81,9 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. )}
- + - +
); } diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index e85f725e..edac8a7c 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -2,12 +2,12 @@ import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { motion } from "framer-motion"; import { TriangleIcon } from "@ui/public"; import { type IEquipment, type IRoom } from "@repo/types"; import Sidebar from "@/components/common/Sidebar"; -import { patchItem, postNewItem } from "@/api/meetings"; +import { getAllRooms, patchItem, postNewItem } from "@/api/meetings"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; import CategoryListSubItem from "./CategoryListSubItem"; @@ -28,6 +28,22 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. const inputRef = useRef(null); + const [items, setItems] = useState([]); + + useEffect(() => { + const fetchItems = async (): Promise => { + try { + const res = await getAllRooms(); + const items = res.filter((item) => item.category.name === title); + setItems(items); + } catch (error) { + throw new Error(); + } + }; + + void fetchItems(); + }, []); + useOnClickOutside(inputRef, () => { if (isModifyingCategoryName) { setIsModifyingCategoryName(false); @@ -118,11 +134,9 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. transition={{ duration: 0.3, ease: "easeInOut" }} className="overflow-hidden pl-24" > - - - - - + {items.map((item) => ( + + ))}
) : null} From c94af4f9b4fec8b4bbf21c8ed9f4b055aa510a2f Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 21 Nov 2024 15:13:20 +0900 Subject: [PATCH 13/52] =?UTF-8?q?FE-Feat:=20room=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=ED=85=9C=20=EC=83=9D=EC=84=B1=20api=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/(items)/_components/AddItemForm.tsx | 57 ++++++++++++------- .../(items)/_components/CategoryList.tsx | 2 +- .../(items)/_components/CategoryListItem.tsx | 22 +++---- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index 1345dcad..b99e4499 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -1,23 +1,32 @@ "use client"; -import { type IEquipment, type ICategory, type IRoom } from "@repo/types"; -import { Input, Radio } from "@ui/index"; +import { type IEquipment, type ICategory, type IRoom, type TItemStatus } from "@repo/types"; +import { Input, Radio, notify } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; import { type FieldValues, useForm } from "react-hook-form"; import { useEffect, useState } from "react"; import { getAllCategories } from "@/api/meetings"; interface EditItemFormProps { + prevCategory: ICategory; defaultItem?: IRoom; - prevCategory: string; onSubmit: (data: FormData, itemId?: string) => Promise; } export default function EditItemForm({ defaultItem, prevCategory, onSubmit }: EditItemFormProps): JSX.Element { - const { register, handleSubmit, setValue } = useForm(); + const { register, handleSubmit, setValue } = useForm({ + defaultValues: { + name: "", + description: "", + capacity: 1, + location: "", + status: defaultItem ? defaultItem.status : "available", + category: prevCategory._id, + }, + }); const [categories, setCategories] = useState([]); - const [currentCategory, setCurrentCategory] = useState(prevCategory); + const [currentCategory, setCurrentCategory] = useState(prevCategory); useEffect(() => { const fetchCategories = async (): Promise => { @@ -33,24 +42,29 @@ export default function EditItemForm({ defaultItem, prevCategory, onSubmit }: Ed void fetchCategories(); }, []); - const itemId = defaultItem?._id; - - const handleFormSubmit = (data: FieldValues): void => { + const handleFormSubmit = async (data: FieldValues): Promise => { const formData = new FormData(); Object.entries(data).forEach(([key, value]) => { - data.append(key, value); + formData.set(key, value as string); }); - const res = onSubmit(formData, itemId); - console.log(res); + formData.set("category", currentCategory._id); + // formData.set("status", defaultItem ? defaultItem.status : "available"); + try { + const res = await onSubmit(formData, defaultItem?._id); + console.log(res); + } catch (error) { + notify({ type: "error", message: "제출실패" }); + } }; return ( - handleSubmit(handleFormSubmit)}> -
+ void handleSubmit(handleFormSubmit)(event)}> +
{ - setValue("status", value); + setValue("status", value as TItemStatus); }} > 사용 가능 @@ -61,21 +75,22 @@ export default function EditItemForm({ defaultItem, prevCategory, onSubmit }: Ed
{ - if (typeof value === "string") { - setCurrentCategory(value); - setValue("category", value); + const selectedCategory = categories.find((category) => category._id === value); + if (selectedCategory) { + setCurrentCategory(selectedCategory); + setValue("category", value as string); } }} isError={false} errorMessage="Error" > - {currentCategory} + {currentCategory.name} {categories.map((category) => { return ( - + {category.name} ); diff --git a/apps/web/app/admin/(items)/_components/CategoryList.tsx b/apps/web/app/admin/(items)/_components/CategoryList.tsx index ef967668..f4f17dd9 100644 --- a/apps/web/app/admin/(items)/_components/CategoryList.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryList.tsx @@ -25,7 +25,7 @@ export default function CategoryList(): JSX.Element {
{categories.map((category) => ( - + ))}
diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index edac8a7c..256da1c9 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -5,7 +5,7 @@ import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; import { useEffect, useRef, useState } from "react"; import { motion } from "framer-motion"; import { TriangleIcon } from "@ui/public"; -import { type IEquipment, type IRoom } from "@repo/types"; +import { type ICategory, type IEquipment, type IRoom } from "@repo/types"; import Sidebar from "@/components/common/Sidebar"; import { getAllRooms, patchItem, postNewItem } from "@/api/meetings"; import CategoryEditDropdown from "./CategoryEditDropdown"; @@ -15,10 +15,10 @@ import AddItemButton from "./AddItemButton"; import EditItemForm from "./AddItemForm"; interface CategoryListItemProps { - title: string; + prevCategory: ICategory; } -export default function CategoryListItem({ title }: CategoryListItemProps): JSX.Element { +export default function CategoryListItem({ prevCategory }: CategoryListItemProps): JSX.Element { const [isModifyingCategoryName, setIsModifyingCategoryName] = useState(false); const [inputValue, setInputValue] = useState(""); @@ -28,21 +28,21 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. const inputRef = useRef(null); - const [items, setItems] = useState([]); + const [rooms, setRooms] = useState([]); useEffect(() => { const fetchItems = async (): Promise => { try { const res = await getAllRooms(); - const items = res.filter((item) => item.category.name === title); - setItems(items); + const items = res.filter((item) => item.category.name === prevCategory.name); + setRooms(items); } catch (error) { throw new Error(); } }; void fetchItems(); - }, []); + }, [prevCategory]); useOnClickOutside(inputRef, () => { if (isModifyingCategoryName) { @@ -106,12 +106,12 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. }} /> ) : ( - title + prevCategory.name )}
- +
@@ -134,7 +134,7 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX. transition={{ duration: 0.3, ease: "easeInOut" }} className="overflow-hidden pl-24" > - {items.map((item) => ( + {rooms.map((item) => ( ))} @@ -142,7 +142,7 @@ export default function CategoryListItem({ title }: CategoryListItemProps): JSX.

회의실 {panelState === "add" ? "추가" : "수정"}

- +
); From 6f9176806d3bf2426f131d6aff82a9e8912b786a Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 21 Nov 2024 15:27:14 +0900 Subject: [PATCH 14/52] =?UTF-8?q?FE-Feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80=20side=20panel=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddCategoryButton.tsx | 12 +++++-- .../(items)/_components/AddCategoryForm.tsx | 16 +++++++++ .../app/admin/(items)/_components/header.tsx | 33 +++++++++++++++++++ apps/web/app/admin/(items)/meetings/page.tsx | 7 ++-- 4 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 apps/web/app/admin/(items)/_components/AddCategoryForm.tsx create mode 100644 apps/web/app/admin/(items)/_components/header.tsx diff --git a/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx b/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx index 617ba197..2c543367 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx @@ -1,5 +1,13 @@ import Button from "@ui/src/components/common/Button"; -export default function AddCategoryButton(): JSX.Element { - return ; +interface AddCategoryButtonProps { + onClick: () => void; +} + +export default function AddCategoryButton({ onClick }: AddCategoryButtonProps): JSX.Element { + return ( + + ); } diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx new file mode 100644 index 00000000..e147beb5 --- /dev/null +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { Button, Input } from "@ui/index"; +import { useForm } from "react-hook-form"; + +export default function AddCategoryForm(): JSX.Element { + const { register } = useForm(); + return ( + + + + + ); +} diff --git a/apps/web/app/admin/(items)/_components/header.tsx b/apps/web/app/admin/(items)/_components/header.tsx new file mode 100644 index 00000000..ed6c4d2b --- /dev/null +++ b/apps/web/app/admin/(items)/_components/header.tsx @@ -0,0 +1,33 @@ +"use client"; +import { useState } from "react"; +import Sidebar from "@/components/common/Sidebar"; +import AddCategoryButton from "./AddCategoryButton"; +import AddCategoryForm from "./AddCategoryForm"; + +export default function ItemsAdminHeader(): JSX.Element { + const [isPanelOpen, setIsPanelOpen] = useState(false); + + const openPanel = (): void => { + if (!isPanelOpen) { + setIsPanelOpen(true); + } + }; + + const closePanel = (): void => { + if (isPanelOpen) { + setIsPanelOpen(false); + } + }; + + return ( + <> +
+

회의실 관리

+ +
+ + + + + ); +} diff --git a/apps/web/app/admin/(items)/meetings/page.tsx b/apps/web/app/admin/(items)/meetings/page.tsx index 9d779e0d..1e2bcbaa 100644 --- a/apps/web/app/admin/(items)/meetings/page.tsx +++ b/apps/web/app/admin/(items)/meetings/page.tsx @@ -1,13 +1,10 @@ -import AddCategoryButton from "../_components/AddCategoryButton"; import CategoryList from "../_components/CategoryList"; +import ItemsAdminHeader from "../_components/header"; export default function Rooms(): JSX.Element { return (
-
-

회의실 관리

- -
+
From 86b8fbaeae7e4537ac06e8b7d1de3d45aa822be0 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Fri, 22 Nov 2024 14:06:19 +0900 Subject: [PATCH 15/52] =?UTF-8?q?FE-Feat:=20submit=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=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 --- .../(items)/_components/AddCategoryForm.tsx | 4 ++-- .../admin/(items)/_components/AddItemForm.tsx | 16 ++++++++++++---- .../(items)/_components/CategoryListItem.tsx | 2 +- .../web/app/admin/(items)/_components/header.tsx | 5 ++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx index e147beb5..256dbafa 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -6,9 +6,9 @@ import { useForm } from "react-hook-form"; export default function AddCategoryForm(): JSX.Element { const { register } = useForm(); return ( -
+ -
diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index b99e4499..1f03189a 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -1,19 +1,25 @@ "use client"; import { type IEquipment, type ICategory, type IRoom, type TItemStatus } from "@repo/types"; -import { Input, Radio, notify } from "@ui/index"; +import { Button, Input, Radio, notify } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; import { type FieldValues, useForm } from "react-hook-form"; import { useEffect, useState } from "react"; import { getAllCategories } from "@/api/meetings"; interface EditItemFormProps { + panelState: string; prevCategory: ICategory; defaultItem?: IRoom; onSubmit: (data: FormData, itemId?: string) => Promise; } -export default function EditItemForm({ defaultItem, prevCategory, onSubmit }: EditItemFormProps): JSX.Element { +export default function EditItemForm({ + panelState, + defaultItem, + prevCategory, + onSubmit, +}: EditItemFormProps): JSX.Element { const { register, handleSubmit, setValue } = useForm({ defaultValues: { name: "", @@ -58,7 +64,7 @@ export default function EditItemForm({ defaultItem, prevCategory, onSubmit }: Ed }; return ( -
void handleSubmit(handleFormSubmit)(event)}> + void handleSubmit(handleFormSubmit)(event)} className="flex flex-col">
- + ); } diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 256da1c9..bdc57327 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -142,7 +142,7 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps

회의실 {panelState === "add" ? "추가" : "수정"}

- +
); diff --git a/apps/web/app/admin/(items)/_components/header.tsx b/apps/web/app/admin/(items)/_components/header.tsx index ed6c4d2b..ba4677d6 100644 --- a/apps/web/app/admin/(items)/_components/header.tsx +++ b/apps/web/app/admin/(items)/_components/header.tsx @@ -26,7 +26,10 @@ export default function ItemsAdminHeader(): JSX.Element {
- +
+

카테고리 추가

+ +
); From 830864bbb9753199e2c0619336afddf062f56586 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Fri, 22 Nov 2024 14:38:01 +0900 Subject: [PATCH 16/52] =?UTF-8?q?FE-Feat:=20form=20input=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=20=EC=84=A4=EC=A0=95=20useEffect=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 --- .../admin/(items)/_components/AddItemForm.tsx | 21 +++++++++--- .../(items)/_components/CategoryListItem.tsx | 32 ++++++++++++++----- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index 1f03189a..50e399c9 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -22,18 +22,29 @@ export default function EditItemForm({ }: EditItemFormProps): JSX.Element { const { register, handleSubmit, setValue } = useForm({ defaultValues: { - name: "", - description: "", - capacity: 1, - location: "", + name: defaultItem ? defaultItem.name : "", + description: defaultItem ? defaultItem.description : "", + capacity: defaultItem ? defaultItem.capacity : 1, + location: defaultItem ? defaultItem.location : "", status: defaultItem ? defaultItem.status : "available", - category: prevCategory._id, + category: defaultItem ? defaultItem.category._id : prevCategory._id, }, }); const [categories, setCategories] = useState([]); const [currentCategory, setCurrentCategory] = useState(prevCategory); + useEffect(() => { + if (defaultItem) { + setValue("name", defaultItem.name); + setValue("description", defaultItem.description); + setValue("capacity", defaultItem.capacity); + setValue("location", defaultItem.location); + setValue("status", defaultItem.status); + setValue("category", defaultItem.category._id); + } + }, [defaultItem, setValue]); + useEffect(() => { const fetchCategories = async (): Promise => { try { diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index bdc57327..6dc42f18 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -25,6 +25,7 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps const [isOpen, setIsOpen] = useState(false); const [isPanelOpen, setIsPanelOpen] = useState(false); const [panelState, setPanelState] = useState(""); + const [defaultItem, setDefaultItem] = useState(); const inputRef = useRef(null); @@ -42,7 +43,7 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps }; void fetchItems(); - }, [prevCategory]); + }, [prevCategory, isPanelOpen]); useOnClickOutside(inputRef, () => { if (isModifyingCategoryName) { @@ -52,15 +53,17 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps const openPanelToAdd = (): void => { if (!isPanelOpen) { - setIsPanelOpen(true); setPanelState("add"); + setIsPanelOpen(true); } }; - const openPanelToEdit = (): void => { + const openPanelToEdit = (item: IRoom): void => { if (!isPanelOpen) { - setIsPanelOpen(true); setPanelState("edit"); + setIsPanelOpen(true); + setDefaultItem(item); + console.log(item); } }; @@ -77,9 +80,11 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps const handleSubmitForm = async (data: FormData, itemId?: string): Promise => { if (panelState === "add") { const res = await postNewItem("room", data); + closePanel(); return res; } else if (panelState === "edit" && itemId) { const res = await patchItem(itemId, data); + closePanel(); return res; } throw new Error("잘못된 입력입니다. 새로고침 후 다시 시도해주세요"); @@ -129,20 +134,31 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps {isOpen ? ( {rooms.map((item) => ( - + { + openPanelToEdit(item); + }} + /> ))} ) : null}

회의실 {panelState === "add" ? "추가" : "수정"}

- +
); From 0f4cfb89f491c09caee1263cfb840df3c1422d72 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Fri, 29 Nov 2024 17:32:32 +0900 Subject: [PATCH 17/52] =?UTF-8?q?FE-Feat:=20=EC=82=AC=EC=9D=B4=EB=93=9C?= =?UTF-8?q?=ED=8C=A8=EB=84=90=20=EB=B2=84=ED=8A=BC=20=ED=95=98=EB=8B=A8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddCategoryForm.tsx | 7 +- .../admin/(items)/_components/AddItemForm.tsx | 88 ++++++++++--------- .../(items)/_components/CategoryListItem.tsx | 8 +- .../app/admin/(items)/_components/header.tsx | 5 +- 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx index 256dbafa..83a98050 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -6,8 +6,11 @@ import { useForm } from "react-hook-form"; export default function AddCategoryForm(): JSX.Element { const { register } = useForm(); return ( -
- + +
+

카테고리 추가

+ +
diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index 50e399c9..6c88b0d0 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -75,48 +75,54 @@ export default function EditItemForm({ }; return ( - void handleSubmit(handleFormSubmit)(event)} className="flex flex-col"> -
- { - setValue("status", value as TItemStatus); - }} - > - 사용 가능 - 사용 불가 - + void handleSubmit(handleFormSubmit)(event)} + className="flex h-full flex-col justify-between" + > +
+

회의실 {panelState === "add" ? "추가" : "수정"}

+
+ { + setValue("status", value as TItemStatus); + }} + > + 사용 가능 + 사용 불가 + +
+ + +
+ { + const selectedCategory = categories.find((category) => category._id === value); + if (selectedCategory) { + setCurrentCategory(selectedCategory); + setValue("category", value as string); + } + }} + isError={false} + errorMessage="Error" + > + {currentCategory.name} + + {categories.map((category) => { + return ( + + {category.name} + + ); + })} + + +
+ +
- - -
- { - const selectedCategory = categories.find((category) => category._id === value); - if (selectedCategory) { - setCurrentCategory(selectedCategory); - setValue("category", value as string); - } - }} - isError={false} - errorMessage="Error" - > - {currentCategory.name} - - {categories.map((category) => { - return ( - - {category.name} - - ); - })} - - -
- - diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 6dc42f18..f3c3998d 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -121,14 +121,11 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps
{isOpen ? ( @@ -152,7 +149,6 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps ) : null} -

회의실 {panelState === "add" ? "추가" : "수정"}

-
-

카테고리 추가

- -
+
); From 1e6c18333a1e763978ec1410cbcf0c1fcf25f202 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sun, 1 Dec 2024 21:27:38 +0900 Subject: [PATCH 18/52] =?UTF-8?q?FE-Refactor:=20zustand=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9,=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C,=20=EC=82=AC=EC=9D=B4=EB=93=9C=EC=9D=B4=ED=8E=99?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EA=B1=B0,=20sidePanel=20=ED=95=9C?= =?UTF-8?q?=EA=B0=9C=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 --- apps/web/api/meetings.ts | 11 ++ .../web/app/(seats)/_components/SeatBlock.tsx | 2 +- .../app/(seats)/_components/SeatButton.tsx | 2 +- .../admin/(items)/_components/AddItemForm.tsx | 5 +- .../_components/CategoryEditDropdown.tsx | 7 +- .../(items)/_components/CategoryList.tsx | 41 ++++-- .../(items)/_components/CategoryListItem.tsx | 122 ++++++------------ .../_components/CategoryListSubItem.tsx | 29 ++++- .../(items)/_components/ConfirmationModal.tsx | 13 +- .../admin/(items)/_components/SidePanel.tsx | 19 +++ .../app/admin/(items)/_components/header.tsx | 30 ++--- .../admin/(items)/_store/useMeetingsStore.tsx | 83 ++++++++++++ 12 files changed, 227 insertions(+), 137 deletions(-) create mode 100644 apps/web/app/admin/(items)/_components/SidePanel.tsx create mode 100644 apps/web/app/admin/(items)/_store/useMeetingsStore.tsx diff --git a/apps/web/api/meetings.ts b/apps/web/api/meetings.ts index 0566c28c..be12c4c4 100644 --- a/apps/web/api/meetings.ts +++ b/apps/web/api/meetings.ts @@ -45,3 +45,14 @@ export const patchItem = async (itemId: string, body: FormData): Promise => { + const { data } = await axiosRequester({ + options: { + method: "DELETE", + url: API_ENDPOINTS.ITEMS.DELETE_ITEM(itemId), + }, + }); + + return data; +}; diff --git a/apps/web/app/(seats)/_components/SeatBlock.tsx b/apps/web/app/(seats)/_components/SeatBlock.tsx index 6e480884..c92f97be 100644 --- a/apps/web/app/(seats)/_components/SeatBlock.tsx +++ b/apps/web/app/(seats)/_components/SeatBlock.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + "use client"; import { clsx } from "clsx"; diff --git a/apps/web/app/(seats)/_components/SeatButton.tsx b/apps/web/app/(seats)/_components/SeatButton.tsx index 9d7fd760..8f5c98f9 100644 --- a/apps/web/app/(seats)/_components/SeatButton.tsx +++ b/apps/web/app/(seats)/_components/SeatButton.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + /* eslint-disable @typescript-eslint/no-unnecessary-condition */ "use client"; import { CancelIcon, RightIcon } from "@ui/public"; diff --git a/apps/web/app/admin/(items)/_components/AddItemForm.tsx b/apps/web/app/admin/(items)/_components/AddItemForm.tsx index 6c88b0d0..035e1dd8 100644 --- a/apps/web/app/admin/(items)/_components/AddItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddItemForm.tsx @@ -65,10 +65,9 @@ export default function EditItemForm({ formData.set(key, value as string); }); formData.set("category", currentCategory._id); - // formData.set("status", defaultItem ? defaultItem.status : "available"); try { - const res = await onSubmit(formData, defaultItem?._id); - console.log(res); + await onSubmit(formData, defaultItem?._id); + notify({ type: "success", message: `${panelState === "add" ? "추가" : "수정"} 되었습니다.` }); } catch (error) { notify({ type: "error", message: "제출실패" }); } diff --git a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx index 71f53dd6..0d8cdc9a 100644 --- a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx @@ -1,18 +1,17 @@ import { Modal } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; -import { type Dispatch } from "react"; interface CategoryEditDropdownProps { isModifying?: boolean; - setIsModifying: Dispatch>; + onClickEdit: () => void; } -export default function CategoryEditDropdown({ isModifying, setIsModifying }: CategoryEditDropdownProps): JSX.Element { +export default function CategoryEditDropdown({ isModifying, onClickEdit }: CategoryEditDropdownProps): JSX.Element { return ( { if (value === "수정") { - setIsModifying(true); + onClickEdit(); } }} size="sm" diff --git a/apps/web/app/admin/(items)/_components/CategoryList.tsx b/apps/web/app/admin/(items)/_components/CategoryList.tsx index f4f17dd9..a827b90e 100644 --- a/apps/web/app/admin/(items)/_components/CategoryList.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryList.tsx @@ -1,12 +1,13 @@ "use client"; -import { useEffect, useState } from "react"; -import { type ICategory } from "@repo/types"; -import { getAllCategories } from "@/api/meetings"; +import { useEffect } from "react"; +import { getAllCategories, getAllRooms } from "@/api/meetings"; +import useMeetingsStore from "../_store/useMeetingsStore"; +import SidePanel from "./SidePanel"; import CategoryListItem from "./CategoryListItem"; export default function CategoryList(): JSX.Element { - const [categories, setCategories] = useState([]); + const { categories, setCategories, rooms, setRooms } = useMeetingsStore(); useEffect(() => { const fetchCategories = async (): Promise => { @@ -18,16 +19,32 @@ export default function CategoryList(): JSX.Element { throw new Error(); } }; - void fetchCategories(); + + const fetchItems = async (): Promise => { + try { + const res = await getAllRooms(); + setRooms(res); + } catch (error) { + throw new Error(); + } + }; + + void fetchItems(); }, []); + + const filteredRoomsByCategory = categories.map((category) => ({ + categoryId: category._id, + rooms: rooms.filter((room) => room.category._id === category._id), + })); + return ( -
-
- {categories.map((category) => ( - - ))} -
-
+ <> + {categories.map((category) => { + const filteredRooms = filteredRoomsByCategory.find((item) => item.categoryId === category._id)?.rooms ?? []; + return ; + })} + + ); } diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index f3c3998d..45e04d44 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -2,74 +2,47 @@ import ListItem from "@ui/src/components/common/ListItem"; import { useOnClickOutside } from "@ui/src/hooks/useOnClickOutside"; -import { useEffect, useRef, useState } from "react"; -import { motion } from "framer-motion"; +import { type PropsWithChildren, useRef, useState, useEffect } from "react"; import { TriangleIcon } from "@ui/public"; -import { type ICategory, type IEquipment, type IRoom } from "@repo/types"; -import Sidebar from "@/components/common/Sidebar"; -import { getAllRooms, patchItem, postNewItem } from "@/api/meetings"; +import { type ICategory, type IRoom } from "@repo/types"; +import { motion } from "framer-motion"; +import { useSidebarStore } from "@/app/store/useSidebarStore"; +import useMeetingsStore from "../_store/useMeetingsStore"; +import AddItemButton from "./AddItemButton"; +import CategoryListSubItem from "./CategoryListSubItem"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; -import CategoryListSubItem from "./CategoryListSubItem"; -import AddItemButton from "./AddItemButton"; -import EditItemForm from "./AddItemForm"; -interface CategoryListItemProps { - prevCategory: ICategory; +interface CategoryListItemProps extends PropsWithChildren { + category: ICategory; + rooms: IRoom[]; } -export default function CategoryListItem({ prevCategory }: CategoryListItemProps): JSX.Element { - const [isModifyingCategoryName, setIsModifyingCategoryName] = useState(false); - const [inputValue, setInputValue] = useState(""); - +export default function CategoryListItem({ category, rooms }: CategoryListItemProps): JSX.Element { + const { isSidebarOpen, openSidebar } = useSidebarStore(); + const { setPanelState } = useMeetingsStore(); const [isOpen, setIsOpen] = useState(false); - const [isPanelOpen, setIsPanelOpen] = useState(false); - const [panelState, setPanelState] = useState(""); - const [defaultItem, setDefaultItem] = useState(); + const [isModifyingCategoryName, setIsModifyingCategoryName] = useState(false); + const [inputValue, setInputValue] = useState(""); const inputRef = useRef(null); - const [rooms, setRooms] = useState([]); - - useEffect(() => { - const fetchItems = async (): Promise => { - try { - const res = await getAllRooms(); - const items = res.filter((item) => item.category.name === prevCategory.name); - setRooms(items); - } catch (error) { - throw new Error(); - } - }; - - void fetchItems(); - }, [prevCategory, isPanelOpen]); - useOnClickOutside(inputRef, () => { if (isModifyingCategoryName) { setIsModifyingCategoryName(false); } }); - const openPanelToAdd = (): void => { - if (!isPanelOpen) { - setPanelState("add"); - setIsPanelOpen(true); - } - }; - - const openPanelToEdit = (item: IRoom): void => { - if (!isPanelOpen) { - setPanelState("edit"); - setIsPanelOpen(true); - setDefaultItem(item); - console.log(item); + useEffect(() => { + if (isModifyingCategoryName && inputRef.current) { + inputRef.current.focus(); } - }; + }, [isModifyingCategoryName]); - const closePanel = (): void => { - if (isPanelOpen) { - setIsPanelOpen(false); + const openPanelToAddItem = (): void => { + if (!isSidebarOpen) { + setPanelState("add"); + openSidebar(); } }; @@ -77,25 +50,13 @@ export default function CategoryListItem({ prevCategory }: CategoryListItemProps setIsOpen(!isOpen); }; - const handleSubmitForm = async (data: FormData, itemId?: string): Promise => { - if (panelState === "add") { - const res = await postNewItem("room", data); - closePanel(); - return res; - } else if (panelState === "edit" && itemId) { - const res = await patchItem(itemId, data); - closePanel(); - return res; - } - throw new Error("잘못된 입력입니다. 새로고침 후 다시 시도해주세요"); - }; - return ( <> {isModifyingCategoryName ? ( ) : ( - prevCategory.name + category.name )} +
- - - + { + openPanelToAddItem(); + }} + /> + {}}> + { + setIsModifyingCategoryName(true); + }} + />
+ - - ); -} - -// "name": "string", -// "description": "string", -// "status": "available", -// "imageUrl": "string", -// "category": "string", -// "capacity": 0, -// "location": "string" diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 45e04d44..2fb821e6 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -20,7 +20,7 @@ interface CategoryListItemProps extends PropsWithChildren { export default function CategoryListItem({ category, rooms }: CategoryListItemProps): JSX.Element { const { isSidebarOpen, openSidebar } = useSidebarStore(); - const { setPanelState } = useMeetingsStore(); + const { setPanelState, setCurrentItem, setCurrentCategory } = useMeetingsStore(); const [isOpen, setIsOpen] = useState(false); const [isModifyingCategoryName, setIsModifyingCategoryName] = useState(false); @@ -42,6 +42,7 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr const openPanelToAddItem = (): void => { if (!isSidebarOpen) { setPanelState("add"); + setCurrentCategory(category); openSidebar(); } }; diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx new file mode 100644 index 00000000..9e1a4de5 --- /dev/null +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { type TItemStatus } from "@repo/types"; +import { Button, Input, Radio } from "@ui/index"; +import Dropdown from "@ui/src/components/common/Dropdown"; +import { useForm } from "react-hook-form"; +import { useEffect } from "react"; +import useMeetingsStore from "../_store/useMeetingsStore"; + +export default function EditItemForm(): JSX.Element { + const { panelState, currentItem, categories, currentCategory } = useMeetingsStore(); + + const { register, handleSubmit, setValue, reset } = useForm({ + defaultValues: { + name: currentItem ? currentItem.name : "", + description: currentItem ? currentItem.description : "", + capacity: currentItem ? currentItem.capacity : 1, + location: currentItem ? currentItem.location : "", + status: currentItem ? currentItem.status : "available", + category: currentItem ? currentItem.category._id : categories[1]?._id, + }, + }); + + useEffect(() => { + if (panelState === "add") { + reset({ + name: "", + description: "", + capacity: 1, + location: "", + status: "available", + category: categories[1]?._id ?? "", + }); + } else if (panelState === "edit" && currentItem) { + reset({ + name: currentItem.name, + description: currentItem.description, + capacity: currentItem.capacity, + location: currentItem.location, + status: currentItem.status, + category: currentItem.category._id, + }); + } + }, [panelState, currentItem, reset, categories]); + + const handleFormSubmit = handleSubmit((data) => { + if (panelState === "add") { + console.log("add", data); + return data; + } + }); + + return ( +
handleSubmit} className="flex h-full flex-col justify-between"> +
+

회의실 {panelState === "add" ? "추가" : "수정"}

+
+ { + setValue("status", value as TItemStatus); + }} + > + 사용 가능 + 사용 불가 + +
+ + +
+ { + const selectedCategory = categories.find((category) => category._id === value); + if (selectedCategory) { + setValue("category", selectedCategory.name); + } + }} + isError={false} + errorMessage="Error" + > + {currentItem ? currentItem.category.name : ""} + + {categories.map((category) => { + return ( + + {category.name} + + ); + })} + + +
+ + +
+ +
+ ); +} + +// "name": "string", +// "description": "string", +// "status": "available", +// "imageUrl": "string", +// "category": "string", +// "capacity": 0, +// "location": "string" diff --git a/apps/web/app/admin/(items)/_components/SidePanel.tsx b/apps/web/app/admin/(items)/_components/SidePanel.tsx index dd5a8a5f..1e808451 100644 --- a/apps/web/app/admin/(items)/_components/SidePanel.tsx +++ b/apps/web/app/admin/(items)/_components/SidePanel.tsx @@ -2,9 +2,19 @@ import Sidebar from "@/components/common/Sidebar"; import { useSidebarStore } from "@/app/store/useSidebarStore"; +import useMeetingsStore from "../_store/useMeetingsStore"; +import EditItemForm from "./EditItemForm"; +import AddCategoryForm from "./AddCategoryForm"; + +const panelContents: Record = { + add: , + edit: , + category: , +}; export default function SidePanel(): JSX.Element { const { isSidebarOpen, closeSidebar } = useSidebarStore(); + const { panelState } = useMeetingsStore(); return ( - side panel + {panelContents[panelState] ?? null} ); } diff --git a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx index 34e561b1..f5cfef7e 100644 --- a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx +++ b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx @@ -10,13 +10,15 @@ interface MeetingsStore { isLoading: boolean; error: string | null; currentItem: IRoom | null; + currentCategory: ICategory | null; setCategories: (categories: ICategory[]) => void; setRooms: (rooms: IRoom[]) => void; setPanelState: (panelState: string) => void; setLoading: (isLoading: boolean) => void; setError: (error: string | null) => void; - setCurrentItem: (item: IRoom) => void; + setCurrentItem: (item: IRoom | null) => void; + setCurrentCategory: (category: ICategory | null) => void; handleAddItem: (data: FormData) => Promise; handleEditItem: (data: FormData, itemId: string) => Promise; @@ -26,10 +28,12 @@ interface MeetingsStore { const useMeetingsStore = create((set) => ({ categories: [], rooms: [], - panelState: "", + panelState: "add", isLoading: false, error: null, currentItem: null, + currentCategory: null, + setCategories: (categories) => { set({ categories }); }, @@ -48,6 +52,10 @@ const useMeetingsStore = create((set) => ({ setCurrentItem: (item) => { set({ currentItem: item }); }, + setCurrentCategory: (category) => { + set({ currentCategory: category }); + }, + handleAddItem: async (data: FormData): Promise => { set({ isLoading: true, error: null }); try { From 6cddbb967013ab7669ad28e16e8181f855fd850e Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 16:23:26 +0900 Subject: [PATCH 20/52] =?UTF-8?q?FE-Refactor:=20Room=20=EC=88=98=EC=A0=95/?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20form=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20=EB=B0=91=20zustand=ED=99=9C=EC=9A=A9=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=EA=B0=92=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/api/meetings.ts | 12 ++- .../(items)/_components/CategoryListItem.tsx | 7 +- .../_components/CategoryListSubItem.tsx | 3 +- .../(items)/_components/EditItemForm.tsx | 77 ++++++++++++------- .../admin/(items)/_components/SidePanel.tsx | 10 +-- .../admin/(items)/_store/useMeetingsStore.tsx | 14 ++-- 6 files changed, 73 insertions(+), 50 deletions(-) diff --git a/apps/web/api/meetings.ts b/apps/web/api/meetings.ts index be12c4c4..47a5055f 100644 --- a/apps/web/api/meetings.ts +++ b/apps/web/api/meetings.ts @@ -22,11 +22,14 @@ export const getAllRooms = async (): Promise => { return data; }; -export const postNewItem = async (itemType: TItemType, body: FormData): Promise => { +export const postNewRoom = async (itemType: TItemType, body: Record): Promise => { const { data } = await axiosRequester({ options: { method: "POST", url: API_ENDPOINTS.ITEMS.CREATE_ITEM(itemType), + headers: { + "Content-Type": "application/json", + }, data: body, }, }); @@ -34,11 +37,14 @@ export const postNewItem = async (itemType: TItemType, body: FormData): Promise< return data; }; -export const patchItem = async (itemId: string, body: FormData): Promise => { +export const patchRoom = async (itemId: string, body: Record): Promise => { const { data } = await axiosRequester({ options: { method: "PATCH", url: API_ENDPOINTS.ITEMS.UPDATE_ITEM(itemId), + headers: { + "Content-Type": "application/json", + }, data: body, }, }); @@ -46,7 +52,7 @@ export const patchItem = async (itemId: string, body: FormData): Promise => { +export const deleteRoom = async (itemId: string): Promise => { const { data } = await axiosRequester({ options: { method: "DELETE", diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 2fb821e6..d7af5f5b 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -39,10 +39,11 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr } }, [isModifyingCategoryName]); - const openPanelToAddItem = (): void => { + const openPanelToAddItem = (selectedCategory: ICategory): void => { if (!isSidebarOpen) { setPanelState("add"); - setCurrentCategory(category); + setCurrentItem(null); + setCurrentCategory(selectedCategory); openSidebar(); } }; @@ -80,7 +81,7 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr
{ - openPanelToAddItem(); + openPanelToAddItem(category); }} /> {}}> diff --git a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx index edd95756..a17dfd5e 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx @@ -13,12 +13,13 @@ interface CategoryListSubItemProps { export default function CategoryListSubItem({ item }: CategoryListSubItemProps): JSX.Element { const { isSidebarOpen, openSidebar } = useSidebarStore(); - const { setPanelState, setCurrentItem } = useMeetingsStore(); + const { setPanelState, setCurrentItem, setCurrentCategory } = useMeetingsStore(); const openPanelToEditItem = (selectedItem: IRoom): void => { if (!isSidebarOpen) { setPanelState("edit"); setCurrentItem(selectedItem); + setCurrentCategory(selectedItem.category); openSidebar(); } }; diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx index 9e1a4de5..01dba420 100644 --- a/apps/web/app/admin/(items)/_components/EditItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -1,23 +1,26 @@ "use client"; -import { type TItemStatus } from "@repo/types"; -import { Button, Input, Radio } from "@ui/index"; +import { type ICategory, type TItemStatus } from "@repo/types"; +import { Button, Input, Radio, notify } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; import { useForm } from "react-hook-form"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; +import { AxiosError } from "axios"; +import { patchRoom, postNewRoom } from "@/api/meetings"; import useMeetingsStore from "../_store/useMeetingsStore"; export default function EditItemForm(): JSX.Element { const { panelState, currentItem, categories, currentCategory } = useMeetingsStore(); + const [selectedCategory, setSelectedCategory] = useState(); const { register, handleSubmit, setValue, reset } = useForm({ defaultValues: { - name: currentItem ? currentItem.name : "", - description: currentItem ? currentItem.description : "", - capacity: currentItem ? currentItem.capacity : 1, - location: currentItem ? currentItem.location : "", - status: currentItem ? currentItem.status : "available", - category: currentItem ? currentItem.category._id : categories[1]?._id, + name: currentItem?.name ?? "", + description: currentItem?.description ?? "", + capacity: currentItem?.capacity ?? 1, + location: currentItem?.location ?? "", + status: currentItem?.status ?? "available", + category: currentCategory?._id, }, }); @@ -29,35 +32,50 @@ export default function EditItemForm(): JSX.Element { capacity: 1, location: "", status: "available", - category: categories[1]?._id ?? "", - }); - } else if (panelState === "edit" && currentItem) { - reset({ - name: currentItem.name, - description: currentItem.description, - capacity: currentItem.capacity, - location: currentItem.location, - status: currentItem.status, - category: currentItem.category._id, + category: currentCategory?._id, }); } - }, [panelState, currentItem, reset, categories]); + }, [panelState, currentItem, currentCategory, reset]); + + const handleFormSubmit = handleSubmit(async (data) => { + const payload = { + ...data, + category: selectedCategory?._id ?? String(currentCategory?._id), + capacity: String(data.capacity), + }; - const handleFormSubmit = handleSubmit((data) => { if (panelState === "add") { - console.log("add", data); - return data; + try { + const res = await postNewRoom("room", payload); + notify({ type: "success", message: "등록완료!" }); + return res; + } catch (error) { + if (error instanceof AxiosError) { + notify({ type: "error", message: error.response?.data.message || "잘못된 요청입니다." }); + } + } + } + + if (panelState === "edit" && currentItem) { + try { + const res = await patchRoom(currentItem._id, payload); + notify({ type: "success", message: "수정완료!" }); + return res; + } catch (error) { + if (error instanceof AxiosError) { + notify({ type: "error", message: error.response?.data?.message || "잘못된 요청입니다." }); + } + } } }); return ( -
handleSubmit} className="flex h-full flex-col justify-between"> +

회의실 {panelState === "add" ? "추가" : "수정"}

{ setValue("status", value as TItemStatus); }} @@ -70,11 +88,12 @@ export default function EditItemForm(): JSX.Element {
{ - const selectedCategory = categories.find((category) => category._id === value); - if (selectedCategory) { - setValue("category", selectedCategory.name); + const selectedValue = categories.find((category) => category._id === value); + if (selectedValue) { + setSelectedCategory(selectedValue); + setValue("category", String(value)); } }} isError={false} diff --git a/apps/web/app/admin/(items)/_components/SidePanel.tsx b/apps/web/app/admin/(items)/_components/SidePanel.tsx index 1e808451..93b931d8 100644 --- a/apps/web/app/admin/(items)/_components/SidePanel.tsx +++ b/apps/web/app/admin/(items)/_components/SidePanel.tsx @@ -6,12 +6,6 @@ import useMeetingsStore from "../_store/useMeetingsStore"; import EditItemForm from "./EditItemForm"; import AddCategoryForm from "./AddCategoryForm"; -const panelContents: Record = { - add: , - edit: , - category: , -}; - export default function SidePanel(): JSX.Element { const { isSidebarOpen, closeSidebar } = useSidebarStore(); const { panelState } = useMeetingsStore(); @@ -23,7 +17,9 @@ export default function SidePanel(): JSX.Element { closeSidebar(); }} > - {panelContents[panelState] ?? null} + {panelState === "add" && } + {panelState === "edit" && } + {panelState === "category" && } ); } diff --git a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx index f5cfef7e..7f847980 100644 --- a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx +++ b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx @@ -1,7 +1,7 @@ import { create } from "zustand"; import { type IRoom, type IEquipment, type ICategory } from "@repo/types"; import { notify } from "@ui/index"; -import { postNewItem, patchItem, deleteItem } from "@/api/meetings"; // API 요청 함수 예시 +import { postNewRoom, patchItem, deleteItem } from "@/api/meetings"; // API 요청 함수 예시 interface MeetingsStore { categories: ICategory[]; @@ -20,8 +20,8 @@ interface MeetingsStore { setCurrentItem: (item: IRoom | null) => void; setCurrentCategory: (category: ICategory | null) => void; - handleAddItem: (data: FormData) => Promise; - handleEditItem: (data: FormData, itemId: string) => Promise; + handleAddItem: (data: Record) => Promise; + handleEditItem: (data: Record, itemId: string) => Promise; handleDeleteItem: (itemId: string) => Promise; } @@ -56,10 +56,10 @@ const useMeetingsStore = create((set) => ({ set({ currentCategory: category }); }, - handleAddItem: async (data: FormData): Promise => { + handleAddItem: async (data): Promise => { set({ isLoading: true, error: null }); try { - const res = await postNewItem("room", data); + const res = await postNewRoom("room", data); set({ isLoading: false }); return res; } catch (error) { @@ -67,7 +67,7 @@ const useMeetingsStore = create((set) => ({ throw new Error(); } }, - handleEditItem: async (data: FormData, itemId: string): Promise => { + handleEditItem: async (data, itemId): Promise => { set({ isLoading: true, error: null }); try { const res = await patchItem(itemId, data); @@ -78,7 +78,7 @@ const useMeetingsStore = create((set) => ({ throw new Error(); } }, - handleDeleteItem: async (itemId: string): Promise => { + handleDeleteItem: async (itemId): Promise => { try { await deleteItem(itemId); notify({ type: "success", message: "삭제되었습니다." }); From 86ebd6ecd4fab890aacc267539142ccac112b081 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 16:24:29 +0900 Subject: [PATCH 21/52] =?UTF-8?q?FE-Refactor:=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EC=A4=91=EB=B3=B5,=20=ED=95=A8=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/admin/(items)/_store/useMeetingsStore.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx index 7f847980..340e6e26 100644 --- a/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx +++ b/apps/web/app/admin/(items)/_store/useMeetingsStore.tsx @@ -1,7 +1,7 @@ import { create } from "zustand"; import { type IRoom, type IEquipment, type ICategory } from "@repo/types"; import { notify } from "@ui/index"; -import { postNewRoom, patchItem, deleteItem } from "@/api/meetings"; // API 요청 함수 예시 +import { postNewRoom, deleteRoom, patchRoom } from "@/api/meetings"; // API 요청 함수 예시 interface MeetingsStore { categories: ICategory[]; @@ -70,7 +70,7 @@ const useMeetingsStore = create((set) => ({ handleEditItem: async (data, itemId): Promise => { set({ isLoading: true, error: null }); try { - const res = await patchItem(itemId, data); + const res = await patchRoom(itemId, data); set({ isLoading: false }); return res; } catch (error) { @@ -80,7 +80,7 @@ const useMeetingsStore = create((set) => ({ }, handleDeleteItem: async (itemId): Promise => { try { - await deleteItem(itemId); + await deleteRoom(itemId); notify({ type: "success", message: "삭제되었습니다." }); } catch (error) { notify({ type: "error", message: "삭제 실패" }); From 5e56199bcd105843ef1bf0fdae0fb34a830d7eb3 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 16:31:24 +0900 Subject: [PATCH 22/52] =?UTF-8?q?FE-Refactor:=20categories,=20rooms=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=A1=9C=EB=94=A9=EB=B6=80?= =?UTF-8?q?=EB=B6=84=EC=97=90=20react-query=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/CategoryList.tsx | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/CategoryList.tsx b/apps/web/app/admin/(items)/_components/CategoryList.tsx index a827b90e..c8fcdb9f 100644 --- a/apps/web/app/admin/(items)/_components/CategoryList.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryList.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; import { getAllCategories, getAllRooms } from "@/api/meetings"; import useMeetingsStore from "../_store/useMeetingsStore"; import SidePanel from "./SidePanel"; @@ -9,29 +10,24 @@ import CategoryListItem from "./CategoryListItem"; export default function CategoryList(): JSX.Element { const { categories, setCategories, rooms, setRooms } = useMeetingsStore(); - useEffect(() => { - const fetchCategories = async (): Promise => { - try { - const res = await getAllCategories(); - const roomCategories = res.filter((category) => category.itemType === "room"); - setCategories(roomCategories); - } catch (error) { - throw new Error(); - } - }; - void fetchCategories(); + const { data: fetchedCategories } = useQuery({ + queryKey: ["categories"], + queryFn: getAllCategories, + }); + const { data: fetchedRooms } = useQuery({ queryKey: ["rooms"], queryFn: getAllRooms }); - const fetchItems = async (): Promise => { - try { - const res = await getAllRooms(); - setRooms(res); - } catch (error) { - throw new Error(); - } - }; + useEffect(() => { + if (fetchedCategories) { + const roomCategories = fetchedCategories.filter((category) => category.itemType === "room"); + setCategories(roomCategories); + } + }, [fetchedCategories, setCategories]); - void fetchItems(); - }, []); + useEffect(() => { + if (fetchedRooms) { + setRooms(fetchedRooms); + } + }, [fetchedRooms, setRooms]); const filteredRoomsByCategory = categories.map((category) => ({ categoryId: category._id, From 1cfc3749263880fe49cc1606b8beff5d6414cb33 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 16:38:46 +0900 Subject: [PATCH 23/52] =?UTF-8?q?FE-Refactor:=20editItemForm=EC=9D=98=20po?= =?UTF-8?q?st=EC=9A=94=EC=B2=AD=20mutate=EB=A1=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/EditItemForm.tsx | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx index 01dba420..d2dced48 100644 --- a/apps/web/app/admin/(items)/_components/EditItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -5,13 +5,14 @@ import { Button, Input, Radio, notify } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; import { useForm } from "react-hook-form"; import { useEffect, useState } from "react"; -import { AxiosError } from "axios"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { patchRoom, postNewRoom } from "@/api/meetings"; import useMeetingsStore from "../_store/useMeetingsStore"; export default function EditItemForm(): JSX.Element { const { panelState, currentItem, categories, currentCategory } = useMeetingsStore(); const [selectedCategory, setSelectedCategory] = useState(); + const queryClient = useQueryClient(); const { register, handleSubmit, setValue, reset } = useForm({ defaultValues: { @@ -37,36 +38,36 @@ export default function EditItemForm(): JSX.Element { } }, [panelState, currentItem, currentCategory, reset]); - const handleFormSubmit = handleSubmit(async (data) => { + const mutation = useMutation({ + mutationFn: async (payload: Record) => { + if (panelState === "add") { + return await postNewRoom("room", payload); + } + + if (panelState === "edit" && currentItem) { + return await patchRoom(currentItem._id, payload); + } + + throw new Error("Unknown panel state"); + }, + onSuccess: async () => { + notify({ type: "success", message: panelState === "add" ? "등록완료!" : "수정완료!" }); + await queryClient.invalidateQueries({ queryKey: ["rooms"] }); + }, + onError: () => { + notify({ type: "error", message: "잘못된 요청입니다." }); + }, + }); + + const handleFormSubmit = handleSubmit((data) => { const payload = { ...data, category: selectedCategory?._id ?? String(currentCategory?._id), capacity: String(data.capacity), }; - if (panelState === "add") { - try { - const res = await postNewRoom("room", payload); - notify({ type: "success", message: "등록완료!" }); - return res; - } catch (error) { - if (error instanceof AxiosError) { - notify({ type: "error", message: error.response?.data.message || "잘못된 요청입니다." }); - } - } - } - - if (panelState === "edit" && currentItem) { - try { - const res = await patchRoom(currentItem._id, payload); - notify({ type: "success", message: "수정완료!" }); - return res; - } catch (error) { - if (error instanceof AxiosError) { - notify({ type: "error", message: error.response?.data?.message || "잘못된 요청입니다." }); - } - } - } + // mutation을 사용하여 서버에 데이터 보내기 + mutation.mutate(payload); }); return ( From 16a34d19038378b88ba69018dd145b0c84419d90 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 16:44:24 +0900 Subject: [PATCH 24/52] =?UTF-8?q?FE-Feat:=20=ED=9A=8C=EC=9D=98=EC=8B=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/CategoryListSubItem.tsx | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx index a17dfd5e..a1f758d1 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx @@ -2,7 +2,10 @@ import ListItem from "@ui/src/components/common/ListItem"; import { type IRoom } from "@repo/types"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { notify } from "@ui/index"; import { useSidebarStore } from "@/app/store/useSidebarStore"; +import { deleteRoom } from "@/api/meetings"; import useMeetingsStore from "../_store/useMeetingsStore"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; @@ -14,6 +17,7 @@ interface CategoryListSubItemProps { export default function CategoryListSubItem({ item }: CategoryListSubItemProps): JSX.Element { const { isSidebarOpen, openSidebar } = useSidebarStore(); const { setPanelState, setCurrentItem, setCurrentCategory } = useMeetingsStore(); + const queryClient = useQueryClient(); const openPanelToEditItem = (selectedItem: IRoom): void => { if (!isSidebarOpen) { @@ -24,10 +28,32 @@ export default function CategoryListSubItem({ item }: CategoryListSubItemProps): } }; + const mutation = useMutation({ + mutationFn: async (itemId: string) => { + return await deleteRoom(itemId); + }, + onSuccess: async () => { + notify({ type: "success", message: "회의실이 삭제되었습니다." }); + await queryClient.invalidateQueries({ queryKey: ["rooms"] }); + }, + onError: () => { + notify({ type: "error", message: "회의실 삭제에 실패했습니다. 다시 시도해주세요" }); + }, + }); + + const handleDeleteRoom = (itemId: string) => { + mutation.mutate(itemId); + }; return ( {item.name} - {}}> + { + handleDeleteRoom(item._id); + }} + > { openPanelToEditItem(item); From b20b5fd2a591023cfd645fb09ff56d1c1cd8b3fe Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 17:13:03 +0900 Subject: [PATCH 25/52] =?UTF-8?q?FE-Feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95/=EC=82=AD=EC=A0=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/api/meetings.ts | 41 ++++++++++++ .../(items)/_components/AddCategoryForm.tsx | 32 +++++++++- .../(items)/_components/CategoryListItem.tsx | 62 +++++++++++++++++-- .../_components/CategoryListSubItem.tsx | 1 + .../(items)/_components/EditItemForm.tsx | 1 - .../app/admin/(items)/_components/header.tsx | 3 + 6 files changed, 132 insertions(+), 8 deletions(-) diff --git a/apps/web/api/meetings.ts b/apps/web/api/meetings.ts index 47a5055f..6a756286 100644 --- a/apps/web/api/meetings.ts +++ b/apps/web/api/meetings.ts @@ -62,3 +62,44 @@ export const deleteRoom = async (itemId: string): Promise => { return data; }; + +export const postNewCategory = async (body: Record): Promise => { + const { data } = await axiosRequester({ + options: { + method: "POST", + url: API_ENDPOINTS.CATEGORIES.CREATE_CATEGORY, + headers: { + "Content-Type": "application/json", + }, + data: body, + }, + }); + + return data; +}; + +export const patchCategory = async (categoryId: string, body: Record): Promise => { + const { data } = await axiosRequester({ + options: { + method: "PATCH", + url: API_ENDPOINTS.CATEGORIES.UPDATE_CATEGORY(categoryId), + headers: { + "Content-Type": "application/json", + }, + data: body, + }, + }); + + return data; +}; + +export const deleteCategory = async (categoryId: string): Promise => { + const { data } = await axiosRequester({ + options: { + method: "DELETE", + url: API_ENDPOINTS.CATEGORIES.DELETE_CATEGORY(categoryId), + }, + }); + + return data; +}; diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx index 83a98050..4f70038c 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -1,12 +1,38 @@ "use client"; -import { Button, Input } from "@ui/index"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Button, Input, notify } from "@ui/index"; import { useForm } from "react-hook-form"; +import { postNewCategory } from "@/api/meetings"; export default function AddCategoryForm(): JSX.Element { - const { register } = useForm(); + const { register, handleSubmit } = useForm(); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async (payload: Record) => { + return await postNewCategory(payload); + }, + onSuccess: async () => { + notify({ type: "success", message: "카테고리가 추가되었습니다!" }); + await queryClient.invalidateQueries({ queryKey: ["categories"] }); + }, + onError: () => { + notify({ type: "error", message: "요청에 실패했습니다." }); + }, + }); + + const handleSubmitForm = handleSubmit((data) => { + const payload = { + ...data, + itemType: "room", + }; + + mutation.mutate(payload); + }); + return ( - +

카테고리 추가

diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index d7af5f5b..987368e4 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -6,7 +6,10 @@ import { type PropsWithChildren, useRef, useState, useEffect } from "react"; import { TriangleIcon } from "@ui/public"; import { type ICategory, type IRoom } from "@repo/types"; import { motion } from "framer-motion"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { notify } from "@ui/index"; import { useSidebarStore } from "@/app/store/useSidebarStore"; +import { deleteCategory, patchCategory } from "@/api/meetings"; import useMeetingsStore from "../_store/useMeetingsStore"; import AddItemButton from "./AddItemButton"; import CategoryListSubItem from "./CategoryListSubItem"; @@ -27,6 +30,8 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr const [inputValue, setInputValue] = useState(""); const inputRef = useRef(null); + const queryClient = useQueryClient(); + useOnClickOutside(inputRef, () => { if (isModifyingCategoryName) { setIsModifyingCategoryName(false); @@ -52,6 +57,50 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr setIsOpen(!isOpen); }; + const deleteMutation = useMutation({ + mutationFn: async (categoryId: string) => { + return await deleteCategory(categoryId); + }, + onSuccess: async () => { + notify({ + type: "success", + message: "카테고리가 삭제되었습니다.", + }); + await queryClient.invalidateQueries({ queryKey: ["categories"] }); + }, + onError: () => { + notify({ type: "error", message: "삭제에 실패했습니다. 다시 시도해주세요." }); + }, + }); + + const handleDeleteCategory = (categoryId: string) => { + deleteMutation.mutate(categoryId); + }; + + const updateMutation = useMutation({ + mutationFn: async (payload: Record) => { + return await patchCategory(category._id, payload); + }, + onSuccess: async () => { + notify({ + type: "success", + message: "카테고리가 수정되었습니다.", + }); + await queryClient.invalidateQueries({ queryKey: ["categories"] }); + }, + onError: () => { + notify({ type: "error", message: "수정에 실패했습니다. 다시 시도해주세요." }); + }, + }); + + const handleUpdateCategory = (): void => { + const payload = { + name: inputValue, + }; + + updateMutation.mutate(payload); + }; + return ( <> @@ -67,9 +116,8 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr }} onKeyDown={(e) => { if (e.key === "Enter") { - // TODO: input 데이터 patch - // eslint-disable-next-line no-console - console.log(inputValue); + handleUpdateCategory(); + setIsModifyingCategoryName(false); } }} /> @@ -84,7 +132,13 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr openPanelToAddItem(category); }} /> - {}}> + { + handleDeleteCategory(category._id); + }} + > { setIsModifyingCategoryName(true); diff --git a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx index a1f758d1..181e7c64 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx @@ -44,6 +44,7 @@ export default function CategoryListSubItem({ item }: CategoryListSubItemProps): const handleDeleteRoom = (itemId: string) => { mutation.mutate(itemId); }; + return ( {item.name} diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx index d2dced48..8f94e7ed 100644 --- a/apps/web/app/admin/(items)/_components/EditItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -66,7 +66,6 @@ export default function EditItemForm(): JSX.Element { capacity: String(data.capacity), }; - // mutation을 사용하여 서버에 데이터 보내기 mutation.mutate(payload); }); diff --git a/apps/web/app/admin/(items)/_components/header.tsx b/apps/web/app/admin/(items)/_components/header.tsx index 9e063f2d..88b76c41 100644 --- a/apps/web/app/admin/(items)/_components/header.tsx +++ b/apps/web/app/admin/(items)/_components/header.tsx @@ -1,13 +1,16 @@ "use client"; import { useSidebarStore } from "@/app/store/useSidebarStore"; +import useMeetingsStore from "../_store/useMeetingsStore"; import AddCategoryButton from "./AddCategoryButton"; export default function ItemsAdminHeader(): JSX.Element { const { isSidebarOpen, openSidebar } = useSidebarStore(); + const { setPanelState } = useMeetingsStore(); const openPanel = (): void => { if (!isSidebarOpen) { + setPanelState("category"); openSidebar(); } }; From 289c27bc4d1bdd558e43a629aebdee61759acde3 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 17:56:18 +0900 Subject: [PATCH 26/52] =?UTF-8?q?FE-Refactor:=20form=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=95=88=EB=90=98=EB=8D=98=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?reset,=20useEffect=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/EditItemForm.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx index 8f94e7ed..efbbd03e 100644 --- a/apps/web/app/admin/(items)/_components/EditItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -7,9 +7,11 @@ import { useForm } from "react-hook-form"; import { useEffect, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { patchRoom, postNewRoom } from "@/api/meetings"; +import { useSidebarStore } from "@/app/store/useSidebarStore"; import useMeetingsStore from "../_store/useMeetingsStore"; export default function EditItemForm(): JSX.Element { + const { closeSidebar } = useSidebarStore(); const { panelState, currentItem, categories, currentCategory } = useMeetingsStore(); const [selectedCategory, setSelectedCategory] = useState(); const queryClient = useQueryClient(); @@ -35,6 +37,15 @@ export default function EditItemForm(): JSX.Element { status: "available", category: currentCategory?._id, }); + } else if (panelState === "edit" && currentItem) { + reset({ + name: currentItem.name, + description: currentItem.description, + capacity: currentItem.capacity, + location: currentItem.location, + status: currentItem.status, + category: currentItem.category._id, + }); } }, [panelState, currentItem, currentCategory, reset]); @@ -52,6 +63,7 @@ export default function EditItemForm(): JSX.Element { }, onSuccess: async () => { notify({ type: "success", message: panelState === "add" ? "등록완료!" : "수정완료!" }); + closeSidebar(); await queryClient.invalidateQueries({ queryKey: ["rooms"] }); }, onError: () => { @@ -75,20 +87,21 @@ export default function EditItemForm(): JSX.Element {

회의실 {panelState === "add" ? "추가" : "수정"}

{ setValue("status", value as TItemStatus); }} > 사용 가능 - 사용 불가 + 사용 불가
{ const selectedValue = categories.find((category) => category._id === value); if (selectedValue) { From 7230f8b3f616289683e6b99d884e775a778f634e Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Mon, 2 Dec 2024 19:20:18 +0900 Subject: [PATCH 27/52] =?UTF-8?q?FE-Refactor:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/(items)/_components/AddCategoryButton.tsx | 13 ------------- apps/web/app/admin/(items)/_components/header.tsx | 6 ++++-- 2 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 apps/web/app/admin/(items)/_components/AddCategoryButton.tsx diff --git a/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx b/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx deleted file mode 100644 index 2c543367..00000000 --- a/apps/web/app/admin/(items)/_components/AddCategoryButton.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Button from "@ui/src/components/common/Button"; - -interface AddCategoryButtonProps { - onClick: () => void; -} - -export default function AddCategoryButton({ onClick }: AddCategoryButtonProps): JSX.Element { - return ( - - ); -} diff --git a/apps/web/app/admin/(items)/_components/header.tsx b/apps/web/app/admin/(items)/_components/header.tsx index 88b76c41..d55f9768 100644 --- a/apps/web/app/admin/(items)/_components/header.tsx +++ b/apps/web/app/admin/(items)/_components/header.tsx @@ -1,8 +1,8 @@ "use client"; +import { Button } from "@ui/index"; import { useSidebarStore } from "@/app/store/useSidebarStore"; import useMeetingsStore from "../_store/useMeetingsStore"; -import AddCategoryButton from "./AddCategoryButton"; export default function ItemsAdminHeader(): JSX.Element { const { isSidebarOpen, openSidebar } = useSidebarStore(); @@ -18,7 +18,9 @@ export default function ItemsAdminHeader(): JSX.Element { return (

회의실 관리

- +
); } From 31e142c82d19d435de9f404a960c86089db5eea8 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 26 Dec 2024 16:40:22 +0900 Subject: [PATCH 28/52] =?UTF-8?q?FE-Refactor:=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=90=9C=20toast=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddCategoryForm.tsx | 7 ++++--- .../(items)/_components/CategoryListItem.tsx | 16 +++++----------- .../(items)/_components/CategoryListSubItem.tsx | 8 ++++---- .../admin/(items)/_components/EditItemForm.tsx | 15 ++++----------- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx index 4f70038c..2b5650b3 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -1,9 +1,10 @@ "use client"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { Button, Input, notify } from "@ui/index"; +import { Button, Input } from "@ui/index"; import { useForm } from "react-hook-form"; import { postNewCategory } from "@/api/meetings"; +import { notify } from "@/app/store/useToastStore"; export default function AddCategoryForm(): JSX.Element { const { register, handleSubmit } = useForm(); @@ -14,11 +15,11 @@ export default function AddCategoryForm(): JSX.Element { return await postNewCategory(payload); }, onSuccess: async () => { - notify({ type: "success", message: "카테고리가 추가되었습니다!" }); + notify("success", "카테고리가 추가되었습니다!"); await queryClient.invalidateQueries({ queryKey: ["categories"] }); }, onError: () => { - notify({ type: "error", message: "요청에 실패했습니다." }); + notify("error", "요청에 실패했습니다."); }, }); diff --git a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx index 987368e4..cfd99428 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListItem.tsx @@ -7,9 +7,9 @@ import { TriangleIcon } from "@ui/public"; import { type ICategory, type IRoom } from "@repo/types"; import { motion } from "framer-motion"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { notify } from "@ui/index"; import { useSidebarStore } from "@/app/store/useSidebarStore"; import { deleteCategory, patchCategory } from "@/api/meetings"; +import { notify } from "@/app/store/useToastStore"; import useMeetingsStore from "../_store/useMeetingsStore"; import AddItemButton from "./AddItemButton"; import CategoryListSubItem from "./CategoryListSubItem"; @@ -62,14 +62,11 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr return await deleteCategory(categoryId); }, onSuccess: async () => { - notify({ - type: "success", - message: "카테고리가 삭제되었습니다.", - }); + notify("success", "카테고리가 삭제되었습니다."); await queryClient.invalidateQueries({ queryKey: ["categories"] }); }, onError: () => { - notify({ type: "error", message: "삭제에 실패했습니다. 다시 시도해주세요." }); + notify("error", "삭제에 실패했습니다. 다시 시도해주세요."); }, }); @@ -82,14 +79,11 @@ export default function CategoryListItem({ category, rooms }: CategoryListItemPr return await patchCategory(category._id, payload); }, onSuccess: async () => { - notify({ - type: "success", - message: "카테고리가 수정되었습니다.", - }); + notify("success", "카테고리가 수정되었습니다."); await queryClient.invalidateQueries({ queryKey: ["categories"] }); }, onError: () => { - notify({ type: "error", message: "수정에 실패했습니다. 다시 시도해주세요." }); + notify("error", "수정에 실패했습니다. 다시 시도해주세요."); }, }); diff --git a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx index 181e7c64..afa1eb4c 100644 --- a/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryListSubItem.tsx @@ -3,9 +3,9 @@ import ListItem from "@ui/src/components/common/ListItem"; import { type IRoom } from "@repo/types"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { notify } from "@ui/index"; import { useSidebarStore } from "@/app/store/useSidebarStore"; import { deleteRoom } from "@/api/meetings"; +import { notify } from "@/app/store/useToastStore"; import useMeetingsStore from "../_store/useMeetingsStore"; import CategoryEditDropdown from "./CategoryEditDropdown"; import ConfirmationModal from "./ConfirmationModal"; @@ -33,15 +33,15 @@ export default function CategoryListSubItem({ item }: CategoryListSubItemProps): return await deleteRoom(itemId); }, onSuccess: async () => { - notify({ type: "success", message: "회의실이 삭제되었습니다." }); + notify("success", "회의실이 삭제되었습니다."); await queryClient.invalidateQueries({ queryKey: ["rooms"] }); }, onError: () => { - notify({ type: "error", message: "회의실 삭제에 실패했습니다. 다시 시도해주세요" }); + notify("error", "회의실 삭제에 실패했습니다. 다시 시도해주세요"); }, }); - const handleDeleteRoom = (itemId: string) => { + const handleDeleteRoom = (itemId: string): void => { mutation.mutate(itemId); }; diff --git a/apps/web/app/admin/(items)/_components/EditItemForm.tsx b/apps/web/app/admin/(items)/_components/EditItemForm.tsx index efbbd03e..21c534c6 100644 --- a/apps/web/app/admin/(items)/_components/EditItemForm.tsx +++ b/apps/web/app/admin/(items)/_components/EditItemForm.tsx @@ -1,13 +1,14 @@ "use client"; import { type ICategory, type TItemStatus } from "@repo/types"; -import { Button, Input, Radio, notify } from "@ui/index"; +import { Button, Input, Radio } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; import { useForm } from "react-hook-form"; import { useEffect, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { patchRoom, postNewRoom } from "@/api/meetings"; import { useSidebarStore } from "@/app/store/useSidebarStore"; +import { notify } from "@/app/store/useToastStore"; import useMeetingsStore from "../_store/useMeetingsStore"; export default function EditItemForm(): JSX.Element { @@ -62,12 +63,12 @@ export default function EditItemForm(): JSX.Element { throw new Error("Unknown panel state"); }, onSuccess: async () => { - notify({ type: "success", message: panelState === "add" ? "등록완료!" : "수정완료!" }); + notify("success", panelState === "add" ? "등록완료!" : "수정완료!"); closeSidebar(); await queryClient.invalidateQueries({ queryKey: ["rooms"] }); }, onError: () => { - notify({ type: "error", message: "잘못된 요청입니다." }); + notify("error", "잘못된 요청입니다."); }, }); @@ -133,11 +134,3 @@ export default function EditItemForm(): JSX.Element { ); } - -// "name": "string", -// "description": "string", -// "status": "available", -// "imageUrl": "string", -// "category": "string", -// "capacity": 0, -// "location": "string" From a2df06ced7aba7e8016505f27cebf725bd5b9fac Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 26 Dec 2024 16:47:28 +0900 Subject: [PATCH 29/52] =?UTF-8?q?FE-Refactor:=20=ED=9A=8C=EC=9D=98?= =?UTF-8?q?=EC=8B=A4=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=97=A4=EB=8D=94=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/{header.tsx => ItemsAdminHeader.tsx} | 0 apps/web/app/admin/(items)/meetings/page.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/web/app/admin/(items)/_components/{header.tsx => ItemsAdminHeader.tsx} (100%) diff --git a/apps/web/app/admin/(items)/_components/header.tsx b/apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx similarity index 100% rename from apps/web/app/admin/(items)/_components/header.tsx rename to apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx diff --git a/apps/web/app/admin/(items)/meetings/page.tsx b/apps/web/app/admin/(items)/meetings/page.tsx index 1e2bcbaa..f3b413d6 100644 --- a/apps/web/app/admin/(items)/meetings/page.tsx +++ b/apps/web/app/admin/(items)/meetings/page.tsx @@ -1,5 +1,5 @@ import CategoryList from "../_components/CategoryList"; -import ItemsAdminHeader from "../_components/header"; +import ItemsAdminHeader from "../_components/ItemsAdminHeader"; export default function Rooms(): JSX.Element { return ( From d4559d536d98bbcb7f0733435ffe414f916a9661 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 26 Dec 2024 16:53:31 +0900 Subject: [PATCH 30/52] =?UTF-8?q?FE-Refactor:=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=ED=8C=A8=EB=84=90=20=EC=A1=B0=EA=B1=B4=EB=B6=80=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/admin/(items)/_components/SidePanel.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/SidePanel.tsx b/apps/web/app/admin/(items)/_components/SidePanel.tsx index 93b931d8..001f6fd6 100644 --- a/apps/web/app/admin/(items)/_components/SidePanel.tsx +++ b/apps/web/app/admin/(items)/_components/SidePanel.tsx @@ -1,5 +1,6 @@ "use client"; +import { ErrorBoundary } from "react-error-boundary"; import Sidebar from "@/components/common/Sidebar"; import { useSidebarStore } from "@/app/store/useSidebarStore"; import useMeetingsStore from "../_store/useMeetingsStore"; @@ -17,9 +18,10 @@ export default function SidePanel(): JSX.Element { closeSidebar(); }} > - {panelState === "add" && } - {panelState === "edit" && } - {panelState === "category" && } + 오류가 발생했습니다.
}> + {(panelState === "add" || panelState === "edit") && } + {panelState === "category" && } + ); } From 1d23d73c9611908cfde15c5b5aa850d04d80d947 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 26 Dec 2024 16:56:26 +0900 Subject: [PATCH 31/52] =?UTF-8?q?FE-Refactor:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=88=98=EC=A0=95=20=ED=8C=9D=EC=98=A4?= =?UTF-8?q?=EB=B2=84=20prop=20naming=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/admin/(items)/_components/CategoryEditDropdown.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx index 0d8cdc9a..7b8b304e 100644 --- a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx @@ -2,13 +2,13 @@ import { Modal } from "@ui/index"; import Dropdown from "@ui/src/components/common/Dropdown"; interface CategoryEditDropdownProps { - isModifying?: boolean; + isEditing?: boolean; onClickEdit: () => void; } -export default function CategoryEditDropdown({ isModifying, onClickEdit }: CategoryEditDropdownProps): JSX.Element { +export default function CategoryEditDropdown({ isEditing, onClickEdit }: CategoryEditDropdownProps): JSX.Element { return ( { if (value === "수정") { onClickEdit(); From 098c8c46bcb380aff8226074cedb855fc052586f Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Thu, 26 Dec 2024 17:43:44 +0900 Subject: [PATCH 32/52] =?UTF-8?q?FE-Refactor:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=ED=8F=BC=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/AddCategoryForm.tsx | 31 ++++++++++++++++--- .../_components/CategoryEditDropdown.tsx | 1 + .../admin/(items)/_store/useMeetingsStore.tsx | 10 +++--- apps/web/next.config.mjs | 3 +- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx index 2b5650b3..4f65cd81 100644 --- a/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx +++ b/apps/web/app/admin/(items)/_components/AddCategoryForm.tsx @@ -3,11 +3,21 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Button, Input } from "@ui/index"; import { useForm } from "react-hook-form"; +import { type AxiosError } from "axios"; import { postNewCategory } from "@/api/meetings"; import { notify } from "@/app/store/useToastStore"; export default function AddCategoryForm(): JSX.Element { - const { register, handleSubmit } = useForm(); + const { + register, + handleSubmit, + reset, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues: { + name: "", + }, + }); const queryClient = useQueryClient(); const mutation = useMutation({ @@ -17,9 +27,14 @@ export default function AddCategoryForm(): JSX.Element { onSuccess: async () => { notify("success", "카테고리가 추가되었습니다!"); await queryClient.invalidateQueries({ queryKey: ["categories"] }); + reset(); }, - onError: () => { - notify("error", "요청에 실패했습니다."); + onError: (error: AxiosError) => { + if (error.response?.status === 409) { + notify("error", "이미 존재하는 카테고리입니다."); + } else { + notify("error", "요청에 실패했습니다."); + } }, }); @@ -36,9 +51,15 @@ export default function AddCategoryForm(): JSX.Element {

카테고리 추가

- +
-
diff --git a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx index 7b8b304e..5f724968 100644 --- a/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryEditDropdown.tsx @@ -5,6 +5,7 @@ interface CategoryEditDropdownProps { isEditing?: boolean; onClickEdit: () => void; } + export default function CategoryEditDropdown({ isEditing, onClickEdit }: CategoryEditDropdownProps): JSX.Element { return ( ((set) => ({ set({ isLoading: false }); return res; } catch (error) { - notify({ type: "error", message: "잘못된 요청입니다." }); + notify("error", "잘못된 요청입니다."); throw new Error(); } }, @@ -74,16 +74,16 @@ const useMeetingsStore = create((set) => ({ set({ isLoading: false }); return res; } catch (error) { - notify({ type: "error", message: "잘못된 요청입니다." }); + notify("error", "잘못된 요청입니다."); throw new Error(); } }, handleDeleteItem: async (itemId): Promise => { try { await deleteRoom(itemId); - notify({ type: "success", message: "삭제되었습니다." }); + notify("success", "삭제되었습니다."); } catch (error) { - notify({ type: "error", message: "삭제 실패" }); + notify("error", "삭제 실패"); } }, })); diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 5aa201ab..af3caec5 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -2,12 +2,13 @@ const nextConfig = { reactStrictMode: true, output: "export", + trailingSlash: true, images: { remotePatterns: [ { protocol: "https", - hostname: "codeit-re-images.s3.ap-northeast-2.amazonaws.com", + hostname: "codeit-server.s3.ap-northeast-2.amazonaws.com", port: "", pathname: "/**", }, From 4349fa2bac5c874bf4282b987daa650785fb1060 Mon Sep 17 00:00:00 2001 From: AdamSeungheonShin Date: Sat, 28 Dec 2024 08:23:25 +0900 Subject: [PATCH 33/52] =?UTF-8?q?FE-Refactor:=20=ED=9A=8C=EC=9D=98?= =?UTF-8?q?=EC=8B=A4=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=BF=BC=EB=A6=AC=20=EC=97=90=EB=9F=AC=20=EC=96=BC?= =?UTF-8?q?=EB=A6=AC=EB=A6=AC=ED=84=B4,=20=EB=A1=9C=EB=94=A9=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B0=8F=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(items)/_components/CategoryList.tsx | 24 +++++++++++++++++-- .../(items)/_components/ItemsAdminHeader.tsx | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/web/app/admin/(items)/_components/CategoryList.tsx b/apps/web/app/admin/(items)/_components/CategoryList.tsx index c8fcdb9f..ad12de0f 100644 --- a/apps/web/app/admin/(items)/_components/CategoryList.tsx +++ b/apps/web/app/admin/(items)/_components/CategoryList.tsx @@ -2,7 +2,10 @@ import { useEffect } from "react"; import { useQuery } from "@tanstack/react-query"; +import EmptyState from "@ui/src/components/common/EmptyState"; import { getAllCategories, getAllRooms } from "@/api/meetings"; +import LoadingBar from "@/components/common/Skeleton/LoadingBar"; +import { notify } from "@/app/store/useToastStore"; import useMeetingsStore from "../_store/useMeetingsStore"; import SidePanel from "./SidePanel"; import CategoryListItem from "./CategoryListItem"; @@ -10,11 +13,19 @@ import CategoryListItem from "./CategoryListItem"; export default function CategoryList(): JSX.Element { const { categories, setCategories, rooms, setRooms } = useMeetingsStore(); - const { data: fetchedCategories } = useQuery({ + const { + data: fetchedCategories, + isLoading: isCategoriesLoading, + error: categoriesError, + } = useQuery({ queryKey: ["categories"], queryFn: getAllCategories, }); - const { data: fetchedRooms } = useQuery({ queryKey: ["rooms"], queryFn: getAllRooms }); + const { + data: fetchedRooms, + isLoading: isRoomsLoading, + error: roomsError, + } = useQuery({ queryKey: ["rooms"], queryFn: getAllRooms }); useEffect(() => { if (fetchedCategories) { @@ -29,6 +40,11 @@ export default function CategoryList(): JSX.Element { } }, [fetchedRooms, setRooms]); + if (categoriesError ?? roomsError) { + notify("error", "데이터를 불러오는데 실패했습니다."); + return
데이터를 불러오는데 실패했습니다.
; + } + const filteredRoomsByCategory = categories.map((category) => ({ categoryId: category._id, rooms: rooms.filter((room) => room.category._id === category._id), @@ -36,6 +52,10 @@ export default function CategoryList(): JSX.Element { return ( <> + {isCategoriesLoading || (isRoomsLoading && )} + {!isCategoriesLoading && categories.length === 0 && ( + + )} {categories.map((category) => { const filteredRooms = filteredRoomsByCategory.find((item) => item.categoryId === category._id)?.rooms ?? []; return ; diff --git a/apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx b/apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx index d55f9768..757346f5 100644 --- a/apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx +++ b/apps/web/app/admin/(items)/_components/ItemsAdminHeader.tsx @@ -16,7 +16,7 @@ export default function ItemsAdminHeader(): JSX.Element { }; return ( -
+

회의실 관리