| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- 'use client';
- import { useCallback, useEffect, useState } from 'react';
- import { fetchApi } from '@/lib/utils/client';
- import { formatNoteCounterparty } from '@/lib/utils/note';
- import Loading from '@/app/component/Loading';
- import { NotePreview } from '@/types/notification';
- import { NoteInboxResponse } from '@/types/response/note/inbox';
- import type { RecipientItem } from '@/types/response/note/searchRecipient';
- import NoteComposeModal from '../_components/NoteComposeModal';
- import NoteDetailModal from '../_components/NoteDetailModal';
- 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 [composeOpen, setComposeOpen] = useState(false);
- const [composeInitialReceiver, setComposeInitialReceiver] = useState<RecipientItem|null>(null);
- const [composeInitialTitle, setComposeInitialTitle] = useState<string|undefined>(undefined);
- const [detailNoteId, setDetailNoteId] = useState<number|null>(null);
- const loadNotes = useCallback(async () => {
- setLoading(true);
- try {
- const res = await fetchApi<NoteInboxResponse>(`/api/note/inbox?page=${page}&perPage=20`);
- if (res.data) {
- setNotes(res.data.list || []);
- setTotal(res.data.total || 0);
- setUnreadCount(res.data.unreadCount || 0);
- }
- } catch {}
- setLoading(false);
- }, [page]);
- useEffect(() => {
- loadNotes();
- }, [loadNotes]);
- const openNote = (note: NotePreview) => {
- setDetailNoteId(note.id);
- if (!note.isRead) {
- setNotes(prev => prev.map(n => n.id === note.id ? { ...n, isRead: true } : n));
- setUnreadCount(prev => Math.max(0, prev - 1));
- }
- };
- const handleOpenCompose = () => {
- setComposeInitialReceiver(null);
- setComposeInitialTitle(undefined);
- setComposeOpen(true);
- };
- const handleReply = (sender: RecipientItem, originalTitle: string) => {
- setDetailNoteId(null);
- setComposeInitialReceiver(sender);
- setComposeInitialTitle(`RE: ${originalTitle}`);
- setComposeOpen(true);
- };
- const handleSent = () => {
- loadNotes();
- };
- const handleDeleted = () => {
- loadNotes();
- };
- 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="note-page__header">
- <h1 className="note-page__title">받은 쪽지함</h1>
- <div className="note-page__actions">
- <span className="note-page__meta">안읽은 쪽지: {unreadCount}건</span>
- <button type="button" onClick={handleOpenCompose} className="note-page__compose-btn">
- 쪽지 보내기
- </button>
- </div>
- </div>
- {loading && <Loading type={2} />}
- {!loading && notes.length === 0 && (
- <div className="note-page__empty">쪽지가 없습니다</div>
- )}
- {!loading && notes.length > 0 && (
- <div className="note-page__list">
- {notes.map(note => {
- const counterparty = formatNoteCounterparty({
- isSystem: note.isSystem,
- name: note.senderName,
- sid: note.senderSID,
- channelName: note.senderChannelName,
- channelHandle: note.senderChannelHandle
- });
- return (
- <div
- key={note.id}
- onClick={() => openNote(note)}
- className={`note-page__item${note.isRead ? '' : ' note-page__item--unread'}`}
- >
- <div className="note-page__item-body">
- <div className="note-page__item-top">
- {note.isSystem && <span className="note-page__badge">시스템</span>}
- <span className={`note-page__item-title${note.isRead ? '' : ' note-page__item-title--unread'}`}>{note.title}</span>
- </div>
- <div className="note-page__item-sub">
- <span className="note-page__item-party">{counterparty.primary}</span>
- {counterparty.secondary && <span className="note-page__item-handle">{counterparty.secondary}</span>}
- <span className="note-page__item-time">· {formatDate(note.createdAt)}</span>
- </div>
- </div>
- {!note.isRead && <div className="note-page__item-dot" />}
- </div>
- );
- })}
- </div>
- )}
- {total > 20 && (
- <div className="note-page__pager">
- <button type="button" onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} className="note-page__pager-btn">이전</button>
- <span className="note-page__pager-info">{page} / {Math.ceil(total / 20)}</span>
- <button type="button" onClick={() => setPage(p => p + 1)} disabled={page >= Math.ceil(total / 20)} className="note-page__pager-btn">다음</button>
- </div>
- )}
- <NoteComposeModal
- open={composeOpen}
- onClose={() => setComposeOpen(false)}
- initialReceiver={composeInitialReceiver}
- initialTitle={composeInitialTitle}
- onSent={handleSent}
- />
- <NoteDetailModal
- noteId={detailNoteId}
- onClose={() => setDetailNoteId(null)}
- onReply={handleReply}
- onDeleted={handleDeleted}
- />
- </>
- );
- }
|