From d9dca0ce6af4e5e0215fff323238728f1f453fc9 Mon Sep 17 00:00:00 2001 From: Erol Date: Sat, 7 Mar 2026 09:46:33 +0100 Subject: [PATCH] =?UTF-8?q?F=C3=B8r=20redigering=20av=20baner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/app/admin/page.tsx | 23 +- .../rediger/[slug]/EditFacilityClient.tsx | 281 ++++++++++++++++++ .../src/app/admin/rediger/[slug]/page.tsx | 14 + 3 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 frontend/src/app/admin/rediger/[slug]/EditFacilityClient.tsx create mode 100644 frontend/src/app/admin/rediger/[slug]/page.tsx diff --git a/frontend/src/app/admin/page.tsx b/frontend/src/app/admin/page.tsx index cef7f59..c131716 100644 --- a/frontend/src/app/admin/page.tsx +++ b/frontend/src/app/admin/page.tsx @@ -1,6 +1,6 @@ "use client"; /** - * TEE OFF ADMIN DASHBOARD v1.8 - RESPONSIVT MED AI-HVISKER, KILL SWITCH & FILTER + * TEE OFF ADMIN DASHBOARD v1.9 - RESPONSIVT MED AI-HVISKER, KILL SWITCH, FILTER & FULL EDIT * --------------------------------------------------------------------------- * PLASSERING: frontend/src/app/admin/page.tsx * FUNKSJON: Live-oppdatering, massevalg, redigering, meny og smart-filtrering. @@ -10,6 +10,7 @@ import { useState, useEffect, useMemo } from 'react'; import { API_URL } from "@/config/constants"; import ScrapeMethodSelect from "@/components/ScrapeMethodSelect"; +import Link from 'next/link'; export default function AdminDashboard() { const [facilities, setFacilities] = useState([]); @@ -418,12 +419,20 @@ export default function AdminDashboard() { - +
+ + + Rediger alt + +
))} diff --git a/frontend/src/app/admin/rediger/[slug]/EditFacilityClient.tsx b/frontend/src/app/admin/rediger/[slug]/EditFacilityClient.tsx new file mode 100644 index 0000000..357b869 --- /dev/null +++ b/frontend/src/app/admin/rediger/[slug]/EditFacilityClient.tsx @@ -0,0 +1,281 @@ +"use client"; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; + +// KOMPONENT 1: Viser flate JSON-objekter (som fasiliteter) som rader med Nøkkel og Verdi +const KeyValueEditor = ({ label, value, onChange }: { label: string, value: any, onChange: (v: any) => void }) => { + const entries = Object.entries(value || {}); + + const updateKey = (oldKey: string, newKey: string, val: any) => { + const newObj: any = {}; + for (const [k, v] of entries) { + if (k === oldKey) { + if (newKey.trim()) newObj[newKey] = val; + } else { + newObj[k] = v; + } + } + onChange(newObj); + }; + + const updateVal = (key: string, val: string) => { + onChange({ ...value, [key]: val }); + }; + + const removeKey = (key: string) => { + const newObj = { ...value }; + delete newObj[key]; + onChange(newObj); + }; + + const addRow = () => { + const tempKey = `ny_rad_${Date.now()}`; + onChange({ ...value, [tempKey]: "" }); + }; + + return ( +
+ +
+ {entries.map(([k, v]) => ( +
+ updateKey(k, e.target.value, v)} + /> + updateVal(k, e.target.value)} + /> + +
+ ))} +
+ +
+ ); +}; + +// KOMPONENT 2: Viser Arrays med objekter (som Greenfee-lister) som små pene kort +const ListObjectEditor = ({ label, value, templateKeys, onChange }: { label: string, value: any[], templateKeys: string[], onChange: (v: any[]) => void }) => { + const items = Array.isArray(value) ? value : []; + + const updateField = (index: number, key: string, val: string | number) => { + const newItems = [...items]; + // Prøv å konvertere til tall hvis det gir mening (for priser) + const parsedVal = (!isNaN(Number(val)) && val !== "") ? Number(val) : val; + newItems[index] = { ...newItems[index], [key]: parsedVal }; + onChange(newItems); + }; + + const addRow = () => { + const newItem: any = {}; + templateKeys.forEach(k => newItem[k] = ""); + onChange([...items, newItem]); + }; + + const removeRow = (index: number) => { + const newItems = items.filter((_, i) => i !== index); + onChange(newItems); + }; + + return ( +
+ +
+ {items.map((item, idx) => ( +
+ +
+ {templateKeys.map(key => ( +
+ + updateField(idx, key, e.target.value)} + /> +
+ ))} +
+
+ ))} +
+ +
+ ); +}; + + +export default function EditFacilityClient({ initialData }: { initialData: any }) { + const router = useRouter(); + const [formData, setFormData] = useState(initialData); + const [activeTab, setActiveTab] = useState('generelt'); + const [saving, setSaving] = useState(false); + + const handleChange = (field: string, value: any) => { + setFormData((prev: any) => ({ ...prev, [field]: value })); + }; + + const handleSave = async () => { + setSaving(true); + try { + const res = await fetch(`/api/admin/facilities/${initialData.id}/full`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(formData) + }); + + if (res.ok) { + alert("Anlegget ble oppdatert suksessfullt!"); + router.refresh(); + } else { + alert("Noe gikk galt under lagring."); + } + } catch (e) { + alert("Nettverksfeil."); + } + setSaving(false); + }; + + const tabs = [ + { id: 'generelt', label: 'Generelt' }, + { id: 'lokasjon', label: 'Lokasjon & Kontakt' }, + { id: 'linker', label: 'Lenker & Media' }, + { id: 'okonomi', label: 'Økonomi & Medlemskap' }, + { id: 'avansert', label: 'Spesial & Strukturer (JSON)' } + ]; + + const Input = ({ field, label, type = "text" }: { field: string, label: string, type?: string }) => ( +
+ + {type === 'textarea' ? ( +