feat: 팝업 수정 작업
This commit is contained in:
@@ -4,10 +4,9 @@ import type { HttpResponse, Upload } from "tus-js-client";
|
||||
import { Button, FileUploader } from "@common/components";
|
||||
import { handleTusUpload } from "@/entities/tus";
|
||||
import { type Attachment } from "@/entities/attachment";
|
||||
import type { PostFormData, PostUpdateFormData } from "@/entities/post";
|
||||
import { setCookie, useAlertStore, useMenuStore } from "@/shared/lib";
|
||||
|
||||
function GeneralFileUploader<T extends PostFormData | PostUpdateFormData>({
|
||||
function GeneralFileUploader<T extends { atflCd?: string | null }>({
|
||||
form,
|
||||
attachmentData,
|
||||
deleteAttachmentIds,
|
||||
@@ -89,10 +88,10 @@ function GeneralFileUploader<T extends PostFormData | PostUpdateFormData>({
|
||||
<FileUploader
|
||||
id="atflCd"
|
||||
multiple
|
||||
className="flex !h-auto max-w-full flex-row-reverse gap-4 [&>label]:h-[3.2rem]"
|
||||
className="flex !h-auto max-w-full flex-row-reverse items-start gap-4 [&>label]:h-[3.2rem]"
|
||||
onUpload={onUpload}
|
||||
customPreview={
|
||||
<div className="flex w-full gap-4">
|
||||
<div className="flex w-full flex-wrap gap-4">
|
||||
{Object.entries(attachments).map(([atflId, attachment]) => {
|
||||
if (deleteAttachmentIds.includes(atflId)) return null;
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ModifyPage as VModifyPage } from "@/pages/popup";
|
||||
|
||||
export default async function ModifyPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ popupSn: string }>;
|
||||
}) {
|
||||
const { popupSn } = await params;
|
||||
return <VModifyPage popupSn={parseInt(popupSn)} />;
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import {
|
||||
popupFormSchema,
|
||||
popupUpdateFormSchema,
|
||||
type PopupFormData,
|
||||
type PopupInfo,
|
||||
type PopupList,
|
||||
type PopupParams,
|
||||
type PopupService,
|
||||
type PopupUpdateFormData,
|
||||
} from "../model/popup-interface";
|
||||
import { client } from "@/shared/api";
|
||||
|
||||
@@ -16,10 +19,25 @@ export class PopupApiService implements PopupService {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getPopupInfo(popupSn: number): Promise<PopupInfo> {
|
||||
const response = await client.get<PopupInfo>(
|
||||
`${ENDPOINT.CMS_SERVICE.POPUPS}/${popupSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async createPopup(data: PopupFormData): Promise<void> {
|
||||
const validatedData = popupFormSchema.parse(data);
|
||||
await client.post(ENDPOINT.CMS_SERVICE.POPUPS, validatedData);
|
||||
}
|
||||
|
||||
async updatePopup(popupSn: number, data: PopupUpdateFormData): Promise<void> {
|
||||
const validatedData = popupUpdateFormSchema.parse(data);
|
||||
await client.put(
|
||||
`${ENDPOINT.CMS_SERVICE.POPUPS}/${popupSn}`,
|
||||
validatedData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const PopupApi = new PopupApiService();
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
export { ModifyPage } from "./ui/modify-page";
|
||||
export { PopupPage } from "./ui/popup-page";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PageableList, PageableParams } from "@/shared/model";
|
||||
import { z } from "zod";
|
||||
import type { PageableList, PageableParams } from "@/shared/model";
|
||||
|
||||
export interface PopupParams extends PageableParams {
|
||||
keyword?: string;
|
||||
@@ -24,6 +24,16 @@ export interface Popup {
|
||||
mdfrNm: string;
|
||||
}
|
||||
|
||||
export interface PopupInfo extends Popup {
|
||||
popupCn: string;
|
||||
popupVtclPstn: number;
|
||||
popupHrzntltyPstn: number;
|
||||
popupLnkgUrl: string;
|
||||
atflCd: string;
|
||||
regDt: string;
|
||||
rgTrNm: string;
|
||||
}
|
||||
|
||||
export const popupFormSchema = z.object({
|
||||
popupTypeCd: z.string(),
|
||||
contsTypeCd: z.string(),
|
||||
@@ -40,10 +50,22 @@ export const popupFormSchema = z.object({
|
||||
rgtr: z.string(),
|
||||
});
|
||||
|
||||
export const popupUpdateFormSchema = popupFormSchema
|
||||
.omit({
|
||||
rgtr: true,
|
||||
})
|
||||
.extend({
|
||||
mdfr: z.string(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
export type PopupList = PageableList<Popup>;
|
||||
export type PopupFormData = z.infer<typeof popupFormSchema>;
|
||||
export type PopupUpdateFormData = z.infer<typeof popupUpdateFormSchema>;
|
||||
|
||||
export interface PopupService {
|
||||
getPopupList(params: PopupParams): Promise<PopupList>;
|
||||
getPopupInfo(popupSn: number): Promise<PopupInfo>;
|
||||
createPopup(data: PopupFormData): Promise<void>;
|
||||
updatePopup(popupSn: number, data: PopupUpdateFormData): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
@@ -8,7 +9,6 @@ import { useUserStore, useAlertStore } from "@/shared/lib";
|
||||
import { popupFormSchema, type PopupFormData } from "../model/popup-interface";
|
||||
import { PopupApi } from "../api/popup-service";
|
||||
import { PopupForm } from "./popup-form";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function CreatePage() {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useAlertStore, useUserStore } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import {
|
||||
popupUpdateFormSchema,
|
||||
type PopupUpdateFormData,
|
||||
} from "../model/popup-interface";
|
||||
import { PopupApi } from "../api/popup-service";
|
||||
import { PopupForm } from "./popup-form";
|
||||
|
||||
function ModifyPage({ popupSn }: { popupSn: number }) {
|
||||
const router = useRouter();
|
||||
const { userInfo } = useUserStore((state) => state);
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { data: popupInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, popupSn],
|
||||
queryFn: () => PopupApi.getPopupInfo(popupSn),
|
||||
enabled: !!popupSn,
|
||||
});
|
||||
|
||||
const { mutateAsync: updatePopup } = useMutation({
|
||||
mutationFn: (data: PopupUpdateFormData) =>
|
||||
PopupApi.updatePopup(popupSn, data),
|
||||
onSuccess: () => {
|
||||
alert("수정되었습니다.", router.back);
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<PopupUpdateFormData>({
|
||||
resolver: zodResolver(popupUpdateFormSchema),
|
||||
values: {
|
||||
...popupInfo,
|
||||
mdfr: userInfo?.userUnqId ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">팝업 수정</h3>
|
||||
</div>
|
||||
<PopupForm form={form} handleSave={updatePopup} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ModifyPage };
|
||||
@@ -1,5 +1,7 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { type UseFormReturn } from "react-hook-form";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { Path, UseFormReturn } from "react-hook-form";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
@@ -12,29 +14,64 @@ import {
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
import { FormWrapper } from "@/widgets/form-wrapper";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import { FormItem, FormControl, FormField } from "@/shared/ui/form";
|
||||
import type { PopupFormData } from "../model/popup-interface";
|
||||
import { GeneralFileUploader } from "@/widgets/post/ui/general-file-uploader";
|
||||
import { useState } from "react";
|
||||
import { AttachmentApi } from "@/entities/attachment";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { setCookie } from "@/shared/lib";
|
||||
import { FormItem, FormControl, FormField } from "@/shared/ui/form";
|
||||
import type {
|
||||
PopupFormData,
|
||||
PopupUpdateFormData,
|
||||
} from "../model/popup-interface";
|
||||
|
||||
const Editor = dynamic(
|
||||
() => import("@/entities/editor").then((module) => module.Editor),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
type PopupFormProps = {
|
||||
form: UseFormReturn<PopupFormData>;
|
||||
type PopupFormProps<T extends PopupFormData | PopupUpdateFormData> = {
|
||||
form: UseFormReturn<T>;
|
||||
popupSn?: string;
|
||||
handleSave: (data: PopupFormData) => Promise<void>;
|
||||
handleSave: (data: T) => Promise<void>;
|
||||
handleDelete?: () => Promise<void>;
|
||||
};
|
||||
|
||||
function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
function PopupForm<T extends PopupFormData | PopupUpdateFormData>({
|
||||
form,
|
||||
popupSn,
|
||||
handleSave,
|
||||
...props
|
||||
}: PopupFormProps<T>) {
|
||||
const atflCd = form.getValues("atflCd" as Path<T>);
|
||||
const [deleteAttachmentIds, setDeleteAttachmentIds] = useState<string[]>([]);
|
||||
|
||||
const { data: attachmentData } = useQuery({
|
||||
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd],
|
||||
queryFn: () => AttachmentApi.getAttachmentsInfo(atflCd as string),
|
||||
enabled: !!atflCd,
|
||||
});
|
||||
|
||||
const saveHandlerInterceptor = async (data: T) => {
|
||||
if (deleteAttachmentIds.length > 0) {
|
||||
await Promise.all(
|
||||
deleteAttachmentIds.map(AttachmentApi.deleteAttachmentByAtflId),
|
||||
);
|
||||
}
|
||||
await handleSave(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCookie("atfl-cd-key", "");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FormWrapper form={form} isModifyMode={!!popupSn} {...props}>
|
||||
<FormWrapper
|
||||
form={form}
|
||||
isModifyMode={!!popupSn}
|
||||
handleSave={saveHandlerInterceptor}
|
||||
{...props}
|
||||
>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
@@ -47,7 +84,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupTtl"
|
||||
name={"popupTtl" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -69,7 +106,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<div className="flex items-center gap-1">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupWdth"
|
||||
name={"popupWdth" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -92,7 +129,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<span className="w-5 text-center">×</span>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupHgt"
|
||||
name={"popupHgt" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -121,7 +158,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<div className="flex items-center gap-1">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupHrzntltyPstn"
|
||||
name={"popupHrzntltyPstn" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -144,7 +181,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<span className="w-5 text-center">/</span>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupVtclPstn"
|
||||
name={"popupVtclPstn" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -172,7 +209,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupTypeCd"
|
||||
name={"popupTypeCd" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -191,12 +228,13 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="contsTypeCd"
|
||||
name={"contsTypeCd" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
value={(field.value ?? "") as string}
|
||||
name="contsTypeCd"
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
@@ -220,7 +258,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupLnkgUrl"
|
||||
name={"popupLnkgUrl" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -241,12 +279,12 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupCn"
|
||||
name={"popupCn" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Editor
|
||||
value={field.value ?? ""}
|
||||
value={(field.value ?? "") as string}
|
||||
onEditorChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -260,6 +298,7 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<GeneralFileUploader
|
||||
form={form}
|
||||
attachmentData={attachmentData}
|
||||
deleteAttachmentIds={deleteAttachmentIds}
|
||||
setDeleteAttachmentIds={setDeleteAttachmentIds}
|
||||
/>
|
||||
@@ -270,13 +309,14 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="popupEndUseYn"
|
||||
name={"popupEndUseYn" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
name="popupEndUseYn"
|
||||
value={(field.value ?? "") as string}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -299,13 +339,14 @@ function PopupForm({ form, popupSn, ...props }: PopupFormProps) {
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="useYn"
|
||||
name={"useYn" as Path<T>}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
name="useYn"
|
||||
value={(field.value ?? "") as string}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
||||
@@ -88,10 +88,10 @@ function GeneralFileUploader<T extends { atflCd?: string | null }>({
|
||||
<FileUploader
|
||||
id="atflCd"
|
||||
multiple
|
||||
className="flex !h-auto max-w-full flex-row-reverse gap-4 [&>label]:h-[3.2rem]"
|
||||
className="flex !h-auto max-w-full flex-row-reverse items-start gap-4 [&>label]:h-[3.2rem]"
|
||||
onUpload={onUpload}
|
||||
customPreview={
|
||||
<div className="flex w-full gap-4">
|
||||
<div className="flex w-full flex-wrap gap-4">
|
||||
{Object.entries(attachments).map(([atflId, attachment]) => {
|
||||
if (deleteAttachmentIds.includes(atflId)) return null;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user