WIP: 설문 결과 페이지 작업중
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { UserSurveyResult } from "@/pages/survey";
|
||||
|
||||
export default async function UserSurveyResultPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ srvySn: string }>;
|
||||
}) {
|
||||
const { srvySn } = await params;
|
||||
return <UserSurveyResult srvySn={srvySn} />;
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
export { SiteApi } from "../api/site-service";
|
||||
export type { Site } from "../model/site-interface";
|
||||
|
||||
export { SiteSelect } from "../ui/site-select";
|
||||
|
||||
@@ -5,5 +5,4 @@ export {
|
||||
type Site,
|
||||
type SiteFormData,
|
||||
} from "./model/site-interface";
|
||||
|
||||
export { SiteSelect } from "./ui/site-select";
|
||||
export { SiteSelect } from "./ui/site-select";
|
||||
|
||||
@@ -10,23 +10,29 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import { SiteApi } from "../api/site-service";
|
||||
|
||||
type SiteSelectProps = {
|
||||
siteId: string;
|
||||
placeholder?: string;
|
||||
enabledAllSite?: boolean;
|
||||
} & React.ComponentPropsWithoutRef<typeof Select>;
|
||||
|
||||
function SiteSelect({
|
||||
defaultValue,
|
||||
placeholder = "선택",
|
||||
enabledAllSite,
|
||||
...props
|
||||
}: SiteSelectProps) {
|
||||
const { data: sites } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SITES],
|
||||
queryFn: () => SiteApi.getSiteList(),
|
||||
enabled: true,
|
||||
select: (data) => {
|
||||
if (enabledAllSite) {
|
||||
return [{ siteId: "ALL", siteNm: "전체" }, ...data.content];
|
||||
}
|
||||
return data.content;
|
||||
},
|
||||
});
|
||||
|
||||
const defaultValueText =
|
||||
sites?.content.find((item) => item.siteId === defaultValue)?.siteNm ?? placeholder;
|
||||
sites?.find((item) => item.siteId === defaultValue)?.siteNm ?? placeholder;
|
||||
|
||||
return (
|
||||
<Select {...props}>
|
||||
@@ -34,7 +40,7 @@ function SiteSelect({
|
||||
<SelectValue placeholder={defaultValueText} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{sites?.content.map((item) => (
|
||||
{sites?.map((item) => (
|
||||
<SelectItem key={item.siteId} value={item.siteId}>
|
||||
{item.siteNm}
|
||||
</SelectItem>
|
||||
|
||||
@@ -212,7 +212,6 @@ const UpCodeManagementDialog = memo(
|
||||
<FormItem className="flex flex-col">
|
||||
<FormControl>
|
||||
<SiteSelect
|
||||
siteId={field.value || ""}
|
||||
value={field.value || ""}
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
|
||||
@@ -15,6 +15,8 @@ import type {
|
||||
ArticleParams,
|
||||
UserSurvey,
|
||||
UserSurveyFormData,
|
||||
UserSurveyResultParams,
|
||||
UserSurveyResult,
|
||||
} from "../model/survey-interface";
|
||||
|
||||
export class SurveyApiService implements SurveyService {
|
||||
@@ -102,6 +104,17 @@ export class SurveyApiService implements SurveyService {
|
||||
public async saveUserSurvey(data: UserSurveyFormData): Promise<void> {
|
||||
await client.post(`${ENDPOINT.CMS_SERVICE.SURVEY}/user`, data);
|
||||
}
|
||||
|
||||
public async getUserSurveyResult(
|
||||
srvySn: number,
|
||||
params: UserSurveyResultParams,
|
||||
): Promise<UserSurveyResult> {
|
||||
const response = await client.get<UserSurveyResult>(
|
||||
`${ENDPOINT.CMS_SERVICE.SURVEY}/rslt/stat/${srvySn}`,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const SurveyApi = new SurveyApiService();
|
||||
|
||||
@@ -3,3 +3,4 @@ export { CreatePage } from "./ui/create-page";
|
||||
export { DetailPage } from "./ui/detail-page";
|
||||
export { ModifyPage } from "./ui/modify-page";
|
||||
export { UserSurveyArticle } from "./ui/user-survey-article";
|
||||
export { UserSurveyResult } from "./ui/user-survey-result";
|
||||
|
||||
@@ -38,6 +38,12 @@ export interface ArticleParams extends PageableParams {
|
||||
delYn?: string;
|
||||
}
|
||||
|
||||
export interface UserSurveyResultParams {
|
||||
keywordType?: string;
|
||||
keyword?: string;
|
||||
siteId?: string;
|
||||
}
|
||||
|
||||
export interface Survey {
|
||||
srvySn: number;
|
||||
srvyTtl: string;
|
||||
@@ -112,6 +118,48 @@ export const userSurveyFormSchema = z.object({
|
||||
),
|
||||
});
|
||||
|
||||
const inputResultSchema = z.object({
|
||||
rspdntNm: z.string(),
|
||||
ansCn: z.string(),
|
||||
ansDt: z.string(),
|
||||
});
|
||||
|
||||
const articleWithResultSchema = articleSchema.omit({ mode: true }).extend({
|
||||
inputRslts: z.array(inputResultSchema),
|
||||
rspnsCount: z.number(),
|
||||
rspnsRate: z.number(),
|
||||
});
|
||||
|
||||
const questionWithArticlesSchema = questionFormSchema.extend({
|
||||
srvyQitemNo: z.number(),
|
||||
articles: z.array(articleWithResultSchema),
|
||||
});
|
||||
|
||||
const respondentStateSchema = z.object({
|
||||
ageN20대: z.number(),
|
||||
ageN30대: z.number(),
|
||||
ageN40대: z.number(),
|
||||
ageN50대: z.number(),
|
||||
ageN60대이상: z.number(),
|
||||
job학생: z.number(),
|
||||
job대학생: z.number(),
|
||||
job회사원: z.number(),
|
||||
job공단직원: z.number(),
|
||||
job공무원: z.number(),
|
||||
job기타: z.number(),
|
||||
mf남성: z.number(),
|
||||
mf여성: z.number(),
|
||||
구분: z.string(),
|
||||
});
|
||||
|
||||
export const userSurveyResultSchema = surveyFormSchema.extend({
|
||||
srvySn: z.number(),
|
||||
srvyPeriodFrTo: z.string(),
|
||||
qitems: z.array(questionWithArticlesSchema),
|
||||
srvyRspntStatList: z.array(respondentStateSchema),
|
||||
totalRspdnt: z.number(),
|
||||
});
|
||||
|
||||
export type SurveyList = PageableList<Survey>;
|
||||
export type SurveyFormData = z.infer<typeof surveyFormSchema>;
|
||||
export type QuestionList = PageableList<Question>;
|
||||
@@ -124,6 +172,7 @@ export type UserSurvey = SurveyDetail & {
|
||||
})[];
|
||||
};
|
||||
export type UserSurveyFormData = z.infer<typeof userSurveyFormSchema>;
|
||||
export type UserSurveyResult = z.infer<typeof userSurveyResultSchema>;
|
||||
|
||||
export interface SurveyService {
|
||||
getSurveyList(params: SurveyParams): Promise<SurveyList>;
|
||||
@@ -139,4 +188,8 @@ export interface SurveyService {
|
||||
saveArticleList(data: Article[]): Promise<void>;
|
||||
getUserSurvey(srvySn: number): Promise<UserSurvey>;
|
||||
saveUserSurvey(data: UserSurveyFormData): Promise<void>;
|
||||
getUserSurveyResult(
|
||||
srvySn: number,
|
||||
params: UserSurveyResultParams,
|
||||
): Promise<UserSurveyResult>;
|
||||
}
|
||||
|
||||
@@ -81,8 +81,24 @@ export const columns: ColumnDef<Survey>[] = [
|
||||
{
|
||||
id: "result",
|
||||
header: "설문 결과",
|
||||
cell: () => (
|
||||
<Button size="sm" color="gray">
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
size="sm"
|
||||
color="gray"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const width = 767;
|
||||
const height = 900;
|
||||
const left = window.screen.width / 2 - width / 2 + window.screenX;
|
||||
const top = window.screen.height / 2 - height / 2 + window.screenY;
|
||||
window.open(
|
||||
`/user-survey-result/${row.original.srvySn}`,
|
||||
"",
|
||||
`width=${width},height=${height},left=${left},top=${top}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
설문 결과
|
||||
</Button>
|
||||
),
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { SurveyApi } from "../api/survey-service";
|
||||
import type { UserSurveyResultParams } from "../model/survey-interface";
|
||||
import { SiteSelect } from "@/entities/site";
|
||||
|
||||
function UserSurveyResult({ srvySn }: { srvySn: string }) {
|
||||
const [params, setParams] = useState<UserSurveyResultParams>({
|
||||
siteId: "ALL",
|
||||
});
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, srvySn, params.siteId],
|
||||
queryFn: () => SurveyApi.getUserSurveyResult(parseInt(srvySn), params),
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-[#f1f6f9] p-4">
|
||||
<div className="card card-border !m-0 max-h-full overflow-auto">
|
||||
<div className="card-header sticky -top-2 z-10 bg-background">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{data?.srvyTtl}
|
||||
</h1>
|
||||
<div className="flex gap-2">
|
||||
<SiteSelect
|
||||
defaultValue="ALL"
|
||||
value={params.siteId}
|
||||
onValueChange={(siteId) => setParams({ siteId })}
|
||||
enabledAllSite
|
||||
/>
|
||||
<Button>인쇄</Button>
|
||||
<Button color="white" onClick={() => window.close()}>
|
||||
닫기
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h2 className="mb-2 flex items-center justify-between gap-2 text-[1.3rem] font-bold text-foreground">
|
||||
응답자 통계
|
||||
<div className="text-[1.2rem] font-medium tracking-tighter text-label">
|
||||
<p>
|
||||
( 총 응답자 수: <span>{data?.totalRspdnt}</span>명 / 설문 기간:{" "}
|
||||
<span>{data?.srvyPeriodFrTo}</span> )
|
||||
</p>
|
||||
</div>
|
||||
</h2>
|
||||
<Table variant="secondary" className="text-center">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead rowSpan={2}>구분</TableHead>
|
||||
<TableHead colSpan={2}>성별</TableHead>
|
||||
<TableHead colSpan={6}>직업</TableHead>
|
||||
<TableHead colSpan={5}>나이</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>남성</TableHead>
|
||||
<TableHead>여성</TableHead>
|
||||
<TableHead>학생</TableHead>
|
||||
<TableHead>대학생</TableHead>
|
||||
<TableHead>회사원</TableHead>
|
||||
<TableHead>공단직원</TableHead>
|
||||
<TableHead>공무원</TableHead>
|
||||
<TableHead>기타</TableHead>
|
||||
<TableHead>20대</TableHead>
|
||||
<TableHead>30대</TableHead>
|
||||
<TableHead>40대</TableHead>
|
||||
<TableHead>50대</TableHead>
|
||||
<TableHead>60대 이상</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data?.srvyRspntStatList.map((item) => (
|
||||
<TableRow key={item.구분}>
|
||||
<TableHead>{item.구분}</TableHead>
|
||||
<TableCell>{item.mf남성}</TableCell>
|
||||
<TableCell>{item.mf여성}</TableCell>
|
||||
<TableCell>{item.job학생}</TableCell>
|
||||
<TableCell>{item.job대학생}</TableCell>
|
||||
<TableCell>{item.job회사원}</TableCell>
|
||||
<TableCell>{item.job공단직원}</TableCell>
|
||||
<TableCell>{item.job공무원}</TableCell>
|
||||
<TableCell>{item.job기타}</TableCell>
|
||||
<TableCell>{item.ageN20대}</TableCell>
|
||||
<TableCell>{item.ageN30대}</TableCell>
|
||||
<TableCell>{item.ageN40대}</TableCell>
|
||||
<TableCell>{item.ageN50대}</TableCell>
|
||||
<TableCell>{item.ageN60대이상}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { UserSurveyResult };
|
||||
Reference in New Issue
Block a user