fixed file creation bug
This commit is contained in:
parent
552662cf16
commit
672408081d
3 changed files with 12401 additions and 39 deletions
12340
docs/Ai-converstion.md
12340
docs/Ai-converstion.md
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -6,7 +6,7 @@ import {
|
||||||
TextField, IconButton, Divider,
|
TextField, IconButton, Divider,
|
||||||
Grid,
|
Grid,
|
||||||
CircularProgress, Checkbox, MenuItem,
|
CircularProgress, Checkbox, MenuItem,
|
||||||
InputAdornment, Collapse
|
Collapse
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh';
|
import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh';
|
||||||
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
|
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
|
||||||
|
|
@ -26,7 +26,7 @@ interface MetadataRow {
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UploadView({ user, folders }: { user: any; folders: any[] }) {
|
export default function UploadView({ folders }: { user: any; folders: any[] }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
|
@ -50,9 +50,10 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
const result = await getMetadataPreviewAction(selectedFile.name);
|
const result = await getMetadataPreviewAction(selectedFile.name);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
// Map data from the new extractor (PDF or Image)
|
||||||
const extractedRows: MetadataRow[] = Object.entries(result.data ?? {}).map(([k, v]) => ({
|
const extractedRows: MetadataRow[] = Object.entries(result.data ?? {}).map(([k, v]) => ({
|
||||||
key: k,
|
key: k,
|
||||||
value: String(v),
|
value: typeof v === 'object' ? JSON.stringify(v) : String(v),
|
||||||
isPending: true,
|
isPending: true,
|
||||||
selected: true
|
selected: true
|
||||||
}));
|
}));
|
||||||
|
|
@ -74,6 +75,8 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
setSelectedFile(file);
|
setSelectedFile(file);
|
||||||
|
// Optional: Auto-run magic enhance on file selection
|
||||||
|
// handleMagicEnhance();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -89,8 +92,10 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
if (newFolderName.trim()) {
|
if (newFolderName.trim()) {
|
||||||
const folderResult = await createFolderAction(newFolderName, targetFolderId || null);
|
const folderResult = await createFolderAction(newFolderName, targetFolderId || null);
|
||||||
if (folderResult.success) {
|
if (folderResult.success) {
|
||||||
// If successful, we want the file to go inside this NEW folder
|
// If successful, the file goes inside this NEW folder
|
||||||
currentParentId = folderResult.node.id;
|
currentParentId = folderResult.node.id;
|
||||||
|
} else {
|
||||||
|
throw new Error(folderResult.error || "Failed to create folder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,7 +122,6 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("✅ Process complete. Returning to dashboard.");
|
|
||||||
router.push("/dashboard");
|
router.push("/dashboard");
|
||||||
router.refresh();
|
router.refresh();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|
@ -129,7 +133,7 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ p: { xs: 3, md: 6 }, borderRadius: 4, maxWidth: 800, mx: 'auto' }} elevation={3}>
|
<Paper sx={{ p: { xs: 3, md: 6 }, borderRadius: 4, maxWidth: 800, mx: 'auto', mt: 4 }} elevation={3}>
|
||||||
<Typography variant="h4" fontWeight={900} color="primary" gutterBottom align="center">
|
<Typography variant="h4" fontWeight={900} color="primary" gutterBottom align="center">
|
||||||
Upload & Enrich
|
Upload & Enrich
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
@ -141,12 +145,10 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Destination Folder"
|
label="Parent Destination"
|
||||||
value={targetFolderId}
|
value={targetFolderId}
|
||||||
onChange={(e) => {
|
onChange={(e) => setTargetFolderId(e.target.value)}
|
||||||
setTargetFolderId(e.target.value);
|
helperText="Choose where your file (and new folder) will live"
|
||||||
if (e.target.value) setShowNewFolderInput(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MenuItem value=""><em>-- Root Directory --</em></MenuItem>
|
<MenuItem value=""><em>-- Root Directory --</em></MenuItem>
|
||||||
{folders?.map((f) => (
|
{folders?.map((f) => (
|
||||||
|
|
@ -155,23 +157,23 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
</TextField>
|
</TextField>
|
||||||
<Button
|
<Button
|
||||||
variant={showNewFolderInput ? "contained" : "outlined"}
|
variant={showNewFolderInput ? "contained" : "outlined"}
|
||||||
onClick={() => {
|
onClick={() => setShowNewFolderInput(!showNewFolderInput)}
|
||||||
setShowNewFolderInput(!showNewFolderInput);
|
|
||||||
if (!showNewFolderInput) setTargetFolderId("");
|
|
||||||
}}
|
|
||||||
sx={{ height: 56, minWidth: 56 }}
|
sx={{ height: 56, minWidth: 56 }}
|
||||||
|
title="Create a new sub-folder"
|
||||||
>
|
>
|
||||||
<CreateNewFolderIcon />
|
<CreateNewFolderIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Collapse in={showNewFolderInput}>
|
<Collapse in={showNewFolderInput}>
|
||||||
<Box sx={{ mt: 2, p: 2, bgcolor: 'grey.50', borderRadius: 2 }}>
|
<Box sx={{ mt: 2, p: 2, bgcolor: 'grey.50', borderRadius: 2, border: '1px solid', borderColor: 'divider' }}>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1 }}>
|
||||||
|
NEW SUB-FOLDER NAME
|
||||||
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
label="New Folder Name"
|
placeholder="e.g. Invoices 2026"
|
||||||
placeholder="Enter name to create folder..."
|
|
||||||
value={newFolderName}
|
value={newFolderName}
|
||||||
onChange={(e) => setNewFolderName(e.target.value)}
|
onChange={(e) => setNewFolderName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -194,17 +196,17 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
fullWidth
|
fullWidth
|
||||||
startIcon={<CloudUploadIcon />}
|
startIcon={<CloudUploadIcon />}
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
sx={{ py: 4, borderStyle: 'dashed', borderWidth: 2 }}
|
sx={{ py: 4, borderStyle: 'dashed', borderWidth: 2, borderRadius: 2 }}
|
||||||
>
|
>
|
||||||
Select File to Upload
|
Select File to Upload
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Paper variant="outlined" sx={{ p: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between', bgcolor: 'primary.50' }}>
|
<Paper variant="outlined" sx={{ p: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between', bgcolor: 'primary.50', borderStyle: 'solid' }}>
|
||||||
<Stack direction="row" spacing={2} alignItems="center">
|
<Stack direction="row" spacing={2} alignItems="center">
|
||||||
<CloudUploadIcon color="primary" />
|
<CloudUploadIcon color="primary" />
|
||||||
<Typography variant="body1" fontWeight="500">{selectedFile.name}</Typography>
|
<Typography variant="body1" fontWeight="600">{selectedFile.name}</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<IconButton onClick={() => setSelectedFile(null)} color="error">
|
<IconButton onClick={() => setSelectedFile(null)} color="error" size="small">
|
||||||
<ClearIcon />
|
<ClearIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
@ -214,35 +216,48 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
|
|
||||||
<Divider sx={{ my: 4 }} />
|
<Divider sx={{ my: 4 }} />
|
||||||
|
|
||||||
{/* MAGIC EXTRACT */}
|
{/* MAGIC EXTRACT SECTION */}
|
||||||
<Box sx={{ mb: 4, p: 2, bgcolor: '#f0f7ff', borderRadius: 2, border: '1px dashed #1976d2' }}>
|
<Box sx={{ mb: 4, p: 2.5, bgcolor: '#f0f7ff', borderRadius: 2, border: '1px dashed #1976d2' }}>
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="subtitle1" fontWeight="bold">Magic Extract</Typography>
|
<Typography variant="subtitle1" fontWeight="bold" color="primary.main">
|
||||||
<Typography variant="caption" color="text.secondary">Populate metadata automatically from file properties.</Typography>
|
Magic Extract
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
Automatically pull Author, GPS, and Camera data from the file.
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleMagicEnhance}
|
onClick={handleMagicEnhance}
|
||||||
disabled={!selectedFile || isExtracting}
|
disabled={!selectedFile || isExtracting}
|
||||||
startIcon={isExtracting ? <CircularProgress size={20} color="inherit" /> : <AutoFixHighIcon />}
|
startIcon={isExtracting ? <CircularProgress size={20} color="inherit" /> : <AutoFixHighIcon />}
|
||||||
|
sx={{ borderRadius: 20, px: 3 }}
|
||||||
>
|
>
|
||||||
{isExtracting ? "Running..." : "Run"}
|
{isExtracting ? "Extracting..." : "Run"}
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* METADATA PREVIEW */}
|
{/* METADATA PREVIEW GRID */}
|
||||||
<Box sx={{ mb: 4 }}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Typography variant="h6" fontWeight="700" sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Typography variant="h6" fontWeight="700" sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<AssignmentIcon color="primary" /> Metadata Preview
|
<AssignmentIcon color="primary" /> Metadata Fields
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
{rows.length === 0 && (
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ fontStyle: 'italic', textAlign: 'center', py: 2 }}>
|
||||||
|
No metadata added yet. Run Magic Extract or add manual fields below.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
{rows.map((row, index) => (
|
{rows.map((row, index) => (
|
||||||
<Grid container spacing={1} key={index} alignItems="center">
|
<Grid container spacing={1} key={index} alignItems="center">
|
||||||
<Grid size={1}>
|
<Grid item xs={1}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={row.selected}
|
checked={row.selected}
|
||||||
|
size="small"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const updated = [...rows];
|
const updated = [...rows];
|
||||||
updated[index].selected = e.target.checked;
|
updated[index].selected = e.target.checked;
|
||||||
|
|
@ -250,9 +265,9 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={5}>
|
<Grid item xs={5}>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth size="small" label="Key" value={row.key}
|
fullWidth size="small" label="Property Name" value={row.key}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const updated = [...rows];
|
const updated = [...rows];
|
||||||
updated[index].key = e.target.value;
|
updated[index].key = e.target.value;
|
||||||
|
|
@ -260,7 +275,7 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={5}>
|
<Grid item xs={5}>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth size="small" label="Value" value={row.value}
|
fullWidth size="small" label="Value" value={row.value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
@ -270,36 +285,43 @@ export default function UploadView({ user, folders }: { user: any; folders: any[
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={1}>
|
<Grid item xs={1}>
|
||||||
<IconButton onClick={() => setRows(rows.filter((_, i) => i !== index))} color="error">
|
<IconButton onClick={() => setRows(rows.filter((_, i) => i !== index))} color="error" size="small">
|
||||||
<DeleteOutlineIcon />
|
<DeleteOutlineIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
startIcon={<AddCircleOutlineIcon />}
|
startIcon={<AddCircleOutlineIcon />}
|
||||||
onClick={() => setRows([...rows, {key: "", value: "", selected: true}])}
|
onClick={() => setRows([...rows, {key: "", value: "", selected: true}])}
|
||||||
|
sx={{ alignSelf: 'flex-start', mt: 1 }}
|
||||||
>
|
>
|
||||||
Add Manual Field
|
Add Manual Field
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* UPLOAD BUTTON */}
|
{/* ACTION BUTTON */}
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="large"
|
size="large"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={!canSubmit || saveStatus === 'saving'}
|
disabled={!canSubmit || saveStatus === 'saving'}
|
||||||
sx={{ py: 2, fontWeight: 'bold' }}
|
sx={{
|
||||||
|
py: 2,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: 4
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{saveStatus === 'saving' ? (
|
{saveStatus === 'saving' ? (
|
||||||
<Stack direction="row" spacing={2} alignItems="center">
|
<Stack direction="row" spacing={2} alignItems="center">
|
||||||
<CircularProgress size={24} color="inherit" />
|
<CircularProgress size={24} color="inherit" />
|
||||||
<Typography>Processing Upload...</Typography>
|
<Typography>Creating Folder & Uploading...</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
"Complete Upload & Save"
|
"Complete Upload & Save"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue