+
프로필 설정
@@ -87,7 +81,7 @@ export default function OnboardingForm() {
placeholder="이름"
value={name}
onChange={(e) => setName(e.target.value)}
- className="w-full border p-2 rounded bg-white dark:bg-gray-800 dark:text-white"
+ className="w-full border p-2 rounded bg-surface dark:bg-dark-surface dark:text-label-invert"
/>
닉네임
setNickname(e.target.value)}
- className="w-full border p-2 rounded bg-white dark:bg-gray-800 dark:text-white"
+ className="w-full border p-2 rounded bg-surface dark:bg-dark-surface dark:text-label-invert"
/>
-
+
닉네임은 영어 알파벳과 숫자, 언더스코어(_)만 사용할 수 있습니다.
자기소개
@@ -106,9 +100,8 @@ export default function OnboardingForm() {
placeholder="자기소개 (선택)"
value={bio}
onChange={(e) => setBio(e.target.value)}
- className="w-full border p-2 rounded bg-white dark:bg-gray-800 dark:text-white resize-none"
+ className="w-full border p-2 rounded bg-surface dark:bg-dark-surface dark:text-label-invert resize-none"
/>
-
diff --git a/app/protected/dashboard/_components/InProgressBooksSection.tsx b/app/protected/dashboard/_components/InProgressBooksSection.tsx
index fd0467f..3ea15a9 100644
--- a/app/protected/dashboard/_components/InProgressBooksSection.tsx
+++ b/app/protected/dashboard/_components/InProgressBooksSection.tsx
@@ -1,7 +1,6 @@
'use client';
import Link from 'next/link';
-
import { MyBook } from '@/types/book';
import Card from '@/components/ui/Card';
@@ -22,20 +21,22 @@ export function InProgressBooksSection({ myBooks }: Props) {
return (
- 📚 진행 중인 책
+ 📚 진행 중인 책
{books.map((book, idx) => (
- {book.title}
+
+ {book.title}
+
{book.author && (
- {book.author}
+ {book.author}
)}
-
+
📈 진행률: {book.progress}%
{book.started_at && (
-
+
등록일: {new Date(book.started_at).toLocaleDateString()}
)}
diff --git a/app/protected/dashboard/_components/SocialFeedItem.tsx b/app/protected/dashboard/_components/SocialFeedItem.tsx
index d375db9..a607aec 100644
--- a/app/protected/dashboard/_components/SocialFeedItem.tsx
+++ b/app/protected/dashboard/_components/SocialFeedItem.tsx
@@ -17,7 +17,6 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt
const router = useRouter();
const supabase = createSupabaseClient();
- // 초기 좋아요 상태 (entry.initialLiked 등 이미 prop으로 넘어온다고 가정하거나, 필요시 fetch)
const [isLiked, setIsLiked] = useState(initialLiked);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isLikeLoading, setIsLikeLoading] = useState(false);
@@ -41,7 +40,6 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [isMenuOpen]);
- // 실제 좋아요 처리 로직
const handleLike = async (e: React.MouseEvent) => {
e.preventDefault();
if (isLikeLoading) return;
@@ -54,21 +52,17 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt
return;
}
- // 1. Optimistic Update: UI 먼저 변경
const previousState = isLiked;
setIsLiked(!previousState);
setIsLikeLoading(true);
try {
if (!previousState) {
- // 좋아요 추가
- const { error } = await supabase.from('likes').insert({
- entry_id: entry.id,
- user_id: user.id,
- });
+ const { error } = await supabase
+ .from('likes')
+ .insert({ entry_id: entry.id, user_id: user.id });
if (error) throw error;
} else {
- // 좋아요 취소
const { error } = await supabase
.from('likes')
.delete()
@@ -77,7 +71,7 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt
}
} catch (error) {
console.error('Like error:', error);
- setIsLiked(previousState); // 실패 시 롤백
+ setIsLiked(previousState);
} finally {
setIsLikeLoading(false);
}
@@ -86,7 +80,7 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt
return (
{profile.name}
- 님이
+ 님이
{isMenuOpen && (
-
+
)}
-
-
-
- {entry.book.title}
-
-
-
- {entry.to_page ? `${entry.to_page}p까지` : ''}
-
- 읽었어요
-
-
-
-
- {formatDistance(targetDate, now, { addSuffix: true, locale: ko })}
-
-
-
-
+
+
+
+ {entry.book.title}
+
+
+
+
+
+ {entry.summary}
+
+
+
+
+
+ {formatDistance(targetDate, now, { addSuffix: true, locale: ko })}
+
+
diff --git a/app/protected/dashboard/_components/TodaySummarySection.tsx b/app/protected/dashboard/_components/TodaySummarySection.tsx
index 224b1fd..a6b2574 100644
--- a/app/protected/dashboard/_components/TodaySummarySection.tsx
+++ b/app/protected/dashboard/_components/TodaySummarySection.tsx
@@ -12,18 +12,20 @@ export function TodaySummarySection({ entry }: Props) {
{entry ? (
- 📖 오늘 읽은 책
-
+
📖 오늘 읽은 책
+
{entry.book.title}
-
+
"{entry.summary?.slice(0, 100)}..."
) : (
- 🕐 오늘은 아직 기록이 없어요
-
+
+ 🕐 오늘은 아직 기록이 없어요
+
+
하루 한 줄 기록, 지금 써보는 건 어때요?
diff --git a/app/protected/dashboard/_components/WeeklyStreakSection.tsx b/app/protected/dashboard/_components/WeeklyStreakSection.tsx
index 1ea0e8a..ad8d514 100644
--- a/app/protected/dashboard/_components/WeeklyStreakSection.tsx
+++ b/app/protected/dashboard/_components/WeeklyStreakSection.tsx
@@ -16,19 +16,18 @@ export function WeeklyStreakSection({ streak, weekActivity }: Props) {
useEffect(() => {
setMounted(true);
- setToday(new Date()); // 브라우저에 접속한 바로 그 시점의 시간을 세팅
+ setToday(new Date());
}, []);
- // 서버에서 렌더링할 때는 아무것도 보여주지 않거나 스켈레톤을 보여줍니다.
if (!mounted) {
- return ; // 레이아웃 시프트 방지용 빈 박스
+ return ;
}
const startDate = startOfWeek(today, { weekStartsOn: 0 });
return (
-
+
📅 이번 주의 리듬
@@ -41,10 +40,10 @@ export function WeeklyStreakSection({ streak, weekActivity }: Props) {
const baseStyle =
'w-8 h-8 rounded-md flex items-center justify-center text-sm font-semibold border';
const stateClass = didWrite
- ? 'bg-green-500 text-white'
+ ? 'bg-success text-white border-success'
: isPast && !isToday
- ? 'bg-red-200 text-red-700 dark:bg-red-900 dark:text-red-300'
- : 'bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400';
+ ? 'bg-danger-subtle text-danger border-danger-muted dark:bg-danger/20 dark:text-danger dark:border-danger/30'
+ : 'bg-surface-raised text-label-muted border-border dark:bg-dark-raised dark:text-label-muted dark:border-dark-border';
return (
@@ -54,7 +53,7 @@ export function WeeklyStreakSection({ streak, weekActivity }: Props) {
})}
-
+
{streak > 0 ? (
<>🔥 현재 {streak}일 연속 기록 중!>
) : (
diff --git a/app/protected/profile/edit/page.tsx b/app/protected/profile/edit/page.tsx
index fa149cf..3a6e4e5 100644
--- a/app/protected/profile/edit/page.tsx
+++ b/app/protected/profile/edit/page.tsx
@@ -9,6 +9,10 @@ import { useProfileUpdate } from '@/hooks/useProfileUpdate';
import { Profile } from '@/types/profile';
import BackButton from '@/components/ui/BackButton';
import Button from '@/components/ui/Button';
+import Input from '@/components/ui/Input';
+import { Textarea } from '@/components/ui/Textarea';
+import FormGroup from '@/components/ui/FormGroup';
+import FormLabel from '@/components/ui/FormLabel';
export default function EditProfilePage() {
const router = useRouter();
@@ -18,7 +22,6 @@ export default function EditProfilePage() {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
- // 1. 프로필 초기 데이터 불러오기
useEffect(() => {
async function loadData() {
const {
@@ -37,7 +40,6 @@ export default function EditProfilePage() {
loadData();
}, [supabase, router]);
- // 커스텀 훅 연결 (imagePath는 훅에서 관리하는 최신 상대 경로)
const { uploading, updating, imagePath, uploadAvatar, updateProfile } = useProfileUpdate(profile);
if (!profile)
@@ -51,26 +53,25 @@ export default function EditProfilePage() {
-
+
프로필 수정
-
+
{/* 이미지 수정 섹션 */}
-
+
{imagePath ? (
) : (
-
+
{nickname?.at(0) || 'U'}
)}
@@ -80,7 +81,7 @@ export default function EditProfilePage() {
)}
-
- 프로필 사진
-
+
프로필 사진
+
나를 나타내는 멋진 사진을 올려보세요.
@@ -101,42 +102,37 @@ export default function EditProfilePage() {
{/* 텍스트 입력 섹션 */}
-
- 이름
-
+ 이름
+ setName(e.target.value)}
- className="w-full px-5 py-4 rounded-[1.5rem] bg-zinc-50 dark:bg-zinc-900 border-2 border-zinc-100 dark:border-zinc-800 focus:border-zinc-900 dark:focus:border-zinc-100 outline-none transition-all font-bold"
placeholder="이름 입력"
/>
-
+
-
-
- 닉네임
-
-
+ 닉네임
+ setNickname(e.target.value)}
- className="w-full px-5 py-4 rounded-[1.5rem] bg-zinc-50 dark:bg-zinc-900 border-2 border-zinc-100 dark:border-zinc-800 focus:border-zinc-900 dark:focus:border-zinc-100 outline-none transition-all font-bold"
placeholder="닉네임 입력"
/>
-
+
-
-
- 한줄 소개
-
-
+
+
닉네임은 영어 알파벳과 숫자, 언더스코어(_)만 사용할 수 있습니다.
📚 진행 중인 책
+📚 진행 중인 책
{book.title}
++ {book.title} +
{book.author && ( -{book.author}
+{book.author}
)} -+
📈 진행률: {book.progress}%
{book.started_at && ( -+
등록일: {new Date(book.started_at).toLocaleDateString()}
)} diff --git a/app/protected/dashboard/_components/SocialFeedItem.tsx b/app/protected/dashboard/_components/SocialFeedItem.tsx index d375db9..a607aec 100644 --- a/app/protected/dashboard/_components/SocialFeedItem.tsx +++ b/app/protected/dashboard/_components/SocialFeedItem.tsx @@ -17,7 +17,6 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt const router = useRouter(); const supabase = createSupabaseClient(); - // 초기 좋아요 상태 (entry.initialLiked 등 이미 prop으로 넘어온다고 가정하거나, 필요시 fetch) const [isLiked, setIsLiked] = useState(initialLiked); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isLikeLoading, setIsLikeLoading] = useState(false); @@ -41,7 +40,6 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt return () => document.removeEventListener('mousedown', handleClickOutside); }, [isMenuOpen]); - // 실제 좋아요 처리 로직 const handleLike = async (e: React.MouseEvent) => { e.preventDefault(); if (isLikeLoading) return; @@ -54,21 +52,17 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt return; } - // 1. Optimistic Update: UI 먼저 변경 const previousState = isLiked; setIsLiked(!previousState); setIsLikeLoading(true); try { if (!previousState) { - // 좋아요 추가 - const { error } = await supabase.from('likes').insert({ - entry_id: entry.id, - user_id: user.id, - }); + const { error } = await supabase + .from('likes') + .insert({ entry_id: entry.id, user_id: user.id }); if (error) throw error; } else { - // 좋아요 취소 const { error } = await supabase .from('likes') .delete() @@ -77,7 +71,7 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt } } catch (error) { console.error('Like error:', error); - setIsLiked(previousState); // 실패 시 롤백 + setIsLiked(previousState); } finally { setIsLikeLoading(false); } @@ -86,7 +80,7 @@ export default function FeedItem({ entry, profile, initialLiked }: SocialFeedEnt return (+ {entry.summary} +
+ + +📖 오늘 읽은 책
-+
📖 오늘 읽은 책
+{entry.book.title}
-+
"{entry.summary?.slice(0, 100)}..."
🕐 오늘은 아직 기록이 없어요
-+
+ 🕐 오늘은 아직 기록이 없어요 +
+하루 한 줄 기록, 지금 써보는 건 어때요?
+
📅 이번 주의 리듬
@@ -41,10 +40,10 @@ export function WeeklyStreakSection({ streak, weekActivity }: Props) {
const baseStyle =
'w-8 h-8 rounded-md flex items-center justify-center text-sm font-semibold border';
const stateClass = didWrite
- ? 'bg-green-500 text-white'
+ ? 'bg-success text-white border-success'
: isPast && !isToday
- ? 'bg-red-200 text-red-700 dark:bg-red-900 dark:text-red-300'
- : 'bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400';
+ ? 'bg-danger-subtle text-danger border-danger-muted dark:bg-danger/20 dark:text-danger dark:border-danger/30'
+ : 'bg-surface-raised text-label-muted border-border dark:bg-dark-raised dark:text-label-muted dark:border-dark-border';
return (
@@ -54,7 +53,7 @@ export function WeeklyStreakSection({ streak, weekActivity }: Props) {
})}
-
+
{streak > 0 ? (
<>🔥 현재 {streak}일 연속 기록 중!>
) : (
diff --git a/app/protected/profile/edit/page.tsx b/app/protected/profile/edit/page.tsx
index fa149cf..3a6e4e5 100644
--- a/app/protected/profile/edit/page.tsx
+++ b/app/protected/profile/edit/page.tsx
@@ -9,6 +9,10 @@ import { useProfileUpdate } from '@/hooks/useProfileUpdate';
import { Profile } from '@/types/profile';
import BackButton from '@/components/ui/BackButton';
import Button from '@/components/ui/Button';
+import Input from '@/components/ui/Input';
+import { Textarea } from '@/components/ui/Textarea';
+import FormGroup from '@/components/ui/FormGroup';
+import FormLabel from '@/components/ui/FormLabel';
export default function EditProfilePage() {
const router = useRouter();
@@ -18,7 +22,6 @@ export default function EditProfilePage() {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
- // 1. 프로필 초기 데이터 불러오기
useEffect(() => {
async function loadData() {
const {
@@ -37,7 +40,6 @@ export default function EditProfilePage() {
loadData();
}, [supabase, router]);
- // 커스텀 훅 연결 (imagePath는 훅에서 관리하는 최신 상대 경로)
const { uploading, updating, imagePath, uploadAvatar, updateProfile } = useProfileUpdate(profile);
if (!profile)
@@ -51,26 +53,25 @@ export default function EditProfilePage() {
-
+
프로필 수정
-
+
{/* 이미지 수정 섹션 */}
-
+
{imagePath ? (
) : (
-
+
{nickname?.at(0) || 'U'}
)}
@@ -80,7 +81,7 @@ export default function EditProfilePage() {
)}
-
- 프로필 사진
-
+
프로필 사진
+
나를 나타내는 멋진 사진을 올려보세요.
@@ -101,42 +102,37 @@ export default function EditProfilePage() {
{/* 텍스트 입력 섹션 */}
-
- 이름
-
+ 이름
+ setName(e.target.value)}
- className="w-full px-5 py-4 rounded-[1.5rem] bg-zinc-50 dark:bg-zinc-900 border-2 border-zinc-100 dark:border-zinc-800 focus:border-zinc-900 dark:focus:border-zinc-100 outline-none transition-all font-bold"
placeholder="이름 입력"
/>
-
+
-
-
- 닉네임
-
-
+ 닉네임
+ setNickname(e.target.value)}
- className="w-full px-5 py-4 rounded-[1.5rem] bg-zinc-50 dark:bg-zinc-900 border-2 border-zinc-100 dark:border-zinc-800 focus:border-zinc-900 dark:focus:border-zinc-100 outline-none transition-all font-bold"
placeholder="닉네임 입력"
/>
-
+
-
-
- 한줄 소개
-
-
+
+
{streak > 0 ? (
<>🔥 현재 {streak}일 연속 기록 중!>
) : (
diff --git a/app/protected/profile/edit/page.tsx b/app/protected/profile/edit/page.tsx
index fa149cf..3a6e4e5 100644
--- a/app/protected/profile/edit/page.tsx
+++ b/app/protected/profile/edit/page.tsx
@@ -9,6 +9,10 @@ import { useProfileUpdate } from '@/hooks/useProfileUpdate';
import { Profile } from '@/types/profile';
import BackButton from '@/components/ui/BackButton';
import Button from '@/components/ui/Button';
+import Input from '@/components/ui/Input';
+import { Textarea } from '@/components/ui/Textarea';
+import FormGroup from '@/components/ui/FormGroup';
+import FormLabel from '@/components/ui/FormLabel';
export default function EditProfilePage() {
const router = useRouter();
@@ -18,7 +22,6 @@ export default function EditProfilePage() {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
- // 1. 프로필 초기 데이터 불러오기
useEffect(() => {
async function loadData() {
const {
@@ -37,7 +40,6 @@ export default function EditProfilePage() {
loadData();
}, [supabase, router]);
- // 커스텀 훅 연결 (imagePath는 훅에서 관리하는 최신 상대 경로)
const { uploading, updating, imagePath, uploadAvatar, updateProfile } = useProfileUpdate(profile);
if (!profile)
@@ -51,26 +53,25 @@ export default function EditProfilePage() {
+
나를 나타내는 멋진 사진을 올려보세요.
+
프로필 수정
프로필 사진
- 프로필 사진
+