feat: 콘텐츠 등록 작업

This commit is contained in:
2025-04-17 17:20:12 +09:00
parent aaa6e72723
commit 41dbb368c3
10 changed files with 265 additions and 5 deletions
@@ -0,0 +1,2 @@
import { CreatePage } from "@/pages/contents";
export default CreatePage;
+4 -1
View File
@@ -13,7 +13,7 @@ export function middleware(req: NextRequest) {
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log("Rewriting to >>>>", newUrl);
console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
@@ -26,6 +26,9 @@ export function middleware(req: NextRequest) {
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
console.log(
`[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`,
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
@@ -26,7 +26,7 @@ export class ContentsApiService implements ContentsService {
public async createContents(data: ContentsFormData): Promise<void> {
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.CONTENTS}`,
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}`,
data,
);
return response.data;
+6 -1
View File
@@ -1,2 +1,7 @@
export { ContentsApi } from "./api/contents-service";
export type { Contents, ContentsParams } from "./model/contents-interface";
export {
contentsFormSchema,
type Contents,
type ContentsParams,
type ContentsFormData,
} from "./model/contents-interface";
@@ -23,9 +23,15 @@ export const contentsSchema = z.object({
});
export const contentsFormSchema = z.object({
contsSn: z.number().nullish(),
contsTtl: z.string(),
contsCn: z.string(),
contsRmrk: z.string().nullish(),
aprvCd: z.string().nullish(),
cclCd: z.string().nullish(),
koglCd: z.string().nullish(),
tag: z.string().nullish(),
useYn: z.string(),
rgtr: z.string().nullish(),
});
export type Contents = z.infer<typeof contentsSchema>;
+1
View File
@@ -1 +1,2 @@
export { ContentsPage } from "./ui/contents-page";
export { CreatePage } from "./ui/create-page";
@@ -0,0 +1,198 @@
import dynamic from "next/dynamic";
import { type UseFormReturn } from "react-hook-form";
import {
Input,
Label,
RadioGroup,
RadioGroupItem,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from "@common/components";
import { FormWrapper } from "@/widgets/form-wrapper";
import { FormItem, FormControl, FormField } from "@/shared/ui/form";
import type { ContentsFormData } from "@/entities/contents";
const Editor = dynamic(
() => import("@/entities/editor").then((module) => module.Editor),
{ ssr: false },
);
interface ContentsFormProps {
form: UseFormReturn<ContentsFormData>;
contsSn?: number;
handleSave: (data: ContentsFormData) => Promise<void>;
handleDelete?: () => Promise<void>;
}
function ContentsForm({ form, contsSn, ...props }: ContentsFormProps) {
return (
<FormWrapper form={form} isModifyMode={!!contsSn} {...props}>
<div className="card">
<Table variant="secondary">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>콘텐츠 제목</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="contsTtl"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="콘텐츠 제목을 입력하십시오."
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>콘텐츠 내용</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="contsCn"
render={({ field }) => (
<FormItem>
<FormControl>
<Editor
value={field.value ?? ""}
onEditorChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>CCL</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="cclCd"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="cclCd"
value={field.value ?? ""}
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>공공누리</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="koglCd"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="koglCd"
value={field.value ?? ""}
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>태그</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="tag"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="태그를 입력하십시오."
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사용여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="useYn"
value={field.value ?? ""}
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
</FormWrapper>
);
}
export { ContentsForm };
@@ -0,0 +1,43 @@
"use client";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { useMutation } from "@tanstack/react-query";
import { zodResolver } from "@hookform/resolvers/zod";
import {
ContentsApi,
contentsFormSchema,
type ContentsFormData,
} from "@/entities/contents";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import { ContentsForm } from "./contents-form";
function CreatePage() {
const router = useRouter();
const { setMessage: alert } = useAlertStore((state) => state);
const { mutateAsync: createContents } = useMutation({
mutationFn: (data: ContentsFormData) => ContentsApi.createContents(data),
onSuccess: () => {
alert("등록되었습니다.", router.back);
},
});
const form = useForm<ContentsFormData>({
resolver: zodResolver(contentsFormSchema),
defaultValues: {
useYn: "Y",
},
});
return (
<div className="card card-border !mb-0 !mt-6">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">콘텐츠 등록</h3>
</div>
<ContentsForm form={form} handleSave={createContents} />
</div>
);
}
export { CreatePage };
@@ -1,7 +1,7 @@
"use client";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { useMutation } from "@tanstack/react-query";
import { zodResolver } from "@hookform/resolvers/zod";
import { TemplateApi } from "../api/template-service";
@@ -15,6 +15,7 @@ import { TemplateForm } from "./template-form";
function CreatePage() {
const router = useRouter();
const { setMessage: alert } = useAlertStore((state) => state);
const { mutateAsync: createTemplate } = useMutation({
mutationFn: (data: TemplateFormData) => TemplateApi.createTemplate(data),
onSuccess: () => {
+1
View File
@@ -26,6 +26,7 @@ export const ENDPOINT = {
MENU_ROLES: "/cms-service/api/v1/menu-roles",
MENU_AUTHRTS: "/cms-service/api/v1/menu-authrts",
CONTENTS: "/cms-service/api/v1/contents",
CONTENTS_DETAILS: "/cms-service/api/v1/contents-details",
USER_ORGANIZATION: "/cms-service/api/v1/user-ognz",
IMAGES: "/cms-service/api/v1/images",
BANNERS: "/cms-service/api/v1/bnrs",