Docker mit traefik und portainer
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useParams, Link, useNavigate } from 'react-router-dom';
|
||||
import type { Recipe } from '../services/api';
|
||||
import { recipeApi, imageApi } from '../services/api';
|
||||
import FileUpload from './FileUpload';
|
||||
import './RecipeDetail.css';
|
||||
|
||||
// Helper function to convert URLs in text to clickable links
|
||||
@@ -33,6 +34,8 @@ const RecipeDetail: React.FC = () => {
|
||||
const [recipe, setRecipe] = useState<Recipe | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [editingImages, setEditingImages] = useState(false);
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const loadRecipe = async () => {
|
||||
@@ -62,6 +65,43 @@ const RecipeDetail: React.FC = () => {
|
||||
loadRecipe();
|
||||
}, [id]);
|
||||
|
||||
const handleImageUpload = async (files: File[]) => {
|
||||
if (!recipe || !id) return;
|
||||
|
||||
try {
|
||||
setUploadProgress(0);
|
||||
await imageApi.uploadImages(parseInt(id), files, setUploadProgress);
|
||||
|
||||
// Reload recipe to get updated images
|
||||
const response = await recipeApi.getRecipe(parseInt(id));
|
||||
if (response.success) {
|
||||
setRecipe(response.data);
|
||||
}
|
||||
|
||||
setUploadProgress(0);
|
||||
} catch (error) {
|
||||
console.error('Error uploading images:', error);
|
||||
setError('Fehler beim Hochladen der Bilder');
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageDelete = async (imageId: number) => {
|
||||
if (!recipe || !id) return;
|
||||
|
||||
try {
|
||||
await imageApi.deleteImage(imageId);
|
||||
|
||||
// Reload recipe to get updated images
|
||||
const response = await recipeApi.getRecipe(parseInt(id));
|
||||
if (response.success) {
|
||||
setRecipe(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting image:', error);
|
||||
setError('Fehler beim Löschen des Bildes');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="recipe-detail">
|
||||
@@ -114,6 +154,13 @@ const RecipeDetail: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="recipe-actions">
|
||||
<button
|
||||
onClick={() => setEditingImages(!editingImages)}
|
||||
className="edit-button"
|
||||
style={{ marginRight: '8px' }}
|
||||
>
|
||||
📸 {editingImages ? 'Fertig' : 'Bilder verwalten'}
|
||||
</button>
|
||||
<Link to={`/recipes/${recipe.id}/edit`} className="edit-button">
|
||||
✏️ Bearbeiten
|
||||
</Link>
|
||||
@@ -174,6 +221,64 @@ const RecipeDetail: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image Management Section */}
|
||||
{editingImages && (
|
||||
<div className="image-management">
|
||||
<h3>Bilder verwalten</h3>
|
||||
|
||||
{/* Upload new images */}
|
||||
<div className="upload-section">
|
||||
<h4>Neue Bilder hochladen</h4>
|
||||
<FileUpload
|
||||
onFilesSelected={handleImageUpload}
|
||||
maxFiles={5}
|
||||
maxFileSize={5}
|
||||
disabled={uploadProgress > 0}
|
||||
/>
|
||||
{uploadProgress > 0 && uploadProgress < 100 && (
|
||||
<div className="upload-progress">
|
||||
<div className="upload-progress-bar" style={{ width: `${uploadProgress}%` }}></div>
|
||||
<span className="upload-progress-text">{uploadProgress}% hochgeladen</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Existing images */}
|
||||
{recipe.images && recipe.images.length > 0 && (
|
||||
<div className="existing-images">
|
||||
<h4>Vorhandene Bilder ({recipe.images.length})</h4>
|
||||
<div className="images-grid">
|
||||
{recipe.images.map((image, index) => (
|
||||
<div key={image.id} className="image-item">
|
||||
<div className="image-preview">
|
||||
<img
|
||||
src={imageApi.getImageUrl(image.filePath)}
|
||||
alt={`Bild ${index + 1}`}
|
||||
/>
|
||||
<button
|
||||
className="delete-image-btn"
|
||||
onClick={() => handleImageDelete(image.id)}
|
||||
title="Bild löschen"
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
</div>
|
||||
<div className="image-info">
|
||||
<span className="image-name">
|
||||
{image.filePath.split('/').pop()}
|
||||
</span>
|
||||
{image.filePath.includes('_0.jpg') && (
|
||||
<span className="main-image-badge">Hauptbild</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Two Column Layout for Description/Ingredients and Preparation */}
|
||||
<div className="recipe-columns">
|
||||
{/* Left Column - Description and Ingredients */}
|
||||
|
||||
Reference in New Issue
Block a user