diff --git a/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx b/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx
deleted file mode 100644
index d26ae3f0..00000000
--- a/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-'use client';
-import { useEffect } from 'react';
-import {
- ModalCloseButton,
- ModalContainer,
- ModalFooter,
- ModalHeading,
- ModalOverlay,
-} from '@/components/common/modal';
-import { useModal, ModalPortal } from '@/contexts/ModalContext';
-export default function Page() {
- const { openModal } = useModal();
- useEffect(() => {
- openModal('modalId');
- }, []);
-
- return (
-
-
-
-
-
- 할 일 목록 추가
-
-
-
-
-
- );
-}
diff --git a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx
deleted file mode 100644
index f2521707..00000000
--- a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-'use client';
-import { useState } from 'react';
-import Button from '@/components/common/Button';
-import FormField from '@/components/common/formField';
-import {
- ModalCloseButton,
- ModalContainer,
- ModalFooter,
- ModalHeading,
- ModalOverlay,
-} from '@/components/common/modal';
-import { useModal, ModalPortal } from '@/contexts/ModalContext';
-import BouncingDots from '@/components/common/loading/BouncingDots';
-import { validateEmptyValue } from '@/utils/validators';
-
-interface TasklistCreateModalProps {
- modalId: string;
- isLoading: boolean;
- createTasklist: (name: string) => void;
-}
-
-export default function TasklistCreateModal({
- modalId,
- isLoading,
- createTasklist,
-}: TasklistCreateModalProps) {
- const [name, setName] = useState('');
- const { closeModal } = useModal();
-
- const handleChangeName = (e: React.ChangeEvent) => {
- setName(e.target.value);
- };
-
- const clearName = () => {
- setName('');
- };
-
- const handleClickAddButton = async () => {
- if (validateEmptyValue(name)) return;
- createTasklist(name);
- clearName();
- closeModal(modalId);
- };
-
- return (
- <>
-
-
-
-
-
- 할 일 목록 추가
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx
index 68755d88..183ac8f4 100644
--- a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx
+++ b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx
@@ -1,10 +1,8 @@
'use client';
import useTasklists from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/useTasklists';
import TasklistItem from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistItem';
-import TasklistCreateModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal';
import TasklistUpdateModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistUpdateModal';
import TasklistDeleteModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistDeleteModal';
-import { ModalTrigger } from '@/components/common/modal';
import { Group } from '@/types/group';
import { Tasklist } from '@/types/tasklist';
import Link from 'next/link';
@@ -20,15 +18,12 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) {
optimisticTasklists,
selectedTasklist,
setSelectedTasklist,
- isCreateLoading,
isUpdateLoading,
isDeleteLoading,
- createTasklist,
updateTasklist,
deleteTasklist,
} = useTasklists(groupId, tasklists);
- const tasklistCreateModalId = `tasklistCreate-${groupId}`;
const tasklistUpdateModalId = selectedTasklist ? `tasklistUpdate-${selectedTasklist.id}` : '';
const tasklistDeleteModalId = selectedTasklist ? `tasklistDelete-${selectedTasklist.id}` : '';
const totalTasklistCount = optimisticTasklists.length;
@@ -41,10 +36,7 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) {
할 일 목록 ({totalTasklistCount}개)
- {/*
- + 새로운 목록 추가하기
- */}
-
+
+ 새로운 목록 추가하기
@@ -60,12 +52,6 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) {
-
-
{selectedTasklist && (
zz>;
-}
diff --git a/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx b/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx
deleted file mode 100644
index 73e93591..00000000
--- a/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-export default function Layout({
- children,
- modal,
-}: {
- children: React.ReactNode;
- modal: React.ReactNode;
-}) {
- return (
- <>
- {children}
- {modal}
- >
- );
-}
diff --git a/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx b/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx
new file mode 100644
index 00000000..49be2bb1
--- /dev/null
+++ b/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx
@@ -0,0 +1,58 @@
+'use client';
+import { useState } from 'react';
+import { useParams, useRouter } from 'next/navigation';
+import Button from '@/components/common/Button';
+import FormField from '@/components/common/formField';
+import Modal from '@/components/common/modal/newModal';
+import BouncingDots from '@/components/common/loading/BouncingDots';
+import { validateEmptyValue } from '@/utils/validators';
+import { createTasklistAction } from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/actions';
+
+export default function Page() {
+ const { groupId } = useParams<{ groupId: string }>();
+ const router = useRouter();
+ const [name, setName] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleChangeName = (e: React.ChangeEvent) => {
+ setName(e.target.value);
+ };
+
+ const handleClickAddButton = async () => {
+ setIsLoading(true);
+ if (validateEmptyValue(name)) {
+ setIsLoading(false);
+ return;
+ }
+ createTasklistAction(Number(groupId), name).then(() => {
+ setIsLoading(false);
+ router.back();
+ });
+ };
+
+ return (
+
+
+
+
+ 할 일 목록 추가
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(content-layout)/[groupId]/(groupId)/@modal/default.tsx b/src/app/@modal/default.tsx
similarity index 100%
rename from src/app/(content-layout)/[groupId]/(groupId)/@modal/default.tsx
rename to src/app/@modal/default.tsx
diff --git a/src/app/[...modal]/page.tsx b/src/app/[...modal]/page.tsx
new file mode 100644
index 00000000..e7c23934
--- /dev/null
+++ b/src/app/[...modal]/page.tsx
@@ -0,0 +1,5 @@
+'use client';
+
+import notFound from '@/app/not-found';
+
+export default notFound;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index de8e1aee..0871d184 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -11,8 +11,10 @@ export const metadata: Metadata = {
export default function RootLayout({
children,
+ modal,
}: Readonly<{
children: React.ReactNode;
+ modal: React.ReactNode;
}>) {
return (
@@ -24,6 +26,7 @@ export default function RootLayout({
{children}
+ {modal}
diff --git a/src/components/common/modal/newModal/CloseButton.tsx b/src/components/common/modal/newModal/CloseButton.tsx
new file mode 100644
index 00000000..634aa37a
--- /dev/null
+++ b/src/components/common/modal/newModal/CloseButton.tsx
@@ -0,0 +1,23 @@
+'use client';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+import clsx from 'clsx';
+
+export default function ModalCloseButton({
+ className,
+ onClick,
+ ...props
+}: React.ComponentProps<'button'>) {
+ const router = useRouter();
+
+ const handleClick = (e: React.MouseEvent) => {
+ onClick?.(e);
+ router.back();
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/common/modal/newModal/Container.tsx b/src/components/common/modal/newModal/Container.tsx
new file mode 100644
index 00000000..e95b4a6b
--- /dev/null
+++ b/src/components/common/modal/newModal/Container.tsx
@@ -0,0 +1,15 @@
+import clsx from 'clsx';
+
+export default function Container({ className, children, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/common/modal/newModal/Description.tsx b/src/components/common/modal/newModal/Description.tsx
new file mode 100644
index 00000000..75ffbb04
--- /dev/null
+++ b/src/components/common/modal/newModal/Description.tsx
@@ -0,0 +1,15 @@
+import clsx from 'clsx';
+
+export default function Description({ className, children, ...props }: React.ComponentProps<'p'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/common/modal/newModal/Footer.tsx b/src/components/common/modal/newModal/Footer.tsx
new file mode 100644
index 00000000..1ff370c8
--- /dev/null
+++ b/src/components/common/modal/newModal/Footer.tsx
@@ -0,0 +1,9 @@
+import clsx from 'clsx';
+
+export default function Footer({ className, children, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/common/modal/newModal/Heading.tsx b/src/components/common/modal/newModal/Heading.tsx
new file mode 100644
index 00000000..59aa69a0
--- /dev/null
+++ b/src/components/common/modal/newModal/Heading.tsx
@@ -0,0 +1,12 @@
+import clsx from 'clsx';
+
+export default function Heading({ className, children, ...props }: React.ComponentProps<'h2'>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/common/modal/newModal/Overlay.tsx b/src/components/common/modal/newModal/Overlay.tsx
new file mode 100644
index 00000000..4b823283
--- /dev/null
+++ b/src/components/common/modal/newModal/Overlay.tsx
@@ -0,0 +1,47 @@
+'use client';
+import { useEffect } from 'react';
+import { useRouter } from 'next/navigation';
+import clsx from 'clsx';
+
+interface OverlayProps extends React.ComponentProps<'div'> {
+ disableOverlayClose?: boolean;
+}
+
+export default function Overlay({
+ disableOverlayClose = false,
+ onClick,
+ className,
+ children,
+ ...props
+}: OverlayProps) {
+ const router = useRouter();
+
+ const handleClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget && !disableOverlayClose) {
+ onClick?.(e);
+ router.back();
+ }
+ };
+
+ useEffect(function lockBodyScroll() {
+ const originalStyle = window.getComputedStyle(document.body).overflow;
+ document.body.style.overflow = 'hidden';
+
+ return () => {
+ document.body.style.overflow = originalStyle;
+ };
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/common/modal/newModal/index.ts b/src/components/common/modal/newModal/index.ts
new file mode 100644
index 00000000..d0138340
--- /dev/null
+++ b/src/components/common/modal/newModal/index.ts
@@ -0,0 +1,17 @@
+import CloseButton from './CloseButton';
+import Container from './Container';
+import Description from './Description';
+import Footer from './Footer';
+import Heading from './Heading';
+import Overlay from './Overlay';
+
+const Modal = {
+ CloseButton,
+ Container,
+ Description,
+ Footer,
+ Heading,
+ Overlay,
+};
+
+export default Modal;