Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/common/DatepickerCalendar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import '@/styles/datepicker-custom.css';

interface Props {
isChangeable: boolean;
deadLine: Date | null;
setDeadLine: React.Dispatch<React.SetStateAction<Date | null>>;
deadLine: Date | string | null;
setDeadLine?: React.Dispatch<React.SetStateAction<Date | string | null>>;
}

const TWO_WEEK_DAYS = 14;
Expand All @@ -38,7 +38,7 @@ const DatepickerCalendar = ({ isChangeable, deadLine, setDeadLine }: Props) => {
disabled={!isChangeable}
ref={deadLineDatePickerRef}
className='disabled:bg-grey-100 disabled:text-grey-500'
selected={deadLine}
selected={deadLine ? new Date(deadLine) : null}
minDate={now}
onChange={handleChangeDate}
locale={ko}
Expand All @@ -62,7 +62,7 @@ const DatepickerCalendar = ({ isChangeable, deadLine, setDeadLine }: Props) => {
마감 일자
</Subtitle2Black>
<span className='flex-1 text-base font-medium'>
{deadLine!.toISOString().split('T')[0]}
{deadLine ? new Date(deadLine).toISOString().split('T')[0] : null}
</span>
<Icon
name='calendar'
Expand Down
1 change: 1 addition & 0 deletions src/components/common/Typography/Typography.variant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const typographyVariants = cva('whitespace-pre-line text-wrap', {
grey500: 'text-grey-500',
grey600: 'text-grey-600',
grey900: 'text-grey-900',
assistive: 'text-assistiveTxt',

// 리디자인 후 삭제
darken: 'text-dark-200',
Expand Down
10 changes: 10 additions & 0 deletions src/components/common/Typography/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ export const Body3Grey600 = customTypography('span', {
color: 'grey600',
});

export const Body3Assistive = customTypography('span', {
type: 'Body3',
color: 'assistive',
});

export const SubTitle1Black = customTypography('span', {
type: 'Subtitle1',
color: 'black',
Expand Down Expand Up @@ -158,6 +163,11 @@ export const Body2Grey200 = customTypography('span', {
color: 'grey200',
});

export const Body2Assistive = customTypography('span', {
type: 'Body2',
color: 'assistive',
});

export const Subtitle2Red500 = customTypography('span', {
type: 'Subtitle2',
color: 'red',
Expand Down
222 changes: 128 additions & 94 deletions src/components/feature/ChartDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import dayjs from 'dayjs';
import { FailResponse } from '@/type/response';
import { Question } from '@/type/survey/surveyResponse';
import { getTextResponsesByQuestionText } from '@/utils/getTextResponseByQuestionText';
import Button from '@/components/common/Button/Button';
import DatepickerCalendar from '@/components/common/DatepickerCalendar';
import {
CardTitle,
HeadPrimary,
PageHeaderTitle,
Body2Assistive,
Body2Black,
Body3Assistive,
H2Black,
SubTitle1Black,
Subtitle2Black,
Subtitle2Green500,
Subtitle2Grey100,
Subtitle2Grey900,
} from '@/components/common/Typography';
import AverageGraph from '@/components/shared/ChartDetail/AverageGraph';
import BarGraph from '@/components/shared/ChartDetail/BarGraph';
Expand All @@ -21,7 +29,7 @@ import { NAV_LINKS, ROUTES } from '@/constants/_navbar';
import { surveyKeys } from '@/hooks/survey/queryKey';
import { useDeleteSurvey } from '@/hooks/survey/useDeleteSurvey';
import { useGetSurveyDetail } from '@/hooks/survey/useGetSurveyDetail';
import { useGetSurveyQrCode } from '@/hooks/survey/useGetSurveyQrCode';
// import { useGetSurveyQrCode } from '@/hooks/survey/useGetSurveyQrCode';
import useNavigate from '@/hooks/useNavigate';
import { useToastStore } from '@/stores/useToastStore';

Expand Down Expand Up @@ -49,9 +57,9 @@ const ChartDetail = ({ id }: Props) => {

const { data: detailData } = useGetSurveyDetail(id);

const { data: qrData } = useGetSurveyQrCode(id, {
enabled: !!detailData,
});
// const { data: qrData } = useGetSurveyQrCode(id, {
// enabled: !!detailData,
// });

const { surveyName, averageScores, additionalQuestions, mandatoryQuestions } =
detailData || {};
Expand All @@ -65,129 +73,155 @@ const ChartDetail = ({ id }: Props) => {
return distributionValues.every((value) => value === 0);
};

const copyQRcode = async () => {
try {
if (!qrData?.body) return;
const imageUrl = `data:image/png;base64,${qrData.body}`;
// const copyQRcode = async () => {
// try {
// if (!qrData?.body) return;
// const imageUrl = `data:image/png;base64,${qrData.body}`;

const response = await fetch(imageUrl);
const blob = await response.blob();
// const response = await fetch(imageUrl);
// const blob = await response.blob();

await navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob }),
]);
// await navigator.clipboard.write([
// new ClipboardItem({ 'image/png': blob }),
// ]);

showToast('QRcode 복사 성공', 'success', 1000);
} catch (error) {
showToast('QRcode 복사 실패', 'warning', 1000);
}
};
// showToast('QRcode 복사 성공', 'success', 1000);
// } catch (error) {
// showToast('QRcode 복사 실패', 'warning', 1000);
// }
// };

return (
<div className='flex flex-col gap-10'>
{detailData &&
averageScores &&
mandatoryQuestions &&
additionalQuestions &&
qrData && (
<>
additionalQuestions && (
// qrData &&
<div className='flex flex-col gap-6'>
<H2Black>{surveyName}</H2Black>
<div className='flex items-center justify-between'>
<PageHeaderTitle>{surveyName}</PageHeaderTitle>
<div className='flex h-8 gap-3'>
<Button size='small'>설문 종료</Button>
<DatepickerCalendar
deadLine={dayjs(detailData.deadline).format(
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
)}
isChangeable={false}
/>
<div className='flex h-12 gap-2'>
<Button variant='secondary' size='sm'>
<Subtitle2Green500>설문 종료</Subtitle2Green500>
</Button>
<Button
size='small'
variant='teritary'
size='sm'
onClick={() => navigate(`${ROUTES.SURVEY.EDIT}/${id}`)}
>
질문 수정
<Subtitle2Grey100>질문 수정</Subtitle2Grey100>
</Button>
<Button size='small' onClick={deleteHandler}>
설문 삭제
<Button variant='grey' size='sm' onClick={deleteHandler}>
<Subtitle2Grey900>설문 삭제</Subtitle2Grey900>
</Button>
</div>
</div>
<div className='flex w-full gap-5'>
<div className='flex w-full flex-col gap-3 rounded border border-gray-300 bg-white-100 p-5'>
<CardTitle>월별 총 만족도 점수 분포도</CardTitle>
<div className='flex h-[336px] w-full gap-6'>
<div className='flex h-full w-full max-w-[1056px] flex-col gap-6 rounded-2xl bg-white-100 p-6'>
<SubTitle1Black>월별 총 만족도 점수 분포도</SubTitle1Black>
{mandatoryQuestions!.every(isAllDistributionZero) ? (
<HeadPrimary>제출된 설문이 없습니다.</HeadPrimary>
<div className='flex min-h-[130px] w-full items-center justify-center'>
<Body2Assistive>제출된 설문이 없습니다.</Body2Assistive>
</div>
) : (
<BarGraph data={mandatoryQuestions![0].radioResponses} />
<div className='h-full w-[1008px]'>
<BarGraph data={mandatoryQuestions![0].radioResponses} />
</div>
)}
</div>
<div className='flex w-1/2 flex-col gap-3 rounded border border-gray-300 bg-white-100 p-5'>
<CardTitle>만족도 평균</CardTitle>
<div className='flex h-full w-[516px] flex-col gap-4 rounded-2xl bg-white-100 p-6'>
<SubTitle1Black>만족도 평균</SubTitle1Black>
{!Object.values(averageScores).every((score) => score === 0) ? (
<AverageGraph averageScores={averageScores} />
<div className='h-full w-full'>
<AverageGraph averageScores={averageScores} />
</div>
) : (
<HeadPrimary>제출된 설문이 없습니다.</HeadPrimary>
<div className='flex min-h-[130px] w-full items-center justify-center'>
<Body2Assistive>제출된 설문이 없습니다.</Body2Assistive>
</div>
)}
</div>
</div>
<div className='flex w-full gap-8'>
<div className='flex w-full flex-col gap-3 rounded border border-gray-300 bg-white-100 p-5'>
<TopCard
title='식단 좋아요 Top3'
top3Data={getTextResponsesByQuestionText(
<div className='flex w-full gap-6'>
<TopCard
title='식단 좋아요 Top3'
type='like'
top3Data={getTextResponsesByQuestionText(
mandatoryQuestions,
'가장 좋아하는 상위 3개 식단',
)}
/>
<TopCard
title='식단 싫어요 Top3'
type='unlike'
top3Data={getTextResponsesByQuestionText(
mandatoryQuestions,
'가장 싫어하는 상위 3개 식단',
)}
/>
</div>
<div className='flex w-full gap-6'>
<TextList
type='desireMenu'
list={
getTextResponsesByQuestionText(
mandatoryQuestions,
'가장 좋아하는 상위 3개 식단',
)}
/>
<TopCard
title='식단 싫어요 Top3'
top3Data={getTextResponsesByQuestionText(
'먹고 싶은 메뉴',
)!
}
title='먹고 싶은 메뉴'
/>
<TextList
type='desireMenu'
list={
getTextResponsesByQuestionText(
mandatoryQuestions,
'가장 싫어하는 상위 3개 식단',
)}
/>
</div>
<div className='flex w-1/3 flex-col gap-3'>
<div className='mb-3 flex flex-1 flex-col gap-3 rounded border border-gray-300 bg-white-100 p-5'>
<CardTitle>설문 조사 링크</CardTitle>
<div className='flex cursor-pointer flex-col items-center justify-center gap-2 rounded border border-gray-300'>
'영양사에게 한마디',
)!
}
title='영양사에게 한마디'
/>
<div className='flex w-full flex-col gap-6 rounded-2xl bg-white-100 p-6'>
<SubTitle1Black>설문 조사 링크</SubTitle1Black>
<div className='flex w-full items-center gap-6'>
<div className='flex min-h-[120px] min-w-[120px] items-center justify-center rounded-lg border border-grey-100'>
<Image
width={180}
height={180}
style={{ borderRadius: 4 }}
src={`data:image/png;base64,${qrData.body}`}
width={96}
height={96}
src={`/imgs/pi-gon-ping.jpg`}
alt='qr이미지'
onClick={() => navigate(`${ROUTES.SURVEY.TAKE}/${id}`)}
/>
<Button
size='xSmall'
width='fit'
className='mb-2 rounded'
onClick={copyQRcode}
>
QRcode 복사
</Button>
</div>
<div className='flex w-full flex-col gap-4'>
<div className='flex flex-col gap-2'>
<Body3Assistive>
QR 코드를 다운로드하거나 URL을 복사하여
<br />
작성한 설문을 간편하게 공유하세요!
</Body3Assistive>
<Body2Black>https://www.nnplanner.com/qrqrqr</Body2Black>
</div>
<div className='flex w-full gap-2'>
<Button variant='outline' size='xs' width='full'>
<Subtitle2Black>QR 코드 다운로드</Subtitle2Black>
</Button>
<Button variant='outline' size='xs' width='full'>
<Subtitle2Black>URL 복사</Subtitle2Black>
</Button>
</div>
</div>
</div>
<TextList
type='desireMenu'
list={
getTextResponsesByQuestionText(
mandatoryQuestions,
'먹고 싶은 메뉴',
)!
}
title='먹고 싶은 메뉴'
/>
</div>
<div className='flex w-2/3 flex-col gap-3'>
<TextList
type='message'
list={
getTextResponsesByQuestionText(
mandatoryQuestions,
'영양사에게 한마디',
)!
}
title='영양사에게 한마디'
/>
</div>
</div>
</>
</div>
)}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/feature/Survey/Create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const SurveyCreate = ({ id }: { id: string }) => {

const [inputs, setInputs] = useState<inputsType[]>([]);
const [surveyName, setSurveyName] = useState('');
const [deadLine, setDeadLine] = useState<Date | null>(twoWeeksLater);
const [deadLine, setDeadLine] = useState<Date | null | string>(twoWeeksLater);
const showToast = useToastStore((state) => state.showToast);

const requestData = {
Expand Down
2 changes: 1 addition & 1 deletion src/components/feature/Survey/Edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const SurveyEdit = ({ id }: Props) => {
const { data: detailSurvey } = useGetSurveyDetail(id);

const [editSurveyName, setEditSurveyName] = useState('');
const [editDeadLine, setEditDeadLine] = useState<Date | null>(null);
const [editDeadLine, setEditDeadLine] = useState<Date | null | string>(null);
const [inputs, setInputs] = useState<inputsType[]>([]);

useEffect(() => {
Expand Down
Loading