From 2a7aa20c4d491cac7e162cee5980fa20ace03913 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sat, 28 Jun 2025 19:30:35 -0300 Subject: [PATCH] Add new themes: Orange and XTree Gold - Introduced a new CSS theme for Orange, featuring a modern color palette with distinct foreground and background colors. - Added an XTree Gold theme that emulates the classic 1980s DOS interface, complete with authentic colors and styles for file management elements. - Both themes include variables for customization and specific styles for various UI components such as cards, popovers, and menus. --- app/calendar/page.tsx | 982 +++++++++++++++++- app/client-nav.tsx | 14 +- app/drive/components/FileBrowser.tsx | 54 - app/drive/components/FileOperations.tsx | 67 -- app/drive/components/FileTree.tsx | 59 -- app/drive/page.tsx | 471 ++++++--- app/footer.tsx | 132 +++ app/layout.tsx | 15 +- app/mail/page.tsx | 40 + app/media/page.tsx | 696 ++++++++++++- app/paper/page.tsx | 339 +++++- app/{templates => sources}/README.md | 0 app/sources/page.tsx | 897 ++++++++++++++++ app/{templates => sources}/prompts.csv | 0 app/templates/components/album-artwork.tsx | 36 - app/templates/components/menu.tsx | 13 - .../components/podcast-empty-placeholder.tsx | 17 - app/templates/components/sidebar.tsx | 41 - app/templates/page.tsx | 213 ---- app/theme-provider.tsx | 5 +- public/themes/3dbevel.css | 92 +- public/themes/oldhollywood.css | 28 - public/themes/orange.css | 27 + public/themes/xeroxui.css | 95 +- public/themes/xtreegold.css | 228 ++++ 25 files changed, 3823 insertions(+), 738 deletions(-) delete mode 100644 app/drive/components/FileBrowser.tsx delete mode 100644 app/drive/components/FileOperations.tsx delete mode 100644 app/drive/components/FileTree.tsx create mode 100644 app/footer.tsx rename app/{templates => sources}/README.md (100%) create mode 100644 app/sources/page.tsx rename app/{templates => sources}/prompts.csv (100%) delete mode 100644 app/templates/components/album-artwork.tsx delete mode 100644 app/templates/components/menu.tsx delete mode 100644 app/templates/components/podcast-empty-placeholder.tsx delete mode 100644 app/templates/components/sidebar.tsx delete mode 100644 app/templates/page.tsx delete mode 100644 public/themes/oldhollywood.css create mode 100644 public/themes/orange.css create mode 100644 public/themes/xtreegold.css diff --git a/app/calendar/page.tsx b/app/calendar/page.tsx index 0489688..56549e4 100644 --- a/app/calendar/page.tsx +++ b/app/calendar/page.tsx @@ -1,8 +1,976 @@ -'use client' +"use client"; +import React, { useState, useMemo } from 'react'; +import { + Calendar, ChevronLeft, ChevronRight, Plus, Search, Settings, + Clock, Users, MapPin, Bell, Repeat, Mail, Phone, Video, + Edit3, Trash2, Copy, Forward, Reply, MoreHorizontal, + Filter, RefreshCw, Print, Import, Export, Share2, + ChevronDown, User, Building, Globe, AlertCircle, + CheckCircle2, X, Eye, EyeOff, Star, Flag, Tag +} from 'lucide-react'; +import { cn } from "@/lib/utils"; -export default function MainPage() { - return ( -
-
- ) -} \ No newline at end of file +// Sample calendar data +const sampleEvents = [ + { + id: 1, + title: "Team Standup", + start: "2025-06-28T09:00:00", + end: "2025-06-28T09:30:00", + type: "meeting", + location: "Conference Room A", + attendees: ["john@company.com", "jane@company.com", "mike@company.com"], + description: "Daily team synchronization meeting", + priority: "high", + status: "accepted", + organizer: "sarah@company.com", + category: "work", + recurring: true + }, + { + id: 2, + title: "Client Presentation", + start: "2025-06-28T14:00:00", + end: "2025-06-28T15:30:00", + type: "meeting", + location: "Board Room", + attendees: ["client@external.com", "manager@company.com"], + description: "Q2 results presentation to key client", + priority: "high", + status: "tentative", + organizer: "me@company.com", + category: "important", + recurring: false + }, + { + id: 3, + title: "Lunch with Marketing Team", + start: "2025-06-28T12:00:00", + end: "2025-06-28T13:00:00", + type: "personal", + location: "Downtown Cafe", + attendees: ["marketing@company.com"], + description: "Monthly team lunch", + priority: "normal", + status: "accepted", + organizer: "marketing@company.com", + category: "social", + recurring: true + }, + { + id: 4, + title: "Project Review", + start: "2025-06-29T10:00:00", + end: "2025-06-29T11:00:00", + type: "meeting", + location: "Online", + attendees: ["team@company.com"], + description: "Weekly project status review", + priority: "normal", + status: "accepted", + organizer: "me@company.com", + category: "work", + recurring: true + }, + { + id: 5, + title: "Doctor Appointment", + start: "2025-06-30T15:00:00", + end: "2025-06-30T16:00:00", + type: "personal", + location: "Medical Center", + attendees: [], + description: "Annual checkup", + priority: "normal", + status: "accepted", + organizer: "me@company.com", + category: "personal", + recurring: false + } +]; + +const categoriesData = [ + { name: "Work", color: "blue", visible: true, count: 12 }, + { name: "Personal", color: "green", visible: true, count: 5 }, + { name: "Important", color: "red", visible: true, count: 3 }, + { name: "Social", color: "purple", visible: true, count: 8 }, + { name: "Travel", color: "orange", visible: false, count: 2 }, + { name: "Family", color: "pink", visible: true, count: 4 } +]; + +// Date utilities +const formatDate = (date) => { + return new Intl.DateTimeFormat('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + }).format(date); +}; + +const formatTime = (dateString) => { + return new Intl.DateTimeFormat('en-US', { + hour: 'numeric', + minute: '2-digit', + hour12: true + }).format(new Date(dateString)); +}; + +const formatDateShort = (date) => { + return new Intl.DateTimeFormat('en-US', { + month: 'short', + day: 'numeric' + }).format(date); +}; + +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + DropdownMenuSeparator, +} from "@/components/ui/dropdown-menu"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, + ContextMenuSeparator, +} from "@/components/ui/context-menu"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, + TooltipProvider, +} from "@/components/ui/tooltip"; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/ui/resizable"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import Footer from '../footer'; + +const CalendarSidebar = ({ isCollapsed, categories, onCategoryToggle, currentDate, onDateSelect }) => { + const [selectedCategories, setSelectedCategories] = useState( + categories.filter(cat => cat.visible).map(cat => cat.name) + ); + + const toggleCategory = (categoryName) => { + const newSelected = selectedCategories.includes(categoryName) + ? selectedCategories.filter(name => name !== categoryName) + : [...selectedCategories, categoryName]; + setSelectedCategories(newSelected); + onCategoryToggle(categoryName); + }; + + const generateCalendarDays = () => { + const today = new Date(); + const currentMonth = today.getMonth(); + const currentYear = today.getFullYear(); + const firstDay = new Date(currentYear, currentMonth, 1); + const lastDay = new Date(currentYear, currentMonth + 1, 0); + const startDate = new Date(firstDay); + startDate.setDate(startDate.getDate() - firstDay.getDay()); + + const days = []; + for (let i = 0; i < 42; i++) { + const date = new Date(startDate); + date.setDate(startDate.getDate() + i); + days.push(date); + } + return days; + }; + + const calendarDays = generateCalendarDays(); + const today = new Date(); + + const quickActions = [ + { icon: Plus, label: "New Event", action: "new-event" }, + { icon: Users, label: "New Meeting", action: "new-meeting" }, + ]; + + if (isCollapsed) { + return ( +
+ {quickActions.map((action, index) => ( + + + + + + {action.label} + + + ))} +
+ ); + } + + return ( +
+ {/* Quick Actions */} +
+

Quick Actions

+
+ {quickActions.map((action, index) => ( + + ))} +
+
+ + {/* Mini Calendar */} +
+
+

+ {today.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} +

+
+ + +
+
+
+ {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map(day => ( +
+ {day} +
+ ))} + {calendarDays.map((date, index) => { + const isToday = date.toDateString() === today.toDateString(); + const isCurrentMonth = date.getMonth() === today.getMonth(); + return ( + + ); + })} +
+
+ + {/* Categories */} +
+

My Calendars

+ +
+ {categories.map((category) => ( +
+ + ({category.count}) +
+ ))} +
+
+
+
+ ); +}; + +const EventCard = ({ event, onAction }) => { + const getCategoryColor = (category) => { + const colors = { + work: "bg-blue-500/20 border-l-blue-500 text-foreground", + personal: "bg-green-500/20 border-l-green-500 text-foreground", + important: "bg-red-500/20 border-l-red-500 text-foreground", + social: "bg-purple-500/20 border-l-purple-500 text-foreground" + }; + return colors[category] || "bg-secondary border-l-muted text-foreground"; + }; + + const getPriorityIcon = (priority) => { + if (priority === 'high') return ; + return null; + }; + + const getStatusIcon = (status) => { + switch (status) { + case 'accepted': return ; + case 'tentative': return ; + case 'declined': return ; + default: return null; + } + }; + + return ( + + +
+
+
+ {getPriorityIcon(event.priority)} +

{event.title}

+
+
+ {event.recurring && } + {getStatusIcon(event.status)} +
+
+
+
+ + {formatTime(event.start)} - {formatTime(event.end)} +
+ {event.location && ( +
+ + {event.location} +
+ )} + {event.attendees.length > 0 && ( +
+ + {event.attendees.length} attendees +
+ )} +
+
+
+ + onAction('open', event)} className="hover:bg-primary hover:text-primary-foreground"> + + Open + + onAction('edit', event)} className="hover:bg-primary hover:text-primary-foreground"> + + Edit + + + onAction('forward', event)} className="hover:bg-primary hover:text-primary-foreground"> + + Forward + + onAction('copy', event)} className="hover:bg-primary hover:text-primary-foreground"> + + Copy + + + onAction('delete', event)} className="text-destructive hover:bg-primary hover:text-primary-foreground"> + + Delete + + +
+ ); +}; + +const CalendarView = ({ view, events, currentDate, onEventAction }) => { + const filteredEvents = events.filter(event => { + const eventDate = new Date(event.start); + if (view === 'day') { + return eventDate.toDateString() === currentDate.toDateString(); + } else if (view === 'week') { + const weekStart = new Date(currentDate); + weekStart.setDate(currentDate.getDate() - currentDate.getDay()); + const weekEnd = new Date(weekStart); + weekEnd.setDate(weekStart.getDate() + 6); + return eventDate >= weekStart && eventDate <= weekEnd; + } else if (view === 'month') { + return eventDate.getMonth() === currentDate.getMonth() && + eventDate.getFullYear() === currentDate.getFullYear(); + } + return true; + }); + + if (view === 'day') { + const hours = Array.from({ length: 24 }, (_, i) => i); + const dayEvents = filteredEvents.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()); + + return ( +
+
+

+ {formatDate(currentDate)} +

+
+ +
+ {dayEvents.length > 0 ? ( + dayEvents.map(event => ( + + )) + ) : ( +
+ +

No events scheduled for this day

+
+ )} +
+
+
+ ); + } + + if (view === 'week') { + const weekStart = new Date(currentDate); + weekStart.setDate(currentDate.getDate() - currentDate.getDay()); + const weekDays = Array.from({ length: 7 }, (_, i) => { + const day = new Date(weekStart); + day.setDate(weekStart.getDate() + i); + return day; + }); + + return ( +
+
+

+ Week of {formatDateShort(weekStart)} - {formatDateShort(weekDays[6])} +

+
+
+ {weekDays.map((day, index) => { + const dayEvents = filteredEvents.filter(event => + new Date(event.start).toDateString() === day.toDateString() + ); + const isToday = day.toDateString() === new Date().toDateString(); + + return ( +
+
+
{day.toLocaleDateString('en-US', { weekday: 'short' })}
+
{day.getDate()}
+
+
+ {dayEvents.map(event => ( +
onEventAction('open', event)} + > +
{event.title}
+
{formatTime(event.start)}
+
+ ))} +
+
+ ); + })} +
+
+ ); + } + + // Month view + const monthStart = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); + const monthEnd = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); + const calendarStart = new Date(monthStart); + calendarStart.setDate(calendarStart.getDate() - monthStart.getDay()); + + const calendarDays = Array.from({ length: 42 }, (_, i) => { + const day = new Date(calendarStart); + day.setDate(calendarStart.getDate() + i); + return day; + }); + + return ( +
+
+

+ {currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })} +

+
+
+ {['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].map(day => ( +
+ {day.substring(0, 3)} +
+ ))} + {calendarDays.map((day, index) => { + const dayEvents = filteredEvents.filter(event => + new Date(event.start).toDateString() === day.toDateString() + ); + const isToday = day.toDateString() === new Date().toDateString(); + const isCurrentMonth = day.getMonth() === currentDate.getMonth(); + + return ( +
+
+ {day.getDate()} +
+
+ {dayEvents.slice(0, 3).map(event => ( +
onEventAction('open', event)} + > +
{event.title}
+
+ ))} + {dayEvents.length > 3 && ( +
+ +{dayEvents.length - 3} more +
+ )} +
+
+ ); + })} +
+
+ ); +}; + +const EventDetails = ({ event }) => { + if (!event) { + return ( +
+ +
No event selected
+
Select an event to view details
+
+ ); + } + + return ( +
+ {/* Header */} +
+
+

{event.title}

+ + + + + + + + Edit + + + + Forward + + + + Reply + + + + + Delete + + + +
+ + {event.category} + +
+ + {/* Content */} + +
+ {/* Time */} +
+ +
+
+ {formatTime(event.start)} - {formatTime(event.end)} +
+
+ {new Date(event.start).toLocaleDateString('en-US', { + weekday: 'long', + month: 'long', + day: 'numeric', + year: 'numeric' + })} +
+
+
+ + {/* Location */} + {event.location && ( +
+ + {event.location} +
+ )} + + {/* Organizer */} +
+ +
+
Organizer
+
{event.organizer}
+
+
+ + {/* Attendees */} + {event.attendees.length > 0 && ( +
+ +
+
Attendees ({event.attendees.length})
+
+ {event.attendees.map((attendee, index) => ( +
+
+ {attendee} +
+ ))} +
+
+
+ )} + + {/* Description */} + {event.description && ( +
+
Description
+
+ {event.description} +
+
+ )} + + {/* Properties */} +
+
+ Priority + + {event.priority} + +
+
+ Status + + {event.status} + +
+
+ Recurring + + {event.recurring ? 'Yes' : 'No'} + +
+
+
+
+ + {/* Footer */} +
+ + + +
+
+ ); +}; + +const CalendarPage = () => { + const [currentDate, setCurrentDate] = useState(new Date()); + const [selectedEvent, setSelectedEvent] = useState(null); + const [categories, setCategories] = useState(categoriesData); + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + + const handleEventAction = (action, event) => { + switch (action) { + case 'open': + setSelectedEvent(event); + break; + case 'edit': + // Handle edit + break; + case 'delete': + // Handle delete + break; + default: + break; + } + }; + + const handleCategoryToggle = (categoryName) => { + setCategories(categories.map(cat => + cat.name === categoryName ? { ...cat, visible: !cat.visible } : cat + )); + }; + + const handleDateSelect = (date) => { + setCurrentDate(date); + }; + + const shortcuts = [ + // Calendar actions row (unique qkeys) + [ + { key: 'Q', label: 'New Event', action: () => console.log('New Event') }, + { key: 'W', label: 'New Meeting', action: () => console.log('New Meeting') }, + { key: 'E', label: 'Edit Event', action: () => console.log('Edit Event') }, + { key: 'R', label: 'Refresh', action: () => console.log('Refresh') }, + { key: 'T', label: 'Today', action: () => setCurrentDate(new Date()) }, + { key: 'Y', label: 'Delete Event', action: () => console.log('Delete Event') }, + { key: 'U', label: 'Duplicate', action: () => console.log('Duplicate Event') }, + { key: 'I', label: 'Invite', action: () => console.log('Invite Attendees') }, + { key: 'O', label: 'Open Event', action: () => console.log('Open Event') }, + { key: 'P', label: 'Print', action: () => console.log('Print Calendar') }, + ], + // Navigation/info row (unique qkeys) + [ + { key: 'A', label: 'Accept', action: () => console.log('Accept Invitation') }, + { key: 'S', label: 'Send', action: () => console.log('Send Invitation') }, + { key: 'D', label: 'Decline', action: () => console.log('Decline Invitation') }, + { key: 'G', label: 'Go to Date', action: () => console.log('Go to Date') }, + { key: 'H', label: 'Help', action: () => console.log('Help') }, + { key: 'J', label: 'Share', action: () => console.log('Share Calendar') }, + { key: 'K', label: 'Show/Hide Weekends', action: () => console.log('Toggle Weekends') }, + { key: 'L', label: 'List View', action: () => console.log('List View') }, + { key: 'Z', label: 'Month View', action: () => console.log('Month View') }, + { key: 'X', label: 'Week View', action: () => console.log('Week View') }, + ] + ]; + + return ( +
+
+ {/* Sidebar */} + + + {/* Main Content */} +
+ {/* Toolbar */} +
+
+ + +
+ + +
+
+ {formatDate(currentDate)} +
+
+
+ + + + + + + + Refresh + + + + Filters + + + + + Print + + + + Share + + + +
+
+ + {/* Calendar Area */} +
+ {/* Three calendar views on the left */} +
+
+ +
+
+ +
+
+ +
+
+ + {/* Event details on the right */} +
+ +
+
+
+
+
+
+ ); +}; + +export default CalendarPage; \ No newline at end of file diff --git a/app/client-nav.tsx b/app/client-nav.tsx index 295a8bc..d281c61 100644 --- a/app/client-nav.tsx +++ b/app/client-nav.tsx @@ -7,16 +7,16 @@ import { useTheme } from './theme-provider'; const examples = [ { name: "Chat", href: "/chat", color: "#25D366" }, // WhatsApp green - { name: "Dashboard", href: "/dashboard", color: "#6366F1" }, // Indigo + { name: "Paper", href: "/paper", color: "#6366F1" }, // Indigo { name: "Mail", href: "/mail", color: "#FFD700" }, // Outlook yellow { name: "Calendar", href: "/calendar", color: "#1DB954" }, // Spotify green { name: "Meet", href: "/meet", color: "#059669" }, // Google Meet green { name: "Drive", href: "/drive", color: "#10B981" }, // Emerald green { name: "Editor", href: "/editor", color: "#2563EB" }, // Word blue + { name: "Player", href: "/player", color: "Yellow" }, // YouTube red { name: "Tables", href: "/tables", color: "#8B5CF6" }, // Purple - { name: "Media", href: "/media", color: "Yellow" }, - { name: "News", href: "/news", color: "blue" }, - { name: "Templates", href: "/templates", color: "#F59E0B" }, // Amber + { name: "Dashboard", href: "/dashboard", color: "#6366F1" }, // Indigo + { name: "Sources", href: "/sources", color: "#F59E0B" }, // Amber { name: "Settings", href: "/settings", color: "#6B7280" }, // Gray ]; @@ -145,6 +145,7 @@ export function Nav() { case 'seasidepostcard': return '🏖️'; case 'typewriter': return '⌨️'; case 'jazzage': return '🎷'; + case 'xtreegold': return 'X'; default: return '🎨'; } }; @@ -263,8 +264,9 @@ export function Nav() { right: 0; z-index: 50; background: hsl(var(--background)); - height: 40px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); + height: auto; + min-height: 40px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); border-bottom: 1px solid hsl(var(--border)); } diff --git a/app/drive/components/FileBrowser.tsx b/app/drive/components/FileBrowser.tsx deleted file mode 100644 index d6e2f49..0000000 --- a/app/drive/components/FileBrowser.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useState, useEffect } from 'react'; -import { core } from '@tauri-apps/api'; - -interface FileItem { - name: string; - path: string; - is_dir: boolean; -} - -interface FileBrowserProps { - path: string; - onFileSelect?: (path: string) => void; -} - -export function FileBrowser({ path, onFileSelect }: FileBrowserProps) { - const [files, setFiles] = useState([]); - const [loading, setLoading] = useState(false); - - useEffect(() => { - const loadFiles = async () => { - setLoading(true); - try { - const result = await core.invoke('list_files', { path }); - setFiles(result); - } catch (error) { - console.error('Error listing files:', error); - } finally { - setLoading(false); - } - }; - loadFiles(); - }, [path]); - - return ( -
-

File Browser: {path || 'Root'}

- {loading ? ( -

Loading...

- ) : ( -
    - {files.map((file) => ( -
  • onFileSelect && onFileSelect(file.path)} - > - {file.is_dir ? '📁' : '📄'} {file.name} -
  • - ))} -
- )} -
- ); -} diff --git a/app/drive/components/FileOperations.tsx b/app/drive/components/FileOperations.tsx deleted file mode 100644 index 30506e2..0000000 --- a/app/drive/components/FileOperations.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { core } from '@tauri-apps/api'; - -import { useState } from 'react'; - - -interface FileOperationsProps { - currentPath: string; - onRefresh: () => void; -} - -export function FileOperations({ currentPath, onRefresh }: FileOperationsProps) { - const [uploadProgress, setUploadProgress] = useState(0); - - const handleUpload = async () => { - try { - } catch (error) { - console.error('Upload failed:', error); - alert('Upload failed!'); - } finally { - setUploadProgress(0); - } - }; - - const createFolder = async () => { - const folderName = prompt('Enter folder name:'); - if (folderName) { - try { - await core.invoke('create_folder', { - path: currentPath, - name: folderName - }); - onRefresh(); - } catch (error) { - console.error('Error creating folder:', error); - alert('Failed to create folder'); - } - } - }; - - return ( -
-

File Operations

-
- - -
- {uploadProgress > 0 && ( -
-
-
- )} -
- ); -} diff --git a/app/drive/components/FileTree.tsx b/app/drive/components/FileTree.tsx deleted file mode 100644 index e3688df..0000000 --- a/app/drive/components/FileTree.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useState, useEffect } from 'react'; -import { core } from '@tauri-apps/api'; -import { FileBrowser } from './FileBrowser'; - -interface FileItem { - name: string; - path: string; - is_dir: boolean; -} - -interface FileTreeProps { - onSelect: (path: string) => void; -} - -export function FileTree({ onSelect }: FileTreeProps) { - const [tree, setTree] = useState([]); - const [expanded, setExpanded] = useState>({}); - - useEffect(() => { - const loadTree = async () => { - try { - const result = await core.invoke('list_files', { path: '' }); - setTree(result.filter(item => item.is_dir)); - } catch (error) { - console.error('Error loading file tree:', error); - } - }; - loadTree(); - }, []); - - const toggleExpand = async (path: string) => { - setExpanded(prev => ({ ...prev, [path]: !prev[path] })); - onSelect(path); - }; - - return ( -
-

File Tree

-
    - {tree.map((item) => ( -
  • -
    toggleExpand(item.path)} - className="flex items-center cursor-pointer hover:bg-gray-100 p-1 rounded" - > - {expanded[item.path] ? '📂' : '📁'} - {item.name} -
    - {expanded[item.path] && ( -
    - -
    - )} -
  • - ))} -
-
- ); -} diff --git a/app/drive/page.tsx b/app/drive/page.tsx index 960c092..b10b90a 100644 --- a/app/drive/page.tsx +++ b/app/drive/page.tsx @@ -1,19 +1,23 @@ "use client"; -import React, { useState, useMemo } from 'react'; -import { - Search, Plus, Upload, Download, Trash2, Share, Star, - Grid, List, MoreVertical, Home, ChevronRight, - Folder, File, Image, Video, Music, FileText, Code, Database, - Package, Archive, Clock, Users, Eye, Edit3 +import React, { useState, useMemo, useRef } from 'react'; +import { + Search, Plus, Upload, Download, Trash2, Share, Star, + Grid, List, MoreVertical, Home, ChevronRight, + Folder, File, Image, Video, Music, FileText, Code, Database, + Package, Archive, Clock, Users, Eye, Edit3, Copy, Scissors, + FolderPlus, FilePlus, RefreshCw, Info, Lock, Unlock, + ArrowRight, ExternalLink, History, Settings } from 'lucide-react'; import { cn } from "@/lib/utils"; +import Footer from '../footer'; + // Simple date formatting functions const formatDate = (dateString) => { const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric' + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' }); }; @@ -21,7 +25,7 @@ const formatDateTime = (dateString) => { const date = new Date(dateString); return date.toLocaleString('en-US', { month: 'short', - day: 'numeric', + day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', @@ -35,13 +39,14 @@ const formatDistanceToNow = (date) => { const diffMinutes = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); - + if (diffMinutes < 1) return 'now'; if (diffMinutes < 60) return `${diffMinutes}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return formatDate(date); }; + import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -52,7 +57,21 @@ import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, } from "@/components/ui/dropdown-menu"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, +} from "@/components/ui/context-menu"; import { Tooltip, TooltipContent, @@ -182,7 +201,7 @@ const getFileIcon = (item) => { if (item.is_dir) { return ; } - + const iconMap = { pdf: , xlsx: , @@ -195,7 +214,7 @@ const getFileIcon = (item) => { mp4: