From d334a23ca7f17243edbfbd3d1729b73ec7b6614d Mon Sep 17 00:00:00 2001 From: rustmailer Date: Tue, 6 Jan 2026 16:27:05 +0800 Subject: [PATCH] chore(ui): add attachment file type icon --- web/src/features/dashboard/index.tsx | 4 +- .../mailbox/components/mail-message-view.tsx | 56 ++++++++++++++--- web/src/features/search/mail-message-view.tsx | 61 +++++++++++++++---- 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/web/src/features/dashboard/index.tsx b/web/src/features/dashboard/index.tsx index bc6ba05..90622c7 100644 --- a/web/src/features/dashboard/index.tsx +++ b/web/src/features/dashboard/index.tsx @@ -164,7 +164,7 @@ export default function MailArchiveDashboard() { return ( <> -
+
@@ -304,7 +304,6 @@ export default function MailArchiveDashboard() { - {/* Attachments */} @@ -459,7 +458,6 @@ export default function MailArchiveDashboard() {
- {/* Footer / Copyright - New Addition */}
© 2025 rustmailer.com - Bichon Email Archiving Project
diff --git a/web/src/features/mailbox/components/mail-message-view.tsx b/web/src/features/mailbox/components/mail-message-view.tsx index 3dbc6ad..f825cb3 100644 --- a/web/src/features/mailbox/components/mail-message-view.tsx +++ b/web/src/features/mailbox/components/mail-message-view.tsx @@ -19,7 +19,7 @@ import { useEffect, useState } from 'react'; import { useMutation } from '@tanstack/react-query'; -import { Loader, Download, Trash2, MessageSquareMore } from 'lucide-react'; +import { Loader, Download, Trash2, MessageSquareMore, FileText, FileImage, FileAudio, FileVideo, FileSpreadsheet, FileArchive, FileCode, FileIcon } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; @@ -83,6 +83,35 @@ const Multilines: React.FC<{ title: string; lines: string[] }> = ({ title, lines ); }; + +const getFileConfig = (mimeType: string) => { + const type = mimeType.toLowerCase(); + if (type.includes('pdf')) { + return { icon: , color: 'text-red-600 bg-red-50 border-red-100' }; + } + if (type.includes('image/')) { + return { icon: , color: 'text-blue-600 bg-blue-50 border-blue-100' }; + } + if (type.includes('audio/')) { + return { icon: , color: 'text-purple-600 bg-purple-50 border-purple-100' }; + } + + if (type.includes('video/')) { + return { icon: , color: 'text-indigo-600 bg-indigo-50 border-indigo-100' }; + } + if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) { + return { icon: , color: 'text-green-600 bg-green-50 border-green-100' }; + } + if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) { + return { icon: , color: 'text-orange-600 bg-orange-50 border-orange-100' }; + } + if (type.includes('text/') || type.includes('json') || type.includes('javascript')) { + return { icon: , color: 'text-slate-600 bg-slate-50 border-slate-100' }; + } + + return { icon: , color: 'text-gray-600 bg-gray-50 border-gray-100' }; +}; + export function MailMessageView({ envelope, showActions = true, @@ -245,11 +274,24 @@ export function MailMessageView({ const nonInline = attachments.filter((a) => !a.inline); return nonInline.length > 0 ? (
- {nonInline.map((attachment, i) => ( -
-
- {attachment.filename} - [{attachment.file_type}] + {nonInline.map((attachment, i) => { + const { icon, color } = getFileConfig(attachment.file_type); + return
+
+
+ {icon} +
+
+ + {attachment.filename} + + + {attachment.file_type.split('/').pop()?.toUpperCase()} + +
@@ -268,7 +310,7 @@ export function MailMessageView({ )}
- ))} + })}
) : ( diff --git a/web/src/features/search/mail-message-view.tsx b/web/src/features/search/mail-message-view.tsx index 7fdbdd2..e115c24 100644 --- a/web/src/features/search/mail-message-view.tsx +++ b/web/src/features/search/mail-message-view.tsx @@ -19,7 +19,7 @@ import { useEffect, useState } from 'react'; import { useMutation } from '@tanstack/react-query'; -import { Loader, Download, Trash2, MessageSquareMore } from 'lucide-react'; +import { Loader, Download, Trash2, MessageSquareMore, FileText, FileImage, FileVideo, FileArchive, FileSpreadsheet, FileCode, FileIcon, FileAudio } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; @@ -84,6 +84,34 @@ const Multilines: React.FC<{ title: string; lines: string[] }> = ({ title, lines ); }; +const getFileConfig = (mimeType: string) => { + const type = mimeType.toLowerCase(); + if (type.includes('pdf')) { + return { icon: , color: 'text-red-600 bg-red-50 border-red-100' }; + } + if (type.includes('image/')) { + return { icon: , color: 'text-blue-600 bg-blue-50 border-blue-100' }; + } + if (type.includes('audio/')) { + return { icon: , color: 'text-purple-600 bg-purple-50 border-purple-100' }; + } + + if (type.includes('video/')) { + return { icon: , color: 'text-indigo-600 bg-indigo-50 border-indigo-100' }; + } + if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) { + return { icon: , color: 'text-green-600 bg-green-50 border-green-100' }; + } + if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) { + return { icon: , color: 'text-orange-600 bg-orange-50 border-orange-100' }; + } + if (type.includes('text/') || type.includes('json') || type.includes('javascript')) { + return { icon: , color: 'text-slate-600 bg-slate-50 border-slate-100' }; + } + + return { icon: , color: 'text-gray-600 bg-gray-50 border-gray-100' }; +}; + export function MailMessageView({ envelope, showActions = true, @@ -184,7 +212,6 @@ export function MailMessageView({ return (
- {/* Header Info */} {showHeader &&
{t('mail.account')}: @@ -216,7 +243,7 @@ export function MailMessageView({
)}
} - {/* Action Bar */} + {showActions && ( <>
@@ -257,7 +284,6 @@ export function MailMessageView({ )} {showAttachments && } - {/* Attachments */} {showAttachments && (
{loading ? ( @@ -265,13 +291,27 @@ export function MailMessageView({ ) : attachments && attachments.length > 0 ? ( (() => { const nonInline = attachments.filter((a) => !a.inline); + return nonInline.length > 0 ? (
- {nonInline.map((attachment, i) => ( -
-
- {attachment.filename} - [{attachment.file_type}] + {nonInline.map((attachment, i) => { + const { icon, color } = getFileConfig(attachment.file_type); + return
+
+
+ {icon} +
+
+ + {attachment.filename} + + + {attachment.file_type.split('/').pop()?.toUpperCase()} + +
@@ -290,7 +330,7 @@ export function MailMessageView({ )}
- ))} + })}
) : ( @@ -304,7 +344,6 @@ export function MailMessageView({
)} {showAttachments && } - {/* Content */}
{loading ? (