2025-08-26 15:45:35 +02:00

234 lines
7.3 KiB
JavaScript
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.

import { useState, useEffect } from 'react';
import { supabase } from '../../lib/supabase';
export default function AdminPanel() {
const [entries, setEntries] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedEntry, setSelectedEntry] = useState(null);
const emptyEntry = {
date: new Date().toLocaleDateString('de-DE'),
title: '',
description: '',
slug: '',
image: '',
content: []
};
// Load data from Supabase on component mount
useEffect(() => {
loadEntries();
}, []);
const loadEntries = async () => {
try {
const { data, error } = await supabase
.from('devlog_entries')
.select('*')
.order('created_at', { ascending: false });
if (error) throw error;
setEntries(data || []);
} catch (error) {
console.error('Fehler beim Laden der Einträge:', error);
alert('Fehler beim Laden der Daten');
} finally {
setLoading(false);
}
};
const handleSave = async (entry, index = null) => {
// Entferne das 'index' und 'editIndex' Feld aus dem Entry
const cleanEntry = { ...entry };
delete cleanEntry.index;
delete cleanEntry.editIndex;
delete cleanEntry.id; // Remove id for new entries
let updatedEntries;
if (index !== null) {
// Bearbeite bestehenden Eintrag - keep the original id
const originalEntry = entries[index];
cleanEntry.id = originalEntry.id;
updatedEntries = entries.map((e, i) => i === index ? cleanEntry : e);
} else {
// Füge neuen Eintrag hinzu
updatedEntries = [...entries, cleanEntry];
}
try {
const response = await fetch('/api/update-devlog', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedEntries),
});
if (!response.ok) throw new Error('Fehler beim Speichern');
// Reload data from Supabase to get updated entries with IDs
await loadEntries();
setSelectedEntry(null);
} catch (error) {
console.error('Fehler:', error);
alert('Fehler beim Speichern der Änderungen');
}
};
const EntryForm = ({ entry, onSave }) => {
const [formData, setFormData] = useState(entry);
const [contentList, setContentList] = useState(entry.content || []);
const handleContentAdd = (type) => {
setContentList([...contentList, { type, value: '' }]);
};
const handleContentChange = (index, value) => {
const newContent = [...contentList];
newContent[index].value = value;
setContentList(newContent);
setFormData({ ...formData, content: newContent });
};
const handleContentRemove = (index) => {
setContentList(contentList.filter((_, i) => i !== index));
setFormData({
...formData,
content: contentList.filter((_, i) => i !== index)
});
};
return (
<div className="space-y-4 bg-white p-6 rounded-lg shadow">
<div className="grid grid-cols-1 gap-4">
<input
type="text"
placeholder="Titel"
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
className="w-full p-2 border rounded"
/>
<input
type="text"
placeholder="Beschreibung"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
className="w-full p-2 border rounded"
/>
<input
type="text"
placeholder="URL-Slug"
value={formData.slug}
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
className="w-full p-2 border rounded"
/>
<input
type="text"
placeholder="Bild-URL"
value={formData.image}
onChange={(e) => setFormData({ ...formData, image: e.target.value })}
className="w-full p-2 border rounded"
/>
<div className="space-y-4">
<h3 className="font-bold">Inhalt</h3>
{contentList.map((content, index) => (
<div key={index} className="flex space-x-2">
<textarea
value={content.value}
onChange={(e) => handleContentChange(index, e.target.value)}
className="w-full p-2 border rounded"
rows="3"
/>
<button
onClick={() => handleContentRemove(index)}
className="px-3 py-1 bg-red-500 text-white rounded hover:bg-red-600"
>
×
</button>
</div>
))}
<div className="flex space-x-2">
<button
onClick={() => handleContentAdd('text')}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
+ Text
</button>
<button
onClick={() => handleContentAdd('image')}
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
>
+ Bild
</button>
</div>
</div>
</div>
<div className="flex justify-end space-x-2 mt-4">
<button
onClick={() => setSelectedEntry(null)}
className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
>
Abbrechen
</button>
<button
onClick={() => onSave(formData)}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Speichern
</button>
</div>
</div>
);
};
if (loading) {
return (
<div className="container mx-auto p-8">
<h1 className="text-3xl font-bold mb-8">DevLog Admin</h1>
<div className="flex justify-center items-center h-64">
<div className="text-xl">Lade Daten...</div>
</div>
</div>
);
}
return (
<div className="container mx-auto p-8">
<h1 className="text-3xl font-bold mb-8">DevLog Admin</h1>
{selectedEntry === null ? (
<div>
<button
onClick={() => setSelectedEntry(emptyEntry)}
className="mb-8 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
>
Neuen Beitrag erstellen
</button>
<div className="space-y-4">
{entries.map((entry, index) => (
<div key={index} className="bg-white p-4 rounded-lg shadow">
<h2 className="text-xl font-bold">{entry.title}</h2>
<p className="text-gray-600">{entry.date}</p>
<p className="mb-4">{entry.description}</p>
<button
onClick={() => setSelectedEntry({ ...entry, editIndex: index })}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Bearbeiten
</button>
</div>
))}
</div>
</div>
) : (
<EntryForm
entry={selectedEntry}
onSave={(formData) => handleSave(formData, selectedEntry.editIndex)}
/>
)}
</div>
);
}