mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
chore(ui): add attachment file type icon
This commit is contained in:
@@ -164,7 +164,7 @@ export default function MailArchiveDashboard() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FixedHeader />
|
<FixedHeader />
|
||||||
<Main>
|
<Main higher>
|
||||||
<div className="flex-1 space-y-6 p-6 md:p-8">
|
<div className="flex-1 space-y-6 p-6 md:p-8">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -304,7 +304,6 @@ export default function MailArchiveDashboard() {
|
|||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* Attachments */}
|
|
||||||
<TabsContent value="attachment" className="space-y-4">
|
<TabsContent value="attachment" className="space-y-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -459,7 +458,6 @@ export default function MailArchiveDashboard() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer / Copyright - New Addition */}
|
|
||||||
<div className="p-6 md:p-8 pt-0 text-center text-xs text-muted-foreground">
|
<div className="p-6 md:p-8 pt-0 text-center text-xs text-muted-foreground">
|
||||||
© 2025 <a href="https://rustmailer.com" target="_blank" rel="noopener noreferrer" className="hover:underline">rustmailer.com</a> - Bichon Email Archiving Project
|
© 2025 <a href="https://rustmailer.com" target="_blank" rel="noopener noreferrer" className="hover:underline">rustmailer.com</a> - Bichon Email Archiving Project
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
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 { Button } from '@/components/ui/button';
|
||||||
import { Separator } from '@/components/ui/separator';
|
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: <FileText className="h-4 w-4" />, color: 'text-red-600 bg-red-50 border-red-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('image/')) {
|
||||||
|
return { icon: <FileImage className="h-4 w-4" />, color: 'text-blue-600 bg-blue-50 border-blue-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('audio/')) {
|
||||||
|
return { icon: <FileAudio className="h-4 w-4" />, color: 'text-purple-600 bg-purple-50 border-purple-100' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.includes('video/')) {
|
||||||
|
return { icon: <FileVideo className="h-4 w-4" />, color: 'text-indigo-600 bg-indigo-50 border-indigo-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
|
||||||
|
return { icon: <FileSpreadsheet className="h-4 w-4" />, color: 'text-green-600 bg-green-50 border-green-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
|
||||||
|
return { icon: <FileArchive className="h-4 w-4" />, color: 'text-orange-600 bg-orange-50 border-orange-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('text/') || type.includes('json') || type.includes('javascript')) {
|
||||||
|
return { icon: <FileCode className="h-4 w-4" />, color: 'text-slate-600 bg-slate-50 border-slate-100' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { icon: <FileIcon className="h-4 w-4" />, color: 'text-gray-600 bg-gray-50 border-gray-100' };
|
||||||
|
};
|
||||||
|
|
||||||
export function MailMessageView({
|
export function MailMessageView({
|
||||||
envelope,
|
envelope,
|
||||||
showActions = true,
|
showActions = true,
|
||||||
@@ -245,11 +274,24 @@ export function MailMessageView({
|
|||||||
const nonInline = attachments.filter((a) => !a.inline);
|
const nonInline = attachments.filter((a) => !a.inline);
|
||||||
return nonInline.length > 0 ? (
|
return nonInline.length > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{nonInline.map((attachment, i) => (
|
{nonInline.map((attachment, i) => {
|
||||||
<div key={i} className="flex items-center">
|
const { icon, color } = getFileConfig(attachment.file_type);
|
||||||
<div className="flex items-center space-x-8">
|
return <div key={i} className="flex items-center">
|
||||||
<span className="truncate text-xs">{attachment.filename}</span>
|
<div className="group flex items-center gap-2 p-1 hover:bg-muted/60 rounded transition-colors min-w-0 w-full">
|
||||||
<span className="text-xs px-2 py-1 rounded">[{attachment.file_type}]</span>
|
<div className={`flex-shrink-0 ${color}`}>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between min-w-0 flex-1 gap-2">
|
||||||
|
<span
|
||||||
|
className="truncate text-xs font-medium text-foreground/90"
|
||||||
|
title={attachment.filename}
|
||||||
|
>
|
||||||
|
{attachment.filename}
|
||||||
|
</span>
|
||||||
|
<span className="flex-shrink-0 text-[9px] font-bold text-muted-foreground/60 bg-muted px-1 py-0.5 rounded uppercase tracking-tighter group-hover:text-foreground transition-colors">
|
||||||
|
{attachment.file_type.split('/').pop()?.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 ml-auto">
|
<div className="flex items-center space-x-4 ml-auto">
|
||||||
<span className="text-gray-500 text-xs shrink-0">
|
<span className="text-gray-500 text-xs shrink-0">
|
||||||
@@ -268,7 +310,7 @@ export function MailMessageView({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-gray-500 text-xs italic">
|
<span className="text-gray-500 text-xs italic">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
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 { Button } from '@/components/ui/button';
|
||||||
import { Separator } from '@/components/ui/separator';
|
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: <FileText className="h-4 w-4" />, color: 'text-red-600 bg-red-50 border-red-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('image/')) {
|
||||||
|
return { icon: <FileImage className="h-4 w-4" />, color: 'text-blue-600 bg-blue-50 border-blue-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('audio/')) {
|
||||||
|
return { icon: <FileAudio className="h-4 w-4" />, color: 'text-purple-600 bg-purple-50 border-purple-100' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.includes('video/')) {
|
||||||
|
return { icon: <FileVideo className="h-4 w-4" />, color: 'text-indigo-600 bg-indigo-50 border-indigo-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
|
||||||
|
return { icon: <FileSpreadsheet className="h-4 w-4" />, color: 'text-green-600 bg-green-50 border-green-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
|
||||||
|
return { icon: <FileArchive className="h-4 w-4" />, color: 'text-orange-600 bg-orange-50 border-orange-100' };
|
||||||
|
}
|
||||||
|
if (type.includes('text/') || type.includes('json') || type.includes('javascript')) {
|
||||||
|
return { icon: <FileCode className="h-4 w-4" />, color: 'text-slate-600 bg-slate-50 border-slate-100' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { icon: <FileIcon className="h-4 w-4" />, color: 'text-gray-600 bg-gray-50 border-gray-100' };
|
||||||
|
};
|
||||||
|
|
||||||
export function MailMessageView({
|
export function MailMessageView({
|
||||||
envelope,
|
envelope,
|
||||||
showActions = true,
|
showActions = true,
|
||||||
@@ -184,7 +212,6 @@ export function MailMessageView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
{/* Header Info */}
|
|
||||||
{showHeader && <div className="grid gap-1 text-xs">
|
{showHeader && <div className="grid gap-1 text-xs">
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<span className="font-medium text-gray-400">{t('mail.account')}:</span>
|
<span className="font-medium text-gray-400">{t('mail.account')}:</span>
|
||||||
@@ -216,7 +243,7 @@ export function MailMessageView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>}
|
</div>}
|
||||||
{/* Action Bar */}
|
|
||||||
{showActions && (
|
{showActions && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center mt-2 space-x-2">
|
<div className="flex items-center mt-2 space-x-2">
|
||||||
@@ -257,7 +284,6 @@ export function MailMessageView({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{showAttachments && <Separator className="my-2" />}
|
{showAttachments && <Separator className="my-2" />}
|
||||||
{/* Attachments */}
|
|
||||||
{showAttachments && (
|
{showAttachments && (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -265,13 +291,27 @@ export function MailMessageView({
|
|||||||
) : attachments && attachments.length > 0 ? (
|
) : attachments && attachments.length > 0 ? (
|
||||||
(() => {
|
(() => {
|
||||||
const nonInline = attachments.filter((a) => !a.inline);
|
const nonInline = attachments.filter((a) => !a.inline);
|
||||||
|
|
||||||
return nonInline.length > 0 ? (
|
return nonInline.length > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{nonInline.map((attachment, i) => (
|
{nonInline.map((attachment, i) => {
|
||||||
<div key={i} className="flex items-center">
|
const { icon, color } = getFileConfig(attachment.file_type);
|
||||||
<div className="flex items-center space-x-8">
|
return <div key={i} className="flex items-center">
|
||||||
<span className="truncate text-xs">{attachment.filename}</span>
|
<div className="group flex items-center gap-2 p-1 hover:bg-muted/60 rounded transition-colors min-w-0 w-full">
|
||||||
<span className="text-xs px-2 py-1 rounded">[{attachment.file_type}]</span>
|
<div className={`flex-shrink-0 ${color}`}>
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between min-w-0 flex-1 gap-2">
|
||||||
|
<span
|
||||||
|
className="truncate text-xs font-medium text-foreground/90"
|
||||||
|
title={attachment.filename}
|
||||||
|
>
|
||||||
|
{attachment.filename}
|
||||||
|
</span>
|
||||||
|
<span className="flex-shrink-0 text-[9px] font-bold text-muted-foreground/60 bg-muted px-1 py-0.5 rounded uppercase tracking-tighter group-hover:text-foreground transition-colors">
|
||||||
|
{attachment.file_type.split('/').pop()?.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 ml-auto">
|
<div className="flex items-center space-x-4 ml-auto">
|
||||||
<span className="text-gray-500 text-xs shrink-0">
|
<span className="text-gray-500 text-xs shrink-0">
|
||||||
@@ -290,7 +330,7 @@ export function MailMessageView({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-gray-500 text-xs italic">
|
<span className="text-gray-500 text-xs italic">
|
||||||
@@ -304,7 +344,6 @@ export function MailMessageView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showAttachments && <Separator className="mb-2" />}
|
{showAttachments && <Separator className="mb-2" />}
|
||||||
{/* Content */}
|
|
||||||
<div className="flex-1 overflow-auto">
|
<div className="flex-1 overflow-auto">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex justify-center items-center py-8">
|
<div className="flex justify-center items-center py-8">
|
||||||
|
|||||||
Reference in New Issue
Block a user