Spaces:
Running
Running
File size: 5,675 Bytes
351d460 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
import type { DragEvent } from 'react';
import { Button, Container, SegmentInput, IconButton } from '@ifrc-go/ui';
import { UploadCloudLineIcon, ArrowRightLineIcon, DeleteBinLineIcon } from '@ifrc-go/icons';
import { Link } from 'react-router-dom';
import styles from '../../pages/UploadPage/UploadPage.module.css';
interface FileUploadSectionProps {
files: File[];
file: File | null;
preview: string | null;
imageType: string;
onFileChange: (file: File | undefined) => void;
onRemoveImage: (index: number) => void;
onAddImage: () => void;
onImageTypeChange: (value: string | undefined) => void;
onChangeFile?: (file: File | undefined) => void;
}
export default function FileUploadSection({
files,
file,
preview,
imageType,
onFileChange,
onRemoveImage,
onAddImage,
onImageTypeChange,
onChangeFile,
}: FileUploadSectionProps) {
const onDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
const dropped = e.dataTransfer.files?.[0];
if (dropped) {
onFileChange(dropped);
}
};
return (
<div className="space-y-6">
<p className="text-gray-700 leading-relaxed max-w-2xl mx-auto">
This app evaluates how well multimodal AI models analyze and describe
crisis maps and drone imagery. Upload an image and the AI will generate a description.
Then you can review and rate the result based on your expertise.
</p>
{/* "More »" link */}
<div className={styles.helpLink}>
<Link
to="/help"
className={styles.helpLink}
>
More <ArrowRightLineIcon className="w-3 h-3" />
</Link>
</div>
{/* Image Type Selection */}
<div className="flex justify-center">
<Container withInternalPadding className="bg-transparent border-none shadow-none">
<SegmentInput
name="image-type"
value={imageType}
onChange={(value) => onImageTypeChange(value as string)}
options={[
{ key: 'crisis_map', label: 'Crisis Maps' },
{ key: 'drone_image', label: 'Drone Imagery' }
]}
keySelector={(o) => o.key}
labelSelector={(o) => o.label}
/>
</Container>
</div>
<div
className={`${styles.dropZone} ${file ? styles.hasFile : ''}`}
onDragOver={(e) => e.preventDefault()}
onDrop={onDrop}
>
{files.length > 1 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-4">
{files.map((file, index) => (
<div key={index} className="relative">
<img
src={URL.createObjectURL(file)}
alt={`Image ${index + 1}`}
className="w-full h-32 object-cover rounded"
/>
<IconButton
name="remove-image"
variant="tertiary"
onClick={() => onRemoveImage(index)}
title="Remove image"
ariaLabel="Remove image"
className="absolute top-2 right-2 bg-white/90 hover:bg-white shadow-md hover:shadow-lg border border-gray-200 hover:border-red-300 transition-all duration-200 backdrop-blur-sm"
>
<DeleteBinLineIcon className="w-4 h-4" />
</IconButton>
<div className="text-xs text-center mt-1">{file.name}</div>
</div>
))}
</div>
) : file && preview ? (
<div className={styles.filePreview}>
<div className={styles.filePreviewImage}>
<img
src={preview}
alt="File preview"
/>
</div>
<p className={styles.fileName}>
{file.name}
</p>
<p className={styles.fileInfo}>
{(file.size / 1024 / 1024).toFixed(2)} MB
</p>
</div>
) : (
<>
<UploadCloudLineIcon className={styles.dropZoneIcon} />
<p className={styles.dropZoneText}>Drag & Drop any file here</p>
<p className={styles.dropZoneSubtext}>or</p>
</>
)}
<div className="flex gap-2">
<label className="inline-block cursor-pointer">
<input
type="file"
className="sr-only"
accept=".jpg,.jpeg,.png,.tiff,.tif,.heic,.heif,.webp,.gif,.pdf"
onChange={e => {
if (file && onChangeFile) {
// If there's already a file, use onChangeFile to replace it
onChangeFile(e.target.files?.[0]);
} else {
// If no file exists, use onFileChange to add it
onFileChange(e.target.files?.[0]);
}
}}
/>
<Button
name="upload"
variant="secondary"
size={1}
onClick={() => (document.querySelector('input[type="file"]') as HTMLInputElement)?.click()}
>
{file ? 'Change Image' : 'Browse Files'}
</Button>
</label>
{file && files.length < 5 && (
<Button
name="add-image"
variant="secondary"
size={1}
onClick={onAddImage}
>
Add Image
</Button>
)}
</div>
</div>
</div>
);
}
|