diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/BackButton.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/BackButton.tsx index 8aad8ff..d7cb45d 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/BackButton.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/BackButton.tsx @@ -18,12 +18,12 @@ export default function BackButton() { > -
+

돌아가기 -

+

); diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DashboardEditSection.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DashboardEditSection.tsx index 2938c06..40d5547 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DashboardEditSection.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DashboardEditSection.tsx @@ -1,20 +1,98 @@ "use client"; +import { useEffect, useState } from "react"; +import { useDashboardStore } from "@/lib/store/useDashboardStore"; +import { DashboardDetail } from "@/lib/types"; +import { fetchDashboard, putDashboard } from "@/lib/apis/dashboardsApi"; +import ColorPalette, { + ColorCode, +} from "@/components/common/color-palette/ColorPalette"; import Input from "@/components/common/input/Input"; import Button from "@/components/common/button/Button"; -export default function DashboardEditSection() { +export default function DashboardEditSection({ + id, + token, +}: { + id: number; + token: string; +}) { + const [data, setData] = useState(null); + const [dashboardName, setDashboardName] = useState(""); + const [selectedColor, setSelectedColor] = useState(""); + const [isFormValid, setIsFormValid] = useState(false); + const setDashboardId = useDashboardStore((state) => state.setDashboardId); + + useEffect(() => { + const getData = async () => { + const data = await fetchDashboard({ + token, + id: String(id), + }); + setData(data); + setDashboardName(data.title); + setSelectedColor(data.color); + }; + + getData(); + }, []); + + const onColorSelect = (color: ColorCode | "") => { + setSelectedColor(() => { + return color; + }); + }; + + useEffect(() => { + const trimmedValue = dashboardName.trim(); + const isValid = trimmedValue !== "" && selectedColor !== ""; + setIsFormValid(isValid); + }, [dashboardName, selectedColor]); + + const onDashboardNameChange = (e: React.ChangeEvent) => { + setDashboardName(e.target.value); + }; + + const editDashboard = async () => { + await putDashboard({ + token, + title: dashboardName, + color: selectedColor, + id, + }); + + window.location.replace(`/dashboard/${id}`); + setDashboardId(String(id)); + }; + + if (!data) return; + return (
-
- 대시보드 이름 +
+ {data.title}
-
+
- + +
- +
diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DeleteButton.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DeleteButton.tsx new file mode 100644 index 0000000..42314ac --- /dev/null +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/DeleteButton.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useDashboardStore } from "@/lib/store/useDashboardStore"; +import { deleteDashboard } from "@/lib/apis/dashboardsApi"; +import ROUTE from "@/lib/constants/route"; + +export default function DeleteButton({ + id, + token, +}: { + id: number; + token: string; +}) { + const router = useRouter(); + const setDashboardId = useDashboardStore((state) => state.setDashboardId); + + const handleDeleteClick = async () => { + await deleteDashboard({ + token, + id, + }); + + router.push(ROUTE.MYDASHBOARD); + setDashboardId(null); + }; + + return ( + + ); +} diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationCard.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationCard.tsx new file mode 100644 index 0000000..b61963a --- /dev/null +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationCard.tsx @@ -0,0 +1,39 @@ +import { Invitation } from "@/lib/types"; +import { deleteInvitation } from "@/lib/apis/dashboardsApi"; +import Button from "@/components/common/button/Button"; + +type InvitationCardProps = Invitation & { + token: string; +}; + +export default function InvitationCard({ + id, + dashboard, + invitee, + token, +}: InvitationCardProps) { + const handleDeleteClick = async () => { + await deleteInvitation({ + token, + dashboardId: dashboard.id, + invitationId: id, + }); + + window.location.reload(); + }; + + return ( +
+
+
{invitee.email}
+ +
+
+ ); +} diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationSection.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationSection.tsx index 7b62e78..a812ee8 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationSection.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InvitationSection.tsx @@ -1,6 +1,13 @@ "use client"; +import { useEffect, useState } from "react"; +import { Invitation } from "@/lib/types"; +import { fetchInvitationList } from "@/lib/apis/dashboardsApi"; import Pagination from "@/components/common/pagenation-button/PagenationButton"; +import InvitationCard from "./InvitationCard"; +import InviteModalButton from "./InviteModalButton"; + +const PAGE_SIZE = 4; export default function InvitationSection({ id, @@ -9,27 +16,67 @@ export default function InvitationSection({ id: number; token: string; }) { - // id 값과 token 값 사용하고 나서는 지워도 되는 코드들 - console.log(id); - console.log(token); + const [items, setItems] = useState([]); + const [page, setPage] = useState(1); + const [totalCount, setTotalCount] = useState(0); + const totalPage = Math.ceil(totalCount / PAGE_SIZE); + + const handleLoad = async () => { + const { invitations, totalCount } = await fetchInvitationList({ + token, + id, + page, + size: PAGE_SIZE, + }); + setItems(invitations); + setTotalCount(totalCount); + }; + + useEffect(() => { + handleLoad(); + }, [page]); + + const handlePrevPage = () => { + if (page > 1) { + setPage((prev) => prev - 1); + } + }; + + const handleNextPage = () => { + if (page < totalPage) { + setPage((prev) => prev + 1); + } + }; return ( -
-
-

- 초대 내역 -

-
- 1 페이지 중 1 - { - console.log(`구성원 페이지 ${page}로 변경`); - }} - /> +
+
+
+

+ 초대 내역 +

+

이메일

+
+
+
+

+ {totalPage} 페이지 중 {page} +

+ +
+
+
+ {items.map((item) => ( + + ))} +
); } diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InviteModalButton.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InviteModalButton.tsx new file mode 100644 index 0000000..f60357f --- /dev/null +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/InviteModalButton.tsx @@ -0,0 +1,28 @@ +import { useModalStore } from "@/lib/store/useModalStore"; +import Button from "@/components/common/button/Button"; +import Image from "next/image"; +import AddIcon from "../../../../../../../public/icon/add_box_icon.svg"; + +export default function InviteModalButton() { + const { openModal } = useModalStore(); + + return ( + + ); +} diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/MemberSection.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/MemberSection.tsx index f99d6b3..cc720e1 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/MemberSection.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/_components/MemberSection.tsx @@ -1,7 +1,5 @@ "use client"; -import Pagination from "@/components/common/pagenation-button/PagenationButton"; - export default function MemberSection({ id, token, @@ -16,18 +14,11 @@ export default function MemberSection({ return (
-

+

구성원

1 페이지 중 1 - { - console.log(`구성원 페이지 ${page}로 변경`); - }} - />
diff --git a/src/app/(after-login)/dashboard/[dashboardid]/edit/page.tsx b/src/app/(after-login)/dashboard/[dashboardid]/edit/page.tsx index cbd9d87..56adb5f 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/edit/page.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/edit/page.tsx @@ -3,22 +3,24 @@ import BackButton from "./_components/BackButton"; import DashboardEditSection from "./_components/DashboardEditSection"; import InvitationSection from "./_components/InvitationSection"; import MemberSection from "./_components/MemberSection"; +import DeleteButton from "./_components/DeleteButton"; export default function Page({ params }: { params: { dashboardid: string } }) { const dashboardId = Number(params.dashboardid); const accessToken = cookies().get("accessToken")?.value ?? ""; return ( -
-
-
+
+
+
- +
+
); diff --git a/src/app/(after-login)/mypage/_components/BackButton.tsx b/src/app/(after-login)/mypage/_components/BackButton.tsx index 62eb7a8..599777a 100644 --- a/src/app/(after-login)/mypage/_components/BackButton.tsx +++ b/src/app/(after-login)/mypage/_components/BackButton.tsx @@ -18,12 +18,12 @@ export default function BackButton() { > -
+

돌아가기 -

+

); diff --git a/src/components/common/pagenation-button/PagenationButton.tsx b/src/components/common/pagenation-button/PagenationButton.tsx index 673679d..655e672 100644 --- a/src/components/common/pagenation-button/PagenationButton.tsx +++ b/src/components/common/pagenation-button/PagenationButton.tsx @@ -59,38 +59,28 @@ const PaginationButton = ({ interface PaginationProps { currentPage: number; totalPages: number; - onPageChange: (page: number) => void; + onPrevClick: () => void; + onNextClick: () => void; className?: string; } export default function Pagination({ currentPage, totalPages, - onPageChange, + onPrevClick, + onNextClick, className = "", }: PaginationProps) { - const handlePrevClick = () => { - if (currentPage > 1) { - onPageChange(currentPage - 1); - } - }; - - const handleNextClick = () => { - if (currentPage < totalPages) { - onPageChange(currentPage + 1); - } - }; - return (
= totalPages} />
diff --git a/src/components/layout/navbar/MemberList.tsx b/src/components/layout/navbar/MemberList.tsx index c2f7091..8b5eec5 100644 --- a/src/components/layout/navbar/MemberList.tsx +++ b/src/components/layout/navbar/MemberList.tsx @@ -32,7 +32,7 @@ export default function MemberList({ data }: MemberListProps) {
))} {hiddenCount > 0 && ( -
+
+{hiddenCount}
)} diff --git a/src/components/layout/navbar/Navbar.tsx b/src/components/layout/navbar/Navbar.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/layout/sidebar/AddButton.tsx b/src/components/layout/sidebar/AddButton.tsx index 3c33143..7fba6c4 100644 --- a/src/components/layout/sidebar/AddButton.tsx +++ b/src/components/layout/sidebar/AddButton.tsx @@ -1,5 +1,3 @@ -"use client"; - import { useModalStore } from "@/lib/store/useModalStore"; import Image from "next/image"; import AddButtonIcon from "../../../../public/icon/add_box_icon.svg"; diff --git a/src/components/layout/sidebar/SideMenuHeader.tsx b/src/components/layout/sidebar/SideMenuHeader.tsx index 43bfc8a..8e0accf 100644 --- a/src/components/layout/sidebar/SideMenuHeader.tsx +++ b/src/components/layout/sidebar/SideMenuHeader.tsx @@ -1,5 +1,3 @@ -"use client"; - import { useIsMobile } from "@/lib/hooks/useCheckViewport"; export default function SideMenuHeader() { diff --git a/src/components/layout/sidebar/SideMenuList.tsx b/src/components/layout/sidebar/SideMenuList.tsx index 829694f..2357692 100644 --- a/src/components/layout/sidebar/SideMenuList.tsx +++ b/src/components/layout/sidebar/SideMenuList.tsx @@ -1,5 +1,3 @@ -"use client"; - import { useEffect, useState, useRef } from "react"; import { useIntersection } from "@/lib/hooks/useIntersection"; import { DashboardList } from "@/lib/types"; diff --git a/src/components/layout/sidebar/index.tsx b/src/components/layout/sidebar/index.tsx index 6666d95..fb964c8 100644 --- a/src/components/layout/sidebar/index.tsx +++ b/src/components/layout/sidebar/index.tsx @@ -1,9 +1,14 @@ +"use client"; + +import { useDashboardStore } from "@/lib/store/useDashboardStore"; import LogoButton from "@/components/common/logo-button/LogoButton"; import AddButton from "./AddButton"; import SideMenuHeader from "./SideMenuHeader"; import SideMenuList from "./SideMenuList"; export default function Sidebar() { + const dashboardId = useDashboardStore((state) => state.dashboardId); + return (
@@ -14,7 +19,7 @@ export default function Sidebar() {
- +
); diff --git a/src/components/modal/create-dashboard/CreateDashboardModal.tsx b/src/components/modal/create-dashboard/CreateDashboardModal.tsx index 9cf89ea..aeb0817 100644 --- a/src/components/modal/create-dashboard/CreateDashboardModal.tsx +++ b/src/components/modal/create-dashboard/CreateDashboardModal.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; +import { useDashboardStore } from "@/lib/store/useDashboardStore"; import { postDashboard } from "@/lib/apis/dashboardsApi"; import Modal from "@/components/common/modal/Modal"; import Input from "@/components/common/input/Input"; @@ -12,6 +13,7 @@ export default function CreateDashboardModal() { const [selectedColor, setSelectedColor] = useState(""); const [isFormValid, setIsFormValid] = useState(false); const router = useRouter(); + const setDashboardId = useDashboardStore((state) => state.setDashboardId); const accessToken = localStorage.getItem("accessToken") ?? ""; const onColorSelect = (color: ColorCode | "") => { @@ -40,7 +42,7 @@ export default function CreateDashboardModal() { const newDashboardId = res.id; router.push(`/dashboard/${newDashboardId}`); - router.refresh(); + setDashboardId(newDashboardId); }; return ( diff --git a/src/lib/apis/dashboardsApi.ts b/src/lib/apis/dashboardsApi.ts index df6c3f1..b18f5d0 100644 --- a/src/lib/apis/dashboardsApi.ts +++ b/src/lib/apis/dashboardsApi.ts @@ -66,6 +66,76 @@ export async function postDashboard({ return res.json(); } +export async function putDashboard({ + token, + title, + color, + id, +}: { + token: string; + title: string; + color: string; + id: number; +}) { + const res = await fetch(`${BASE_URL}/dashboards/${id}`, { + method: "PUT", + headers: { + Accept: "application/json", + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title, + color, + }), + }); + + return res.json(); +} + +export async function deleteDashboard({ + token, + id, +}: { + token: string; + id: number; +}) { + await fetch(`${BASE_URL}/dashboards/${id}`, { + method: "DELETE", + headers: { + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + return null; +} + +export async function fetchInvitationList({ + token, + id, + page, + size, +}: { + token: string; + id: number; + page: number; + size: number; +}) { + const res = await fetch( + `${BASE_URL}/dashboards/${id}/invitations?page=${page}&size=${size}`, + { + headers: { + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + cache: "no-store", + } + ); + + return res.json(); +} + export async function postInvitation({ token, id, @@ -89,3 +159,26 @@ export async function postInvitation({ return res.json(); } + +export async function deleteInvitation({ + token, + dashboardId, + invitationId, +}: { + token: string; + dashboardId: number; + invitationId: number; +}) { + await fetch( + `${BASE_URL}/dashboards/${dashboardId}/invitations/${invitationId}`, + { + method: "DELETE", + headers: { + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + + return null; +}