diff --git a/src/features/write/components/editor.tsx b/src/features/write/components/editor.tsx
index 24043f51..2edae873 100644
--- a/src/features/write/components/editor.tsx
+++ b/src/features/write/components/editor.tsx
@@ -25,8 +25,10 @@ export default function Editor({ initialContent, handleContentChange }: EditorPr
manager={manager}
autoRender="end"
initialContent={initialContent}
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
- onChange={({ helpers, state }) => handleContentChange(helpers.getMarkdown(state))}
+ onChange={({ helpers, state }) =>
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ handleContentChange(helpers.getMarkdown && helpers.getMarkdown(state))
+ }
placeholder="본문을 작성해보세요!"
classNames={[
css`
diff --git a/src/requests/directory/hooks.ts b/src/requests/directory/hooks.ts
index cbede863..6218ec47 100644
--- a/src/requests/directory/hooks.ts
+++ b/src/requests/directory/hooks.ts
@@ -50,7 +50,7 @@ export const useCreateDirectory = () => {
const optimisticDirectory = {
id: -1,
...newDirectory,
- tag: '',
+ tag: 'NORMAL' as Directory.Item['tag'],
documentCount: 0,
}
diff --git a/src/requests/quiz/client.tsx b/src/requests/quiz/client.tsx
index cd6acb9d..0fc1cbbe 100644
--- a/src/requests/quiz/client.tsx
+++ b/src/requests/quiz/client.tsx
@@ -129,3 +129,14 @@ export const collectionQuizzesInfo = async ({ collectionId }: { collectionId: nu
throw error
}
}
+
+export const getQuizRecords = async () => {
+ try {
+ const { data } = await http.get
(
+ API_ENDPOINTS.QUIZ.GET.ALL_RECORDS
+ )
+ return data
+ } catch (error: unknown) {
+ throw error
+ }
+}
diff --git a/src/requests/quiz/server.tsx b/src/requests/quiz/server.tsx
index e9635968..8b78be3a 100644
--- a/src/requests/quiz/server.tsx
+++ b/src/requests/quiz/server.tsx
@@ -35,3 +35,31 @@ export const getQuizSetById = async ({
throw error
}
}
+
+export const getQuizRecords = async () => {
+ try {
+ const { data } = await httpServer.get(
+ API_ENDPOINTS.QUIZ.GET.ALL_RECORDS
+ )
+ return data
+ } catch (error: unknown) {
+ throw error
+ }
+}
+
+export const getQuizDetailRecord = async ({
+ quizSetId,
+ quizSetType,
+}: {
+ quizSetId: string
+ quizSetType: Quiz.Set.Type
+}) => {
+ try {
+ const { data } = await httpServer.get(
+ API_ENDPOINTS.QUIZ.GET.RECORD(quizSetId, quizSetType)
+ )
+ return data
+ } catch (error: unknown) {
+ throw error
+ }
+}
diff --git a/src/shared/components/custom/icon/svg-components.tsx b/src/shared/components/custom/icon/svg-components.tsx
index 914d1d9c..2b4f94da 100644
--- a/src/shared/components/custom/icon/svg-components.tsx
+++ b/src/shared/components/custom/icon/svg-components.tsx
@@ -2533,3 +2533,74 @@ export function SpeechBubbleColor({ ...props }) {
)
}
+
+// 퀴즈 기록 아이콘
+export const DocumentTypeRoundIcon = ({ ...props }) => {
+ return (
+
+ )
+}
+
+export const TodayQuizTypeRoundIcon = ({ ...props }) => {
+ return (
+
+ )
+}
+
+export const CollectionTypeRoundIcon = ({ ...props }) => {
+ const clipId = useId()
+
+ return (
+
+ )
+}
diff --git a/src/shared/components/ui/calendar.tsx b/src/shared/components/ui/calendar.tsx
new file mode 100644
index 00000000..d0612e43
--- /dev/null
+++ b/src/shared/components/ui/calendar.tsx
@@ -0,0 +1,93 @@
+'use client'
+
+import * as React from 'react'
+import { DayPicker } from 'react-day-picker'
+
+import { cn } from '@/shared/lib/utils'
+import Icon from '../custom/icon'
+import { isSameMonth } from 'date-fns'
+
+export type CalendarProps = React.ComponentProps & {
+ today: Date
+ selectedMonth: Date
+}
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = false,
+ selectedMonth,
+ today,
+ ...props
+}: CalendarProps) {
+ const [month, setMonth] = React.useState(selectedMonth || today)
+ const isCurrentMonth = (month: Date) => isSameMonth(month, today)
+
+ return (
+ ,
+ IconRight: ({ ...props }) => (
+
+ ),
+ }}
+ {...props}
+ />
+ )
+}
+Calendar.displayName = 'Calendar'
+
+export { Calendar }
diff --git a/src/shared/hooks/use-check-list-ignore-ids.ts b/src/shared/hooks/use-check-list-ignore-ids.ts
index 0bbff64e..f7070a08 100644
--- a/src/shared/hooks/use-check-list-ignore-ids.ts
+++ b/src/shared/hooks/use-check-list-ignore-ids.ts
@@ -56,9 +56,9 @@ export function useCheckListIgnoreIds(
if (idx > -1) {
const item = listRef.current[idx]
- if (item.checked !== checked) {
+ if (item?.checked !== checked) {
const arr = [...listRef.current]
- arr[idx] = { ...item, id, checked }
+ arr[idx] = { ...item, id, checked } as T
set(arr)
}
}
diff --git a/src/shared/hooks/use-check-list.ts b/src/shared/hooks/use-check-list.ts
index 9fac3b40..07792ea9 100644
--- a/src/shared/hooks/use-check-list.ts
+++ b/src/shared/hooks/use-check-list.ts
@@ -41,9 +41,9 @@ export function useCheckList(initialItems: T[]) {
if (idx > -1) {
const item = listRef.current[idx]
- if (item.checked !== checked) {
+ if (item?.checked !== checked) {
const arr = [...listRef.current]
- arr[idx] = { ...item, checked }
+ arr[idx] = { ...item, checked } as T
set(arr)
}
}
diff --git a/src/shared/lib/tanstack-query/query-keys.ts b/src/shared/lib/tanstack-query/query-keys.ts
index de5d375c..90c23434 100644
--- a/src/shared/lib/tanstack-query/query-keys.ts
+++ b/src/shared/lib/tanstack-query/query-keys.ts
@@ -38,6 +38,10 @@ export const queries = createQueryKeyStore({
queryFn: () => REQUEST.quiz.fetchDocumentQuizzes(params),
enabled: !!params.documentId,
}),
+ allRecords: () => ({
+ queryKey: [''],
+ queryFn: () => REQUEST.quiz.getQuizRecords(),
+ }),
setRecord: (params: { quizSetId: string; quizSetType: Quiz.Set.Type }) => ({
queryKey: [params],
queryFn: () => REQUEST.quiz.fetchQuizSetRecord(params),
diff --git a/src/shared/utils/date.ts b/src/shared/utils/date.ts
index e7bed85c..049d1b4e 100644
--- a/src/shared/utils/date.ts
+++ b/src/shared/utils/date.ts
@@ -124,3 +124,13 @@ export function getCurrentTime() {
return `${hours}:${minutes}`
}
+
+// YYYY-MM-DD 형식으로 반환
+export const getFormattedDate = (date: Date) => {
+ const formattedDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
+ 2,
+ '0'
+ )}-${String(date.getDate()).padStart(2, '0')}`
+
+ return formattedDate
+}
diff --git a/src/shared/utils/time.ts b/src/shared/utils/time.ts
index c438d97a..f8a00543 100644
--- a/src/shared/utils/time.ts
+++ b/src/shared/utils/time.ts
@@ -14,6 +14,17 @@ export const msToMin = (ms: number) => {
return minutes
}
+export const msToElapsedTimeKorean = (ms: number) => {
+ const totalSeconds = Math.floor(ms / 1000)
+ const hours = Math.floor(totalSeconds / 3600)
+ const minutes = Math.floor((totalSeconds % 3600) / 60)
+ const seconds = Math.floor(totalSeconds % 60)
+
+ return [hours && `${hours}시간`, minutes && `${minutes}분`, seconds && `${seconds}초`]
+ .filter((value) => value)
+ .join(' ')
+}
+
export const getTimeUntilMidnight = () => {
const now = new Date()
const midnight = new Date(now)
diff --git a/src/types/schema.d.ts b/src/types/schema.d.ts
index 53b55bf2..dc8f29b8 100644
--- a/src/types/schema.d.ts
+++ b/src/types/schema.d.ts
@@ -1589,7 +1589,7 @@ export interface components {
UpdateQuizResultQuizDto: {
/** Format: int64 */
id?: number
- answer: boolean
+ answer?: boolean
choseAnswer?: string
/** Format: int32 */
elapsedTime?: number
@@ -1695,9 +1695,13 @@ export interface components {
maxConsecutiveDays?: number
}
GetSingleQuizSetRecordDto: {
+ /** Format: int64 */
+ id?: number
question?: string
answer?: string
explanation?: string
+ /** @enum {string} */
+ quizType?: 'MIX_UP' | 'MULTIPLE_CHOICE'
options?: string[]
choseAnswer?: string
documentName?: string
@@ -1730,14 +1734,17 @@ export interface components {
| 'DOCUMENT_QUIZ_SET'
| 'COLLECTION_QUIZ_SET'
| 'FIRST_QUIZ_SET'
- /** Format: date-time */
- solvedDate?: string
}
GetQuizRecordResponse: {
/** Format: int32 */
currentConsecutiveDays?: number
/** Format: int32 */
maxConsecutiveDays?: number
+ quizRecords?: components['schemas']['GetQuizRecordSolvedDateDto'][]
+ }
+ GetQuizRecordSolvedDateDto: {
+ /** Format: date */
+ solvedDate?: string
quizRecords?: components['schemas']['GetQuizRecordDto'][]
}
GetQuizSetDirectoryDto: {
@@ -1923,8 +1930,8 @@ export interface components {
is3xxRedirection?: boolean
}
JspConfigDescriptor: {
- jspPropertyGroups?: components['schemas']['JspPropertyGroupDescriptor'][]
taglibs?: components['schemas']['TaglibDescriptor'][]
+ jspPropertyGroups?: components['schemas']['JspPropertyGroupDescriptor'][]
}
JspPropertyGroupDescriptor: {
defaultContentType?: string
@@ -1933,11 +1940,11 @@ export interface components {
errorOnELNotFound?: string
pageEncoding?: string
scriptingInvalid?: string
+ isXml?: string
includePreludes?: string[]
includeCodas?: string[]
trimDirectiveWhitespaces?: string
errorOnUndeclaredNamespace?: string
- isXml?: string
buffer?: string
urlPatterns?: string[]
}
@@ -1964,13 +1971,13 @@ export interface components {
hosts?: string[]
redirectView?: boolean
propagateQueryProperties?: boolean
- attributesCSV?: string
attributesMap?: {
[key: string]: Record
}
attributes?: {
[key: string]: string
}
+ attributesCSV?: string
}
ServletContext: {
sessionCookieConfig?: components['schemas']['SessionCookieConfig']
@@ -2011,10 +2018,10 @@ export interface components {
effectiveMinorVersion?: number
serverInfo?: string
servletContextName?: string
+ defaultSessionTrackingModes?: ('COOKIE' | 'URL' | 'SSL')[]
filterRegistrations?: {
[key: string]: components['schemas']['FilterRegistration']
}
- defaultSessionTrackingModes?: ('COOKIE' | 'URL' | 'SSL')[]
effectiveSessionTrackingModes?: ('COOKIE' | 'URL' | 'SSL')[]
jspConfigDescriptor?: components['schemas']['JspConfigDescriptor']
requestCharacterEncoding?: string