Project-Image-Uploader/ERWEITERUNG.md

469 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Image-Uploader Erweiterung: Multi-Image Upload mit Beschreibung
## 🎯 Ziel
Erweiterung der bestehenden Single-Image-Upload-Funktionalität zu einem Multi-Image-Upload mit Beschreibungstext für spätere Slideshow-Nutzung.
## 📊 Aufwandsschätzung
**Geschätzter Aufwand: 8-12 Stunden** (verteilt auf 2-3 Arbeitstage)
### Komplexitätsbewertung: ⭐⭐⭐☆☆ (Mittel)
## 🔄 Änderungsübersicht
### Frontend Änderungen (5-7 Stunden)
- **Neue Multi-Upload-Komponente**
- **UI für Beschreibungstext**
- **Vorschau-Galerie**
- **Upload-Progress für mehrere Dateien**
### Backend Änderungen (2-3 Stunden)
- **Neue API-Endpoints**
- **Datenbank/JSON-Struktur für Upload-Gruppen**
- **Batch-Upload-Verarbeitung**
### Integration & Testing (1-2 Stunden)
- **Frontend-Backend-Integration**
- **Error Handling**
- **UI/UX Tests**
---
## 🏗️ Technische Umsetzung
### 1. Backend-Erweiterungen
#### 1.1 Neue Datenstruktur
```javascript
// Neue Upload-Group Struktur
{
groupId: "unique-group-id",
description: "Benutzer-Beschreibung",
uploadDate: "2025-10-11T10:30:00Z",
images: [
{
fileName: "abc123.jpg",
originalName: "foto1.jpg",
filePath: "/upload/abc123.jpg",
uploadOrder: 1
},
{
fileName: "def456.png",
originalName: "foto2.png",
filePath: "/upload/def456.png",
uploadOrder: 2
}
]
}
```
#### 1.2 Neue API-Endpoints
- `POST /api/upload/batch` - Multi-Image Upload
- `GET /api/groups/:groupId` - Upload-Gruppe abrufen
- `GET /api/groups` - Alle Upload-Gruppen auflisten
- `GET /api/slideshow/:groupId` - Slideshow-Daten
#### 1.3 Dateien zu erstellen/ändern:
```
backend/src/
├── routes/
│ ├── upload.js # ✏️ Erweitern
│ ├── batch-upload.js # 🆕 Neu
│ └── groups.js # 🆕 Neu
├── models/
│ └── uploadGroup.js # 🆕 Neu
├── utils/
│ └── groupStorage.js # 🆕 Neu (JSON-basiert)
└── constants.js # ✏️ Neue Endpoints hinzufügen
```
### 2. Frontend-Erweiterungen
#### 2.1 Neue Komponenten
```
frontend/src/Components/
├── ComponentUtils/
│ ├── MultiImageUpload.js # 🆕 Haupt-Upload-Komponente
│ ├── ImagePreviewGallery.js # 🆕 Vorschau der ausgewählten Bilder
│ ├── DescriptionInput.js # 🆕 Textfeld für Beschreibung
│ ├── UploadProgress.js # 🆕 Progress-Anzeige für alle Dateien
│ └── Css/
│ ├── MultiUpload.css # 🆕 Styling
│ └── ImageGallery.css # 🆕 Galerie-Styling
├── Pages/
│ ├── MultiUploadPage.js # 🆕 Neue Seite für Multi-Upload
│ ├── SlideshowPage.js # 🆕 Slideshow-Anzeige
│ └── GroupsOverviewPage.js # 🆕 Übersicht aller Upload-Gruppen
└── Utils/
└── batchUpload.js # 🆕 Batch-Upload-Logik
```
#### 2.2 Routing-Erweiterungen
```javascript
// Neue Routen in App.js
<Route path="/multi-upload" component={MultiUploadPage} />
<Route path="/slideshow/:groupId" component={SlideshowPage} />
<Route path="/groups" component={GroupsOverviewPage} />
```
---
## 📋 Detaillierte Implementierungsschritte
### Phase 1: Backend-Grundlage (2-3h)
#### Schritt 1.1: Upload-Gruppen Datenmodell
```javascript
// backend/src/models/uploadGroup.js
class UploadGroup {
constructor(description) {
this.groupId = generateId();
this.description = description;
this.uploadDate = new Date().toISOString();
this.images = [];
}
addImage(fileName, originalName, uploadOrder) {
this.images.push({
fileName,
originalName,
filePath: `/upload/${fileName}`,
uploadOrder
});
}
}
```
#### Schritt 1.2: JSON-basierte Speicherung
```javascript
// backend/src/utils/groupStorage.js
const fs = require('fs');
const path = require('path');
const GROUPS_FILE = path.join(__dirname, '../data/upload-groups.json');
class GroupStorage {
static saveGroup(group) {
// JSON-Datei lesen, Gruppe hinzufügen, zurückschreiben
}
static getGroup(groupId) {
// Gruppe aus JSON-Datei laden
}
static getAllGroups() {
// Alle Gruppen laden
}
}
```
#### Schritt 1.3: Batch-Upload API
```javascript
// backend/src/routes/batch-upload.js
router.post('/api/upload/batch', (req, res) => {
const { description } = req.body;
const files = req.files.images; // Array von Dateien
const group = new UploadGroup(description);
// Alle Dateien verarbeiten
files.forEach((file, index) => {
const fileName = generateId() + '.' + getFileExtension(file.name);
file.mv(`upload/${fileName}`);
group.addImage(fileName, file.name, index + 1);
});
GroupStorage.saveGroup(group);
res.json({ groupId: group.groupId, message: 'Upload successful' });
});
```
### Phase 2: Frontend Multi-Upload UI (3-4h)
#### Schritt 2.1: Multi-Image Dropzone
```javascript
// frontend/src/Components/ComponentUtils/MultiImageUpload.js
import { useDropzone } from 'react-dropzone';
function MultiImageUpload({ onImagesSelected }) {
const { getRootProps, getInputProps, acceptedFiles } = useDropzone({
accept: 'image/*',
multiple: true,
onDrop: (files) => {
onImagesSelected(files);
}
});
return (
<div {...getRootProps()} className="multi-dropzone">
<input {...getInputProps()} />
<p>Ziehe mehrere Bilder hierher oder klicke zum Auswählen</p>
<p>({acceptedFiles.length} Dateien ausgewählt)</p>
</div>
);
}
```
#### Schritt 2.2: Bild-Vorschau Galerie
```javascript
// frontend/src/Components/ComponentUtils/ImagePreviewGallery.js
function ImagePreviewGallery({ images, onRemoveImage, onReorderImages }) {
return (
<div className="image-preview-gallery">
{images.map((image, index) => (
<div key={index} className="image-preview-item">
<img src={URL.createObjectURL(image)} alt={`Preview ${index + 1}`} />
<button onClick={() => onRemoveImage(index)}></button>
<div className="image-order">{index + 1}</div>
</div>
))}
</div>
);
}
```
#### Schritt 2.3: Beschreibungs-Input
```javascript
// frontend/src/Components/ComponentUtils/DescriptionInput.js
function DescriptionInput({ description, onDescriptionChange, maxLength = 500 }) {
return (
<div className="description-input">
<label>Beschreibung für diese Bildersammlung:</label>
<textarea
value={description}
onChange={(e) => onDescriptionChange(e.target.value)}
maxLength={maxLength}
placeholder="Beschreibe diese Bildersammlung für die spätere Slideshow..."
/>
<div className="character-count">{description.length}/{maxLength}</div>
</div>
);
}
```
### Phase 3: Upload-Logik & Progress (2-3h)
#### Schritt 3.1: Batch-Upload Funktion
```javascript
// frontend/src/Utils/batchUpload.js
async function uploadImageBatch(images, description, onProgress) {
const formData = new FormData();
images.forEach((image, index) => {
formData.append('images', image);
});
formData.append('description', description);
try {
const response = await fetch('/api/upload/batch', {
method: 'POST',
body: formData,
onUploadProgress: (progressEvent) => {
const progress = (progressEvent.loaded / progressEvent.total) * 100;
onProgress(progress);
}
});
return await response.json();
} catch (error) {
throw new Error(`Upload failed: ${error.message}`);
}
}
```
#### Schritt 3.2: Upload Progress Komponente
```javascript
// frontend/src/Components/ComponentUtils/UploadProgress.js
function UploadProgress({ progress, currentFile, totalFiles }) {
return (
<div className="upload-progress">
<div className="progress-bar">
<div className="progress-fill" style={{ width: `${progress}%` }}></div>
</div>
<div className="progress-text">
{currentFile && `Uploading: ${currentFile} (${Math.round(progress)}%)`}
{totalFiles && `${currentFile} von ${totalFiles} Dateien`}
</div>
</div>
);
}
```
### Phase 4: Hauptseite Integration (1-2h)
#### Schritt 4.1: Multi-Upload Seite
```javascript
// frontend/src/Components/Pages/MultiUploadPage.js
function MultiUploadPage() {
const [selectedImages, setSelectedImages] = useState([]);
const [description, setDescription] = useState('');
const [uploading, setUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const handleUpload = async () => {
if (selectedImages.length === 0) return;
setUploading(true);
try {
const result = await uploadImageBatch(
selectedImages,
description,
setUploadProgress
);
// Redirect zur Slideshow oder Erfolgsseite
history.push(`/slideshow/${result.groupId}`);
} catch (error) {
// Error handling
} finally {
setUploading(false);
}
};
return (
<div className="multi-upload-page">
<Navbar />
<div className="upload-container">
<MultiImageUpload onImagesSelected={setSelectedImages} />
{selectedImages.length > 0 && (
<>
<ImagePreviewGallery
images={selectedImages}
onRemoveImage={removeImageAtIndex}
onReorderImages={reorderImages}
/>
<DescriptionInput
description={description}
onDescriptionChange={setDescription}
/>
<button
onClick={handleUpload}
disabled={uploading || selectedImages.length === 0}
className="upload-button"
>
{uploading ? 'Uploading...' : `${selectedImages.length} Bilder hochladen`}
</button>
</>
)}
{uploading && (
<UploadProgress
progress={uploadProgress}
totalFiles={selectedImages.length}
/>
)}
</div>
</div>
);
}
```
### Phase 5: Slideshow & Navigation (2h)
#### Schritt 5.1: Slideshow Komponente
```javascript
// frontend/src/Components/Pages/SlideshowPage.js
function SlideshowPage() {
const { groupId } = useParams();
const [group, setGroup] = useState(null);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
useEffect(() => {
fetch(`/api/groups/${groupId}`)
.then(res => res.json())
.then(setGroup);
}, [groupId]);
if (!group) return <div>Loading...</div>;
return (
<div className="slideshow-page">
<div className="slideshow-header">
<h1>{group.description}</h1>
<p>Hochgeladen am: {new Date(group.uploadDate).toLocaleDateString()}</p>
</div>
<div className="slideshow-container">
<img
src={group.images[currentImageIndex].filePath}
alt={`Bild ${currentImageIndex + 1}`}
className="slideshow-image"
/>
<div className="slideshow-controls">
<button onClick={() => setCurrentImageIndex(prev =>
prev > 0 ? prev - 1 : group.images.length - 1
)}>
Vorheriges
</button>
<span>{currentImageIndex + 1} / {group.images.length}</span>
<button onClick={() => setCurrentImageIndex(prev =>
prev < group.images.length - 1 ? prev + 1 : 0
)}>
Nächstes
</button>
</div>
</div>
<div className="slideshow-thumbnails">
{group.images.map((image, index) => (
<img
key={index}
src={image.filePath}
alt={`Thumbnail ${index + 1}`}
className={`thumbnail ${index === currentImageIndex ? 'active' : ''}`}
onClick={() => setCurrentImageIndex(index)}
/>
))}
</div>
</div>
);
}
```
---
## 🎨 UI/UX Verbesserungen
### Drag & Drop Features
- **Bilder-Reihenfolge ändern**: Drag & Drop in der Vorschau
- **Bilder entfernen**: X-Button auf jedem Vorschaubild
- **Bulk-Aktionen**: Alle entfernen, Reihenfolge umkehren
### Responsive Design
- **Mobile-optimiert**: Touch-friendly Upload und Slideshow
- **Tablet-Ansicht**: Optimierte Galerie-Darstellung
- **Desktop**: Erweiterte Features wie Keyboard-Navigation
### Benutzerfreundlichkeit
- **Progress-Feedback**: Echtzeitanzeige des Upload-Fortschritts
- **Error Handling**: Klare Fehlermeldungen bei Upload-Problemen
- **Auto-Save**: Beschreibung zwischenspeichern
- **Vorschau-Modus**: Slideshow vor Upload testen
---
## 🧪 Testing-Strategie
### Unit Tests
- Upload-Gruppe Datenmodell
- Batch-Upload API-Endpoints
- Frontend-Komponenten (Jest/React Testing Library)
### Integration Tests
- End-to-End Upload-Flow
- Slideshow-Navigation
- Error-Szenarien
### Performance Tests
- Multiple große Dateien (>10MB)
- Viele kleine Dateien (>50 Bilder)
- Speicher-Verbrauch bei großen Uploads
---