diff --git a/apps/client/src/layout/Layout.tsx b/apps/client/src/layout/Layout.tsx
index 1ce2359a..0b9a39b3 100644
--- a/apps/client/src/layout/Layout.tsx
+++ b/apps/client/src/layout/Layout.tsx
@@ -4,11 +4,12 @@ import { Sidebar } from '../shared/components/sidebar/Sidebar';
const Layout = () => {
return (
<>
- {/* TODO: 필요시 레이아웃 추가 */}
- {/* TODO: 사이드바 추가 */}
-
+
-
+
+
+
+
>
);
};
diff --git a/apps/client/src/pages/level/Level.tsx b/apps/client/src/pages/level/Level.tsx
index f627dfeb..77debd4d 100644
--- a/apps/client/src/pages/level/Level.tsx
+++ b/apps/client/src/pages/level/Level.tsx
@@ -42,7 +42,7 @@ export default function Level() {
-
+
diff --git a/apps/client/src/pages/myBookmark/MyBookmark.tsx b/apps/client/src/pages/myBookmark/MyBookmark.tsx
index eb262445..a3e1ba7c 100644
--- a/apps/client/src/pages/myBookmark/MyBookmark.tsx
+++ b/apps/client/src/pages/myBookmark/MyBookmark.tsx
@@ -1,5 +1,47 @@
+import { REMIND_MOCK_DATA } from "@pages/remind/constants";
+import { Badge, Card } from "@pinback/design-system/ui";
+import { useState } from "react";
+
const MyBookmark = () => {
- return MyBookmark
;
+ const [activeBadge, setActiveBadge] = useState('all');
+
+ const handleBadgeClick = (badgeType: string) => {
+ setActiveBadge(badgeType);
+ };
+
+ return (
+
+
나의 북마크
+
+ handleBadgeClick('all')}
+ isActive={activeBadge === 'all'}
+ />
+ handleBadgeClick('notRead')}
+ isActive={activeBadge === 'notRead'}
+ />
+
+
+
+ {/* TODO: API 연결 후 수정 */}
+ {REMIND_MOCK_DATA.map((data) => (
+
+ ))}
+
+
+ );
};
export default MyBookmark;
diff --git a/apps/client/src/pages/remind/Remind.tsx b/apps/client/src/pages/remind/Remind.tsx
index ed39e0e9..3aff4818 100644
--- a/apps/client/src/pages/remind/Remind.tsx
+++ b/apps/client/src/pages/remind/Remind.tsx
@@ -1,5 +1,47 @@
+import { Badge, Card } from '@pinback/design-system/ui';
+import { useState } from 'react';
+import { REMIND_MOCK_DATA } from './constants';
+
const Remind = () => {
- return Remind
;
+ const [activeBadge, setActiveBadge] = useState('notRead');
+
+ const handleBadgeClick = (badgeType: string) => {
+ setActiveBadge(badgeType);
+ };
+
+ return (
+
+
리마인드
+
+ handleBadgeClick('notRead')}
+ isActive={activeBadge === 'notRead'}
+ />
+ handleBadgeClick('read')}
+ isActive={activeBadge === 'read'}
+ />
+
+
+
+ {/* TODO: API 연결 후 수정 */}
+ {REMIND_MOCK_DATA.map((data) => (
+
+ ))}
+
+
+ );
};
export default Remind;
diff --git a/apps/client/src/pages/remind/constants/index.ts b/apps/client/src/pages/remind/constants/index.ts
new file mode 100644
index 00000000..8850f894
--- /dev/null
+++ b/apps/client/src/pages/remind/constants/index.ts
@@ -0,0 +1,114 @@
+export const REMIND_MOCK_DATA = [
+ {
+ id: 1,
+ title: '리액트 쿼리 강의 듣기',
+ content: '1강부터 5강까지 완강하고 복습하기',
+ timeRemaining: '1시간 30분',
+ category: '공부',
+ },
+ {
+ id: 2,
+ title: '장보기',
+ content: '우유, 계란, 채소 구매',
+ timeRemaining: '3시간 00분',
+ category: '생활',
+ },
+ {
+ id: 3,
+ title: '프로젝트 기획서 초안 작성',
+ content: '주요 기능 및 화면 흐름 정리',
+ timeRemaining: '완료',
+ category: '업무',
+ },
+ {
+ id: 4,
+ title: '헬스장 가기',
+ content: '하체 운동 루틴 진행',
+ timeRemaining: '5시간 10분',
+ category: '운동',
+ },
+ {
+ id: 5,
+ title: '주간 회고 작성',
+ content: '이번 주에 배운 점과 개선할 점 정리',
+ timeRemaining: '완료',
+ category: '자기계발',
+ },
+ {
+ id: 6,
+ title: '저녁 약속',
+ content: '강남역 2번 출구에서 친구 만나기',
+ timeRemaining: '8시간 00분',
+ category: '약속',
+ },
+ {
+ id: 7,
+ title: '알고리즘 문제 풀기',
+ content: '백준 골드 문제 2개 풀기',
+ timeRemaining: '2시간 45분',
+ category: '공부',
+ },
+ {
+ id: 8,
+ title: '이메일 회신',
+ content: 'A사에서 온 제휴 문의 메일 확인하고 회신',
+ timeRemaining: '완료',
+ category: '업무',
+ },
+ {
+ id: 9,
+ title: '방 청소하기',
+ content: '책상 정리 및 바닥 청소기 돌리기',
+ timeRemaining: '0시간 50분',
+ category: '생활',
+ },
+ {
+ id: 10,
+ title: 'TypeScript 스터디 준비',
+ content: '제네릭 파트 발표 자료 만들기',
+ timeRemaining: '10시간 00분',
+ category: '공부',
+ },
+ {
+ id: 11,
+ title: '은행 업무 보기',
+ content: '만기 된 예금 재예치하기',
+ timeRemaining: '완료',
+ category: '금융',
+ },
+ {
+ id: 12,
+ title: '블로그 글 작성',
+ content: '이번 주에 해결한 기술적 문제에 대해 포스팅하기',
+ timeRemaining: '1일 2시간',
+ category: '자기계발',
+ },
+ {
+ id: 13,
+ title: '가족과 통화하기',
+ content: '부모님께 안부 전화드리기',
+ timeRemaining: '4시간 20분',
+ category: '약속',
+ },
+ {
+ id: 14,
+ title: '주말 산책 계획',
+ content: '가까운 공원 산책 코스 찾아보기',
+ timeRemaining: '2일 0시간',
+ category: '여가',
+ },
+ {
+ id: 15,
+ title: '팀 회의록 정리',
+ content: '어제 진행된 스프린트 계획 회의 내용 정리해서 공유',
+ timeRemaining: '완료',
+ category: '업무',
+ },
+ {
+ id: 16,
+ title: '팀 회의록 정리',
+ content: '어제 진행된 스프린트 계획 회의 내용 정리해서 공유',
+ timeRemaining: '완료',
+ category: '업무',
+ },
+];
diff --git a/packages/design-system/src/components/badge/Badge.stories.tsx b/packages/design-system/src/components/badge/Badge.stories.tsx
index f005d292..d9a17129 100644
--- a/packages/design-system/src/components/badge/Badge.stories.tsx
+++ b/packages/design-system/src/components/badge/Badge.stories.tsx
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
-import { within, userEvent } from '@storybook/test';
+import { within, userEvent, fn } from '@storybook/test';
import Badge from './Badge';
+import { useState } from 'react';
const meta: Meta = {
title: 'Components/Badge',
@@ -9,6 +10,8 @@ const meta: Meta = {
args: {
text: '알림',
countNum: 3,
+ onClick: () => alert('onClick 실행'),
+ isActive: true,
},
argTypes: {
text: { control: 'text', description: '뱃지 라벨 텍스트' },
@@ -16,6 +19,18 @@ const meta: Meta = {
control: { type: 'number', min: 0 },
description: '카운트 숫자(옵션)',
},
+
+ isActive: {
+ control: 'boolean',
+ description: '활성화 여부',
+ table: {
+ type: { summary: 'boolean' },
+ },
+ },
+ onClick: {
+ action: 'clicked',
+ description: '뱃지 클릭 시 호출되는 콜백 함수(옵션)',
+ },
},
parameters: {
docs: {
@@ -33,22 +48,44 @@ type Story = StoryObj;
export const Default: Story = {};
export const NoCount: Story = {
- args: { text: '카운트 없음', countNum: undefined },
+ args: { text: '카운트 없음', countNum: 0, isActive: false },
};
export const LargeCount: Story = {
args: { text: '메시지', countNum: 12000 },
};
-export const Clicked: Story = {
- args: { text: '클릭해줘', countNum: 5 },
+export const ActiveBadge: Story = {
+ render: (args) => {
+ const ClickedBadge = () => {
+ const [isActive, setIsActive] = useState(false);
+
+ return (
+ {
+ setIsActive((prev) => !prev); // 토글
+ args.onClick?.(); // 액션 로그
+ }}
+ />
+ );
+ };
+
+ return ;
+ },
+ args: {
+ text: '클릭해줘',
+ countNum: 5,
+ onClick: fn(),
+ },
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
await userEvent.click(await canvas.findByText(String(args.text)));
},
parameters: {
docs: {
- description: { story: '로드 직후 자동 클릭으로 활성 상태 미리보기.' },
+ description: { story: 'onClick으로 뱃지 활/비활성화' },
},
},
};
diff --git a/packages/design-system/src/components/badge/Badge.tsx b/packages/design-system/src/components/badge/Badge.tsx
index 9418c6c6..54442b4b 100644
--- a/packages/design-system/src/components/badge/Badge.tsx
+++ b/packages/design-system/src/components/badge/Badge.tsx
@@ -1,43 +1,50 @@
import { cva } from 'class-variance-authority';
-import { useState } from 'react';
export interface BadgeProps {
text: string;
countNum?: number;
+ isActive: boolean;
+ onClick?: () => void;
}
+
const BadgeTxtStyleVariants = cva('sub3-b', {
variants: {
- click: {
+ active: {
true: 'text-font-black-1',
false: 'text-font-ltgray-4',
} as const,
},
defaultVariants: {
- click: false,
+ active: false,
},
});
+
const BadgeStyleVariants = cva(
'text-white-bg sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]',
{
variants: {
- click: {
+ active: {
true: 'bg-main500',
false: 'bg-gray300',
} as const,
},
defaultVariants: {
- click: false,
+ active: false,
},
}
);
-const Badge = ({ text, countNum }: BadgeProps) => {
- const [isClick, setIsClick] = useState(false);
+
+const Badge = ({ text, countNum, isActive, onClick }: BadgeProps) => {
return (
setIsClick(true)}
+ onClick={onClick}
>
- {text}
- {countNum}
+
+ {text}
+
+
+ {countNum}
+
);
};
diff --git a/packages/tailwind-config/shared-styles.css b/packages/tailwind-config/shared-styles.css
index 2d2dfd8d..03e77c0a 100644
--- a/packages/tailwind-config/shared-styles.css
+++ b/packages/tailwind-config/shared-styles.css
@@ -158,6 +158,16 @@
box-shadow: 0px 0px 32px 0px rgba(0, 0, 0, 0.1);
}
+.scrollbar-hide::-webkit-scrollbar {
+ display: none;
+}
+
+/* 기타 브라우저용 (IE, Edge 구버전, Firefox) */
+.scrollbar-hide {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
+
@theme {
--color-*: initial;