feat: Complete Smart Resume Formatter with R2 and Gemini AI integration
Some checks failed
Profile Linker Docker Build / Build and push Docker image (push) Failing after 3s
Some checks failed
Profile Linker Docker Build / Build and push Docker image (push) Failing after 3s
- Integrated Cloudflare R2 for template storage and converted file management - Added Google Gemini AI for resume parsing and HTML generation - Created backend API endpoints for templates, conversion, and history - Refactored frontend to use real API instead of mock data - Fixed Docker networking issues (IPv6/IPv4) for R2 connectivity - Added resumeService.ts for frontend API integration - Updated Vite configuration for proper asset serving in Docker - Successfully tested with 13 templates from R2 bucket
This commit is contained in:
60
frontend/components/FileItem.tsx
Normal file
60
frontend/components/FileItem.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import { FileText, FileCode, Download, Eye } from 'lucide-react';
|
||||
import { FileData } from '../types';
|
||||
import { formatBytes, formatDate } from '../utils/formatters';
|
||||
|
||||
interface FileItemProps {
|
||||
file: FileData;
|
||||
onPreview: (url: string) => void;
|
||||
}
|
||||
|
||||
const FileItem: React.FC<FileItemProps> = ({ file, onPreview }) => {
|
||||
const getFileIcon = () => {
|
||||
switch (file.type) {
|
||||
case 'pdf':
|
||||
return <FileText className="w-5 h-5 text-red-500" />;
|
||||
case 'html':
|
||||
return <FileCode className="w-5 h-5 text-blue-500" />;
|
||||
default:
|
||||
return <FileText className="w-5 h-5 text-slate-500" />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group grid grid-cols-1 md:grid-cols-[2fr,1fr,1.5fr,1.5fr] gap-4 items-center p-3 border-b border-slate-200 dark:border-slate-700 last:border-b-0 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors">
|
||||
<div className="flex items-center gap-3 truncate">
|
||||
{getFileIcon()}
|
||||
<span className="font-medium text-slate-700 dark:text-slate-300 truncate group-hover:text-primary transition-colors" title={file.name}>{file.name}</span>
|
||||
</div>
|
||||
<div className="text-sm text-slate-500 dark:text-slate-400">
|
||||
{formatBytes(file.size)}
|
||||
</div>
|
||||
<div className="text-sm text-slate-500 dark:text-slate-400 hidden md:block">
|
||||
{formatDate(file.lastModified)}
|
||||
</div>
|
||||
<div className="flex items-center justify-start md:justify-end gap-2">
|
||||
{file.type === 'html' && (
|
||||
<button
|
||||
onClick={() => onPreview(file.url)}
|
||||
className="flex items-center gap-1.5 text-sm bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-300 dark:hover:bg-slate-600 px-3 py-1.5 rounded-md transition-colors font-semibold"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</button>
|
||||
)}
|
||||
<a
|
||||
href={file.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
download={file.name}
|
||||
className="flex items-center gap-1.5 text-sm bg-primary hover:bg-blue-600 text-white px-3 py-1.5 rounded-md transition-colors font-semibold"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileItem;
|
||||
Reference in New Issue
Block a user