From 697b377975efc48c945950bd308146a645e9499b Mon Sep 17 00:00:00 2001 From: anyl92 Date: Sun, 15 Dec 2024 18:41:20 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20Timer=20Image=20Dialog=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(components)/ThemeInfo/Container.tsx | 2 +- .../(components)/ThemeInfo/ThemeImage.tsx | 70 -------- .../ThemeInfo/ThemeTimerImage.tsx | 94 ++++++++++ app/admin/(style)/themeInfo.modules.sass | 5 +- app/components/atoms/timerImage.atom.ts | 18 ++ .../Dialog-new/Image-Dialog-new/Dialog.tsx | 80 +++------ .../Image-Dialog-new/DialogBody.tsx | 27 +-- .../Preview-Dialog-new/DialogBody.tsx | 2 + app/components/common/Dialog-new/dialog.sass | 20 ++- app/queries/useTimerImageUpload.ts | 160 ++++++++++++++++++ public/images/svg/timer_preview_entire.svg | 48 ++++++ 11 files changed, 388 insertions(+), 138 deletions(-) delete mode 100644 app/admin/(components)/ThemeInfo/ThemeImage.tsx create mode 100644 app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx create mode 100644 app/components/atoms/timerImage.atom.ts create mode 100644 app/queries/useTimerImageUpload.ts create mode 100644 public/images/svg/timer_preview_entire.svg diff --git a/app/admin/(components)/ThemeInfo/Container.tsx b/app/admin/(components)/ThemeInfo/Container.tsx index eb31974..e50767c 100644 --- a/app/admin/(components)/ThemeInfo/Container.tsx +++ b/app/admin/(components)/ThemeInfo/Container.tsx @@ -11,7 +11,7 @@ import ThemeDrawer from "../ThemeDrawer/Container"; import ThemeInfoTitle from "./ThemeInfoTitle"; import ThemeInfoHint from "./ThemeInfoHint"; -import ThemeImage from "./ThemeImage"; +import ThemeImage from "./ThemeTimerImage"; export default function ThemeInfo() { const { open } = useModal(); diff --git a/app/admin/(components)/ThemeInfo/ThemeImage.tsx b/app/admin/(components)/ThemeInfo/ThemeImage.tsx deleted file mode 100644 index d699434..0000000 --- a/app/admin/(components)/ThemeInfo/ThemeImage.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import Image from "next/image"; -import React, { useRef } from "react"; - -import Dialog from "@/components/common/Dialog-new/Image-Dialog-new/Dialog"; -import PreviewDialog from "@/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog"; -import useModal from "@/hooks/useModal"; - -export default function ThemeImage() { - const QuestionProps = { - src: "/images/svg/icon_question.svg", - alt: "gallery_image", - width: 24, - height: 24, - }; - - const previewProps = { - src: "/images/svg/icon_preview.svg", - alt: "NEXT ROOM", - width: 120, - height: 120, - }; - const { open } = useModal(); - - const imgInputRef = useRef(null); - const handleAddImageBtnClick = () => { - // 숨겨진 input 클릭 트리거 - // imgInputRef.current?.click(); - open(Dialog, { type: "put" }); - }; - const handlePreviewImageBtnClick = () => { - // 숨겨진 input 클릭 트리거 - // imgInputRef.current?.click(); - open(PreviewDialog, { type: "put" }); - }; - - return ( -
-
- 타이머 배경 - -
-
-
- -
- -
-
- - - -
-
- ); -} diff --git a/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx new file mode 100644 index 0000000..e50c8ab --- /dev/null +++ b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx @@ -0,0 +1,94 @@ +import Image from "next/image"; +import React, { ChangeEvent, useRef, useState } from "react"; + +import Dialog from "@/components/common/Dialog-new/Image-Dialog-new/Dialog"; +import PreviewDialog from "@/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog"; +import useModal from "@/hooks/useModal"; +import { useTimerImageWrite } from "@/components/atoms/timerImage.atom"; + +export default function ThemeTimerImage() { + const QuestionProps = { + src: "/images/svg/icon_question.svg", + alt: "gallery_image", + width: 24, + height: 24, + }; + + const [isTimerImage, setIsTimerImage] = useState(false); + const [timerImageFile, setTimerImageFile] = useState(); + const setTimerImage = useTimerImageWrite(); + + const defaultTimerImage = "/images/svg/icon_preview.svg"; + const TimerImageProps = { + src: isTimerImage ? "" : defaultTimerImage, + alt: "NEXT ROOM", + width: 120, + height: 120, + }; + const { open } = useModal(); + + const addImageInputRef = useRef(null); + const fileReset = () => { + if (addImageInputRef.current) { + addImageInputRef.current.value = ""; + } + }; + const handleFileInputChange = (e: ChangeEvent) => { + if (!e.target.files) { + return; + } + const file: File = e.target.files[0]; + setTimerImageFile(file); + setTimerImage({ timerImage: file }); + + if (file) { + open(Dialog, { type: "put" }); + } + fileReset(); + // console.log(file, "file"); + }; + const handleAddTimerImageBtnClick = () => { + addImageInputRef.current?.click(); + }; + const handlePreviewBtnClick = () => { + open(PreviewDialog, { type: "put" }); + }; + + return ( +
+
+ 타이머 배경 + +
+
+
+ +
+ +
+
+ + {isTimerImage ? ( + + ) : ( + + )} +
+
+ ); +} diff --git a/app/admin/(style)/themeInfo.modules.sass b/app/admin/(style)/themeInfo.modules.sass index 4df2eb7..ede7979 100644 --- a/app/admin/(style)/themeInfo.modules.sass +++ b/app/admin/(style)/themeInfo.modules.sass @@ -13,7 +13,8 @@ @include title24SB color: $color-white position: relative - + div + cursor: default .drawer-open width: calc(100% - 520px) @@ -40,6 +41,7 @@ position: absolute right: 0 top: 30px + cursor: pointer .theme-infomation-text @include body14M @@ -323,6 +325,7 @@ span @include title16SB margin-right: 2px + cursor: default img vertical-align: bottom diff --git a/app/components/atoms/timerImage.atom.ts b/app/components/atoms/timerImage.atom.ts new file mode 100644 index 0000000..99a6640 --- /dev/null +++ b/app/components/atoms/timerImage.atom.ts @@ -0,0 +1,18 @@ +import { + atom, + useRecoilValue, + useRecoilState, + useSetRecoilState, +} from "recoil"; + +interface TimerImageType { + timerImage: Blob | MediaSource | null; +} +const timerImage = atom({ + key: "timerImage", + default: { timerImage: null }, +}); + +export const useTimerImage = () => useRecoilState(timerImage); +export const useTimerImageValue = () => useRecoilValue(timerImage); +export const useTimerImageWrite = () => useSetRecoilState(timerImage); diff --git a/app/components/common/Dialog-new/Image-Dialog-new/Dialog.tsx b/app/components/common/Dialog-new/Image-Dialog-new/Dialog.tsx index 46aec76..d870b4b 100644 --- a/app/components/common/Dialog-new/Image-Dialog-new/Dialog.tsx +++ b/app/components/common/Dialog-new/Image-Dialog-new/Dialog.tsx @@ -1,37 +1,21 @@ -import React, { forwardRef, useRef } from "react"; -import { SubmitHandler, useForm } from "react-hook-form"; +import React, { FormEvent, forwardRef, useRef } from "react"; import Image from "next/image"; -import { usePutTheme } from "@/mutations/putTheme"; -import { useDeleteTheme } from "@/mutations/deleteTheme"; -import { - useSelectedTheme, - useSelectedThemeReset, -} from "@/components/atoms/selectedTheme.atom"; -import { - useCreateThemeReset, - useCreateThemeValue, -} from "@/components/atoms/createTheme.atom"; +import { useSelectedTheme } from "@/components/atoms/selectedTheme.atom"; import useClickOutside from "@/hooks/useClickOutside"; -import { deleteProps, xProps } from "@/admin/(consts)/sidebar"; +import { xProps } from "@/admin/(consts)/sidebar"; import useModal from "@/hooks/useModal"; import ModalPortal from "@/components/common/Dialog-new/ModalPortal"; +import "@/components/common/Dialog-new/dialog.sass"; +import useTimerImageUpload from "@/queries/useTimerImageUpload"; +import { useTimerImageValue } from "@/components/atoms/timerImage.atom"; import DialogBody from "./DialogBody"; -import "@/components/common/Dialog-new/dialog.sass"; - interface DialogProps { type?: string | ""; } -interface FormValues { - id: number; - title: string; - timeLimit: number; - hintLimit: number; -} - const Dialog = forwardRef((props) => { const { open, close } = useModal(); const { type = "" } = props; @@ -42,41 +26,31 @@ const Dialog = forwardRef((props) => { open(Dialog, { type: "delete" }); }; - const { handleSubmit } = useForm(); const [selectedTheme, setSelectedTheme] = useSelectedTheme(); - const createTheme = useCreateThemeValue(); - const resetCreateTheme = useCreateThemeReset(); - const resetSelectedTheme = useSelectedThemeReset(); - const isDisabled = - type === "put" - ? (String(createTheme.title) === String(selectedTheme.title) && - Number(createTheme.timeLimit) === Number(selectedTheme.timeLimit) && - Number(createTheme.hintLimit) === Number(selectedTheme.hintLimit)) || - !(createTheme.title && createTheme.timeLimit && createTheme.hintLimit) - : !(createTheme.title && createTheme.timeLimit && createTheme.hintLimit); + const { timerImage } = useTimerImageValue(); + // const { mutateAsync: putTheme } = usePostTimerImage(); + const { handleProcess } = useTimerImageUpload(); - const { mutateAsync: putTheme } = usePutTheme(); - const { mutateAsync: deleteTheme } = useDeleteTheme(); - - const onSubmit: SubmitHandler = () => { + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); const { id } = selectedTheme; const submitData = { - ...createTheme, - id, + themeId: id, + timerImageFile: timerImage, }; - - if (type === "put") { - putTheme(submitData); - setSelectedTheme(submitData); - } else if (type === "delete") { - deleteTheme({ id }); - resetSelectedTheme(); + try { + // await handleProcess(submitData); + } catch (error) { + console.error(error); } - close(); - resetCreateTheme(); - return close(); + // postTimerImage(submitData); + + // putTheme(submitData); + // close(); + + // return close(); }; useClickOutside(formRef, close); @@ -86,18 +60,20 @@ const Dialog = forwardRef((props) => {
e.stopPropagation()} >
-

타이머 배경 수정

+

타이머 배경 올리기

-

힌트폰에 곧바로 적용됩니다

+

+ 힌트폰에 곧바로 적용됩니다 +

-
+ {isTimerImage && ( +
+ +
+ )}
((props) => { +const Dialog = forwardRef(() => { const { close } = useModal(); - const { type = "" } = props; const formRef = useRef(null); const [selectedTheme] = useSelectedTheme(); @@ -47,7 +42,7 @@ const Dialog = forwardRef((props) => { return ( e.stopPropagation()} @@ -77,8 +72,4 @@ const Dialog = forwardRef((props) => { ); }); -Dialog.defaultProps = { - type: "", -}; - export default Dialog; diff --git a/app/components/common/Dialog-new/Preview-Dialog-new/DialogBody.tsx b/app/components/common/Dialog-new/Preview-Dialog-new/DialogBody.tsx deleted file mode 100644 index 63fb5f7..0000000 --- a/app/components/common/Dialog-new/Preview-Dialog-new/DialogBody.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// TODO: 안쓰는파일 삭제필요 - -import React from "react"; -import Image from "next/image"; - -import ThemeTextField from "@/(shared)/(ThemeTextField)/Container"; -import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; -import { - hintCountTextFieldProps, - nameTextFieldProps, - timeTextFieldProps, -} from "@/admin/(components)/CreateTheme/createTheme"; - -export default function DialogBody() { - const selectedTheme = useSelectedThemeValue(); - const previewProps = { - src: "/images/svg/preview.svg", - alt: "NEXT ROOM", - width: 158, - height: 340, - }; - return ( -
- 배경 적용 미리보기 - -

- *예시 이미지입니다. 앱에서 세부 설정을 진행해 주세요. -

-
- ); -} diff --git a/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx b/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx index 291f89f..a19ff29 100644 --- a/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx +++ b/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx @@ -1,17 +1,6 @@ import React, { forwardRef, useRef } from "react"; -import { SubmitHandler, useForm } from "react-hook-form"; import Image from "next/image"; -import { usePutTheme } from "@/mutations/putTheme"; -import { useDeleteTheme } from "@/mutations/deleteTheme"; -import { - useSelectedTheme, - useSelectedThemeReset, -} from "@/components/atoms/selectedTheme.atom"; -import { - useCreateThemeReset, - useCreateThemeValue, -} from "@/components/atoms/createTheme.atom"; import useClickOutside from "@/hooks/useClickOutside"; import useModal from "@/hooks/useModal"; import ModalPortal from "@/components/common/Dialog-new/ModalPortal"; @@ -20,73 +9,31 @@ import { smallXProps, previewProps, statusBarProps, - timerPreviewProps, } from "@/admin/(consts)/sidebar"; +import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; -interface DialogProps { - type?: string | ""; -} - -interface FormValues { - id: number; - title: string; - timeLimit: number; - hintLimit: number; -} - -const PreviewDialog = forwardRef((props) => { - const { open, close } = useModal(); - const { type = "" } = props; - const formRef = useRef(null); - - const { handleSubmit } = useForm(); - const [selectedTheme, setSelectedTheme] = useSelectedTheme(); - const createTheme = useCreateThemeValue(); - const resetCreateTheme = useCreateThemeReset(); - const resetSelectedTheme = useSelectedThemeReset(); - const isDisabled = - type === "put" - ? (String(createTheme.title) === String(selectedTheme.title) && - Number(createTheme.timeLimit) === Number(selectedTheme.timeLimit) && - Number(createTheme.hintLimit) === Number(selectedTheme.hintLimit)) || - !(createTheme.title && createTheme.timeLimit && createTheme.hintLimit) - : !(createTheme.title && createTheme.timeLimit && createTheme.hintLimit); - - const { mutateAsync: putTheme } = usePutTheme(); - const { mutateAsync: deleteTheme } = useDeleteTheme(); +const PreviewDialog = forwardRef(() => { + const selectedTheme = useSelectedThemeValue(); + const { close } = useModal(); - const onSubmit: SubmitHandler = () => { - const { id } = selectedTheme; - - const submitData = { - ...createTheme, - id, - }; - - if (type === "put") { - putTheme(submitData); - setSelectedTheme(submitData); - } else if (type === "delete") { - deleteTheme({ id }); - resetSelectedTheme(); - } - close(); - resetCreateTheme(); - - return close(); + const timerPreviewProps = { + src: selectedTheme.themeImageUrl!, + alt: "timer priview image", + width: 284, + height: 555, }; - useClickOutside(formRef, close); + const divRef = useRef(null); + + useClickOutside(divRef, close); return ( - e.stopPropagation()} - > +
+

+ *예시 이미지입니다. 앱에서 세부 설정을 진행해 주세요. +

@@ -100,13 +47,9 @@ const PreviewDialog = forwardRef((props) => { > - +
); }); -PreviewDialog.defaultProps = { - type: "", -}; - export default PreviewDialog; diff --git a/app/components/common/Dialog-new/dialog.sass b/app/components/common/Dialog-new/dialog.sass index c2b58f8..a0fb384 100644 --- a/app/components/common/Dialog-new/dialog.sass +++ b/app/components/common/Dialog-new/dialog.sass @@ -139,6 +139,14 @@ @include caption12M color: #378EFF +.preview-dialog-caption + width: 100% + display: flex + align-items: center + justify-content: left + margin-bottom: 14px + @include body14M + .timer-preview-image-box position: relative width: 158px From ef00b853243db7bd4875c680ba8edf3867661b44 Mon Sep 17 00:00:00 2001 From: anyl92 Date: Sun, 29 Dec 2024 18:19:51 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD=EC=A0=9C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ThemeInfo/ThemeTimerImage.tsx | 15 +++-- .../DeleteDialog.tsx | 55 +++++++++++++++++++ .../Timer-Image-Delete-Dialog/DialogBody.tsx | 9 +++ app/mutations/deleteTimerImage.ts | 49 +++++++++++++++++ app/mutations/useTimerImageUpload.ts | 2 +- 5 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog.tsx create mode 100644 app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DialogBody.tsx create mode 100644 app/mutations/deleteTimerImage.ts diff --git a/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx index 863c7a9..054cff7 100644 --- a/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx +++ b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx @@ -7,7 +7,7 @@ import useModal from "@/hooks/useModal"; import { useTimerImageWrite } from "@/components/atoms/timerImage.atom"; import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; import { defaultTimerImage, QuestionIconProps } from "@/admin/(consts)/sidebar"; -import DeleteDialog from "@/components/common/DeleteDialog/DeleteDialog"; +import DeleteDialog from "@/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog"; export default function ThemeTimerImage() { const selectedTheme = useSelectedThemeValue(); @@ -50,7 +50,7 @@ export default function ThemeTimerImage() { }; const handleDelTimerImageBtnClick = () => { - // open(DeleteDialog); + open(DeleteDialog); }; return ( @@ -63,12 +63,11 @@ export default function ThemeTimerImage() {
{isTimerImage && ( -
-
diff --git a/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog.tsx b/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog.tsx new file mode 100644 index 0000000..c8b7a10 --- /dev/null +++ b/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog.tsx @@ -0,0 +1,55 @@ +import React, { forwardRef, useRef } from "react"; +import Image from "next/image"; + +import useClickOutside from "@/hooks/useClickOutside"; +import { xProps } from "@/admin/(consts)/sidebar"; +import useModal from "@/hooks/useModal"; +import "@/components/common/Dialog-new/dialog.sass"; +import { useDeleteTimerImage } from "@/mutations/deleteTimerImage"; +import ModalPortal from "@/components/common/Dialog-new/ModalPortal"; +import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; + +import DialogBody from "./DialogBody"; + +const DeleteDialog = forwardRef(() => { + const { close } = useModal(); + const divRef = useRef(null); + const selectedTheme = useSelectedThemeValue(); + + const { mutateAsync: deleteTimerImage } = useDeleteTimerImage(); + + const handleSubmit = async () => { + const { id } = selectedTheme; + await deleteTimerImage(id); + + return close(); + }; + + useClickOutside(divRef, close); + + return ( + +
+
+

정말로 삭제하시겠어요?

+ +
+ +
+
+ + +
+
+
+
+ ); +}); + +export default DeleteDialog; diff --git a/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DialogBody.tsx b/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DialogBody.tsx new file mode 100644 index 0000000..a088f08 --- /dev/null +++ b/app/components/common/Dialog-new/Timer-Image-Delete-Dialog/DialogBody.tsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function DialogBody() { + return ( +
+
타이머 배경은 기본 이미지로 되돌아갑니다.
+
+ ); +} diff --git a/app/mutations/deleteTimerImage.ts b/app/mutations/deleteTimerImage.ts new file mode 100644 index 0000000..5f52f1f --- /dev/null +++ b/app/mutations/deleteTimerImage.ts @@ -0,0 +1,49 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { AxiosResponse } from "axios"; + +import { useToastWrite } from "@/components/atoms/toast.atom"; +import { apiClient } from "@/lib/reactQueryProvider"; +import { QUERY_KEY } from "@/queries/getHintList"; +import { MutationConfigOptions } from "@/types"; + +type Response = void; + +const MUTATION_KEY = ["DeleteTimerImage"]; +const deleteTimerImage = async (themeId: number) => { + const URL_PATH = `/v1/theme/timer/${themeId}`; + const res = await apiClient.delete>(URL_PATH); + + return res.data; +}; + +export const useDeleteTimerImage = (configOptions?: MutationConfigOptions) => { + const queryClient = useQueryClient(); + const setToast = useToastWrite(); + + const info = useMutation({ + mutationKey: MUTATION_KEY, + mutationFn: (req) => deleteTimerImage(req), + ...configOptions?.options, + onSuccess: () => { + queryClient.invalidateQueries(QUERY_KEY); + setToast({ + isOpen: true, + title: "타이머 배경이 삭제되었습니다.", + text: "", + }); + // console.log("성공 시 실행") + }, + onSettled: () => { + // console.log("항상 실행"); + }, + onError: (error) => { + setToast({ + isOpen: true, + title: `${(error as any)?.response?.data?.message || error}`, + text: "", + }); + }, + }); + + return info; +}; diff --git a/app/mutations/useTimerImageUpload.ts b/app/mutations/useTimerImageUpload.ts index c38efbe..8c38abb 100644 --- a/app/mutations/useTimerImageUpload.ts +++ b/app/mutations/useTimerImageUpload.ts @@ -100,7 +100,7 @@ const useTimerImageUpload = () => { queryClient.invalidateQueries(QUERY_KEY); setToast({ isOpen: true, - title: "타이머 배경이 등록됐습니다.", + title: "타이머 배경이 등록되었습니다.", text: "힌트폰에서 세부 조정할 수 있습니다.", }); }, From 638d81c83970b5b8c6d871b7afe05abbdc25621f Mon Sep 17 00:00:00 2001 From: lgrin-byte Date: Mon, 30 Dec 2024 01:17:25 +0900 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B2=A8=EB=B6=80=EC=8B=9C=20=EC=9E=AC=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=EB=90=98=EB=8A=94=20=EC=9D=B4=EC=8A=88=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/(components)/Sidebar.tsx | 2 +- app/admin/Admin.tsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/admin/(components)/Sidebar.tsx b/app/admin/(components)/Sidebar.tsx index 2f8e026..ee97e4b 100644 --- a/app/admin/(components)/Sidebar.tsx +++ b/app/admin/(components)/Sidebar.tsx @@ -62,7 +62,7 @@ export default function Sidebar(props: Props) { `/admin?themeId=${encodeURIComponent(selectedThemeId)} ` ); - }, [selectedThemeId]); + }, [selectedThemeId, params]); const navigateToNewTheme = () => { resetSelectedTheme(); diff --git a/app/admin/Admin.tsx b/app/admin/Admin.tsx index b91483e..12a5619 100644 --- a/app/admin/Admin.tsx +++ b/app/admin/Admin.tsx @@ -21,7 +21,6 @@ type Theme = { function Admin() { const { data: categories = [], isLoading } = useGetThemeList(); - const isLoggedIn = useCheckSignIn(); const [selectedTheme, setSelectedTheme] = useSelectedTheme(); @@ -30,6 +29,12 @@ function Admin() { const [toast, setToast] = useToastInfo(); const router = useRouter(); + useEffect(() => { + if (!isLoading && categories.length > 0 && selectedTheme.id === 0) { + setSelectedTheme(categories[categories.length - 1]); + } + }, [isLoading]); + const handleClickSelected = (theme: Theme) => { setSelectedTheme(theme); setSelectedThemeId(theme.id); @@ -57,10 +62,6 @@ function Admin() { isOpen: toast.isOpen, }; - if (!isLoggedIn || isLoading) { - return ; - } - return ; } From 8937a2aa3324ec302ff8c17f25b57e167e79802b Mon Sep 17 00:00:00 2001 From: anyl92 Date: Mon, 30 Dec 2024 02:06:41 +0900 Subject: [PATCH 6/8] =?UTF-8?q?style:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20dimm?= =?UTF-8?q?ed=20=EC=B6=94=EA=B0=80,=20cover=20=EC=86=8D=EC=84=B1=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=84=B8=EB=A1=9C=EA=B8=B0=EC=A4=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A7=9E=EC=B6=A4=20=EB=93=B1=20css=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/(consts)/sidebar.ts | 21 ++++------ .../Image-Dialog-new/DialogBody.tsx | 11 ++---- .../Preview-Dialog-new/PreviewDialog.tsx | 38 +++++++++---------- app/components/common/Dialog-new/dialog.sass | 36 +++++++++++------- app/components/common/Toast/toast.sass | 7 ++-- app/mutations/deleteTimerImage.ts | 2 +- app/mutations/useTimerImageUpload.ts | 2 +- package-lock.json | 17 +++++++++ 8 files changed, 73 insertions(+), 61 deletions(-) diff --git a/app/admin/(consts)/sidebar.ts b/app/admin/(consts)/sidebar.ts index bd2bfeb..5a85af4 100644 --- a/app/admin/(consts)/sidebar.ts +++ b/app/admin/(consts)/sidebar.ts @@ -43,20 +43,6 @@ export const smallXProps = { height: 16, }; -export const previewProps = { - src: "/images/svg/image.png", - alt: "x icon", - width: 315, - height: 682, -}; - -export const statusBarProps = { - src: "/images/svg/status_bar.svg", - alt: "status_bar", - width: 315, - height: 40, -}; - export const timerPreviewProps = { src: "/images/svg/timer_preview.svg", alt: "timer_preview", @@ -78,6 +64,13 @@ export const QuestionIconProps = { height: 24, }; +export const timerPreviewLineProps = { + src: "/images/svg/timer_preview_entire.svg", + alt: "TIMER_LINE_IMAGE", + width: 158, + height: 340, +}; + export const defaultTimerImage = "/images/svg/icon_preview.svg"; export const defaultTimerImagePreview = "/images/svg/timer_preview.svg"; export const timerTooltipProps = { diff --git a/app/components/common/Dialog-new/Image-Dialog-new/DialogBody.tsx b/app/components/common/Dialog-new/Image-Dialog-new/DialogBody.tsx index 0618b41..08542d8 100644 --- a/app/components/common/Dialog-new/Image-Dialog-new/DialogBody.tsx +++ b/app/components/common/Dialog-new/Image-Dialog-new/DialogBody.tsx @@ -2,6 +2,7 @@ import React from "react"; import Image from "next/image"; import { useTimerImageValue } from "@/components/atoms/timerImage.atom"; +import { timerPreviewLineProps } from "@/admin/(consts)/sidebar"; export default function DialogBody() { const { timerImage } = useTimerImageValue(); @@ -12,18 +13,14 @@ export default function DialogBody() { width: 158, height: 340, }; - const previewProps = { - src: "/images/svg/timer_preview_entire.svg", - alt: "TIMER_LINE_IMAGE", - width: 158, - height: 340, - }; + return (
배경 적용 미리보기
+
- +

*예시 이미지입니다. 앱에서 세부 설정을 진행해 주세요. diff --git a/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx b/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx index a19ff29..377eb4c 100644 --- a/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx +++ b/app/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog.tsx @@ -5,11 +5,7 @@ import useClickOutside from "@/hooks/useClickOutside"; import useModal from "@/hooks/useModal"; import ModalPortal from "@/components/common/Dialog-new/ModalPortal"; import "@/components/common/Dialog-new/dialog.sass"; -import { - smallXProps, - previewProps, - statusBarProps, -} from "@/admin/(consts)/sidebar"; +import { smallXProps, timerPreviewLineProps } from "@/admin/(consts)/sidebar"; import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; const PreviewDialog = forwardRef(() => { @@ -18,9 +14,9 @@ const PreviewDialog = forwardRef(() => { const timerPreviewProps = { src: selectedTheme.themeImageUrl!, - alt: "timer priview image", - width: 284, - height: 555, + alt: "TIMER_PREVIEW_IMAGE", + width: 315, + height: 682, }; const divRef = useRef(null); @@ -30,23 +26,23 @@ const PreviewDialog = forwardRef(() => { return (

-
-

- *예시 이미지입니다. 앱에서 세부 설정을 진행해 주세요. -

+

+ *예시 이미지입니다. 앱에서 세부 설정을 진행해 주세요. +

+
- - +
+
+
-
); diff --git a/app/components/common/Dialog-new/dialog.sass b/app/components/common/Dialog-new/dialog.sass index b6d9b86..4a50346 100644 --- a/app/components/common/Dialog-new/dialog.sass +++ b/app/components/common/Dialog-new/dialog.sass @@ -138,26 +138,23 @@ left: 50% transform: translate(-50%, -50%) display: flex - gap: 16px + flex-direction: column + gap: 14px .preview_image position: relative width: 315px height: 682px - .preview - - border-radius: 13px - object-fit: contain - position: absolute + border-radius: 13px + overflow: hidden .status_bar position: absolute + width: 100% + height: 100% .mobile_preview - position: absolute - bottom: 32px - left: 50% - - transform: translateX( -50%) - + object-fit: cover + width: 100% + height: 100% .preview-text @include caption12M @@ -183,9 +180,17 @@ display: flex align-items: center justify-content: left - margin-bottom: 14px @include body14M +.preview-dialog-box + display: flex + gap: 16px + .timer-dimmed-box + position: absolute + width: 100% + height: 100% + background-color: $color-black60 + .timer-preview-image-box position: relative width: 158px @@ -195,6 +200,11 @@ overflow: hidden .timer-preview-image object-fit: cover + .timer-dimmed-box + position: absolute + width: 100% + height: 100% + background-color: $color-black60 .timer-preview-line position: absolute top: 0 diff --git a/app/components/common/Toast/toast.sass b/app/components/common/Toast/toast.sass index 9b6dd98..dbcbbde 100644 --- a/app/components/common/Toast/toast.sass +++ b/app/components/common/Toast/toast.sass @@ -10,7 +10,7 @@ .toast-message width: 388px - background-color: $color-black + background-color: $color-white padding: 24px border-radius: 8px box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) @@ -20,11 +20,10 @@ @include body14R .toast-title - color: $color-white - + color: $color-black .toast-body - color: $color-white70 + color: $color-black // 애니메이션 효과 @keyframes fadeIn diff --git a/app/mutations/deleteTimerImage.ts b/app/mutations/deleteTimerImage.ts index 5f52f1f..5cecb6d 100644 --- a/app/mutations/deleteTimerImage.ts +++ b/app/mutations/deleteTimerImage.ts @@ -28,7 +28,7 @@ export const useDeleteTimerImage = (configOptions?: MutationConfigOptions) => { queryClient.invalidateQueries(QUERY_KEY); setToast({ isOpen: true, - title: "타이머 배경이 삭제되었습니다.", + title: "타이머 배경을 삭제했습니다.", text: "", }); // console.log("성공 시 실행") diff --git a/app/mutations/useTimerImageUpload.ts b/app/mutations/useTimerImageUpload.ts index 8c38abb..2d2931a 100644 --- a/app/mutations/useTimerImageUpload.ts +++ b/app/mutations/useTimerImageUpload.ts @@ -100,7 +100,7 @@ const useTimerImageUpload = () => { queryClient.invalidateQueries(QUERY_KEY); setToast({ isOpen: true, - title: "타이머 배경이 등록되었습니다.", + title: "타이머 배경을 등록했습니다.", text: "힌트폰에서 세부 조정할 수 있습니다.", }); }, diff --git a/package-lock.json b/package-lock.json index 0b5e31d..54aabb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "devDependencies": { "@svgr/webpack": "^8.0.1", "@types/http-proxy": "^1.17.15", + "@types/js-cookie": "^3.0.6", "@types/lodash": "^4.17.13", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.61.0", @@ -55,6 +56,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.3", + "js-cookie": "^3.0.5", "prettier": "^2.8.8" } }, @@ -3720,6 +3722,12 @@ "@types/node": "*" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -6841,6 +6849,15 @@ "node": ">= 0.4" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", From 5e32212fd0b6c1f26a54995c6694ca5090af66d1 Mon Sep 17 00:00:00 2001 From: lgrin-byte Date: Tue, 7 Jan 2025 03:22:43 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20Hydration=20=EB=B6=88=EC=9D=BC?= =?UTF-8?q?=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 --- app/admin/(components)/Sidebar.tsx | 25 ++++++++++++++----------- app/admin/Admin.tsx | 3 --- app/admin/AdminView.tsx | 2 -- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/admin/(components)/Sidebar.tsx b/app/admin/(components)/Sidebar.tsx index ee97e4b..37a82d6 100644 --- a/app/admin/(components)/Sidebar.tsx +++ b/app/admin/(components)/Sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import Image from "next/image"; import classNames from "classnames"; import { useRouter, useSearchParams } from "next/navigation"; @@ -11,6 +11,7 @@ import { subscribeLinkURL, } from "@/admin/(consts)/sidebar"; import { + getLoginInfo, getSelectedThemeId, getStatus, removeThemeId, @@ -27,8 +28,6 @@ interface Theme { } interface Props { - adminCode: string; - shopName: string; categories: Theme[]; selectedTheme: Theme; handleClickSelected: (theme: Theme) => void; @@ -45,12 +44,16 @@ export default function Sidebar(props: Props) { const searchParams = useSearchParams(); const selectedThemeId = getSelectedThemeId(); const params = new URLSearchParams(searchParams.toString()).toString(); - const { - adminCode = "", - shopName = "", - categories, - handleClickSelected, - } = props; + const { categories, handleClickSelected } = props; + const [loginInfo, setLoginInfo] = useState({ + adminCode: "", + shopName: "", + }); + + useEffect(() => { + const { adminCode, shopName } = getLoginInfo(); // getLoginInfo로 값 가져오기 + setLoginInfo({ adminCode, shopName }); // 상태 업데이트 + }, []); // const handleLogout = () => { // removeAccessToken(); @@ -100,7 +103,7 @@ export default function Sidebar(props: Props) {
- {shopName?.replaceAll(`"`, "")} + {loginInfo.shopName?.replaceAll(`"`, "")}
우리 지점 테마
@@ -164,7 +167,7 @@ export default function Sidebar(props: Props) {

관리자 코드

- {adminCode?.replaceAll(`"`, "")} + {loginInfo.adminCode?.replaceAll(`"`, "")}

diff --git a/app/admin/Admin.tsx b/app/admin/Admin.tsx index 12a5619..fd93752 100644 --- a/app/admin/Admin.tsx +++ b/app/admin/Admin.tsx @@ -24,7 +24,6 @@ function Admin() { const isLoggedIn = useCheckSignIn(); const [selectedTheme, setSelectedTheme] = useSelectedTheme(); - const { adminCode, shopName } = getLoginInfo(); const [toast, setToast] = useToastInfo(); const router = useRouter(); @@ -54,8 +53,6 @@ function Admin() { }, [toast, setToast]); const SidebarViewProps = { - adminCode, - shopName, categories, selectedTheme, handleClickSelected, diff --git a/app/admin/AdminView.tsx b/app/admin/AdminView.tsx index 1e956b1..521ca17 100644 --- a/app/admin/AdminView.tsx +++ b/app/admin/AdminView.tsx @@ -16,8 +16,6 @@ interface Theme { } interface Props { - adminCode: string; - shopName: string; categories: Theme[]; selectedTheme: Theme; isOpen: boolean; From bfee9a9d2d3d5acf2d8297cdeea33adbe5bdb8ec Mon Sep 17 00:00:00 2001 From: lgrin-byte Date: Tue, 7 Jan 2025 03:31:04 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=ED=88=B4=ED=8C=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(components)/ThemeInfo/ThemeImage.tsx | 80 ------------------- .../ThemeInfo/ThemeTimerImage.tsx | 12 ++- app/admin/Admin.tsx | 1 + app/admin/AdminView.tsx | 8 +- app/queries/getThemeList.ts | 2 +- 5 files changed, 18 insertions(+), 85 deletions(-) delete mode 100644 app/admin/(components)/ThemeInfo/ThemeImage.tsx diff --git a/app/admin/(components)/ThemeInfo/ThemeImage.tsx b/app/admin/(components)/ThemeInfo/ThemeImage.tsx deleted file mode 100644 index 909a121..0000000 --- a/app/admin/(components)/ThemeInfo/ThemeImage.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Image from "next/image"; -import React, { useRef, useState } from "react"; - -import Tooltip from "@/admin/(components)/Tooltip/Container"; -import Dialog from "@/components/common/Dialog-new/Image-Dialog-new/Dialog"; -import PreviewDialog from "@/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog"; -import useModal from "@/hooks/useModal"; - -export default function ThemeImage() { - const QuestionProps = { - src: "/images/svg/icon_question.svg", - alt: "gallery_image", - width: 24, - height: 24, - }; - - const previewProps = { - src: "/images/svg/icon_preview.svg", - alt: "NEXT ROOM", - width: 120, - height: 120, - }; - const { open } = useModal(); - - const imgInputRef = useRef(null); - const handleAddImageBtnClick = () => { - // 숨겨진 input 클릭 트리거 - // imgInputRef.current?.click(); - open(Dialog); - }; - const handlePreviewImageBtnClick = () => { - // 숨겨진 input 클릭 트리거 - // imgInputRef.current?.click(); - open(PreviewDialog); - }; - const [isHovered, setIsHovered] = useState(false); - - return ( -
-
- 타이머 배경 -
- setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - className="tooptip-button" - {...QuestionProps} - /> - {isHovered && } -
-
-
-
- -
- -
-
- - - -
-
- ); -} diff --git a/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx index 054cff7..c577428 100644 --- a/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx +++ b/app/admin/(components)/ThemeInfo/ThemeTimerImage.tsx @@ -1,5 +1,5 @@ import Image from "next/image"; -import React, { ChangeEvent, useRef } from "react"; +import React, { ChangeEvent, useRef, useState } from "react"; import Dialog from "@/components/common/Dialog-new/Image-Dialog-new/Dialog"; import PreviewDialog from "@/components/common/Dialog-new/Preview-Dialog-new/PreviewDialog"; @@ -8,6 +8,7 @@ import { useTimerImageWrite } from "@/components/atoms/timerImage.atom"; import { useSelectedThemeValue } from "@/components/atoms/selectedTheme.atom"; import { defaultTimerImage, QuestionIconProps } from "@/admin/(consts)/sidebar"; import DeleteDialog from "@/components/common/Dialog-new/Timer-Image-Delete-Dialog/DeleteDialog"; +import Tooltip from "@/admin/(components)/Tooltip/Container"; export default function ThemeTimerImage() { const selectedTheme = useSelectedThemeValue(); @@ -52,12 +53,19 @@ export default function ThemeTimerImage() { const handleDelTimerImageBtnClick = () => { open(DeleteDialog); }; + const [isHovered, setIsHovered] = useState(false); return (
타이머 배경 - + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + className="tooptip-button" + /> + {isHovered && }
diff --git a/app/admin/Admin.tsx b/app/admin/Admin.tsx index fd93752..88dee7e 100644 --- a/app/admin/Admin.tsx +++ b/app/admin/Admin.tsx @@ -57,6 +57,7 @@ function Admin() { selectedTheme, handleClickSelected, isOpen: toast.isOpen, + isLoading, }; return ; diff --git a/app/admin/AdminView.tsx b/app/admin/AdminView.tsx index 521ca17..73a5394 100644 --- a/app/admin/AdminView.tsx +++ b/app/admin/AdminView.tsx @@ -7,6 +7,7 @@ import Toast from "@/components/common/Toast/Toast"; import NotiDialog from "@/components/common/Dialog-new/Noti-Dialog-new/Dialog"; import useModal from "@/hooks/useModal"; import { getLocalStorage } from "@/utils/storageUtil"; +import Loader from "@/components/Loader/Loader"; interface Theme { id: number; @@ -19,19 +20,22 @@ interface Props { categories: Theme[]; selectedTheme: Theme; isOpen: boolean; + isLoading: boolean; handleClickSelected: (theme: Theme) => void; } function AdminView(props: Props) { - const { isOpen } = props; - const { open } = useModal(); + const { isOpen, isLoading } = props; + const { open, closeAll } = useModal(); const isHideDialog = getLocalStorage("hideDialog"); useEffect(() => { + closeAll(); if (!isHideDialog) { open(NotiDialog, { type: "put" }); } }, []); + if (isLoading) return ; return (
diff --git a/app/queries/getThemeList.ts b/app/queries/getThemeList.ts index 3e69519..78236a7 100644 --- a/app/queries/getThemeList.ts +++ b/app/queries/getThemeList.ts @@ -71,6 +71,6 @@ export const useGetThemeList = (configOptions?: QueryConfigOptions) => { ...info, isInitialLoading: info.isLoading, isRefetching: info.isFetching && !info.isLoading, - isLoading: info.isLoading || info.isFetching, + isLoading: info.isLoading, }; };