Refactor code structure for improved readability and maintainability
Some checks failed
GBCI / build (push) Has been cancelled

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-06-28 09:58:11 -03:00
parent 1954b928dc
commit c95d1d9deb
18 changed files with 2179 additions and 787 deletions

View file

@ -9,12 +9,13 @@ const examples = [
{ name: "Chat", href: "/chat", color: "#25D366" }, // WhatsApp green
{ name: "Dashboard", href: "/dashboard", 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: "Tables", href: "/tables", color: "#8B5CF6" }, // Purple
{ name: "Meet", href: "/meet", color: "#059669" }, // Google Meet green
{ name: "Videos", href: "/videos", color: "#DC2626" }, // YouTube red
{ name: "Music", href: "/music", color: "#1DB954" }, // Spotify green
{ name: "Media", href: "/media", color: "Yellow" },
{ name: "News", href: "/news", color: "blue" },
{ name: "Templates", href: "/templates", color: "#F59E0B" }, // Amber
{ name: "Settings", href: "/settings", color: "#6B7280" }, // Gray
];

View file

@ -1,34 +0,0 @@
"use client";
import React, { useState } from 'react';
import { format } from 'date-fns';
export function CalendarDateRangePicker() {
const [dateRange, setDateRange] = useState({
startDate: new Date(),
endDate: new Date()
});
return (
<div className="flex items-center gap-2">
<button
className="px-3 py-1 border rounded"
onClick={() => {
const date = new Date(prompt("Enter start date (YYYY-MM-DD)") || dateRange.startDate);
setDateRange(prev => ({ ...prev, startDate: date }));
}}
>
Start: {format(dateRange.startDate, 'MMM dd, yyyy')}
</button>
<span>to</span>
<button
className="px-3 py-1 border rounded"
onClick={() => {
const date = new Date(prompt("Enter end date (YYYY-MM-DD)") || dateRange.endDate);
setDateRange(prev => ({ ...prev, endDate: date }));
}}
>
End: {format(dateRange.endDate, 'MMM dd, yyyy')}
</button>
</div>
);
}

View file

@ -1,16 +0,0 @@
import React from 'react';
export function MainNav() {
return (
<nav className="flex space-x-4">
{['Overview', 'Customers', 'Products', 'Settings'].map((item) => (
<button
key={item}
className="px-3 py-2 text-sm font-medium hover:text-primary"
>
{item}
</button>
))}
</nav>
);
}

View file

@ -1,8 +0,0 @@
import React from 'react';
export function Overview() {
return (
<div className="p-4 border rounded-lg">
</div>
);
}

View file

@ -1,30 +0,0 @@
import React from 'react';
const salesData = [
{ name: "Olivia Martin", email: "olivia.martin@email.com", amount: "+$1,999.00" },
{ name: "Jackson Lee", email: "jackson.lee@email.com", amount: "+$39.00" },
{ name: "Isabella Nguyen", email: "isabella.nguyen@email.com", amount: "+$299.00" },
{ name: "William Kim", email: "will@email.com", amount: "+$99.00" },
{ name: "Sofia Davis", email: "sofia.davis@email.com", amount: "+$39.00" },
];
export function RecentSales() {
return (
<div className="space-y-4">
{salesData.map((item, index) => (
<div key={index} className="flex items-center justify-between p-2 border-b">
<div className="flex items-center space-x-3">
<div className="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
{item.name[0]}
</div>
<div>
<p className="font-medium">{item.name}</p>
<p className="text-sm text-gray-500">{item.email}</p>
</div>
</div>
<span className="font-medium">{item.amount}</span>
</div>
))}
</div>
);
}

View file

@ -1,21 +0,0 @@
import React from 'react';
export function Search() {
return (
<div className="relative max-w-md">
<input
type="text"
placeholder="Search..."
className="w-full pl-8 pr-4 py-2 rounded-full border focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<svg
className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
);
}

View file

@ -1,106 +0,0 @@
"use client";
import React, { useState } from 'react';
const groups = [
{
label: "Personal Account",
teams: [
{ label: "Alicia Koch", value: "personal" },
],
},
{
label: "Teams",
teams: [
{ label: "Acme Inc.", value: "acme-inc" },
{ label: "Monsters Inc.", value: "monsters" },
],
},
];
export function TeamSwitcher() {
const [open, setOpen] = useState(false);
const [showNewTeamDialog, setShowNewTeamDialog] = useState(false);
const [selectedTeam, setSelectedTeam] = useState(groups[0].teams[0]);
return (
<div className="relative">
<button
onClick={() => setOpen(true)}
className="flex items-center space-x-2"
>
<div className="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
{selectedTeam.label[0]}
</div>
<span>{selectedTeam.label}</span>
</button>
{open && (
<div className="absolute z-10 mt-2 w-56 bg-white rounded-md shadow-lg">
<div className="p-2">
<input
type="text"
placeholder="Search team..."
className="w-full p-2 border rounded"
/>
</div>
{groups.map((group) => (
<div key={group.label} className="py-1">
<p className="px-3 py-1 text-sm font-medium text-gray-500">{group.label}</p>
{group.teams.map((team) => (
<button
key={team.value}
onClick={() => {
setSelectedTeam(team);
setOpen(false);
}}
className="w-full text-left px-3 py-2 hover:bg-gray-100"
>
{team.label}
</button>
))}
</div>
))}
<div className="border-t p-2">
<button
onClick={() => {
setOpen(false);
setShowNewTeamDialog(true);
}}
className="w-full p-2 text-left hover:bg-gray-100"
>
Create Team
</button>
</div>
</div>
)}
{showNewTeamDialog && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="bg-white p-4 rounded-md">
<h3 className="text-lg font-medium mb-4">Create team</h3>
<input
type="text"
placeholder="Team name"
className="w-full p-2 border rounded mb-4"
/>
<div className="flex justify-end space-x-2">
<button
onClick={() => setShowNewTeamDialog(false)}
className="px-4 py-2 border rounded"
>
Cancel
</button>
<button
onClick={() => setShowNewTeamDialog(false)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Create
</button>
</div>
</div>
</div>
)}
</div>
);
}

View file

@ -1,35 +0,0 @@
"use client";
import React, { useState } from 'react';
export function UserNav() {
const [open, setOpen] = useState(false);
return (
<div className="relative">
<button
onClick={() => setOpen(!open)}
className="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center"
>
U
</button>
{open && (
<div className="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10">
<div className="p-2 border-b">
<p className="font-medium">shadcn</p>
<p className="text-sm text-gray-500">m@example.com</p>
</div>
{['Profile', 'Billing', 'Settings', 'New Team', 'Log out'].map((item) => (
<button
key={item}
className="w-full text-left px-3 py-2 hover:bg-gray-100"
>
{item}
</button>
))}
</div>
)}
</div>
);
}

View file

@ -1,183 +1,202 @@
"use client";
import React, { useState, useEffect, useMemo } from 'react';
import { Search, Plus, Upload, Download, Trash2, Copy, Move, Share, Star, Grid, List, Filter, SortAsc, Eye, Edit3, Archive, Clock, Users, Lock, Folder, File, Image, Video, Music, FileText, Code, Database, Package } from 'lucide-react';
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
} from 'lucide-react';
import { cn } from "@/lib/utils";
// Simple date formatting functions
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
};
// Enhanced file system with realistic data structure
const formatDateTime = (dateString) => {
const date = new Date(dateString);
return date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
});
};
const formatDistanceToNow = (date) => {
const now = new Date();
const diffMs = now - new Date(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";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-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";
// File system data
const fileSystemData = {
"": {
name: "Root",
id: "root",
name: "My Drive",
path: "",
is_dir: true,
children: ["projects", "documents", "media", "shared", "archives", "templates"]
children: ["projects", "documents", "media", "shared"]
},
"projects": {
id: "projects",
name: "Projects",
path: "projects",
is_dir: true,
size: null,
modified: "2025-01-15T10:30:00Z",
created: "2024-12-01T09:00:00Z",
starred: true,
shared: false,
tags: ["work", "development"],
children: ["web-apps", "mobile-apps", "data-science", "ai-research", "blockchain"]
children: ["web-apps", "mobile-apps", "ai-research"]
},
"projects/web-apps": {
id: "web-apps",
name: "Web Applications",
path: "projects/web-apps",
is_dir: true,
size: null,
modified: "2025-01-14T16:45:00Z",
created: "2024-12-01T09:15:00Z",
starred: false,
shared: true,
tags: ["frontend", "backend", "fullstack"],
children: ["dashboard-pro", "e-commerce-platform", "social-media-app", "file-manager"]
children: ["dashboard-pro", "package.json", "README.md"]
},
"projects/web-apps/dashboard-pro": {
name: "Dashboard Pro",
path: "projects/web-apps/dashboard-pro",
is_dir: true,
size: null,
modified: "2025-01-13T14:20:00Z",
created: "2024-12-10T11:00:00Z",
starred: true,
shared: true,
tags: ["react", "typescript", "tailwind"],
children: ["src", "components", "styles", "tests", "package.json", "README.md", "deployment.yaml"]
},
"projects/web-apps/dashboard-pro/package.json": {
"projects/web-apps/package.json": {
id: "package-json",
name: "package.json",
path: "projects/web-apps/dashboard-pro/package.json",
path: "projects/web-apps/package.json",
is_dir: false,
size: 2048,
type: "json",
modified: "2025-01-13T14:20:00Z",
created: "2024-12-10T11:00:00Z",
starred: false,
shared: false,
tags: ["config", "dependencies"]
shared: false
},
"projects/web-apps/dashboard-pro/README.md": {
"projects/web-apps/README.md": {
id: "readme-md",
name: "README.md",
path: "projects/web-apps/dashboard-pro/README.md",
path: "projects/web-apps/README.md",
is_dir: false,
size: 5120,
type: "markdown",
modified: "2025-01-12T09:30:00Z",
created: "2024-12-10T11:05:00Z",
starred: false,
shared: true,
tags: ["documentation"]
shared: true
},
"documents": {
id: "documents",
name: "Documents",
path: "documents",
is_dir: true,
size: null,
modified: "2025-01-14T12:00:00Z",
created: "2024-11-01T08:00:00Z",
starred: false,
shared: false,
tags: ["personal", "work"],
children: ["proposals", "contracts", "presentations", "reports", "spreadsheets"]
children: ["proposals", "Q1-Strategy.pdf", "Budget-2025.xlsx"]
},
"documents/proposals": {
name: "Proposals",
path: "documents/proposals",
is_dir: true,
size: null,
modified: "2025-01-10T15:30:00Z",
created: "2024-11-15T10:00:00Z",
starred: true,
shared: true,
tags: ["business", "client"],
children: ["Q1-2025-Strategy.pdf", "AI-Integration-Proposal.docx", "Budget-Proposal-2025.xlsx"]
},
"documents/proposals/Q1-2025-Strategy.pdf": {
name: "Q1 2025 Strategy.pdf",
path: "documents/proposals/Q1-2025-Strategy.pdf",
"documents/Q1-Strategy.pdf": {
id: "q1-strategy",
name: "Q1 Strategy.pdf",
path: "documents/Q1-Strategy.pdf",
is_dir: false,
size: 1048576,
type: "pdf",
modified: "2025-01-10T15:30:00Z",
created: "2025-01-08T14:00:00Z",
starred: true,
shared: true,
tags: ["strategy", "quarterly", "important"]
shared: true
},
"media": {
id: "media",
name: "Media",
path: "media",
is_dir: true,
size: null,
modified: "2025-01-13T18:45:00Z",
created: "2024-10-01T12:00:00Z",
starred: false,
shared: false,
tags: ["photos", "videos", "audio"],
children: ["photos", "videos", "audio", "graphics"]
children: ["photos", "videos", "vacation-2024.jpg"]
},
"media/photos": {
name: "Photos",
path: "media/photos",
is_dir: true,
size: null,
modified: "2025-01-13T18:45:00Z",
created: "2024-10-01T12:15:00Z",
starred: false,
shared: false,
tags: ["photography"],
children: ["vacation-2024.jpg", "team-photo.jpg", "product-shots.jpg", "nature-landscape.jpg"]
},
"media/photos/vacation-2024.jpg": {
"media/vacation-2024.jpg": {
id: "vacation-photo",
name: "vacation-2024.jpg",
path: "media/photos/vacation-2024.jpg",
path: "media/vacation-2024.jpg",
is_dir: false,
size: 3145728,
type: "image",
modified: "2024-12-25T20:00:00Z",
created: "2024-12-25T20:00:00Z",
starred: true,
shared: false,
tags: ["vacation", "memories"]
shared: false
},
"shared": {
id: "shared",
name: "Shared",
path: "shared",
is_dir: true,
size: null,
modified: "2025-01-12T11:20:00Z",
created: "2024-11-01T08:30:00Z",
starred: false,
shared: true,
tags: ["collaboration", "team"],
children: ["team-resources", "client-deliverables", "public-assets"]
children: ["team-resources", "client-files"]
}
};
const getFileIcon = (item) => {
if (item.is_dir) {
return item.starred ? <Star className="w-4 h-4 text-primary" /> : <Folder className="w-4 h-4 text-accent" />;
return <Folder className="w-4 h-4 text-blue-600" />;
}
const iconMap = {
pdf: <FileText className="w-4 h-4 text-red-500" />,
docx: <FileText className="w-4 h-4 text-blue-500" />,
xlsx: <Database className="w-4 h-4 text-green-500" />,
json: <Code className="w-4 h-4 text-yellow-500" />,
xlsx: <Database className="w-4 h-4 text-green-600" />,
json: <Code className="w-4 h-4 text-yellow-600" />,
markdown: <Edit3 className="w-4 h-4 text-purple-500" />,
md: <Edit3 className="w-4 h-4 text-purple-500" />,
jpg: <Image className="w-4 h-4 text-pink-500" />,
jpeg: <Image className="w-4 h-4 text-pink-500" />,
png: <Image className="w-4 h-4 text-pink-500" />,
mp4: <Video className="w-4 h-4 text-red-600" />,
mp3: <Music className="w-4 h-4 text-green-600" />,
zip: <Archive className="w-4 h-4 text-orange-500" />,
yaml: <Code className="w-4 h-4 text-blue-400" />
mp3: <Music className="w-4 h-4 text-green-600" />
};
return iconMap[item.type] || <File className="w-4 h-4 text-muted-foreground" />;
return iconMap[item.type] || <File className="w-4 h-4 text-gray-500" />;
};
const formatFileSize = (bytes) => {
@ -187,18 +206,7 @@ const formatFileSize = (bytes) => {
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
};
const formatDate = (dateString) => {
if (!dateString) return '';
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
const FileTree = ({ onSelect, selectedPath }) => {
const FolderTree = ({ onSelect, selectedPath, isCollapsed }) => {
const [expanded, setExpanded] = useState({ "": true, "projects": true });
const toggleExpand = (path) => {
@ -206,6 +214,14 @@ const FileTree = ({ onSelect, selectedPath }) => {
onSelect(path);
};
const navLinks = [
{ title: "My Drive", path: "", icon: Home },
{ title: "Shared", path: "shared", icon: Users },
{ title: "Starred", path: "starred", icon: Star },
{ title: "Recent", path: "recent", icon: Clock },
{ title: "Trash", path: "trash", icon: Trash2 },
];
const renderTreeItem = (path, level = 0) => {
const item = fileSystemData[path];
if (!item || !item.is_dir) return null;
@ -214,25 +230,27 @@ const FileTree = ({ onSelect, selectedPath }) => {
const isSelected = selectedPath === path;
return (
<div key={path}>
<div
<div key={item.id}>
<button
onClick={() => toggleExpand(path)}
className={`flex items-center cursor-pointer hover:bg-accent hover:text-accent-foreground p-2 rounded transition-colors
${isSelected ? 'bg-primary text-primary-foreground' : 'text-foreground'}`}
style={{ paddingLeft: `${level * 16 + 8}px` }}
className={cn(
"flex items-center w-full px-2 py-1.5 text-sm rounded-md hover:bg-accent transition-colors",
isSelected && "bg-muted",
isCollapsed ? "justify-center" : "justify-start"
)}
>
<div className="flex items-center space-x-2 flex-1">
{getFileIcon(item)}
<span className="text-sm font-medium truncate">{item.name}</span>
{item.starred && <Star className="w-3 h-3 text-yellow-500 fill-current" />}
{item.shared && <Users className="w-3 h-3 text-blue-500" />}
</div>
<div className="text-xs text-muted-foreground">
{isExpanded ? '▼' : '▶'}
</div>
</div>
{isExpanded && item.children && (
<div>
{!isCollapsed && (
<>
<ChevronRight className={cn("w-4 h-4 mr-1 transition-transform", isExpanded && "rotate-90")} />
{getFileIcon(item)}
<span className="ml-2 truncate">{item.name}</span>
{item.starred && <Star className="w-3 h-3 ml-auto text-yellow-500 fill-current" />}
</>
)}
{isCollapsed && getFileIcon(item)}
</button>
{!isCollapsed && isExpanded && item.children && (
<div className="ml-4">
{item.children.map(childPath => {
const fullPath = path ? `${path}/${childPath}` : childPath;
return renderTreeItem(fullPath, level + 1);
@ -244,24 +262,55 @@ const FileTree = ({ onSelect, selectedPath }) => {
};
return (
<div className="w-80 border-r border-border bg-card overflow-y-auto">
<div className="p-4 border-b border-border">
<h2 className="text-lg font-semibold text-card-foreground flex items-center">
<Folder className="w-5 h-5 mr-2 text-primary" />
File Explorer
</h2>
</div>
<div className="p-2">
{renderTreeItem("")}
<div className="flex flex-col h-full">
<div className={cn("p-2", isCollapsed ? "px-1" : "")}>
<nav className="space-y-1">
{navLinks.map((link) =>
isCollapsed ? (
<Tooltip key={link.path} delayDuration={0}>
<TooltipTrigger asChild>
<Button
variant={selectedPath === link.path ? "default" : "ghost"}
size="icon"
className="w-9 h-9"
onClick={() => onSelect(link.path)}
>
<link.icon className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="right">{link.title}</TooltipContent>
</Tooltip>
) : (
<Button
key={link.path}
variant={selectedPath === link.path ? "default" : "ghost"}
className="w-full justify-start"
onClick={() => onSelect(link.path)}
>
<link.icon className="mr-2 h-4 w-4" />
{link.title}
</Button>
)
)}
</nav>
</div>
{!isCollapsed && (
<>
<Separator />
<ScrollArea className="flex-1 p-2">
{renderTreeItem("")}
</ScrollArea>
</>
)}
</div>
);
};
const FileBrowser = ({ path, viewMode, searchTerm, sortBy, filterType }) => {
const currentItem = fileSystemData[path];
const FileList = ({ path, searchTerm, filterType }) => {
const [selectedFile, setSelectedFile] = useState(null);
const files = useMemo(() => {
const currentItem = fileSystemData[path];
if (!currentItem || !currentItem.is_dir || !currentItem.children) return [];
let items = currentItem.children.map(childName => {
@ -269,317 +318,251 @@ const FileBrowser = ({ path, viewMode, searchTerm, sortBy, filterType }) => {
return fileSystemData[childPath];
}).filter(Boolean);
// Apply search filter
if (searchTerm) {
items = items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
(item.tags && item.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase())))
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}
// Apply type filter
if (filterType && filterType !== 'all') {
items = items.filter(item => {
if (filterType === 'folders') return item.is_dir;
if (filterType === 'files') return !item.is_dir;
if (filterType === 'starred') return item.starred;
if (filterType === 'shared') return item.shared;
return true;
});
}
// Apply sorting
items.sort((a, b) => {
switch (sortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'modified':
return new Date(b.modified || 0) - new Date(a.modified || 0);
case 'size':
return (b.size || 0) - (a.size || 0);
case 'type':
return a.type?.localeCompare(b.type || '') || 0;
default:
return 0;
}
return items.sort((a, b) => {
if (a.is_dir && !b.is_dir) return -1;
if (!a.is_dir && b.is_dir) return 1;
return a.name.localeCompare(b.name);
});
}, [path, searchTerm, filterType]);
return items;
}, [currentItem, searchTerm, sortBy, filterType, path]);
if (viewMode === 'grid') {
return (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4 p-4">
return (
<ScrollArea className="h-full">
<div className="flex flex-col">
{files.map((item) => (
<div key={item.path} className="group relative">
<div className="bg-card border border-border rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer hover:bg-accent hover:text-accent-foreground">
<div className="flex flex-col items-center space-y-2">
<div className="text-3xl">
{getFileIcon(item)}
</div>
<div className="text-sm font-medium text-center truncate w-full text-card-foreground group-hover:text-accent-foreground">
{item.name}
</div>
{!item.is_dir && (
<div className="text-xs text-muted-foreground">
{formatFileSize(item.size)}
</div>
)}
<div className="flex space-x-1">
<button
key={item.id}
className={cn(
"flex items-center gap-3 p-3 text-left text-sm transition-all hover:bg-accent border-b",
selectedFile?.id === item.id && "bg-muted"
)}
onClick={() => setSelectedFile(item)}
>
<div className="flex items-center gap-3 flex-1 min-w-0">
{getFileIcon(item)}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<div className="font-medium truncate">{item.name}</div>
{item.starred && <Star className="w-3 h-3 text-yellow-500 fill-current" />}
{item.shared && <Users className="w-3 h-3 text-blue-500" />}
</div>
<div className="text-xs text-muted-foreground">
{item.is_dir ? 'Folder' : formatFileSize(item.size)}
</div>
</div>
</div>
</div>
<div className="text-xs text-muted-foreground">
{formatDistanceToNow(new Date(item.modified), { addSuffix: true })}
</div>
</button>
))}
</div>
</ScrollArea>
);
};
const FileDisplay = ({ file }) => {
if (!file) {
return (
<div className="flex flex-col items-center justify-center h-full text-center text-muted-foreground">
<File className="w-12 h-12 mb-4" />
<div className="text-lg font-medium">No file selected</div>
<div className="text-sm">Select a file to view details</div>
</div>
);
}
return (
<div className="overflow-hidden">
<div className="bg-muted border-b border-border p-2">
<div className="grid grid-cols-12 gap-4 text-sm font-medium text-muted-foreground">
<div className="col-span-5">Name</div>
<div className="col-span-2">Modified</div>
<div className="col-span-1">Size</div>
<div className="col-span-2">Type</div>
<div className="col-span-2">Tags</div>
<div className="flex flex-col h-full">
<div className="flex items-center p-2">
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Download className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Download</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Share className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Share</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Star className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Star</TooltipContent>
</Tooltip>
</div>
<div className="ml-auto">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Rename</DropdownMenuItem>
<DropdownMenuItem>Make a copy</DropdownMenuItem>
<DropdownMenuItem>Move to trash</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="divide-y divide-border">
{files.map((item) => (
<div key={item.path} className="grid grid-cols-12 gap-4 p-3 hover:bg-accent hover:text-accent-foreground cursor-pointer group">
<div className="col-span-5 flex items-center space-x-3">
{getFileIcon(item)}
<span className="font-medium truncate">{item.name}</span>
<div className="flex space-x-1">
{item.starred && <Star className="w-3 h-3 text-yellow-500 fill-current" />}
{item.shared && <Users className="w-3 h-3 text-blue-500" />}
</div>
</div>
<div className="col-span-2 text-sm text-muted-foreground flex items-center">
{formatDate(item.modified)}
</div>
<div className="col-span-1 text-sm text-muted-foreground flex items-center">
{formatFileSize(item.size)}
</div>
<div className="col-span-2 text-sm text-muted-foreground flex items-center">
{item.is_dir ? 'Folder' : item.type?.toUpperCase() || '—'}
</div>
<div className="col-span-2 flex items-center">
<div className="flex flex-wrap gap-1">
{item.tags?.slice(0, 2).map(tag => (
<span key={tag} className="text-xs bg-secondary text-secondary-foreground px-2 py-1 rounded-full">
{tag}
</span>
))}
{item.tags?.length > 2 && (
<span className="text-xs text-muted-foreground">+{item.tags.length - 2}</span>
)}
</div>
<Separator />
<div className="flex-1 flex-col">
<div className="flex items-start gap-4 p-4">
<div className="p-2 rounded-lg bg-accent">
{getFileIcon(file)}
</div>
<div className="flex-1 min-w-0">
<div className="font-semibold">{file.name}</div>
<div className="text-sm text-muted-foreground">
{file.is_dir ? 'Folder' : `${file.type?.toUpperCase() || 'File'}${formatFileSize(file.size)}`}
</div>
</div>
))}
{file.modified && (
<div className="text-xs text-muted-foreground">
{formatDate(file.modified)}
</div>
)}
</div>
<Separator />
<div className="p-4 space-y-4">
<div>
<div className="text-sm font-medium">Location</div>
<div className="text-sm text-muted-foreground">/{file.path || ''}</div>
</div>
<div>
<div className="text-sm font-medium">Modified</div>
<div className="text-sm text-muted-foreground">
{formatDateTime(file.modified)}
</div>
</div>
{!file.is_dir && (
<div>
<div className="text-sm font-medium">Size</div>
<div className="text-sm text-muted-foreground">{formatFileSize(file.size)}</div>
</div>
)}
</div>
</div>
</div>
);
};
const FileOperations = ({ currentPath, onRefresh }) => {
const [showUploadProgress, setShowUploadProgress] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const handleUpload = async () => {
setShowUploadProgress(true);
// Simulate upload progress
for (let i = 0; i <= 100; i += 10) {
setUploadProgress(i);
await new Promise(resolve => setTimeout(resolve, 100));
}
setTimeout(() => {
setShowUploadProgress(false);
setUploadProgress(0);
onRefresh();
}, 500);
};
const createFolder = () => {
const folderName = prompt('Enter folder name:');
if (folderName) {
onRefresh();
}
};
return (
<div className="border-t border-border bg-card p-4">
<div className="flex flex-wrap gap-2">
<button
onClick={handleUpload}
className="flex items-center space-x-2 bg-primary text-primary-foreground px-4 py-2 rounded-md hover:opacity-90 transition-opacity"
>
<Upload className="w-4 h-4" />
<span>Upload Files</span>
</button>
<button
onClick={createFolder}
className="flex items-center space-x-2 bg-secondary text-secondary-foreground px-4 py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors"
>
<Plus className="w-4 h-4" />
<span>New Folder</span>
</button>
<button className="flex items-center space-x-2 bg-secondary text-secondary-foreground px-4 py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors">
<Download className="w-4 h-4" />
<span>Download</span>
</button>
<button className="flex items-center space-x-2 bg-secondary text-secondary-foreground px-4 py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors">
<Share className="w-4 h-4" />
<span>Share</span>
</button>
<button className="flex items-center space-x-2 bg-destructive text-destructive-foreground px-4 py-2 rounded-md hover:opacity-90 transition-opacity">
<Trash2 className="w-4 h-4" />
<span>Delete</span>
</button>
</div>
{showUploadProgress && (
<div className="mt-4">
<div className="flex justify-between text-sm text-muted-foreground mb-1">
<span>Uploading files...</span>
<span>{uploadProgress}%</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div
className="bg-primary h-2 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
/>
</div>
</div>
)}
</div>
);
};
export default function DriveScreen() {
export default function FileManager() {
const [isCollapsed, setIsCollapsed] = useState(false);
const [currentPath, setCurrentPath] = useState('');
const [refreshKey, setRefreshKey] = useState(0);
const [viewMode, setViewMode] = useState('list');
const [searchTerm, setSearchTerm] = useState('');
const [sortBy, setSortBy] = useState('name');
const [filterType, setFilterType] = useState('all');
const handleRefresh = () => {
setRefreshKey(prev => prev + 1);
};
const [selectedFile, setSelectedFile] = useState(null);
const currentItem = fileSystemData[currentPath];
const breadcrumbs = currentPath ? currentPath.split('/') : [];
return (
<div className="flex h-screen bg-background text-foreground">
<FileTree onSelect={setCurrentPath} selectedPath={currentPath} />
<div className="flex-1 flex flex-col overflow-hidden">
{/* Header */}
<div className="border-b border-border bg-card p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-2">
<h1 className="text-2xl font-bold text-card-foreground">
{currentItem?.name || 'Root'}
</h1>
{currentItem?.starred && <Star className="w-5 h-5 text-yellow-500 fill-current" />}
{currentItem?.shared && <Users className="w-5 h-5 text-blue-500" />}
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => setViewMode(viewMode === 'list' ? 'grid' : 'list')}
className="p-2 hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
>
{viewMode === 'list' ? <Grid className="w-4 h-4" /> : <List className="w-4 h-4" />}
</button>
</div>
</div>
{/* Breadcrumbs */}
<div className="flex items-center space-x-2 text-sm text-muted-foreground mb-4">
<button
onClick={() => setCurrentPath('')}
className="hover:text-foreground transition-colors"
>
Home
</button>
{breadcrumbs.map((crumb, index) => {
const path = breadcrumbs.slice(0, index + 1).join('/');
return (
<React.Fragment key={path}>
<span>/</span>
<button
onClick={() => setCurrentPath(path)}
className="hover:text-foreground transition-colors"
>
{crumb}
</button>
</React.Fragment>
);
})}
</div>
{/* Search and Filters */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<input
type="text"
placeholder="Search files and folders..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 pr-4 py-2 w-64 bg-input border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-ring text-foreground placeholder:text-muted-foreground"
/>
</div>
<select
value={filterType}
onChange={(e) => setFilterType(e.target.value)}
className="px-3 py-2 bg-input border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-ring text-foreground"
>
<option value="all">All Items</option>
<option value="folders">Folders</option>
<option value="files">Files</option>
<option value="starred">Starred</option>
<option value="shared">Shared</option>
</select>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="px-3 py-2 bg-input border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-ring text-foreground"
>
<option value="name">Sort by Name</option>
<option value="modified">Sort by Modified</option>
<option value="size">Sort by Size</option>
<option value="type">Sort by Type</option>
</select>
</div>
</div>
</div>
{/* File Browser */}
<div className="flex-1 overflow-y-auto bg-background">
<FileBrowser
key={`${currentPath}-${refreshKey}`}
path={currentPath}
viewMode={viewMode}
searchTerm={searchTerm}
sortBy={sortBy}
filterType={filterType}
<TooltipProvider delayDuration={0}>
<ResizablePanelGroup direction="horizontal" className="h-full max-h-[800px]">
{/* Left Sidebar */}
<ResizablePanel
defaultSize={20}
collapsedSize={4}
collapsible={true}
minSize={15}
maxSize={30}
onCollapse={() => setIsCollapsed(true)}
onResize={() => setIsCollapsed(false)}
className={cn(isCollapsed && "min-w-[50px] transition-all duration-300")}
>
<FolderTree
onSelect={setCurrentPath}
selectedPath={currentPath}
isCollapsed={isCollapsed}
/>
</div>
{/* File Operations */}
<FileOperations currentPath={currentPath} onRefresh={handleRefresh} />
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
{/* Middle File List */}
<ResizablePanel defaultSize={50} minSize={30}>
<Tabs defaultValue="all" className="flex flex-col h-full">
<div className="flex items-center px-4 py-2">
<h1 className="text-xl font-bold">{currentItem?.name || 'My Drive'}</h1>
<TabsList className="ml-auto">
<TabsTrigger value="all">All</TabsTrigger>
<TabsTrigger value="starred">Starred</TabsTrigger>
</TabsList>
</div>
<Separator />
<div className="bg-background/95 p-4 backdrop-blur">
<div className="flex items-center gap-2">
<div className="relative flex-1">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search files"
className="pl-8"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<Select value={filterType} onValueChange={setFilterType}>
<SelectTrigger className="w-[140px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All items</SelectItem>
<SelectItem value="folders">Folders</SelectItem>
<SelectItem value="files">Files</SelectItem>
<SelectItem value="starred">Starred</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<TabsContent value="all" className="m-0 flex-1">
<FileList
path={currentPath}
searchTerm={searchTerm}
filterType={filterType}
/>
</TabsContent>
<TabsContent value="starred" className="m-0 flex-1">
<FileList
path={currentPath}
searchTerm={searchTerm}
filterType="starred"
/>
</TabsContent>
</Tabs>
</ResizablePanel>
<ResizableHandle withHandle />
{/* Right File Details */}
<ResizablePanel defaultSize={30} minSize={25}>
<FileDisplay file={selectedFile} />
</ResizablePanel>
</ResizablePanelGroup>
</TooltipProvider>
);
}

8
app/news/page.tsx Normal file
View file

@ -0,0 +1,8 @@
'use client'
export default function MainPage() {
return (
<div className="flex flex-col h-screen bg-gray-50">
</div>
)
}

8
app/paper/page.tsx Normal file
View file

@ -0,0 +1,8 @@
'use client'
export default function MainPage() {
return (
<div className="flex flex-col h-screen bg-gray-50">
</div>
)
}

1
app/templates/README.md Normal file
View file

@ -0,0 +1 @@
Prompts come from: https://github.com/0xeb/TheBigPromptLibrary

View file

@ -1,71 +0,0 @@
export interface Album {
name: string
artist: string
cover: string
}
export const listenNowAlbums: Album[] = [
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1611348586804-61bf6c080437?w=300&dpr=2&q=80",
},
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1468817814611-b7edf94b5d60?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1528143358888-6d3c7f67bd5d?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1490300472339-79e4adc6be4a?w=300&dpr=2&q=80",
},
]
export const madeForYouAlbums: Album[] = [
{
name: "Thinking Components",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1615247001958-f4bc92fa6a4a?w=300&dpr=2&q=80",
},
{
name: "Functional Fury",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1513745405825-efaf9a49315f?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1614113489855-66422ad300a4?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1446185250204-f94591f7d702?w=300&dpr=2&q=80",
},
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1468817814611-b7edf94b5d60?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1490300472339-79e4adc6be4a?w=300&dpr=2&q=80",
},
]

View file

@ -1,16 +0,0 @@
export type Playlist = (typeof playlists)[number]
export const playlists = [
"Recently Added",
"Recently Played",
"Top Songs",
"Top Albums",
"Top Artists",
"Logic Discography",
"Bedtime Beats",
"Feeling Happy",
"I miss Y2K Pop",
"Runtober",
"Mellow Days",
"Eminem Essentials",
]

View file

@ -1,52 +1,213 @@
import React from 'react';
import { Menu } from './components/menu';
import { Sidebar } from './components/sidebar';
import { PodcastEmptyPlaceholder } from './components/podcast-empty-placeholder';
import { AlbumArtwork } from './components/album-artwork';
import { listenNowAlbums, madeForYouAlbums } from './data/albums';
import { playlists } from './data/playlists';
import { ChevronDown, ChevronRight, Search, Star, Grid, List } from 'lucide-react'
export default function TemplatesPage() {
return (
<div className="min-h-screen bg-white">
<Menu />
<div className="flex">
<div className="w-64 border-r border-gray-200">
<Sidebar playlists={playlists} />
</div>
<div className="flex-1 p-4 overflow-auto">
<div className="mb-8">
<h2 className="text-2xl font-bold">Listen Now</h2>
<p className="text-gray-500">Top picks for you. Updated daily.</p>
<div className="flex overflow-x-auto py-4 space-x-4">
{listenNowAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
width={250}
height={330}
/>
))}
</div>
</div>
<div className="mb-8">
<h2 className="text-2xl font-bold">Made for You</h2>
<p className="text-gray-500">Your personal playlists. Updated daily.</p>
<div className="flex overflow-x-auto py-4 space-x-4">
{madeForYouAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
width={150}
height={150}
aspectRatio="square"
/>
))}
</div>
</div>
<PodcastEmptyPlaceholder />
</div>
</div>
</div>
);
interface Prompt {
id: string
name: string
category: string
description: string
icon: string
featured?: boolean
}
const prompts: Prompt[] = [
{
id: "1",
name: "SOP Analyzer",
category: "Education",
description: "Analyzes statements of purpose for academic applications",
icon: "📝",
featured: true
},
{
id: "2",
name: "Break This GPT",
category: "Security",
description: "Tests GPT vulnerabilities and security",
icon: "🔓",
featured: true
},
// Add all your prompts here...
{
id: "100",
name: "Coinflipper Game",
category: "Games",
description: "Simple coin flipping game",
icon: "🪙"
}
]
const categories = Array.from(new Set(prompts.map(p => p.category)))
const featuredPrompts = prompts.filter(p => p.featured)
export default function PromptsPage() {
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="sticky top-0 z-10 bg-white/80 backdrop-blur-sm border-b">
<div className="container mx-auto px-4 py-3 flex items-center justify-between">
<div className="flex items-center space-x-6">
<h1 className="text-xl font-bold">Prompt Store</h1>
<nav className="hidden md:flex space-x-6">
<button className="text-sm font-medium">Discover</button>
<button className="text-sm font-medium">Categories</button>
<button className="text-sm font-medium">Top Charts</button>
<button className="text-sm font-medium">Collections</button>
</nav>
</div>
<div className="flex items-center space-x-4">
<button className="p-2 rounded-full hover:bg-gray-100">
<Search className="h-4 w-4" />
</button>
<button className="px-4 py-2 text-sm font-medium rounded-full bg-black text-white">
Sign In
</button>
</div>
</div>
</header>
{/* Main Content */}
<main className="container mx-auto px-4 py-6">
{/* Hero Section */}
<section className="mb-12">
<div className="bg-gradient-to-r from-blue-500 to-purple-600 rounded-2xl p-8 text-white">
<h2 className="text-3xl font-bold mb-2">Discover Amazing GPTs</h2>
<p className="text-lg mb-6">Browse hundreds of specialized AI assistants</p>
<div className="relative max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
<input
type="text"
placeholder="Search prompts..."
className="w-full pl-10 pr-4 py-2 rounded-full text-gray-900 focus:outline-none"
/>
</div>
</div>
</section>
{/* Featured Section */}
<section className="mb-12">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold">Featured Prompts</h2>
<button className="text-blue-600 text-sm font-medium flex items-center">
See All <ChevronRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{featuredPrompts.map(prompt => (
<div key={prompt.id} className="bg-white rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow">
<div className="p-5">
<div className="flex items-center mb-4">
<span className="text-3xl mr-3">{prompt.icon}</span>
<div>
<h3 className="font-semibold">{prompt.name}</h3>
<p className="text-xs text-gray-500">{prompt.category}</p>
</div>
</div>
<p className="text-sm text-gray-600 mb-4 line-clamp-2">{prompt.description}</p>
<div className="flex justify-between items-center">
<div className="flex items-center">
<Star className="h-4 w-4 text-yellow-500 fill-yellow-500" />
<span className="text-xs ml-1">Featured</span>
</div>
<button className="text-xs font-medium text-blue-600 hover:text-blue-800">
Get Prompt
</button>
</div>
</div>
</div>
))}
</div>
</section>
{/* Categories Section */}
<section>
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold">Browse by Category</h2>
<div className="flex items-center space-x-2">
<button className="p-2 rounded-md hover:bg-gray-100">
<Grid className="h-5 w-5" />
</button>
<button className="p-2 rounded-md hover:bg-gray-100">
<List className="h-5 w-5" />
</button>
</div>
</div>
<div className="space-y-8">
{categories.map(category => {
const categoryPrompts = prompts.filter(p => p.category === category)
return (
<div key={category}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-semibold">{category}</h3>
<button className="text-sm text-blue-600 font-medium flex items-center">
See All <ChevronRight className="h-4 w-4 ml-1" />
</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{categoryPrompts.slice(0, 4).map(prompt => (
<div key={prompt.id} className="bg-white rounded-lg border overflow-hidden hover:shadow-sm transition-shadow">
<div className="p-4">
<div className="flex items-center mb-3">
<span className="text-2xl mr-2">{prompt.icon}</span>
<h4 className="font-medium">{prompt.name}</h4>
</div>
<p className="text-sm text-gray-600 mb-4 line-clamp-2">{prompt.description}</p>
<button className="w-full py-2 text-sm font-medium rounded-md bg-gray-100 hover:bg-gray-200">
View Details
</button>
</div>
</div>
))}
</div>
</div>
)
})}
</div>
</section>
</main>
{/* Footer */}
<footer className="bg-gray-100 border-t mt-12">
<div className="container mx-auto px-4 py-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
<div>
<h4 className="font-semibold mb-4">The Prompt Store</h4>
<ul className="space-y-2 text-sm">
<li><a href="#" className="hover:text-blue-600">About Us</a></li>
<li><a href="#" className="hover:text-blue-600">Careers</a></li>
<li><a href="#" className="hover:text-blue-600">News</a></li>
</ul>
</div>
<div>
<h4 className="font-semibold mb-4">For Developers</h4>
<ul className="space-y-2 text-sm">
<li><a href="#" className="hover:text-blue-600">Submit a Prompt</a></li>
<li><a href="#" className="hover:text-blue-600">Documentation</a></li>
<li><a href="#" className="hover:text-blue-600">API</a></li>
</ul>
</div>
<div>
<h4 className="font-semibold mb-4">Support</h4>
<ul className="space-y-2 text-sm">
<li><a href="#" className="hover:text-blue-600">Help Center</a></li>
<li><a href="#" className="hover:text-blue-600">Community</a></li>
<li><a href="#" className="hover:text-blue-600">Contact Us</a></li>
</ul>
</div>
<div>
<h4 className="font-semibold mb-4">Legal</h4>
<ul className="space-y-2 text-sm">
<li><a href="#" className="hover:text-blue-600">Terms of Service</a></li>
<li><a href="#" className="hover:text-blue-600">Privacy Policy</a></li>
<li><a href="#" className="hover:text-blue-600">Cookie Policy</a></li>
</ul>
</div>
</div>
<div className="border-t mt-8 pt-8 text-sm text-gray-500">
© {new Date().getFullYear()} Prompt Store. All rights reserved.
</div>
</div>
</footer>
</div>
)
}

1567
app/templates/prompts.csv Normal file

File diff suppressed because it is too large Load diff