You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
// frontend/components/mail/MailRow.tsx — 메일 목록 행 (아바타 + AI pill)
|
|
"use client";
|
|
import { Icon } from "@/components/Icon";
|
|
import type { EmailRow, MailAccount, Person } from "@/lib/types";
|
|
|
|
export function aiCount(m: EmailRow): number {
|
|
return m.ai ? m.ai.tasks.length + m.ai.events.length + (m.ai.replies.length ? 1 : 0) : 0;
|
|
}
|
|
|
|
export function MailRow({
|
|
m,
|
|
person,
|
|
account,
|
|
accColor,
|
|
on,
|
|
onClick,
|
|
onStar,
|
|
}: {
|
|
m: EmailRow;
|
|
person?: Person;
|
|
account?: MailAccount;
|
|
accColor?: string; // 계정별 구분 색(계정 tone 이 겹치므로 팔레트에서 배정)
|
|
on: boolean;
|
|
onClick: () => void;
|
|
onStar: (id: string) => void;
|
|
}) {
|
|
// 표시 이름: 연락처(person) > 백엔드 표시이름(from_name) > 원문(from). 메일주소 대신 이름.
|
|
const name = person?.name ?? (m.from_name || m.from);
|
|
// 아바타는 '어느 계정' 표시 — 계정 색 + 계정 이니셜(색 dot 대신 명확하게).
|
|
const accName = account?.name ?? m.account;
|
|
const accInitial = accName.slice(0, 1).toUpperCase();
|
|
const color = accColor ?? (account?.tone ? `var(--${account.tone})` : "var(--muted)");
|
|
const showPills = m.ai && (m.ai.priority === "높음" || aiCount(m) > 0);
|
|
return (
|
|
<div className={"mrow" + (on ? " on" : "") + (m.read ? "" : " unread")} onClick={onClick}>
|
|
<div className="mr-ava" style={{ background: color }} title={`${accName} · ${account?.email ?? ""}`}>
|
|
{accInitial}
|
|
</div>
|
|
<div className="mr-body">
|
|
<div className="mr-top">
|
|
<span className="mr-from">
|
|
<b>{name}</b>
|
|
</span>
|
|
<span className="mr-time">{m.time}</span>
|
|
</div>
|
|
<div className="mr-subj">{m.subject}</div>
|
|
<div className="mr-prev">{m.preview}</div>
|
|
{showPills && m.ai && (
|
|
<div className="mr-tags">
|
|
{m.ai.priority === "높음" && (
|
|
<span className="ai-pill prio">
|
|
<Icon name="flag" />
|
|
중요
|
|
</span>
|
|
)}
|
|
{m.ai.tasks.length > 0 && (
|
|
<span className="ai-pill task">
|
|
<Icon name="check" />할 일 {m.ai.tasks.length}
|
|
</span>
|
|
)}
|
|
{m.ai.events.length > 0 && (
|
|
<span className="ai-pill event">
|
|
<Icon name="cal" />
|
|
일정 {m.ai.events.length}
|
|
</span>
|
|
)}
|
|
{m.ai.replies.length > 0 && (
|
|
<span className="ai-pill reply">
|
|
<Icon name="reply" />
|
|
답장 추천
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="mr-flags">
|
|
<button
|
|
className={"mr-star" + (m.starred ? " on" : "")}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onStar(m.id);
|
|
}}
|
|
aria-label="별표"
|
|
>
|
|
<Icon name="star" w={m.starred ? 0 : 1.9} fill={m.starred ? "currentColor" : "none"} />
|
|
</button>
|
|
{m.has_attach && (
|
|
<span className="mr-attach">
|
|
<Icon name="clip" />
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|