feat: 설문 등록 페이지 작업
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
import { SurveyPage } from "@/pages/survey";
|
||||
export default SurveyPage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { CreatePage } from "@/pages/survey";
|
||||
export default CreatePage;
|
||||
@@ -5,6 +5,7 @@ export const POSTING_TYPE = {
|
||||
POPUP: "POPUP",
|
||||
BANNER: "BANNER",
|
||||
IMAGE: "IMAGE",
|
||||
SURVEY: "SURVEY",
|
||||
} as const;
|
||||
|
||||
export type PostingType = (typeof POSTING_TYPE)[keyof typeof POSTING_TYPE];
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type {
|
||||
SurveyParams,
|
||||
SurveyList,
|
||||
SurveyService,
|
||||
SurveyFormData,
|
||||
} from "../model/survey-interface";
|
||||
|
||||
export class SurveyApiService implements SurveyService {
|
||||
public async getSurveyList(params: SurveyParams): Promise<SurveyList> {
|
||||
const response = await client.get<SurveyList>(ENDPOINT.CMS_SERVICE.SURVEY, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async createSurvey(data: SurveyFormData): Promise<void> {
|
||||
await client.post(ENDPOINT.CMS_SERVICE.SURVEY, data);
|
||||
}
|
||||
}
|
||||
|
||||
export const SurveyApi = new SurveyApiService();
|
||||
@@ -0,0 +1,2 @@
|
||||
export { SurveyPage } from "./ui/survey-page";
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
@@ -0,0 +1,38 @@
|
||||
import { z } from "zod";
|
||||
import type { PageableList, PageableParams } from "@/shared/model";
|
||||
|
||||
export interface SurveyParams extends PageableParams {
|
||||
keywordType?: string;
|
||||
keyword?: string;
|
||||
srvyTtl?: string;
|
||||
srvyPrps?: string;
|
||||
srvyTrgt?: string;
|
||||
srvyWrtGdCn?: string;
|
||||
delYn?: string;
|
||||
}
|
||||
|
||||
export interface Survey {
|
||||
srvySn: number;
|
||||
srvyTtl: string;
|
||||
srvyPrps: string;
|
||||
srvyTrgt: string;
|
||||
srvyTrgtNm: string;
|
||||
delYn: string;
|
||||
regDt: string;
|
||||
rgTrNm: string;
|
||||
}
|
||||
|
||||
export const surveyFormSchema = z.object({
|
||||
srvyTtl: z.string(),
|
||||
srvyPrps: z.string(),
|
||||
srvyTrgt: z.string().nullish(),
|
||||
srvyWrtGdCn: z.string().nullish(),
|
||||
});
|
||||
|
||||
export type SurveyList = PageableList<Survey>;
|
||||
export type SurveyFormData = z.infer<typeof surveyFormSchema>;
|
||||
|
||||
export interface SurveyService {
|
||||
getSurveyList(params: SurveyParams): Promise<SurveyList>;
|
||||
createSurvey(data: SurveyFormData): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { Checkbox } from "@common/components";
|
||||
import type { Survey } from "./survey-interface";
|
||||
|
||||
export const columns: ColumnDef<Survey>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={table.getIsAllPageRowsSelected()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onCheckedChange={(checked) =>
|
||||
table.toggleAllPageRowsSelected(!!checked)
|
||||
}
|
||||
className="h-4 w-4 rounded border-gray-300"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onCheckedChange={(checked) => row.toggleSelected(!!checked)}
|
||||
className="h-4 w-4 rounded border-gray-300"
|
||||
/>
|
||||
),
|
||||
meta: {
|
||||
width: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "no",
|
||||
header: "NO",
|
||||
cell: ({ row, table }) => {
|
||||
const { pageIndex, pageSize } = table.getState().pagination;
|
||||
const totalRows = table.getRowCount();
|
||||
return totalRows - (pageIndex * pageSize + row.index);
|
||||
},
|
||||
meta: {
|
||||
width: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "srvyTtl",
|
||||
header: "설문제목",
|
||||
},
|
||||
{
|
||||
accessorKey: "srvyTrgt",
|
||||
header: "설문대상",
|
||||
},
|
||||
{
|
||||
id: "preview",
|
||||
header: "설문 보기",
|
||||
cell: () => {},
|
||||
},
|
||||
{
|
||||
id: "result",
|
||||
header: "설문 결과",
|
||||
cell: () => {},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,40 @@
|
||||
"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 { useAlertStore } from "@/shared/lib";
|
||||
import {
|
||||
surveyFormSchema,
|
||||
type SurveyFormData,
|
||||
} from "../model/survey-interface";
|
||||
import { SurveyApi } from "../api/survey-service";
|
||||
import { SurveyForm } from "./survey-form";
|
||||
|
||||
function CreatePage() {
|
||||
const router = useRouter();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { mutateAsync: createSurvey } = useMutation({
|
||||
mutationFn: (data: SurveyFormData) => SurveyApi.createSurvey(data),
|
||||
onSuccess: () => {
|
||||
alert("등록되었습니다.", router.back);
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<SurveyFormData>({
|
||||
resolver: zodResolver(surveyFormSchema),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">설문 등록</h3>
|
||||
</div>
|
||||
<SurveyForm form={form} handleSave={createSurvey} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { CreatePage };
|
||||
@@ -0,0 +1,153 @@
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
import { FormWrapper } from "@/widgets/form-wrapper";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import { FormControl, FormField, FormItem } from "@/shared/ui/form";
|
||||
import type { SurveyFormData } from "../model/survey-interface";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
|
||||
type SurveyFormProps = {
|
||||
form: UseFormReturn<SurveyFormData>;
|
||||
handleSave: (data: SurveyFormData) => Promise<void>;
|
||||
};
|
||||
|
||||
function SurveyForm({ form, ...props }: SurveyFormProps) {
|
||||
return (
|
||||
<FormWrapper form={form} {...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="srvyTtl"
|
||||
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="srvyPrps"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="설문 목적을 입력하십시오."
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>설문 작성 안내 내용</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="srvyWrtGdCn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="설문 작성 안내 내용을 입력하십시오."
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>설문 대상</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="srvyTrgt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CodeSelect
|
||||
upCd="SRVYTARGET"
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
</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 { SurveyForm };
|
||||
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
type PaginationState,
|
||||
type RowSelectionState,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button, DataTable, Input, Label } from "@common/components";
|
||||
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
|
||||
import { AuthorityButton } from "@/entities/menu-authority";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { SurveyApi } from "../api/survey-service";
|
||||
import type { SurveyParams } from "../model/survey-interface";
|
||||
import { columns } from "../model/table-columns";
|
||||
|
||||
type PostingTarget = {
|
||||
pstgTrgtNm: string;
|
||||
pstgTrgtNo: number;
|
||||
};
|
||||
|
||||
function SurveyPage() {
|
||||
const router = useRouter();
|
||||
|
||||
const [surveyParams, setSurveyParams] = useState<SurveyParams>({
|
||||
srvyTtl: "",
|
||||
});
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 5,
|
||||
});
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
|
||||
|
||||
const { data: surveyList, refetch } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, pagination],
|
||||
queryFn: () =>
|
||||
SurveyApi.getSurveyList({
|
||||
...surveyParams,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
});
|
||||
|
||||
const table = useReactTable({
|
||||
data: surveyList?.content ?? [],
|
||||
columns,
|
||||
rowCount: surveyList?.totalElements ?? 0,
|
||||
state: {
|
||||
pagination,
|
||||
rowSelection,
|
||||
},
|
||||
manualPagination: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
refetch();
|
||||
};
|
||||
|
||||
const handleRowClick = (row: unknown) => {};
|
||||
|
||||
useEffect(() => {
|
||||
const postingTargets = table
|
||||
.getFilteredSelectedRowModel()
|
||||
.rows.map((row) => ({
|
||||
pstgTrgtNm: row.original.srvyTtl,
|
||||
pstgTrgtNo: row.original.srvySn,
|
||||
}));
|
||||
|
||||
if (!postingTargets.length) {
|
||||
setPostingTargets([]);
|
||||
return;
|
||||
}
|
||||
|
||||
setPostingTargets(postingTargets);
|
||||
}, [rowSelection]);
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label htmlFor="" className="mr-2 text-label">
|
||||
설문 제목
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
|
||||
value={surveyParams.srvyTtl}
|
||||
onChange={(e) =>
|
||||
setSurveyParams({ ...surveyParams, srvyTtl: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">설문 목록</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<PostingDialog
|
||||
postingType={POSTING_TYPE.SURVEY}
|
||||
postingTargets={postingTargets}
|
||||
/>
|
||||
<AuthorityButton
|
||||
authority="regAuthrtYn"
|
||||
onClick={() => router.push("/survey/register")}
|
||||
>
|
||||
등록
|
||||
</AuthorityButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] min-h-[30rem] overflow-auto !p-0">
|
||||
<DataTable table={table} onRowClick={handleRowClick} viewPagination />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { SurveyPage };
|
||||
@@ -40,6 +40,7 @@ export const ENDPOINT = {
|
||||
UP_CODE: "/cms-service/api/v1/up-codes",
|
||||
POPUPS: "/cms-service/api/v1/popups",
|
||||
SCHEDULES: "/cms-service/api/v1/schdls",
|
||||
SURVEY: "/cms-service/api/v1/srvy",
|
||||
},
|
||||
BOARD_SERVICE: {
|
||||
BBS: "/board-service/api/v1/bbs",
|
||||
|
||||
Reference in New Issue
Block a user