192 lines
9.9 KiB
TypeScript
192 lines
9.9 KiB
TypeScript
// app/admin/AdminForm.tsx
|
|
|
|
'use client';
|
|
|
|
import { useActionState, useEffect, useState } from 'react';
|
|
import { FaPlus, FaTrash, FaArrowUp, FaArrowDown } from 'react-icons/fa'; // Importera pilarna
|
|
import { saveConfigAction } from './actions';
|
|
|
|
export default function AdminForm({ initialConfig }: { initialConfig: any }) {
|
|
const [state, formAction, isPending] = useActionState(saveConfigAction, null);
|
|
const [showToast, setShowToast] = useState(false);
|
|
|
|
const [classes, setClasses] = useState<Array<{ name: string, color: string, desc: string }>>(
|
|
Array.isArray(initialConfig.beachPage.classes)
|
|
? initialConfig.beachPage.classes
|
|
: [
|
|
{ name: 'Grön', color: '#16a34a', desc: 'Nybörjare' },
|
|
{ name: 'Blå', color: '#2563eb', desc: 'Spelat lite' },
|
|
{ name: 'Svart', color: '#111827', desc: 'Spelat mycket' }
|
|
]
|
|
);
|
|
|
|
const addClass = () => setClasses([...classes, { name: '', color: '#406185', desc: '' }]);
|
|
|
|
const removeClass = (index: number) => {
|
|
setClasses(classes.filter((_, i) => i !== index));
|
|
};
|
|
|
|
const updateClass = (index: number, field: keyof typeof classes[0], value: string) => {
|
|
const newClasses = [...classes];
|
|
newClasses[index][field] = value;
|
|
setClasses(newClasses);
|
|
};
|
|
|
|
// Funktioner för att byta plats på klasserna
|
|
const moveUp = (index: number) => {
|
|
if (index === 0) return;
|
|
const newClasses = [...classes];
|
|
[newClasses[index - 1], newClasses[index]] = [newClasses[index], newClasses[index - 1]];
|
|
setClasses(newClasses);
|
|
};
|
|
|
|
const moveDown = (index: number) => {
|
|
if (index === classes.length - 1) return;
|
|
const newClasses = [...classes];
|
|
[newClasses[index + 1], newClasses[index]] = [newClasses[index], newClasses[index + 1]];
|
|
setClasses(newClasses);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (state?.success) {
|
|
setShowToast(true);
|
|
const timer = setTimeout(() => setShowToast(false), 3000);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [state]);
|
|
|
|
return (
|
|
<form action={formAction} className="flex flex-col gap-8">
|
|
{showToast && (
|
|
<div className="fixed top-5 right-5 bg-green-500 text-white px-6 py-3 rounded-lg shadow-2xl font-bold z-50 animate-bounce">
|
|
✓ Ändringar sparade!
|
|
</div>
|
|
)}
|
|
|
|
{/* Startsida */}
|
|
<section className="border-b pb-6">
|
|
<h2 className="text-xl font-bold mb-4 border-l-4 border-[#fdb84b] pl-2 text-[#406185]">Startsida</h2>
|
|
<label className="flex items-center gap-2 cursor-pointer">
|
|
<input type="checkbox" name="showBeachPromo" defaultChecked={initialConfig.showBeachPromo} className="w-5 h-5 accent-[#406185]" />
|
|
<span className="font-semibold">Visa Beach-Promo bar</span>
|
|
</label>
|
|
</section>
|
|
|
|
{/* Beach-Turnering */}
|
|
<section className="flex flex-col gap-6">
|
|
<h2 className="text-xl font-bold border-l-4 border-[#fdb84b] pl-2 text-[#406185]">Beach-Turnering</h2>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-bold mb-1">Matchstart</label>
|
|
<input type="datetime-local" name="matchStart" defaultValue={initialConfig.beachPage.matchStart} className="w-full border p-2 rounded focus:ring-2 focus:ring-[#406185] outline-none" />
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-bold mb-1">Länk till Mat- & Sommarfesten</label>
|
|
<input name="festivalLink" defaultValue={initialConfig.beachPage.festivalLink} className="w-full border p-2 rounded focus:ring-2 focus:ring-[#406185] outline-none" />
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-bold mb-1">Meddelande vid stängd anmälan</label>
|
|
<textarea name="closeReason" defaultValue={initialConfig.beachPage.closeReason} className="w-full border p-2 rounded h-20" />
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-bold mb-1">Övrig info (en rad per punkt på sajten)</label>
|
|
<textarea name="otherInfo" defaultValue={initialConfig.beachPage.otherInfo} className="w-full border p-2 rounded h-24" />
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-bold mb-1">Pris-text (längst ner på infosidan)</label>
|
|
<input name="prizesText" defaultValue={initialConfig.beachPage.prizesText} className="w-full border p-2 rounded" />
|
|
</div>
|
|
|
|
<label className="flex items-center gap-2 cursor-pointer text-red-600 bg-red-50 p-3 rounded-lg border border-red-100">
|
|
<input type="checkbox" name="isClosedOverride" defaultChecked={initialConfig.beachPage.isClosedOverride} className="w-5 h-5 accent-red-600" />
|
|
<span className="font-bold uppercase">Stäng anmälan manuellt</span>
|
|
</label>
|
|
</section>
|
|
|
|
{/* SPELKLASSER - Moderniserad för att matcha */}
|
|
<section className="flex flex-col gap-4">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<h2 className="text-xl font-bold border-l-4 border-[#fdb84b] pl-2 text-[#406185]">Spelklasser</h2>
|
|
<button
|
|
type="button"
|
|
onClick={addClass}
|
|
className="flex items-center gap-2 text-sm font-bold text-white bg-[#406185] px-4 py-2 rounded hover:brightness-110 transition-all active:scale-95"
|
|
>
|
|
<FaPlus size={12} /> Lägg till
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
{classes.map((c, index) => (
|
|
<div key={index} className="flex flex-col md:flex-row gap-3 items-center w-full">
|
|
|
|
{/* Sorteringspilar */}
|
|
<div className="flex md:flex-col gap-1 text-gray-400">
|
|
<button type="button" onClick={() => moveUp(index)} disabled={index === 0} className="hover:text-[#406185] disabled:opacity-30 disabled:hover:text-gray-400 p-1">
|
|
<FaArrowUp size={14} />
|
|
</button>
|
|
<button type="button" onClick={() => moveDown(index)} disabled={index === classes.length - 1} className="hover:text-[#406185] disabled:opacity-30 disabled:hover:text-gray-400 p-1">
|
|
<FaArrowDown size={14} />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex-1 flex gap-2 w-full">
|
|
{/* Färgplockare som ser ut som ett vanligt inputfält */}
|
|
<div className="w-12 h-10 border rounded overflow-hidden shrink-0 focus-within:ring-2 focus-within:ring-[#406185]">
|
|
<input
|
|
type="color"
|
|
value={c.color}
|
|
onChange={(e) => updateClass(index, 'color', e.target.value)}
|
|
className="w-16 h-16 -m-2 cursor-pointer outline-none"
|
|
title="Välj färg"
|
|
/>
|
|
</div>
|
|
|
|
<input
|
|
type="text"
|
|
value={c.name}
|
|
onChange={(e) => updateClass(index, 'name', e.target.value)}
|
|
placeholder="Lagnamn (t.ex. Grön)"
|
|
required
|
|
className="w-1/3 border p-2 rounded focus:ring-2 focus:ring-[#406185] outline-none min-w-25"
|
|
/>
|
|
|
|
<input
|
|
type="text"
|
|
value={c.desc}
|
|
onChange={(e) => updateClass(index, 'desc', e.target.value)}
|
|
placeholder="Beskrivning (t.ex. Nybörjare)"
|
|
className="flex-1 border p-2 rounded focus:ring-2 focus:ring-[#406185] outline-none min-w-30"
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => removeClass(index)}
|
|
className="text-gray-400 hover:text-red-500 p-2 rounded transition-colors self-end md:self-auto shrink-0"
|
|
title="Ta bort klass"
|
|
>
|
|
<FaTrash />
|
|
</button>
|
|
</div>
|
|
))}
|
|
{classes.length === 0 && (
|
|
<p className="text-gray-500 italic text-sm">Inga klasser inlagda. Besökarna kommer inte kunna välja nivå.</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Dold JSON-sträng för Server Action */}
|
|
<input type="hidden" name="classesJSON" value={JSON.stringify(classes)} />
|
|
</section>
|
|
|
|
<button type="submit" disabled={isPending} className="bg-[#406185] text-white py-4 mt-4 rounded-lg font-bold text-lg hover:bg-black transition-all active:scale-95 disabled:bg-gray-400 shadow-lg">
|
|
{isPending ? 'Sparar...' : 'Spara inställningar'}
|
|
</button>
|
|
</form>
|
|
);
|
|
} |