courrier refactor
This commit is contained in:
parent
367b79bf0b
commit
b056438814
@ -305,11 +305,11 @@ export default function CourrierPage() {
|
||||
</div>
|
||||
|
||||
{selectedEmail && (
|
||||
<div className="flex-1 h-full overflow-hidden flex flex-col">
|
||||
<div className="flex-1 h-full overflow-hidden flex flex-col bg-white/95 backdrop-blur-sm">
|
||||
<div className="border-b p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">{selectedEmail.subject || '(No subject)'}</h2>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<h2 className="text-xl font-semibold text-gray-900">{selectedEmail.subject || '(No subject)'}</h2>
|
||||
<div className="text-sm text-gray-500">
|
||||
From: {selectedEmail.from?.[0]?.name || selectedEmail.from?.[0]?.address || 'Unknown'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Trash, Mail, MailOpen, Archive } from 'lucide-react';
|
||||
import { Trash2, EyeOff, Archive } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger
|
||||
} from '@/components/ui/tooltip';
|
||||
|
||||
interface BulkActionsToolbarProps {
|
||||
selectedCount: number;
|
||||
@ -20,74 +14,41 @@ export default function BulkActionsToolbar({
|
||||
onBulkAction
|
||||
}: BulkActionsToolbarProps) {
|
||||
return (
|
||||
<div className="border-b p-2 flex items-center gap-2 bg-accent/20">
|
||||
<div className="text-xs font-medium flex-1">
|
||||
{selectedCount} {selectedCount === 1 ? 'message' : 'messages'} selected
|
||||
<div className="bg-white border-b border-gray-100 px-4 py-2">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedCount} selected
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-gray-600 hover:text-gray-900 h-8 px-2"
|
||||
onClick={() => onBulkAction('mark-read')}
|
||||
>
|
||||
<EyeOff className="h-4 w-4 mr-1" />
|
||||
<span className="text-sm">Mark Read</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-gray-600 hover:text-gray-900 h-8 px-2"
|
||||
onClick={() => onBulkAction('archive')}
|
||||
>
|
||||
<Archive className="h-4 w-4 mr-1" />
|
||||
<span className="text-sm">Archive</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-red-600 hover:text-red-700 h-8 px-2"
|
||||
onClick={() => onBulkAction('delete')}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 mr-1" />
|
||||
<span className="text-sm">Delete</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => onBulkAction('mark-read')}
|
||||
>
|
||||
<MailOpen className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Mark as read</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => onBulkAction('mark-unread')}
|
||||
>
|
||||
<Mail className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Mark as unread</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => onBulkAction('archive')}
|
||||
>
|
||||
<Archive className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Archive</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-destructive hover:text-destructive"
|
||||
onClick={() => onBulkAction('delete')}
|
||||
>
|
||||
<Trash className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Delete</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -34,7 +34,7 @@ export default function EmailContent({ email }: EmailContentProps) {
|
||||
|
||||
setContent(
|
||||
<div
|
||||
className="email-content prose prose-sm max-w-none dark:prose-invert"
|
||||
className="email-content prose max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
|
||||
dir="auto"
|
||||
/>
|
||||
@ -58,30 +58,16 @@ export default function EmailContent({ email }: EmailContentProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-t mt-4 pt-4">
|
||||
<h3 className="text-sm font-medium mb-2 flex items-center gap-1">
|
||||
<Paperclip className="h-4 w-4" />
|
||||
Attachments ({email.attachments.length})
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="mt-6 border-t border-gray-200 pt-6">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-4">Attachments</h3>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{email.attachments.map((attachment, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center gap-1 text-xs"
|
||||
asChild
|
||||
>
|
||||
<a
|
||||
href={`/api/courrier/${email.id}/attachment/${encodeURIComponent(attachment.filename)}`}
|
||||
download={attachment.filename}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FileDown className="h-3 w-3 mr-1" />
|
||||
{attachment.filename} ({(attachment.size / 1024).toFixed(0)}KB)
|
||||
</a>
|
||||
</Button>
|
||||
<div key={index} className="flex items-center space-x-2 p-2 border rounded">
|
||||
<Paperclip className="h-4 w-4 text-gray-400" />
|
||||
<span className="text-sm text-gray-600 truncate">
|
||||
{attachment.filename}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@ -91,7 +77,7 @@ export default function EmailContent({ email }: EmailContentProps) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-full p-8">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -106,7 +92,7 @@ export default function EmailContent({ email }: EmailContentProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="p-6">
|
||||
{content}
|
||||
{renderAttachments()}
|
||||
</div>
|
||||
|
||||
@ -40,7 +40,7 @@ export default function EmailHeader({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-b flex flex-col">
|
||||
<div className="border-b bg-white/95 backdrop-blur-sm flex flex-col">
|
||||
{/* Courrier Title */}
|
||||
<div className="p-3 border-b border-gray-100">
|
||||
<div className="flex items-center gap-2">
|
||||
@ -52,13 +52,13 @@ export default function EmailHeader({
|
||||
<div className="px-4 py-2 flex items-center">
|
||||
<div className="flex-1">
|
||||
<form onSubmit={handleSearch} className="relative">
|
||||
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search emails..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-8 pr-8 h-9"
|
||||
className="pl-8 pr-8 h-9 bg-gray-50"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
@ -66,7 +66,7 @@ export default function EmailHeader({
|
||||
onClick={clearSearch}
|
||||
className="absolute right-2 top-1/2 transform -translate-y-1/2"
|
||||
>
|
||||
<X className="h-4 w-4 text-muted-foreground" />
|
||||
<X className="h-4 w-4 text-gray-400" />
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
@ -80,7 +80,7 @@ export default function EmailHeader({
|
||||
type="submit"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-8 w-8"
|
||||
className="h-8 w-8 text-gray-600 hover:text-gray-900"
|
||||
onClick={handleSearch}
|
||||
>
|
||||
<Search className="h-4 w-4" />
|
||||
@ -95,7 +95,7 @@ export default function EmailHeader({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 text-gray-600 hover:text-gray-900">
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Loader2, Mail } from 'lucide-react';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Email } from '@/hooks/use-courrier';
|
||||
import EmailListItem from './EmailListItem';
|
||||
@ -57,8 +57,8 @@ export default function EmailList({
|
||||
// Render loading state
|
||||
if (isLoading && emails.length === 0) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-full p-8">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<div className="flex justify-center items-center h-full p-8 bg-white/95 backdrop-blur-sm">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -66,12 +66,12 @@ export default function EmailList({
|
||||
// Render empty state
|
||||
if (emails.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center items-center h-full p-8 text-center">
|
||||
<h3 className="text-lg font-semibold mb-2">No emails found</h3>
|
||||
<p className="text-muted-foreground">
|
||||
<div className="flex flex-col justify-center items-center h-full p-8 text-center bg-white/95 backdrop-blur-sm">
|
||||
<Mail className="h-8 w-8 text-gray-400 mb-2" />
|
||||
<p className="text-gray-500 text-sm">
|
||||
{currentFolder === 'INBOX'
|
||||
? "Your inbox is empty. You're all caught up!"
|
||||
: `The ${currentFolder} folder is empty.`}
|
||||
: `No emails in this folder`}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -84,11 +84,13 @@ export default function EmailList({
|
||||
const someSelected = selectedEmailIds.length > 0 && selectedEmailIds.length < emails.length;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-col h-full bg-white/95 backdrop-blur-sm">
|
||||
<EmailListHeader
|
||||
allSelected={allSelected}
|
||||
someSelected={someSelected}
|
||||
onToggleSelectAll={onToggleSelectAll}
|
||||
currentFolder={currentFolder}
|
||||
totalEmails={totalEmails}
|
||||
/>
|
||||
|
||||
{selectedEmailIds.length > 0 && (
|
||||
@ -98,8 +100,11 @@ export default function EmailList({
|
||||
/>
|
||||
)}
|
||||
|
||||
<ScrollArea className="flex-1" onScroll={handleScroll}>
|
||||
<div className="divide-y">
|
||||
<div
|
||||
className="flex-1 overflow-y-auto"
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<div className="divide-y divide-gray-100">
|
||||
{emails.map((email) => (
|
||||
<EmailListItem
|
||||
key={email.id}
|
||||
@ -107,11 +112,11 @@ export default function EmailList({
|
||||
isSelected={selectedEmailIds.includes(email.id)}
|
||||
isActive={selectedEmail?.id === email.id}
|
||||
onSelect={() => onSelectEmail(email.id)}
|
||||
onToggleSelect={(e) => {
|
||||
onToggleSelect={(e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onToggleSelect(email.id);
|
||||
}}
|
||||
onToggleStarred={(e) => {
|
||||
onToggleStarred={(e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onToggleStarred(email.id);
|
||||
}}
|
||||
@ -119,12 +124,12 @@ export default function EmailList({
|
||||
))}
|
||||
|
||||
{isLoading && emails.length > 0 && (
|
||||
<div className="p-4 flex justify-center">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||
<div className="flex items-center justify-center p-4">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { ChevronDown, Inbox } from 'lucide-react';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -15,45 +15,37 @@ interface EmailListHeaderProps {
|
||||
allSelected: boolean;
|
||||
someSelected: boolean;
|
||||
onToggleSelectAll: () => void;
|
||||
currentFolder?: string;
|
||||
totalEmails?: number;
|
||||
}
|
||||
|
||||
export default function EmailListHeader({
|
||||
allSelected,
|
||||
someSelected,
|
||||
onToggleSelectAll,
|
||||
currentFolder = 'Inbox',
|
||||
totalEmails = 0
|
||||
}: EmailListHeaderProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between border-b px-4 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={allSelected}
|
||||
ref={(input) => {
|
||||
if (input) {
|
||||
(input as unknown as HTMLInputElement).indeterminate = someSelected && !allSelected;
|
||||
}
|
||||
}}
|
||||
onClick={onToggleSelectAll}
|
||||
/>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 p-0">
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem onClick={onToggleSelectAll}>
|
||||
{allSelected ? 'Unselect all' : 'Select all'}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onToggleSelectAll} disabled={!someSelected}>
|
||||
Unselect all
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Select messages to perform actions
|
||||
<div className="border-b border-gray-100 bg-white/95">
|
||||
<div className="flex items-center justify-between px-4 h-14">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
checked={allSelected}
|
||||
ref={(input) => {
|
||||
if (input) {
|
||||
(input as unknown as HTMLInputElement).indeterminate = someSelected && !allSelected;
|
||||
}
|
||||
}}
|
||||
onCheckedChange={onToggleSelectAll}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<h2 className="text-base font-semibold text-gray-900 capitalize">{currentFolder.toLowerCase()}</h2>
|
||||
</div>
|
||||
|
||||
<span className="text-sm text-gray-600">
|
||||
{totalEmails} emails
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -95,64 +95,69 @@ export default function EmailListItem({
|
||||
return `hsl(${h}, 70%, 80%)`;
|
||||
};
|
||||
|
||||
// Get preview text from email content
|
||||
const getPreviewText = () => {
|
||||
if (email.preview) return email.preview;
|
||||
|
||||
let content = email.content || '';
|
||||
|
||||
// Strip HTML tags if present
|
||||
content = content.replace(/<[^>]+>/g, ' ');
|
||||
|
||||
// Clean up whitespace
|
||||
content = content.replace(/\s+/g, ' ').trim();
|
||||
|
||||
// Limit to ~70 chars
|
||||
if (content.length > 70) {
|
||||
return content.substring(0, 70) + '...';
|
||||
}
|
||||
|
||||
return content || 'No preview available';
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-start gap-2 p-3 cursor-pointer transition-colors hover:bg-accent/50 relative',
|
||||
isActive && 'bg-accent',
|
||||
!email.read && 'font-medium'
|
||||
'flex items-center gap-3 px-4 py-2 hover:bg-gray-50/80 cursor-pointer',
|
||||
isActive ? 'bg-blue-50/50' : '',
|
||||
!email.read ? 'bg-blue-50/20' : ''
|
||||
)}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-[3rem]">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onClick={onToggleSelect}
|
||||
className="mt-1"
|
||||
/>
|
||||
<div onClick={onToggleStarred}>
|
||||
<Star
|
||||
className={cn(
|
||||
'h-5 w-5 cursor-pointer transition-colors',
|
||||
email.starred ? 'fill-yellow-400 text-yellow-400' : 'text-muted-foreground hover:text-yellow-400'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Avatar className="mt-1 h-8 w-8 font-semibold text-sm" style={{ backgroundColor: getAvatarColor() }}>
|
||||
<AvatarFallback>{getSenderInitial()}</AvatarFallback>
|
||||
</Avatar>
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onClick={onToggleSelect}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex justify-between items-baseline mb-1">
|
||||
<div className="font-medium truncate">
|
||||
{getSenderName()}
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span className={`text-sm truncate ${!email.read ? 'font-semibold text-gray-900' : 'text-gray-600'}`}>
|
||||
{getSenderName()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground whitespace-nowrap ml-2">
|
||||
{formatDate(email.date)}
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
<span className="text-xs text-gray-500 whitespace-nowrap">
|
||||
{formatDate(email.date)}
|
||||
</span>
|
||||
<button
|
||||
className="h-6 w-6 text-gray-400 hover:text-yellow-400"
|
||||
onClick={onToggleStarred}
|
||||
>
|
||||
<Star className={`h-4 w-4 ${email.starred ? 'fill-yellow-400 text-yellow-400' : ''}`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-baseline">
|
||||
<div className="font-medium truncate">
|
||||
{email.subject || '(No subject)'}
|
||||
</div>
|
||||
{email.hasAttachments && (
|
||||
<Badge variant="outline" className="ml-2 text-xs">📎</Badge>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm text-gray-900 truncate">
|
||||
{email.subject || '(No subject)'}
|
||||
</h3>
|
||||
|
||||
<div className="text-sm text-muted-foreground truncate mt-1">
|
||||
{email.preview || 'No preview available'}
|
||||
<div className="text-xs text-gray-500 truncate">
|
||||
{getPreviewText()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!email.read && (
|
||||
<div className="absolute right-3 top-3">
|
||||
<div className="h-2 w-2 rounded-full bg-primary"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -65,23 +65,23 @@ export default function EmailSidebar({
|
||||
);
|
||||
|
||||
return (
|
||||
<aside className="w-60 border-r h-full flex flex-col">
|
||||
<div className="p-4">
|
||||
<aside className="w-60 border-r h-full flex flex-col bg-white/95 backdrop-blur-sm">
|
||||
<div className="p-2 border-b border-gray-100">
|
||||
<Button
|
||||
className="w-full gap-2"
|
||||
className="w-full gap-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center justify-center transition-all py-1.5 text-sm"
|
||||
size="sm"
|
||||
onClick={onCompose}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
Compose
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="px-4 py-2">
|
||||
<div className="px-2 py-2 border-b border-gray-100 flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-start gap-2"
|
||||
size="icon"
|
||||
className="text-gray-600 hover:text-gray-900 hover:bg-gray-100 h-8 w-8"
|
||||
onClick={onRefresh}
|
||||
disabled={isLoading}
|
||||
>
|
||||
@ -89,48 +89,66 @@ export default function EmailSidebar({
|
||||
"h-4 w-4",
|
||||
isLoading && "animate-spin"
|
||||
)} />
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1">
|
||||
<div className="space-y-1 p-2">
|
||||
<div className="text-xs font-medium text-muted-foreground px-2 py-1">
|
||||
FOLDERS
|
||||
<div className="p-3">
|
||||
<div className="text-sm font-medium text-gray-500 mb-2">
|
||||
Folders
|
||||
</div>
|
||||
|
||||
{/* Standard folders */}
|
||||
{visibleStandardFolders.map(folder => (
|
||||
<Button
|
||||
key={folder}
|
||||
variant={currentFolder === folder ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
className="w-full justify-start gap-2"
|
||||
onClick={() => onFolderChange(folder)}
|
||||
>
|
||||
{getFolderIcon(folder)}
|
||||
<span className="capitalize">{folder.toLowerCase()}</span>
|
||||
</Button>
|
||||
))}
|
||||
<ul className="space-y-0.5 px-2">
|
||||
{visibleStandardFolders.map(folder => (
|
||||
<li key={folder}>
|
||||
<Button
|
||||
variant={currentFolder === folder ? "secondary" : "ghost"}
|
||||
className={`w-full justify-start py-2 ${
|
||||
currentFolder === folder ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
onClick={() => onFolderChange(folder)}
|
||||
>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center">
|
||||
{getFolderIcon(folder)}
|
||||
<span className="ml-2 capitalize">{folder.toLowerCase()}</span>
|
||||
</div>
|
||||
{folder === 'INBOX' && (
|
||||
<span className="ml-auto bg-blue-600 text-white text-xs px-2 py-0.5 rounded-full">
|
||||
{/* Unread count would go here */}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Custom folders section */}
|
||||
{customFolders.length > 0 && (
|
||||
<>
|
||||
<div className="text-xs font-medium text-muted-foreground px-2 py-1 mt-4">
|
||||
CUSTOM FOLDERS
|
||||
<div className="text-sm font-medium text-gray-500 mt-4 mb-2">
|
||||
Custom Folders
|
||||
</div>
|
||||
{customFolders.map(folder => (
|
||||
<Button
|
||||
key={folder}
|
||||
variant={currentFolder === folder ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
className="w-full justify-start gap-2"
|
||||
onClick={() => onFolderChange(folder)}
|
||||
>
|
||||
{getFolderIcon(folder)}
|
||||
<span className="truncate">{folder}</span>
|
||||
</Button>
|
||||
))}
|
||||
<ul className="space-y-0.5 px-2">
|
||||
{customFolders.map(folder => (
|
||||
<li key={folder}>
|
||||
<Button
|
||||
variant={currentFolder === folder ? "secondary" : "ghost"}
|
||||
className={`w-full justify-start py-2 ${
|
||||
currentFolder === folder ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
onClick={() => onFolderChange(folder)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{getFolderIcon(folder)}
|
||||
<span className="ml-2 truncate">{folder}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user