| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- 'use client';
- import { useEffect, useState } from 'react';
- import { fetchApi } from '@/lib/utils/client';
- import { NotePreview } from '@/types/notification';
- import { NoteInboxResponse, NoteDetail } from '@/types/response/note/inbox';
- export default function NoteInboxPage() {
- const [notes, setNotes] = useState<NotePreview[]>([]);
- const [total, setTotal] = useState(0);
- const [unreadCount, setUnreadCount] = useState(0);
- const [page, setPage] = useState(1);
- const [loading, setLoading] = useState(true);
- const [selectedNote, setSelectedNote] = useState<NoteDetail|null>(null);
- useEffect(() => {
- loadNotes();
- }, [page]);
- const loadNotes = async () => {
- setLoading(true);
- try {
- const res = await fetchApi<NoteInboxResponse>(`/api/note/inbox?pageNum=${page}&perPage=20`);
- if (res.data) {
- setNotes(res.data.list || []);
- setTotal(res.data.total || 0);
- setUnreadCount(res.data.unreadCount || 0);
- }
- } catch {}
- setLoading(false);
- };
- const openNote = async (note: NotePreview) => {
- // TODO: 상세 조회 API 호출 + 읽음 처리
- setSelectedNote({
- id: note.id,
- title: note.title,
- content: '(쪽지 내용 로딩...)',
- senderName: note.senderName || '시스템',
- createdAt: note.createdAt
- });
- if (!note.isRead) {
- setNotes(prev => prev.map(n => n.id === note.id ? { ...n, isRead: true } : n));
- setUnreadCount(prev => Math.max(0, prev - 1));
- }
- };
- const formatDate = (dateStr: string) => {
- const d = new Date(dateStr);
- return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
- };
- return (
- <div className="container mx-auto max-w-2xl p-4">
- <div className="flex items-center justify-between mb-4">
- <h1 className="text-xl font-bold">받은 쪽지함</h1>
- <div className="flex gap-2">
- <span className="text-sm text-gray-500">안읽은 쪽지: {unreadCount}건</span>
- <a href="/note/send" className="text-sm text-blue-500 hover:underline">쪽지 보내기</a>
- </div>
- </div>
- {loading && <div className="text-center text-gray-500 py-10">로딩 중...</div>}
- {!loading && notes.length === 0 && (
- <div className="text-center text-gray-500 py-10">쪽지가 없습니다</div>
- )}
- <div className="flex flex-col gap-2">
- {notes.map(note => (
- <div key={note.id} onClick={() => openNote(note)} className={`flex items-center gap-3 p-3 rounded-lg cursor-pointer border transition-colors ${note.isRead ? 'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700' : 'bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800'}`}>
- <div className="flex-1 overflow-hidden">
- <div className="flex items-center gap-2">
- {note.isSystem && <span className="text-xs bg-gray-200 dark:bg-gray-700 px-1.5 py-0.5 rounded">시스템</span>}
- <span className={`text-sm ${note.isRead ? 'font-normal' : 'font-semibold'}`}>{note.title}</span>
- </div>
- <div className="text-xs text-gray-500 mt-1">
- {note.senderName || '시스템'} · {formatDate(note.createdAt)}
- </div>
- </div>
- {!note.isRead && <div className="w-2 h-2 rounded-full bg-blue-500 flex-shrink-0" />}
- </div>
- ))}
- </div>
- {total > 20 && (
- <div className="flex justify-center gap-2 mt-4">
- <button type="button" onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} className="px-3 py-1 text-sm border rounded disabled:opacity-50">이전</button>
- <span className="px-3 py-1 text-sm">{page} / {Math.ceil(total / 20)}</span>
- <button type="button" onClick={() => setPage(p => p + 1)} disabled={page >= Math.ceil(total / 20)} className="px-3 py-1 text-sm border rounded disabled:opacity-50">다음</button>
- </div>
- )}
- {/* 쪽지 상세 모달 */}
- {selectedNote && (
- <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={() => setSelectedNote(null)}>
- <div className="bg-white dark:bg-gray-900 rounded-xl w-full max-w-md mx-4 p-6" onClick={e => e.stopPropagation()}>
- <div className="flex items-center justify-between mb-3">
- <h3 className="font-bold text-lg">{selectedNote.title}</h3>
- <button type="button" onClick={() => setSelectedNote(null)} className="text-gray-400 hover:text-gray-600 text-xl">×</button>
- </div>
- <div className="text-xs text-gray-500 mb-3">{selectedNote.senderName} · {formatDate(selectedNote.createdAt)}</div>
- <div className="text-sm whitespace-pre-wrap">{selectedNote.content}</div>
- </div>
- </div>
- )}
- </div>
- );
- }
|