- )}
-
- {it.threadId ? (
- // A message/mail item → open the conversation to read + reply.
- // key by threadId: the SDK's useQuery only refetches when the ACTION changes, not the
- // variables — so switching items must remount MailReader to load the new thread's history.
-
-
-
- ) : (
- // A non-threaded item (e.g. a system alert) → show its detail.
-
-
{it.title}
- {it.summary &&
{it.summary}
}
-
- )}
- >
- );
-}
diff --git a/src/components/dashboard/mail.tsx b/src/components/dashboard/mail.tsx
deleted file mode 100644
index 9ee09da..0000000
--- a/src/components/dashboard/mail.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-"use client";
-
-// ============================================================
-// Mail components used INSIDE the Inbox (not a separate tab).
-// The Inbox is the one unified surface — mentions, system messages
-// and mail all live there. These render the mail body + reply, and
-// compose a new message. HTML bodies render in a sandboxed iframe.
-// ============================================================
-
-import { type CSSProperties, useEffect, useRef, useState } from "react";
-import { Avatar, Btn, Field, Icon, Modal, Pill } from "./ui";
-import { useMailThread, useMailCompose, type MailAttachment, type MailPerson } from "@/lib/mail-api";
-import { useUploadAttachment, useDownloadUrl, isImage, type UploadedAttachment } from "@/lib/media-api";
-
-const timeOf = (iso?: string) => {
- if (!iso) return "";
- const d = new Date(iso);
- return Number.isNaN(+d) ? "" : d.toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
-};
-const CUSTOMER_GRAD = "linear-gradient(135deg,#10b981,#059669)";
-const inputStyle: CSSProperties = {
- width: "100%", padding: "9px 12px", borderRadius: 10, border: "1px solid var(--border)",
- background: "var(--panel)", color: "var(--text)", fontSize: 14, outline: "none",
-};
-
-function fmtBytes(n: number): string {
- if (!n) return "";
- if (n < 1024) return `${n} B`;
- if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`;
- return `${(n / (1024 * 1024)).toFixed(1)} MB`;
-}
-
-/** Resolves a signed URL for a stored attachment and renders it inline (image) or as a file chip. */
-function MailAttachmentView({ att }: { att: MailAttachment }) {
- const getUrl = useDownloadUrl();
- const [url, setUrl] = useState(null);
- useEffect(() => {
- let alive = true;
- getUrl(att.contentRef, att.mimeType).then((u) => { if (alive) setUrl(u); }).catch(() => {});
- return () => { alive = false; };
- }, [att.contentRef, att.mimeType, getUrl]);
-
- const label = att.filename || "Attachment";
- if (isImage(att.mimeType)) {
- return url
- ?
- :
Loading image…
;
- }
- return (
-
-
- {label}
- {att.sizeBytes > 0 && {fmtBytes(att.sizeBytes)}}
-
- );
-}
-
-/** A small staged-file chip shown in a composer before send, with a remove button. */
-function StagedChip({ file, onRemove }: { file: UploadedAttachment; onRemove: () => void }) {
- return (
-