feat: 템플릿 관리 폼 작업
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
NEXT_PUBLIC_PORT=3000
|
||||
NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000
|
||||
@@ -36,3 +36,5 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.env*
|
||||
@@ -0,0 +1,2 @@
|
||||
import { CreatePage } from "@/pages/template";
|
||||
export default CreatePage;
|
||||
@@ -18,6 +18,7 @@
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@tanstack/react-query": "^5.67.1",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@tinymce/tinymce-react": "^6.1.0",
|
||||
"axios": "^1.8.1",
|
||||
"chart.js": "^4.4.8",
|
||||
"dayjs": "^1.11.13",
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
TemplateService,
|
||||
TemplateList,
|
||||
TemplateParams,
|
||||
TemplateFormData,
|
||||
} from "../model/template-interface";
|
||||
|
||||
export class TemplateApiService implements TemplateService {
|
||||
@@ -14,6 +15,14 @@ export class TemplateApiService implements TemplateService {
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async createTemplate(data: TemplateFormData): Promise<void> {
|
||||
const response = await client.post<void>(
|
||||
`${ENDPOINT.CMS_SERVICE.TEMPLATE}`,
|
||||
data,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const TemplateApi = new TemplateApiService();
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
export { TemplatePage } from "./ui/template-page";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import type { PageableParams, PageableList } from "@/shared/model";
|
||||
|
||||
export interface TemplateParams extends PageableParams {
|
||||
@@ -18,8 +19,16 @@ export interface Template {
|
||||
mdfrNm: string;
|
||||
}
|
||||
|
||||
export type TemplateList = PageableList<Template>;
|
||||
export const templateFormSchema = z.object({
|
||||
tmpltNm: z.string(),
|
||||
tmpltCn: z.string(),
|
||||
tmpltSeCd: z.string(),
|
||||
useYn: z.string(),
|
||||
});
|
||||
|
||||
export type TemplateList = PageableList<Template>;
|
||||
export type TemplateFormData = z.infer<typeof templateFormSchema>;
|
||||
export interface TemplateService {
|
||||
getTemplateList(params: TemplateParams): Promise<TemplateList>;
|
||||
createTemplate(data: TemplateFormData): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { TemplateApi } from "../api/template-service";
|
||||
import {
|
||||
templateFormSchema,
|
||||
type TemplateFormData,
|
||||
} from "../model/template-interface";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
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: () => {
|
||||
alert("등록되었습니다.", router.back);
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<TemplateFormData>({
|
||||
resolver: zodResolver(templateFormSchema),
|
||||
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>
|
||||
<TemplateForm form={form} handleSave={createTemplate} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { CreatePage };
|
||||
@@ -0,0 +1,134 @@
|
||||
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 { CodeSelect } from "@/entities/code";
|
||||
import { FormItem, FormControl, FormField } from "@/shared/ui/form";
|
||||
import type { TemplateFormData } from "../model/template-interface";
|
||||
|
||||
const Editor = dynamic(
|
||||
() => import("@/shared/ui/editor").then((module) => module.Editor),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
interface TemplateFormProps {
|
||||
form: UseFormReturn<TemplateFormData>;
|
||||
tmpltSn?: number;
|
||||
handleSave: (data: TemplateFormData) => Promise<void>;
|
||||
handleDelete?: () => Promise<void>;
|
||||
}
|
||||
|
||||
function TemplateForm({ form, tmpltSn, ...props }: TemplateFormProps) {
|
||||
return (
|
||||
<FormWrapper form={form} isModifyMode={!!tmpltSn} {...props}>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="80%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableHead>템플릿 구분</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tmpltSeCd"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CodeSelect
|
||||
{...field}
|
||||
upCd="TMPLTCLCD"
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>템플릿명</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tmpltNm"
|
||||
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="tmpltCn"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Editor />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="useYn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
name="useYn"
|
||||
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 { TemplateForm };
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Editor as TinyEditor } from "@tinymce/tinymce-react";
|
||||
|
||||
const Editor = () => {
|
||||
return (
|
||||
<TinyEditor
|
||||
apiKey={process.env.NEXT_PUBLIC_TINY_API_KEY}
|
||||
init={{
|
||||
language: "ko_KR",
|
||||
height: 500,
|
||||
menubar: false,
|
||||
plugins: "lists advlist table",
|
||||
toolbar:
|
||||
"undo redo | blocks fontfamily fontsize | bold italic underline strikethrough forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist blockquote | table",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { Editor };
|
||||
Generated
+23
-5
@@ -66,6 +66,9 @@ importers:
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.21.2
|
||||
version: 8.21.2(react-dom@18.3.1)(react@18.3.1)
|
||||
'@tinymce/tinymce-react':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(react-dom@18.3.1)(react@18.3.1)
|
||||
axios:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1
|
||||
@@ -1632,7 +1635,7 @@ packages:
|
||||
magic-string: 0.27.0
|
||||
react-docgen-typescript: 2.2.2(typescript@5.6.3)
|
||||
typescript: 5.6.3
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.3.8:
|
||||
@@ -3097,7 +3100,7 @@ packages:
|
||||
browser-assert: 1.2.1
|
||||
storybook: 8.6.0(prettier@3.5.2)
|
||||
ts-dedent: 2.2.0
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
dev: true
|
||||
|
||||
/@storybook/components@8.6.0(storybook@8.6.0):
|
||||
@@ -3229,7 +3232,7 @@ packages:
|
||||
resolve: 1.22.10
|
||||
storybook: 8.6.0(prettier@3.5.2)
|
||||
tsconfig-paths: 4.2.0
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
@@ -3363,6 +3366,21 @@ packages:
|
||||
'@testing-library/dom': 10.4.0
|
||||
dev: true
|
||||
|
||||
/@tinymce/tinymce-react@6.1.0(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-K0MP3yYVKe8+etUwsg6zyRq+q9TGLaVf005WiBHiB8JZEomAwbBPERGunhU9uOqNQ5gJs8yVOPZ68Xcd1UHclA==}
|
||||
peerDependencies:
|
||||
react: ^18.3.1
|
||||
react-dom: ^18.3.1
|
||||
tinymce: ^7.0.0 || ^6.0.0 || ^5.5.1
|
||||
peerDependenciesMeta:
|
||||
tinymce:
|
||||
optional: true
|
||||
dependencies:
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
dev: false
|
||||
|
||||
/@types/aria-query@5.0.4:
|
||||
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
|
||||
dev: true
|
||||
@@ -3675,7 +3693,7 @@ packages:
|
||||
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -8474,7 +8492,7 @@ packages:
|
||||
debug: 4.4.0
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.5(typescript@5.6.3)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
Reference in New Issue
Block a user