WIP: 설문 보기 작업중
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
import { SurveyArticle } from "@/pages/survey";
|
||||
export default SurveyArticle;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { UserSurveyArticle } from "@/pages/survey";
|
||||
|
||||
export default async function UserSurveyArticlePage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ srvySn: string }>;
|
||||
}) {
|
||||
const { srvySn } = await params;
|
||||
return <UserSurveyArticle srvySn={srvySn} />;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
Article,
|
||||
ArticleList,
|
||||
ArticleParams,
|
||||
UserSurvey,
|
||||
} from "../model/survey-interface";
|
||||
|
||||
export class SurveyApiService implements SurveyService {
|
||||
@@ -89,6 +90,13 @@ export class SurveyApiService implements SurveyService {
|
||||
public async saveArticleList(data: Article[]): Promise<void> {
|
||||
await client.post(ENDPOINT.CMS_SERVICE.SURVEY_ARTICLE, data);
|
||||
}
|
||||
|
||||
public async getUserSurvey(srvySn: number): Promise<UserSurvey> {
|
||||
const response = await client.get<UserSurvey>(
|
||||
`${ENDPOINT.CMS_SERVICE.SURVEY}/user/${srvySn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const SurveyApi = new SurveyApiService();
|
||||
|
||||
@@ -2,4 +2,4 @@ export { SurveyPage } from "./ui/survey-page";
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
export { DetailPage } from "./ui/detail-page";
|
||||
export { ModifyPage } from "./ui/modify-page";
|
||||
export { SurveyArticle } from "./ui/survey-article";
|
||||
export { UserSurveyArticle } from "./ui/user-survey-article";
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ArticleManagementDialog } from "../ui/article-management-dialog";
|
||||
|
||||
const QUESTION_TYPE = {
|
||||
RADIO: "객관식",
|
||||
CHECK: "다중응답",
|
||||
CHECKBOX: "다중응답",
|
||||
INPUT: "주관식",
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PageableList, PageableParams } from "@/shared/model";
|
||||
|
||||
export const QUESTION_TYPE_CODE = {
|
||||
RADIO: "RADIO",
|
||||
CHECK: "CHECK",
|
||||
CHECKBOX: "CHECKBOX",
|
||||
INPUT: "INPUT",
|
||||
} as const;
|
||||
|
||||
@@ -95,6 +95,11 @@ export type QuestionList = PageableList<Question>;
|
||||
export type QuestionFormData = z.infer<typeof questionFormSchema>;
|
||||
export type Article = z.infer<typeof articleSchema>;
|
||||
export type ArticleList = PageableList<Article>;
|
||||
export type UserSurvey = SurveyDetail & {
|
||||
qitems: (Question & {
|
||||
articles: Article[];
|
||||
})[];
|
||||
};
|
||||
|
||||
export interface SurveyService {
|
||||
getSurveyList(params: SurveyParams): Promise<SurveyList>;
|
||||
@@ -108,4 +113,5 @@ export interface SurveyService {
|
||||
deleteQuestion(srvyQitemNo: number): Promise<void>;
|
||||
getArticleList(params: ArticleParams): Promise<ArticleList>;
|
||||
saveArticleList(data: Article[]): Promise<void>;
|
||||
getUserSurvey(srvySn: number): Promise<UserSurvey>;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export const columns: ColumnDef<Survey>[] = [
|
||||
const left = window.screen.width / 2 - width / 2 + window.screenX;
|
||||
const top = window.screen.height / 2 - height / 2 + window.screenY;
|
||||
window.open(
|
||||
`/survey-article/${row.original.srvySn}`,
|
||||
`/user-survey-article/${row.original.srvySn}`,
|
||||
"",
|
||||
`width=${width},height=${height},left=${left},top=${top}`,
|
||||
);
|
||||
|
||||
@@ -107,7 +107,7 @@ function QuestionForm({ form, actionSlot, handleSave }: QuestionFormProps) {
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{qstnTypeCd === "CHECK" && (
|
||||
{qstnTypeCd === "CHECKBOX" && (
|
||||
<TableRow>
|
||||
<TableHead>최대선택갯수</TableHead>
|
||||
<TableCell className="border">
|
||||
@@ -134,7 +134,7 @@ function QuestionForm({ form, actionSlot, handleSave }: QuestionFormProps) {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{(qstnTypeCd === "RADIO" || qstnTypeCd === "CHECK") && (
|
||||
{(qstnTypeCd === "RADIO" || qstnTypeCd === "CHECKBOX") && (
|
||||
<TableRow>
|
||||
<TableHead>기타답변여부</TableHead>
|
||||
<TableCell className="border">
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Input,
|
||||
Label,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
|
||||
function SurveyArticle() {
|
||||
return (
|
||||
<div className="h-screen bg-[#f1f6f9]">
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h1 className="text-2xl font-bold text-foreground">설문 제목</h1>
|
||||
<div className="flex gap-2">
|
||||
<Button>제출</Button>
|
||||
<Button color="white">닫기</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div>
|
||||
<h2 className="text-[1.3rem] font-bold text-foreground">
|
||||
설문 참여자 정보
|
||||
</h2>
|
||||
<div className="my-4 flex gap-4">
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>응답자명</span>
|
||||
<Input
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="이름을 입력해주세요"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>직업</span>
|
||||
<CodeSelect upCd="" className="w-full" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>생년월일</span>
|
||||
<Input type="date" className="!text-[1.3rem]" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>성별</span>
|
||||
<RadioGroup name="gndrCd" className="h-[3.2rem]">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="M" id="M" />
|
||||
<Label htmlFor="M">남자</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="F" id="F" />
|
||||
<Label htmlFor="F">여자</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="my-8" />
|
||||
<div className="h-[8rem] overflow-auto rounded-sm bg-accent p-4">
|
||||
답변 설명
|
||||
</div>
|
||||
<div className="mt-10 flex flex-col gap-10">
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex size-8 items-center justify-center rounded-full bg-primary text-[1.3rem] font-medium text-primary-foreground">
|
||||
1
|
||||
</span>
|
||||
<RadioArticle />
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex size-8 items-center justify-center rounded-full bg-primary text-[1.3rem] font-medium text-primary-foreground">
|
||||
2
|
||||
</span>
|
||||
<CheckboxArticle />
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex size-8 items-center justify-center rounded-full bg-primary text-[1.3rem] font-medium text-primary-foreground">
|
||||
3
|
||||
</span>
|
||||
<TextareaArticle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RadioArticle() {
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">질문 1</p>
|
||||
<RadioGroup name="name">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="1" id="r1" />
|
||||
<Label htmlFor="r1" className="!text-[1.4rem]">
|
||||
답변1
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="2" id="r2" />
|
||||
<Label htmlFor="r2" className="!text-[1.4rem]">
|
||||
답변2
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CheckboxArticle() {
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">질문 2</p>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex h-[1.8rem] items-center gap-2">
|
||||
<Checkbox id="chk1" />
|
||||
<Label htmlFor="chk1" className="!text-[1.4rem]">
|
||||
답변1
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex h-[1.8rem] items-center gap-2">
|
||||
<Checkbox id="chk2" />
|
||||
<Label htmlFor="chk2" className="!text-[1.4rem]">
|
||||
답변2
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TextareaArticle() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">질문 3</p>
|
||||
<Textarea className="!text-[1.3rem]" placeholder="답변을 입력해주세요" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { SurveyArticle };
|
||||
@@ -0,0 +1,211 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Input,
|
||||
Label,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { CodeSelect } from "@/entities/code";
|
||||
import { SurveyApi } from "../api/survey-service";
|
||||
import type { Article } from "../model/survey-interface";
|
||||
import { useState } from "react";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
|
||||
function UserSurveyArticle({ srvySn }: { srvySn: string }) {
|
||||
const { data: userSurvey } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, srvySn],
|
||||
queryFn: () => SurveyApi.getUserSurvey(parseInt(srvySn)),
|
||||
});
|
||||
|
||||
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">
|
||||
{userSurvey?.srvyTtl}
|
||||
</h1>
|
||||
<div className="flex gap-2">
|
||||
<Button>제출</Button>
|
||||
<Button color="white" onClick={() => window.close()}>
|
||||
닫기
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<div>
|
||||
<h2 className="text-[1.3rem] font-bold text-foreground">
|
||||
설문 참여자 정보
|
||||
</h2>
|
||||
<div className="my-4 flex gap-4">
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>응답자명</span>
|
||||
<Input
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="이름을 입력해주세요"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>직업</span>
|
||||
<CodeSelect upCd="" className="w-full" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>생년월일</span>
|
||||
<Input type="date" className="!text-[1.3rem]" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<span>성별</span>
|
||||
<RadioGroup name="gndrCd" className="h-[3.2rem]">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="M" id="M" />
|
||||
<Label htmlFor="M">남자</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="F" id="F" />
|
||||
<Label htmlFor="F">여자</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="my-8" />
|
||||
<div className="h-[8rem] overflow-auto rounded-sm bg-accent p-4">
|
||||
{userSurvey?.srvyWrtGdCn}
|
||||
</div>
|
||||
<div className="mt-10 flex flex-col gap-10">
|
||||
{userSurvey?.qitems.map((item) => (
|
||||
<div className="flex items-start gap-4" key={item.srvyQitemNo}>
|
||||
<span className="flex size-8 items-center justify-center rounded-full bg-primary text-[1.3rem] font-medium text-primary-foreground">
|
||||
{item.qstnSeq}
|
||||
</span>
|
||||
{item.qstnTypeCd === "RADIO" && (
|
||||
<RadioArticle
|
||||
name={`r-${item.srvyQitemNo}`}
|
||||
qstnCn={item.qstnCn}
|
||||
articles={item.articles}
|
||||
/>
|
||||
)}
|
||||
{item.qstnTypeCd === "CHECKBOX" && (
|
||||
<CheckboxArticle
|
||||
name={`chk-${item.srvyQitemNo}`}
|
||||
qstnCn={item.qstnCn}
|
||||
maxChcCnt={item.maxChcCnt}
|
||||
articles={item.articles}
|
||||
/>
|
||||
)}
|
||||
{item.qstnTypeCd === "INPUT" && (
|
||||
<TextareaArticle
|
||||
qstnCn={item.qstnCn}
|
||||
article={item.articles[0]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RadioArticle({
|
||||
name,
|
||||
qstnCn,
|
||||
articles,
|
||||
}: {
|
||||
name: string;
|
||||
qstnCn: string;
|
||||
articles: Article[];
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">{qstnCn}</p>
|
||||
<RadioGroup name={name}>
|
||||
<div className="flex flex-col gap-3">
|
||||
{articles.map((article, index) => (
|
||||
<div
|
||||
className="flex items-center space-x-2"
|
||||
key={article.srvyArtclNo}
|
||||
>
|
||||
<RadioGroupItem
|
||||
value={article.srvyArtclNo?.toString() ?? ""}
|
||||
id={`${name}-${index}`}
|
||||
/>
|
||||
<Label htmlFor={`${name}-${index}`} className="!text-[1.4rem]">
|
||||
{article.artclCn}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CheckboxArticle({
|
||||
name,
|
||||
qstnCn,
|
||||
maxChcCnt,
|
||||
articles,
|
||||
}: {
|
||||
name: string;
|
||||
qstnCn: string;
|
||||
maxChcCnt: number;
|
||||
articles: Article[];
|
||||
}) {
|
||||
const [selectedValues, setSelectedValues] = useState<number[]>([]);
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const handleCheckboxChange = (value: number) => (checked: boolean) => {
|
||||
if (checked && selectedValues.length >= maxChcCnt) {
|
||||
alert("최대 선택 개수를 초과했습니다.");
|
||||
return;
|
||||
}
|
||||
if (selectedValues.includes(value)) {
|
||||
setSelectedValues((prev) => prev.filter((item) => item !== value));
|
||||
return;
|
||||
}
|
||||
setSelectedValues((prev) => [...prev, value]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">
|
||||
{qstnCn} (최대 {maxChcCnt}개 선택)
|
||||
</p>
|
||||
<div className="flex flex-col gap-3">
|
||||
{articles.map((article, index) => (
|
||||
<div
|
||||
className="flex h-[1.8rem] items-center gap-2"
|
||||
key={article.srvyArtclNo}
|
||||
>
|
||||
<Checkbox
|
||||
id={`${name}-${index}`}
|
||||
checked={selectedValues.includes(article.srvyArtclNo ?? 0)}
|
||||
onCheckedChange={handleCheckboxChange(article.srvyArtclNo ?? 0)}
|
||||
/>
|
||||
<Label htmlFor={`${name}-${index}`} className="!text-[1.4rem]">
|
||||
{article.artclCn}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TextareaArticle({ qstnCn }: { qstnCn: string; article?: Article }) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<p className="mb-4 text-2xl font-bold text-foreground">{qstnCn}</p>
|
||||
<Textarea className="!text-[1.3rem]" placeholder="답변을 입력해주세요" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { UserSurveyArticle };
|
||||
Reference in New Issue
Block a user