456 lines
19 KiB
TypeScript
456 lines
19 KiB
TypeScript
import { useRouter } from "next/navigation";
|
|
import { useEffect, useState } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import type { UseFormReturn, FieldPath } from "react-hook-form";
|
|
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
|
import {
|
|
Input,
|
|
Label,
|
|
RadioGroup,
|
|
RadioGroupItem,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
Textarea,
|
|
Button,
|
|
} from "@common/components";
|
|
import { BOARD_POSTING_CODE } from "@/entities/board";
|
|
import { AttachmentApi } from "@/entities/attachment";
|
|
import type { Post, PostFormData, PostUpdateFormData } from "@/entities/post";
|
|
import { ENDPOINT } from "@/shared/config";
|
|
import { useUserStore, setCookie, useMenuStore } from "@/shared/lib";
|
|
import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form";
|
|
import { PostingPeriodSelector } from "./posting-period-selector";
|
|
import { GeneralFileUploader } from "./general-file-uploader";
|
|
import { WithPreviewFileUploader } from "./with-preview-file-uploader";
|
|
|
|
type PostFormProps<T extends PostFormData | PostUpdateFormData> = {
|
|
form: UseFormReturn<T>;
|
|
handleSave: (data: T) => Promise<Post>;
|
|
};
|
|
|
|
function PostForm<T extends PostFormData | PostUpdateFormData>({
|
|
form,
|
|
handleSave,
|
|
}: PostFormProps<T>) {
|
|
const atflCd = form.getValues("atflCd" as FieldPath<T>);
|
|
|
|
const router = useRouter();
|
|
const { confirm } = useConfirm();
|
|
|
|
const bbsInfo = useMenuStore((state) => state.bbsInfo);
|
|
const userInfo = useUserStore((state) => state.userInfo);
|
|
|
|
const [deleteAttachmentIds, setDeleteAttachmentIds] = useState<string[]>([]);
|
|
|
|
const { data: attachmentData } = useQuery({
|
|
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd],
|
|
queryFn: () => AttachmentApi.getAttachmentsInfo(atflCd as string),
|
|
enabled: !!atflCd,
|
|
});
|
|
|
|
const onSubmit = async (data: T) => {
|
|
if (await confirm("저장하시겠습니까?")) {
|
|
if (deleteAttachmentIds.length > 0) {
|
|
await Promise.all(
|
|
deleteAttachmentIds.map(AttachmentApi.deleteAttachmentByAtflId),
|
|
);
|
|
}
|
|
await handleSave(data);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
setCookie("atfl-cd-key", "");
|
|
}, []);
|
|
|
|
return (
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
<div className="card">
|
|
<p className="mb-2 text-right text-sm text-muted-foreground">
|
|
*은 필수입력 항목입니다.
|
|
</p>
|
|
<Table variant="secondary">
|
|
<colgroup>
|
|
<col width="15%" />
|
|
<col width="85%" />
|
|
</colgroup>
|
|
<TableBody>
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>제목</TableHead>
|
|
<TableCell className="border">
|
|
<FormField
|
|
control={form.control}
|
|
name={"pstTtl" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
value={(field.value as string) ?? ""}
|
|
className="!text-[1.3rem]"
|
|
placeholder="제목을 입력해주세요"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead scope="row">작성자</TableHead>
|
|
<TableCell className="border">
|
|
<FormField
|
|
control={form.control}
|
|
name={"wrtrNm" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
value={(field.value as string) ?? ""}
|
|
className="w-[20rem] !text-[1.3rem]"
|
|
placeholder="작성자를 입력해주세요"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
{!userInfo?.userUnqId && (
|
|
<TableRow>
|
|
<TableHead scope="row" data-required>비밀번호</TableHead>
|
|
<TableCell className="border">
|
|
<FormField
|
|
control={form.control}
|
|
name={"pstPswd" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem className="flex flex-col">
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
type="password"
|
|
value={(field.value as string) ?? ""}
|
|
className="w-[20rem] !text-[1.3rem]"
|
|
placeholder="비밀번호를 입력해주세요"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
<TableRow>
|
|
<TableHead scope="row">내용</TableHead>
|
|
<TableCell className="border">
|
|
<FormField
|
|
control={form.control}
|
|
name={"pstCn" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Textarea
|
|
{...field}
|
|
value={(field.value as string) ?? ""}
|
|
onChange={field.onChange}
|
|
className="h-[20rem] w-full bg-form !text-[1.3rem] !leading-6"
|
|
placeholder="내용을 입력해주세요"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
{bbsInfo?.rfrncUseYn === "Y" && (
|
|
<TableRow>
|
|
<TableHead scope="row">참조 URL</TableHead>
|
|
<TableCell className="border">
|
|
<FormField
|
|
control={form.control}
|
|
name={"rfrncUrl" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
{...field}
|
|
value={(field.value as string) ?? ""}
|
|
className="!text-[1.3rem]"
|
|
placeholder="URL을 입력해주세요"
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{bbsInfo?.popupUseYn === "Y" && (
|
|
<TableRow>
|
|
<TableHead scope="row">팝업여부</TableHead>
|
|
<TableCell className="border">
|
|
<div className="flex items-center gap-4">
|
|
<FormField
|
|
control={form.control}
|
|
name={"popupYn" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<RadioGroup
|
|
name="popupYn"
|
|
value={field.value?.toString() ?? ""}
|
|
onValueChange={field.onChange}
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="Y" id="popupY" />
|
|
<Label htmlFor="popupY">사용</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="N" id="popupN" />
|
|
<Label htmlFor="popupN">사용안함</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name={"popupYnEtc" as FieldPath<T>}
|
|
render={({ field }) => {
|
|
const fieldValues = form.getValues().popupYnEtc?.[0];
|
|
return (
|
|
<FormItem>
|
|
<FormControl>
|
|
<PostingPeriodSelector
|
|
defaultValues={{ ...fieldValues }}
|
|
disabled={
|
|
form.getValues(
|
|
"popupYn" as FieldPath<T>,
|
|
) === "N"
|
|
}
|
|
handlePeriodValueChange={(
|
|
periodCode,
|
|
date,
|
|
) => {
|
|
field.onChange([
|
|
{
|
|
...fieldValues,
|
|
...date,
|
|
bbsPstgTypeCd:
|
|
BOARD_POSTING_CODE.팝업게시,
|
|
pstgPrdSeCd: periodCode,
|
|
},
|
|
]);
|
|
}}
|
|
handlePostingDateChange={(date) => {
|
|
field.onChange([
|
|
{ ...fieldValues, ...date },
|
|
]);
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{bbsInfo?.upndFixUseYn === "Y" && (
|
|
<TableRow>
|
|
<TableHead scope="row">상단고정</TableHead>
|
|
<TableCell className="border">
|
|
<div className="flex items-center gap-4">
|
|
<FormField
|
|
control={form.control}
|
|
name={"upndFixYn" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<RadioGroup
|
|
name="upndFixYn"
|
|
value={field.value?.toString() ?? ""}
|
|
onValueChange={field.onChange}
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="Y" id="upndFixY" />
|
|
<Label htmlFor="upndFixY">사용</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="N" id="upndFixN" />
|
|
<Label htmlFor="upndFixN">사용안함</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name={"upndFixYnEtc" as FieldPath<T>}
|
|
render={({ field }) => {
|
|
const fieldValues =
|
|
form.getValues().upndFixYnEtc?.[0];
|
|
return (
|
|
<FormItem>
|
|
<FormControl>
|
|
<PostingPeriodSelector
|
|
defaultValues={{ ...fieldValues }}
|
|
disabled={
|
|
form.getValues(
|
|
"upndFixYn" as FieldPath<T>,
|
|
) === "N"
|
|
}
|
|
handlePeriodValueChange={(
|
|
periodCode,
|
|
date,
|
|
) => {
|
|
field.onChange([
|
|
{
|
|
...fieldValues,
|
|
...date,
|
|
bbsPstgTypeCd:
|
|
BOARD_POSTING_CODE.상단게시,
|
|
pstgPrdSeCd: periodCode,
|
|
},
|
|
]);
|
|
}}
|
|
handlePostingDateChange={(date) => {
|
|
field.onChange([
|
|
{ ...fieldValues, ...date },
|
|
]);
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{bbsInfo?.pstgPrdUseYn === "Y" && (
|
|
<TableRow>
|
|
<TableHead scope="row">게시기간</TableHead>
|
|
<TableCell className="border">
|
|
<div className="flex items-center gap-4">
|
|
<FormField
|
|
control={form.control}
|
|
name={"prdPstgYn" as FieldPath<T>}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<RadioGroup
|
|
name="prdPstgYn"
|
|
value={field.value?.toString() ?? ""}
|
|
onValueChange={field.onChange}
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="Y" id="prdPstgY" />
|
|
<Label htmlFor="prdPstgY">사용</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="N" id="prdPstgN" />
|
|
<Label htmlFor="prdPstgN">사용안함</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name={"prdPstgYnEtc" as FieldPath<T>}
|
|
render={({ field }) => {
|
|
const fieldValues =
|
|
form.getValues().prdPstgYnEtc?.[0];
|
|
return (
|
|
<FormItem>
|
|
<FormControl>
|
|
<PostingPeriodSelector
|
|
defaultValues={{ ...fieldValues }}
|
|
disabled={
|
|
form.getValues(
|
|
"prdPstgYn" as FieldPath<T>,
|
|
) === "N"
|
|
}
|
|
handlePeriodValueChange={(
|
|
periodCode,
|
|
date,
|
|
) => {
|
|
field.onChange([
|
|
{
|
|
...fieldValues,
|
|
...date,
|
|
bbsPstgTypeCd:
|
|
BOARD_POSTING_CODE.기간게시,
|
|
pstgPrdSeCd: periodCode,
|
|
},
|
|
]);
|
|
}}
|
|
handlePostingDateChange={(date) => {
|
|
field.onChange([
|
|
{ ...fieldValues, ...date },
|
|
]);
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
</FormItem>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{bbsInfo?.atflUseYn === "Y" && (
|
|
<TableRow>
|
|
<TableHead scope="row">첨부파일</TableHead>
|
|
<TableCell className="border">
|
|
{bbsInfo?.bbsTypeCd === "BBSIMAGE" ? (
|
|
<WithPreviewFileUploader
|
|
form={form}
|
|
attachmentData={attachmentData}
|
|
/>
|
|
) : (
|
|
<GeneralFileUploader
|
|
form={form}
|
|
attachmentData={attachmentData}
|
|
deleteAttachmentIds={deleteAttachmentIds}
|
|
setDeleteAttachmentIds={setDeleteAttachmentIds}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
<div className="flex justify-center gap-4 pb-12 pt-10">
|
|
<Button
|
|
size="lg"
|
|
color="white"
|
|
type="button"
|
|
onClick={() => router.back()}
|
|
>
|
|
취소
|
|
</Button>
|
|
<Button size="lg" type="submit">
|
|
저장
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</Form>
|
|
);
|
|
}
|
|
|
|
export { PostForm };
|