diff --git a/src/app/admin/(block)/block-menu.tsx b/src/app/admin/(block)/block-menu.tsx index 90735648..6e5a1289 100644 --- a/src/app/admin/(block)/block-menu.tsx +++ b/src/app/admin/(block)/block-menu.tsx @@ -2,7 +2,7 @@ import React, { SetStateAction } from "react"; import { blockTypes } from "@config/block_types"; import Link from "next/link"; import Image from "next/image"; -import Portal from "@app/components/Portal"; +import Portal from "@app/components/portal"; import Contour from "@app/admin/(block)/components/contour"; import { usePathname } from "next/navigation"; @@ -32,7 +32,7 @@ const BlockMenu = ({ isOpen, setIsOpen }: Props) => {
diff --git a/src/app/admin/(block)/calendar/components/schedule-list.tsx b/src/app/admin/(block)/calendar/components/schedule-list.tsx index 40a66f2d..3b8b5530 100644 --- a/src/app/admin/(block)/calendar/components/schedule-list.tsx +++ b/src/app/admin/(block)/calendar/components/schedule-list.tsx @@ -1,9 +1,9 @@ import Image from "next/image"; -import { useRouter } from "next/navigation"; +import { usePathname, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { BsCalendarXFill } from "react-icons/bs"; -interface Schedule { +export interface Schedule { id: number; title: string; url: string; @@ -61,15 +61,17 @@ function EmptyState({ message }: { message: React.ReactNode }) { ); } -function ScheduleItem({ +export function ScheduleItem({ schedule, onDelete, }: { schedule: Schedule; - onDelete: (id: number) => void; + onDelete?: (id: number) => void; }) { const router = useRouter(); const status = getScheduleStatus(schedule); + const pathname = usePathname(); + const isCalendarPage = pathname.includes("/admin/calendar"); const handleEdit = () => { router.push(`/admin/calendar/manage?mode=edit&id=${schedule.id}`); @@ -96,28 +98,30 @@ function ScheduleItem({ }} />
-
+
{formatDate(schedule.dateStart)} ~ {formatDate(schedule.dateEnd)}
{schedule.title}
-
- - -
+ {isCalendarPage && ( +
+ + +
+ )}
-
+ {isCalendarPage &&
} ); } diff --git a/src/app/admin/(block)/divider/components/divider-preview.tsx b/src/app/admin/(block)/divider/components/divider-preview.tsx index dd8d1f6e..1603774b 100644 --- a/src/app/admin/(block)/divider/components/divider-preview.tsx +++ b/src/app/admin/(block)/divider/components/divider-preview.tsx @@ -10,21 +10,12 @@ interface DividerContentProps { type: DividerType; } -const DividerContent = ({ type }: DividerContentProps) => { +export const DividerContent = ({ type }: DividerContentProps) => { const commonClasses = "flex h-12 items-center justify-center"; switch (type) { case "Space": return
; - case "Dashed": - case "Solid": - return ( -
-
-
- ); case "Point": return
· · ·
; case "Zigzag": @@ -38,6 +29,15 @@ const DividerContent = ({ type }: DividerContentProps) => { />
); + case "Dashed": + case "Solid": + return ( +
+
+
+ ); default: return
; } diff --git a/src/app/admin/(block)/event/components/event-form.tsx b/src/app/admin/(block)/event/components/event-form.tsx index 91118ccc..c6d22f01 100644 --- a/src/app/admin/(block)/event/components/event-form.tsx +++ b/src/app/admin/(block)/event/components/event-form.tsx @@ -8,7 +8,6 @@ import ButtonBox from "../../components/buttons/button-box"; import AddButton from "../../components/buttons/add-button"; import FormInput from "../../components/form-input"; import { useRouter, useSearchParams } from "next/navigation"; -import { getSequence } from "lib/get-sequence"; import "react-datepicker/dist/react-datepicker.css"; import { adminApiInstance } from "../../../../../utils/apis"; diff --git a/src/app/admin/(block)/event/components/event-preview.tsx b/src/app/admin/(block)/event/components/event-preview.tsx index 5ad312a4..5cecd378 100644 --- a/src/app/admin/(block)/event/components/event-preview.tsx +++ b/src/app/admin/(block)/event/components/event-preview.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from "react"; import { IoIosArrowDown } from "react-icons/io"; import { twMerge } from "tailwind-merge"; +import { usePathname } from "next/navigation"; export default function EventPreview({ title, @@ -24,6 +25,8 @@ export default function EventPreview({ const [isTitleOverflowing, setIsTitleOverflowing] = useState(false); // 타이틀 텍스트 넘침 여부 const descriptionRef = useRef(null); const titleRef = useRef(null); + const pathname = usePathname(); + const isEvent = pathname.includes("event"); const calculateTimeLeft = () => { if (endDate && endTime) { @@ -88,7 +91,13 @@ export default function EventPreview({ } }; return ( -
+
void; selectedImageUrl: string; + connectingUrl?: string; + title: string | null; } -const ImageBox = ({ selectedImageUrl }: Props) => { +const ImageBox = ({ selectedImageUrl, connectingUrl, title }: Props) => { return ( -
- {/**/} - {/* */} - {/**/} -
- 이미지 URL을 확인해주세요 +
+
+ {/**/} + {/* */} + {/**/} + +
+ 이미지 URL을 확인해주세요 +
+
+ {title && ( +
{title}
+ )}
); }; diff --git a/src/app/admin/(block)/image/page.tsx b/src/app/admin/(block)/image/page.tsx index aff506c8..4c1af7b3 100644 --- a/src/app/admin/(block)/image/page.tsx +++ b/src/app/admin/(block)/image/page.tsx @@ -137,6 +137,8 @@ const Page = () => { {/*/>*/} void; +} +const CircleButton = ({ text, onClick }: Props) => { + return ( +
+ +
+ ); +}; + +export default CircleButton; diff --git a/src/app/admin/components/preview/components/preview-calendar.tsx b/src/app/admin/components/preview/components/preview-calendar.tsx new file mode 100644 index 00000000..d060516e --- /dev/null +++ b/src/app/admin/components/preview/components/preview-calendar.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { Block } from "@app/admin/page"; +import { ScheduleItem } from "@app/admin/(block)/calendar/components/schedule-list"; + +// interface Schedule { +// id: number; +// title: string; +// url: string; +// dateStart: string; +// dateEnd: string; +// } + +interface Props { + block: Block; +} +const PreviewCalendar = ({ block }: Props) => { + const { id, title: subject, url, dateStart: start, dateEnd: end } = block; + const title = subject as string; + const dateStart = start as string; + const dateEnd = end as string; + const schedule = { id, title, url, dateStart, dateEnd }; + return ( +
+ +
+ ); +}; + +export default PreviewCalendar; diff --git a/src/app/admin/components/preview/components/preview-divider.tsx b/src/app/admin/components/preview/components/preview-divider.tsx new file mode 100644 index 00000000..5897f01a --- /dev/null +++ b/src/app/admin/components/preview/components/preview-divider.tsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from "react"; +import { Block } from "@app/admin/page"; +import { DividerType } from "@app/admin/(block)/divider/types"; +import { DividerContent } from "@app/admin/(block)/divider/components/divider-preview"; + +interface Props { + block: Block; +} + +const PreviewDivider = ({ block }: Props) => { + const [selectedDivider, setSelectedDivider] = useState("Space"); + const { style } = block; + + useEffect(() => { + if (!style) return; + setSelectedDivider(getDividerStyle(style)); + }, [block]); + + const getDividerStyle = (type: number): DividerType => { + const styles: Record = { + 1: "Space", + 2: "Dashed", + 3: "Solid", + 4: "Point", + 5: "Zigzag", + }; + return styles[type] || "Space"; + }; + return ( +
+ +
+ ); +}; + +export default PreviewDivider; diff --git a/src/app/admin/components/preview/components/preview-event.tsx b/src/app/admin/components/preview/components/preview-event.tsx new file mode 100644 index 00000000..4f449b0c --- /dev/null +++ b/src/app/admin/components/preview/components/preview-event.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { Block } from "@app/admin/page"; +import EventPreview from "@app/admin/(block)/event/components/event-preview"; + +interface Props { + block: Block; +} +const PreviewEvent = ({ block }: Props) => { + const { title: subject, subText01, dateStart: start, dateEnd: end } = block; + const title = subject as string; + const description = subText01 as string; + const dateStart = new Date(start as string); + const dateEnd = new Date(end as string); + + return ( + + ); +}; + +export default PreviewEvent; diff --git a/src/app/admin/components/preview/components/preview-image.tsx b/src/app/admin/components/preview/components/preview-image.tsx new file mode 100644 index 00000000..a8aa8cd0 --- /dev/null +++ b/src/app/admin/components/preview/components/preview-image.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Block } from "@app/admin/page"; +import ImageBox from "@app/admin/(block)/image/components/image-box"; + +interface Props { + block: Block; +} +const PreviewImage = ({ block }: Props) => { + const { imgUrl, url, title } = block; + return ( +
+ +
+ ); +}; + +export default PreviewImage; diff --git a/src/app/admin/components/preview/components/preview-link.tsx b/src/app/admin/components/preview/components/preview-link.tsx new file mode 100644 index 00000000..0a69f236 --- /dev/null +++ b/src/app/admin/components/preview/components/preview-link.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { Block } from "@app/admin/page"; +import Link from "next/link"; +import Image from "next/image"; +import { usePathname } from "next/navigation"; + +interface Props { + block: Block; +} +const PreviewLink = ({ block }: Props) => { + const pathname = usePathname(); + const isAdmin = pathname.includes("admin"); + const { url, title, imgUrl } = block; + + return ( +
+ +
+ +
+ {title || "링크 이동"} +
+ + + {imgUrl ? ( + {title + ) : ( + + )} +
+ ); +}; + +export default PreviewLink; diff --git a/src/app/admin/components/preview/components/preview-text.tsx b/src/app/admin/components/preview/components/preview-text.tsx new file mode 100644 index 00000000..e118a619 --- /dev/null +++ b/src/app/admin/components/preview/components/preview-text.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Block } from "@app/admin/page"; +import { twMerge } from "tailwind-merge"; + +interface Props { + block: Block; +} +const PreviewText = ({ block }: Props) => { + const { title } = block; + return ( +
+ {title} +
+ ); +}; + +export default PreviewText; diff --git a/src/app/admin/components/preview/components/preview-video.tsx b/src/app/admin/components/preview/components/preview-video.tsx new file mode 100644 index 00000000..7d87abe8 --- /dev/null +++ b/src/app/admin/components/preview/components/preview-video.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { Block } from "@app/admin/page"; + +interface Props { + block: Block; +} +const PreviewVideo = ({ block }: Props) => { + const { url: videoUrl } = block; + return ( +
+ {videoUrl && ( + +
동영상 주소를 확인해주세요
+
+ )} +
+ ); +}; + +export default PreviewVideo; diff --git a/src/app/admin/components/preview/components/preview.tsx b/src/app/admin/components/preview/components/preview.tsx new file mode 100644 index 00000000..7a816a72 --- /dev/null +++ b/src/app/admin/components/preview/components/preview.tsx @@ -0,0 +1,95 @@ +import React, { SetStateAction, Suspense, useEffect, useState } from "react"; +import ProfileBox from "@app/admin/components/profile-box"; + +import PreviewLink from "@app/admin/components/preview/components/preview-link"; +import PreviewText from "@app/admin/components/preview/components/preview-text"; +import { Block } from "@app/admin/page"; +import dynamic from "next/dynamic"; +import { adminApiInstance } from "../../../../../utils/apis"; +import { usePathname } from "next/navigation"; +import CircleButton from "@app/admin/components/buttons/circle-button"; +const PreviewImage = dynamic( + () => import("@app/admin/components/preview/components/preview-image"), +); +const PreviewVideo = dynamic( + () => import("@app/admin/components/preview/components/preview-video"), +); +const PreviewDivider = dynamic( + () => import("@app/admin/components/preview/components/preview-divider"), +); +const PreviewCalendar = dynamic( + () => import("@app/admin/components/preview/components/preview-calendar"), +); +const PreviewEvent = dynamic( + () => import("@app/admin/components/preview/components/preview-event"), +); + +interface Props { + setIsOpen?: React.Dispatch>; +} +const Preview = ({ setIsOpen }: Props) => { + const [blocks, setBlocks] = useState([]); + const pathname = usePathname(); + const isAdmin = pathname.includes("admin"); + + useEffect(() => { + getBlocks().then(); + }, []); + + async function getBlocks() { + const blockApis = await adminApiInstance; + const response = await blockApis.getBlocks(); + if (!response) return; + if (response.ok) { + const { data } = await response.json(); + setBlocks(data); + } else { + sessionStorage.removeItem("token"); + // alert("블록 조회 실패"); + } + } + + const setComponentType = (block: Block) => { + const { type } = block; + return { + 1: , + 2: ( + Loading video...

}> + +
+ ), + 3: , + 4: , + 5: , + 6: , + 7: , + }[type]; + }; + return ( + //
+ <> + +
    + {blocks.map((block, index) => { + return ( +
  • + {setComponentType(block)} +
  • + ); + })} +
+ {isAdmin && ( +
+ setIsOpen && setIsOpen(false)} + /> +
+ )} + + ); +}; + +export default Preview; diff --git a/src/app/admin/components/preview/preview-modal.tsx b/src/app/admin/components/preview/preview-modal.tsx new file mode 100644 index 00000000..a4f1cb4c --- /dev/null +++ b/src/app/admin/components/preview/preview-modal.tsx @@ -0,0 +1,54 @@ +import React, { SetStateAction, Suspense } from "react"; +import Portal from "@app/components/portal"; +import CircleButton from "@app/admin/components/buttons/circle-button"; +import ProfileBox from "@app/admin/components/profile-box"; +import { Block } from "@app/admin/page"; +import PreviewText from "@app/admin/components/preview/components/preview-text"; +import PreviewLink from "@app/admin/components/preview/components/preview-link"; +import dynamic from "next/dynamic"; +import Preview from "@app/admin/components/preview/components/preview"; +const PreviewImage = dynamic( + () => import("@app/admin/components/preview/components/preview-image"), +); +const PreviewVideo = dynamic( + () => import("@app/admin/components/preview/components/preview-video"), +); +const PreviewDivider = dynamic( + () => import("@app/admin/components/preview/components/preview-divider"), +); +const PreviewCalendar = dynamic( + () => import("@app/admin/components/preview/components/preview-calendar"), +); +const PreviewEvent = dynamic( + () => import("@app/admin/components/preview/components/preview-event"), +); + +interface Props { + isOpen: boolean; + setIsOpen: React.Dispatch>; + data: Block[]; +} + +const PreviewModal = ({ isOpen, setIsOpen, data }: Props) => { + if (!isOpen) return null; + + const handleClose = () => { + setIsOpen(false); + }; + + console.log(data[data.length - 1]); + return ( + + +
+ +
+
+ ); +}; + +export default PreviewModal; diff --git a/src/app/admin/components/profile-box.tsx b/src/app/admin/components/profile-box.tsx new file mode 100644 index 00000000..3a04348c --- /dev/null +++ b/src/app/admin/components/profile-box.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { twMerge } from "tailwind-merge"; +import HomeMenu from "@app/admin/components/home-menu"; +import Link from "next/link"; +import { ClientRoute } from "@config/route"; +import Image from "next/image"; +import { useTheme } from "@components/providers/theme-provider"; +import { usePathname } from "next/navigation"; + +const ProfileBox = () => { + const { theme } = useTheme(); + const pathname = usePathname(); + const isAdmin = pathname === "/admin"; + + return ( + <> +
+
+ {"공유 +
+ + profile + momomoc + + + {!isAdmin && } +
+ + ); +}; + +export default ProfileBox; diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 52c9b850..070a996d 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -13,8 +13,11 @@ import { useEffect, useRef, useState } from "react"; import { postBlock } from "../../lib/post-block"; import { adminApiInstance } from "../../utils/apis"; import { twMerge } from "tailwind-merge"; +import ProfileBox from "@app/admin/components/profile-box"; +import PreviewModal from "@app/admin/components/preview/preview-modal"; +import CircleButton from "@app/admin/components/buttons/circle-button"; -interface Block { +export interface Block { id: number; type: number; sequence: number; @@ -81,6 +84,7 @@ function Admin() { const [showRealTime, setShowRealTime] = useState(0); const [isScrollTopVisible, setIsScrollTopVisible] = useState(false); const [isBlockMenuOn, setIsBlockMenuOn] = useState(false); + const [isPreviewOn, setIsPreviewOn] = useState(false); const [blocks, setBlocks] = useState([]); @@ -135,28 +139,13 @@ function Admin() { } }); }; - - const { theme } = useTheme(); + const handlePreviewOpen = () => { + setIsPreviewOn(true); + }; return (
-
- {!isAdmin && } - - profile - momomoc - -
+
@@ -227,17 +216,17 @@ function Admin() { /> )) )} + + {isAdmin && ( <>
-
- -
+ +