diff --git a/src/apis/apis/poll.ts b/src/apis/apis/poll.ts new file mode 100644 index 00000000..d66d31dd --- /dev/null +++ b/src/apis/apis/poll.ts @@ -0,0 +1,99 @@ +import { VoterPollInfo } from '../../type/type'; +import { ApiUrl } from '../endpoints'; +import { request } from '../primitives'; +import { + GetPollResponseType, + GetVoterPollInfoResponseType, + PatchPollResponseType, + PostPollResponseType, + PostVoterPollInfoResponseType, +} from '../responses/poll'; + +// Template +/* +export async function apiFunc( + +): Promise { + const requestUrl: string = ApiUrl. + const response = await request( + method, + requestUrl, + data, + params, + ); + + return response.data; +} +*/ + +// POST /api/polls/{tableId} +export async function postPoll(tableId: number): Promise { + const requestUrl: string = ApiUrl.poll; + const response = await request( + 'POST', + requestUrl + `/${tableId}`, + null, + null, + ); + + return response.data; +} + +// GET /api/polls/{pollId} +export async function getPollInfo( + pollId: number, +): Promise { + const requestUrl: string = ApiUrl.poll; + const response = await request( + 'GET', + requestUrl + `/${pollId}`, + null, + null, + ); + return response.data; +} + +// PATCH /api/polls/{pollId} +export async function patchEndPoll( + pollId: number, +): Promise { + const requestUrl: string = ApiUrl.poll; + const response = await request( + 'PATCH', + requestUrl + `/${pollId}`, + null, + null, + ); + return response.data; +} + +// GET /api/polls/{pollId}/votes +export async function getVoterPollInfo( + pollId: number, +): Promise { + const requestUrl: string = ApiUrl.poll; + const response = await request( + 'GET', + requestUrl + `/${pollId}/votes`, + null, + null, + ); + + return response.data; +} + +// POST /api/polls/{pollId}/votes +export async function postVoterPollInfo( + pollId: number, + voterInfo: VoterPollInfo, +): Promise { + const requestUrl: string = ApiUrl.poll; + const response = await request( + 'POST', + requestUrl + `/${pollId}/votes`, + voterInfo, + null, + ); + + return response.data; +} diff --git a/src/apis/endpoints.ts b/src/apis/endpoints.ts index 95241a7c..7589d94c 100644 --- a/src/apis/endpoints.ts +++ b/src/apis/endpoints.ts @@ -10,4 +10,5 @@ export const ApiUrl = { table: makeUrl('/table'), parliamentary: makeUrl('/table/parliamentary'), customize: makeUrl('/table/customize'), + poll: makeUrl('/polls'), }; diff --git a/src/apis/responses/poll.ts b/src/apis/responses/poll.ts new file mode 100644 index 00000000..01295050 --- /dev/null +++ b/src/apis/responses/poll.ts @@ -0,0 +1,28 @@ +import { BasePollInfo, PollInfo, VoterPollInfo } from '../../type/type'; + +// POST /api/polls/{tableId} +export interface PostPollResponseType extends BasePollInfo { + id: number; +} + +// GET /api/polls/{pollId} +export interface GetPollResponseType extends PollInfo { + id: number; +} + +// PATCH /api/polls/{pollId} +export interface PatchPollResponseType extends PollInfo { + id: number; +} + +// GET /api/polls/{pollId}/votes +export interface GetVoterPollInfoResponseType extends PollInfo { + id: number; + participateCode: string; +} + +// POST /api/polls/{pollId}/votes + +export interface PostVoterPollInfoResponseType extends VoterPollInfo { + id: number; +} diff --git a/src/assets/debateEnd/crown.svg b/src/assets/debateEnd/crown.svg new file mode 100644 index 00000000..4c356156 --- /dev/null +++ b/src/assets/debateEnd/crown.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/icons/CheckBox.tsx b/src/components/icons/CheckBox.tsx new file mode 100644 index 00000000..14fb5712 --- /dev/null +++ b/src/components/icons/CheckBox.tsx @@ -0,0 +1,37 @@ +import DTCheck from './Check'; +import clsx from 'clsx'; + +interface CheckBoxProps { + checked?: boolean; + size?: number | string; + className?: string; +} + +/** + *text-* 클래스가 포함되면 자동으로 DTCheck로 전달 + */ +export default function CheckBox({ + checked = false, + size = 24, + className = '', +}: CheckBoxProps) { + // text-로 시작하는 클래스만 추출 + const textClass = + className.split(' ').find((c) => c.startsWith('text-')) ?? ''; + + return ( +
+ {checked && } +
+ ); +} diff --git a/src/hooks/mutations/useCreatePoll.ts b/src/hooks/mutations/useCreatePoll.ts new file mode 100644 index 00000000..173adbee --- /dev/null +++ b/src/hooks/mutations/useCreatePoll.ts @@ -0,0 +1,12 @@ +import { postPoll } from '../../apis/apis/poll'; +import { PostPollResponseType } from '../../apis/responses/poll'; +import { usePreventDuplicateMutation } from './usePreventDuplicateMutation'; + +export default function usePostPoll(onSuccess: (id: number) => void) { + return usePreventDuplicateMutation({ + mutationFn: (id: number) => postPoll(id), + onSuccess: (response: PostPollResponseType) => { + onSuccess(response.id); + }, + }); +} diff --git a/src/hooks/mutations/useFetchEndPoll.ts b/src/hooks/mutations/useFetchEndPoll.ts new file mode 100644 index 00000000..53574760 --- /dev/null +++ b/src/hooks/mutations/useFetchEndPoll.ts @@ -0,0 +1,12 @@ +import { patchEndPoll } from '../../apis/apis/poll'; +import { PatchPollResponseType } from '../../apis/responses/poll'; +import { usePreventDuplicateMutation } from './usePreventDuplicateMutation'; + +export default function useFetchEndPoll(onSuccess: (id: number) => void) { + return usePreventDuplicateMutation({ + mutationFn: (pollId: number) => patchEndPoll(pollId), + onSuccess: (response: PatchPollResponseType) => { + onSuccess(response.id); + }, + }); +} diff --git a/src/hooks/mutations/usePostVoterPollInfo.ts b/src/hooks/mutations/usePostVoterPollInfo.ts new file mode 100644 index 00000000..3e19e5e6 --- /dev/null +++ b/src/hooks/mutations/usePostVoterPollInfo.ts @@ -0,0 +1,18 @@ +import { postVoterPollInfo } from '../../apis/apis/poll'; +import { VoterPollInfo } from '../../type/type'; +import { usePreventDuplicateMutation } from './usePreventDuplicateMutation'; + +export default function usePostVoterPollInfo(onSuccess: () => void) { + return usePreventDuplicateMutation({ + mutationFn: ({ + pollId, + voterInfo, + }: { + pollId: number; + voterInfo: VoterPollInfo; + }) => postVoterPollInfo(pollId, voterInfo), + onSuccess: () => { + onSuccess(); + }, + }); +} diff --git a/src/hooks/query/useGetPollInfo.ts b/src/hooks/query/useGetPollInfo.ts new file mode 100644 index 00000000..ab5948c1 --- /dev/null +++ b/src/hooks/query/useGetPollInfo.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@tanstack/react-query'; +import { getPollInfo } from '../../apis/apis/poll'; +import { GetPollResponseType } from '../../apis/responses/poll'; + +export function useGetPollInfo( + pollId: number, + options?: { refetchInterval?: number | false; enabled?: boolean }, +) { + return useQuery({ + queryKey: ['Poll', pollId], + queryFn: () => getPollInfo(pollId), + refetchInterval: options?.refetchInterval ?? false, + enabled: options?.enabled, + }); +} diff --git a/src/hooks/query/useGetVoterPollInfo.ts b/src/hooks/query/useGetVoterPollInfo.ts new file mode 100644 index 00000000..d10efbd5 --- /dev/null +++ b/src/hooks/query/useGetVoterPollInfo.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query'; +import { getVoterPollInfo } from '../../apis/apis/poll'; +import { GetVoterPollInfoResponseType } from '../../apis/responses/poll'; + +export function useGetVoterPollInfo( + pollId: number, + options?: { enabled?: boolean }, +) { + return useQuery({ + queryKey: ['VoterPoll', pollId], + queryFn: () => getVoterPollInfo(pollId), + enabled: options?.enabled, + }); +} diff --git a/src/layout/components/footer/StickyFooterWrapper.tsx b/src/layout/components/footer/StickyFooterWrapper.tsx index 3a52d507..4fdbb240 100644 --- a/src/layout/components/footer/StickyFooterWrapper.tsx +++ b/src/layout/components/footer/StickyFooterWrapper.tsx @@ -4,7 +4,7 @@ export default function StickyFooterWrapper(props: PropsWithChildren) { const { children } = props; return ( -