234 lines
7.3 KiB
JavaScript
234 lines
7.3 KiB
JavaScript
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>
|
||
);
|
||
}
|