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 (
+
+
+ 치삐의 지식나무 숲 레벨
+
+
+
+ {rows.map((row) => (
+ -
+
+
+
+
+
+
+
+ {row.name}
+
+
+
+
+ {row.rangeLabel}
+
+
+
+
+ ))}
+
+
+
+
+ 정보를 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 @@
+