124_webcalibre2/src/app/upload/upload-view.tsx

189 lines
7 KiB
TypeScript
Raw Normal View History

'use client';
import { useState } from "react";
import {
Box, Button, Typography, Paper, LinearProgress, Stack,
2026-01-12 11:53:20 +00:00
TextField, MenuItem, IconButton, Tooltip, Divider,
Grid
} from "@mui/material";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import CreateNewFolderIcon from "@mui/icons-material/CreateNewFolder";
import FolderIcon from "@mui/icons-material/Folder";
import AssignmentIcon from '@mui/icons-material/Assignment';
2026-01-12 11:53:20 +00:00
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import { useRouter } from "next/navigation";
import { uploadFileAction, createFolderAction } from "./_actions";
const formatFileSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
2026-01-12 11:53:20 +00:00
interface MetadataPair {
key: string;
value: string;
}
2026-01-12 11:53:20 +00:00
export default function UploadView({ user, folders = [] }: any) {
const router = useRouter();
const [file, setFile] = useState<File | null>(null);
const [description, setDescription] = useState("");
const [parentId, setParentId] = useState("");
const [status, setStatus] = useState<'idle' | 'uploading' | 'success'>('idle');
2026-01-12 11:53:20 +00:00
// Dynamic Metadata State
const [customMetadata, setCustomMetadata] = useState<MetadataPair[]>([]);
const [showFolderInput, setShowFolderInput] = useState(false);
const [newFolderName, setNewFolderName] = useState("");
const [isCreatingFolder, setIsCreatingFolder] = useState(false);
2026-01-12 11:53:20 +00:00
const addMetadataRow = () => setCustomMetadata([...customMetadata, { key: "", value: "" }]);
const removeMetadataRow = (index: number) => {
setCustomMetadata(customMetadata.filter((_, i) => i !== index));
};
2026-01-12 11:53:20 +00:00
const updateMetadataRow = (index: number, field: 'key' | 'value', val: string) => {
const updated = [...customMetadata];
updated[index][field] = val;
setCustomMetadata(updated);
};
const handleUpload = async () => {
if (!file) return;
setStatus('uploading');
2026-01-12 11:53:20 +00:00
const formData = new FormData();
formData.append("file", file);
formData.append("description", description);
formData.append("parentId", parentId);
2026-01-12 11:53:20 +00:00
// Pass custom metadata as a JSON string
const metadataObj = customMetadata.reduce((acc, curr) => {
if (curr.key.trim()) acc[curr.key.trim()] = curr.value;
return acc;
}, {} as Record<string, string>);
formData.append("customMetadata", JSON.stringify(metadataObj));
try {
const result = await uploadFileAction(formData);
if (result.success) {
setStatus('success');
setFile(null);
setDescription("");
2026-01-12 11:53:20 +00:00
setCustomMetadata([]);
router.refresh();
}
} catch (err) {
2026-01-12 11:53:20 +00:00
alert("Upload failed.");
setStatus('idle');
}
};
return (
<Paper sx={{ p: { xs: 3, md: 6 }, borderRadius: 4, maxWidth: 800, mx: 'auto' }} elevation={3}>
<Typography variant="h4" fontWeight={900} color="primary" gutterBottom align="center">
Add to Library
</Typography>
<Stack spacing={4} sx={{ mt: 4 }}>
2026-01-12 11:53:20 +00:00
{/* Section 1: Destination (Condensed for brevity, same as your original) */}
<Box>
<Typography variant="h6" gutterBottom fontWeight="700" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<FolderIcon color="primary" /> 1. Destination
</Typography>
<Stack direction="row" spacing={1}>
<TextField
2026-01-12 11:53:20 +00:00
select fullWidth label="Target Project / Folder"
value={parentId}
onChange={(e) => setParentId(e.target.value)}
>
2026-01-12 11:53:20 +00:00
<MenuItem value=""><em>-- Root --</em></MenuItem>
{folders.map((f: any) => (
<MenuItem key={f.id} value={f.id}>{f.name}</MenuItem>
))}
</TextField>
2026-01-12 11:53:20 +00:00
<IconButton color="primary" onClick={() => setShowFolderInput(!showFolderInput)} sx={{ border: '1px solid #ccc', borderRadius: 1 }}>
<CreateNewFolderIcon />
</IconButton>
</Stack>
</Box>
2026-01-12 11:53:20 +00:00
{/* Section 2: Upload Area */}
<Box>
2026-01-12 11:53:20 +00:00
<Typography variant="h6" gutterBottom fontWeight="700" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CloudUploadIcon color="primary" /> 2. Upload File
</Typography>
2026-01-12 11:53:20 +00:00
<input type="file" id="file-input" hidden onChange={(e) => setFile(e.target.files?.[0] || null)} />
<label htmlFor="file-input">
<Button variant="outlined" component="span" fullWidth sx={{ p: 4, borderStyle: 'dashed' }}>
{file ? file.name : "Select File"}
</Button>
</label>
</Box>
2026-01-12 11:53:20 +00:00
{/* Section 3: Dynamic Metadata Area */}
<Box>
2026-01-12 11:53:20 +00:00
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
<Typography variant="h6" fontWeight="700" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<AssignmentIcon color="primary" /> 3. Custom Attributes
</Typography>
2026-01-12 11:53:20 +00:00
<Button startIcon={<AddCircleOutlineIcon />} size="small" onClick={addMetadataRow}>
Add Field
</Button>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 2 }}>
Add specific details like Author, Latitude, Aperture, or Part Number.
</Typography>
<Stack spacing={2}>
{customMetadata.map((row, index) => (
<Grid container spacing={1} key={index} alignItems="center">
<Grid item xs={5}>
<TextField
fullWidth size="small" placeholder="Key (e.g. Latitude)"
value={row.key} onChange={(e) => updateMetadataRow(index, 'key', e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
fullWidth size="small" placeholder="Value"
value={row.value} onChange={(e) => updateMetadataRow(index, 'value', e.target.value)}
/>
</Grid>
<Grid item xs={1}>
<IconButton color="error" onClick={() => removeMetadataRow(index)}>
<DeleteOutlineIcon />
</IconButton>
</Grid>
</Grid>
))}
<TextField
label="General Description"
multiline rows={2} fullWidth
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</Stack>
</Box>
2026-01-12 11:53:20 +00:00
<Button
variant="contained" size="large" fullWidth
disabled={!file || status === 'uploading'}
onClick={handleUpload}
>
{status === 'uploading' ? 'Processing...' : 'Add to Library'}
</Button>
</Stack>
</Paper>
);
}