diff --git a/apps/client/src/pages/level/Level.tsx b/apps/client/src/pages/level/Level.tsx index 513eb1a7..572d7809 100644 --- a/apps/client/src/pages/level/Level.tsx +++ b/apps/client/src/pages/level/Level.tsx @@ -1,5 +1,5 @@ const Level = () => { - return
Level
; + return
; }; export default Level; diff --git a/apps/client/src/pages/level/components/LevelInfoCard.tsx b/apps/client/src/pages/level/components/LevelInfoCard.tsx new file mode 100644 index 00000000..324dc1e6 --- /dev/null +++ b/apps/client/src/pages/level/components/LevelInfoCard.tsx @@ -0,0 +1,68 @@ +import { cn } from '@pinback/design-system/utils'; +import { Level } from '@pinback/design-system/ui'; +import { Icon, type IconName } from '@pinback/design-system/icons'; +import { TREE_LEVEL_TABLE, type TreeLevel } from '../utils/treeLevel'; + +const LEVEL_TOOLTIP_ICON = { + 1: 'tooltip_1', + 2: 'tooltip_2', + 3: 'tooltip_3', + 4: 'tooltip_4', + 5: 'tooltip_5', +} as const satisfies Record; + +export default function LevelInfoCard() { + const rows = [...TREE_LEVEL_TABLE].reverse(); + + return ( +
+

+ 치삐의 지식나무 숲 레벨 +

+ + + +
+

+ 정보를 1분 동안 읽고 도토리를 모아보세요. 치삐를 행복하게 만들 수 + 있어요. +

+
+
+ ); +} diff --git a/apps/client/src/pages/level/components/TreeStatusCard.tsx b/apps/client/src/pages/level/components/TreeStatusCard.tsx new file mode 100644 index 00000000..16ee63ec --- /dev/null +++ b/apps/client/src/pages/level/components/TreeStatusCard.tsx @@ -0,0 +1,38 @@ +import { Level, Progress } from '@pinback/design-system/ui'; +import { cn } from '@pinback/design-system/utils'; +import { getTreeLevel } from '../utils/treeLevel'; + +export interface TreeStatusCardProps { + acorns: number; +} + +export default function TreeStatusCard({ acorns }: TreeStatusCardProps) { + const info = getTreeLevel(acorns); + + const barPercent = Math.min(100, info.level * 20); + + return ( +
+
+ {barPercent}% +
+ +
+ {info.name} + +
+ +
+ +
+
+ ); +} diff --git a/apps/client/src/pages/level/utils/treeLevel.ts b/apps/client/src/pages/level/utils/treeLevel.ts new file mode 100644 index 00000000..44541475 --- /dev/null +++ b/apps/client/src/pages/level/utils/treeLevel.ts @@ -0,0 +1,50 @@ +type TreeLevelRowShape = { + level: number; + name: string; + min: number; + max?: number; + rangeLabel: string; +}; + +export const TREE_LEVEL_TABLE = [ + { level: 1, name: '잊힌 기록의 숲', min: 0, max: 0, rangeLabel: '0개' }, + { level: 2, name: '햇살의 터전', min: 1, max: 2, rangeLabel: '1–2개' }, + { level: 3, name: '기록의 오솔길', min: 3, max: 4, rangeLabel: '3–4개' }, + { level: 4, name: '지식 나무 언덕', min: 5, max: 6, rangeLabel: '5–6개' }, + { level: 5, name: '도토리 만개 숲', min: 7, rangeLabel: '7개 이상' }, +] as const satisfies readonly TreeLevelRowShape[]; + +export type TreeLevel = (typeof TREE_LEVEL_TABLE)[number]['level']; +export type TreeLevelRow = TreeLevelRowShape; + +export type TreeLevelResult = TreeLevelRow & { + progressToNext: number; + nextMin?: number; + remainingToNext?: number; +}; + +function findLevelRow(count: number, rows: readonly TreeLevelRow[]) { + const idx = rows.findIndex( + (r) => count >= r.min && (r.max === undefined || count <= r.max) + ); + const i = idx === -1 ? 0 : idx; + return { row: rows[i], next: rows[i + 1] as TreeLevelRow | undefined }; +} + +function calcProgress(count: number, row: TreeLevelRow, next?: TreeLevelRow) { + if (!next) + return { + progressToNext: 1 as const, + nextMin: undefined, + remainingToNext: undefined, + }; + const span = Math.max(1, next.min - row.min); + const progressToNext = Math.min(1, (count - row.min) / span); + const remainingToNext = Math.max(0, next.min - count); + return { progressToNext, nextMin: next.min, remainingToNext }; +} + +export function getTreeLevel(acorns: number): TreeLevelResult { + const { row, next } = findLevelRow(acorns, TREE_LEVEL_TABLE); + return { ...row, ...calcProgress(acorns, row, next) }; +} diff --git a/packages/design-system/src/icons/iconNames.ts b/packages/design-system/src/icons/iconNames.ts index 0d3f7d27..80a49c25 100644 --- a/packages/design-system/src/icons/iconNames.ts +++ b/packages/design-system/src/icons/iconNames.ts @@ -11,5 +11,10 @@ export const iconNames = [ 'ic_details_disable', 'ic_info', 'ic_plus', + 'tooltip_1', + 'tooltip_2', + 'tooltip_3', + 'tooltip_4', + 'tooltip_5', ] as const; export type IconName = (typeof iconNames)[number]; diff --git a/packages/design-system/src/icons/index.ts b/packages/design-system/src/icons/index.ts index 6888b4be..7d528256 100644 --- a/packages/design-system/src/icons/index.ts +++ b/packages/design-system/src/icons/index.ts @@ -1 +1,2 @@ export { Icon } from './components/icon'; +export type { IconName } from './iconNames'; diff --git a/packages/design-system/src/icons/source/tooltip_1.svg b/packages/design-system/src/icons/source/tooltip_1.svg new file mode 100644 index 00000000..a7171254 --- /dev/null +++ b/packages/design-system/src/icons/source/tooltip_1.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/source/tooltip_2.svg b/packages/design-system/src/icons/source/tooltip_2.svg new file mode 100644 index 00000000..db08ea53 --- /dev/null +++ b/packages/design-system/src/icons/source/tooltip_2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/source/tooltip_3.svg b/packages/design-system/src/icons/source/tooltip_3.svg new file mode 100644 index 00000000..36c4b050 --- /dev/null +++ b/packages/design-system/src/icons/source/tooltip_3.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/source/tooltip_4.svg b/packages/design-system/src/icons/source/tooltip_4.svg new file mode 100644 index 00000000..48318629 --- /dev/null +++ b/packages/design-system/src/icons/source/tooltip_4.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/source/tooltip_5.svg b/packages/design-system/src/icons/source/tooltip_5.svg new file mode 100644 index 00000000..feddcb3f --- /dev/null +++ b/packages/design-system/src/icons/source/tooltip_5.svg @@ -0,0 +1,10 @@ + + + + + + + + + +