115 lines
6.6 KiB
TypeScript
115 lines
6.6 KiB
TypeScript
// app/admin/NoticeTab.tsx
|
|
|
|
"use client";
|
|
|
|
import { AlertCircle, AlertTriangle, BellRing, Info, Loader2, Save, CheckCircle } from 'lucide-react';
|
|
import React, { useEffect, useState } from 'react';
|
|
import { readJsonFile, writeJsonFile } from '../actions/jsonEditor';
|
|
|
|
interface NoticeData {
|
|
isActive: boolean;
|
|
type: 'notice' | 'warning' | 'important';
|
|
message: string;
|
|
}
|
|
|
|
export const NoticeTab = ({ isOffline }: { isOffline: boolean }) => {
|
|
const [notice, setNotice] = useState<NoticeData>({ isActive: false, type: 'warning', message: '' });
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
const [saveStatus, setSaveStatus] = useState<'idle' | 'success' | 'error'>('idle');
|
|
|
|
useEffect(() => {
|
|
const fetchNotice = async () => {
|
|
const res = await readJsonFile('notice.json');
|
|
if (res.success && res.data) {
|
|
setNotice(res.data);
|
|
}
|
|
setIsLoading(false);
|
|
};
|
|
fetchNotice();
|
|
}, []);
|
|
|
|
const handleSave = async () => {
|
|
setIsSaving(true);
|
|
setSaveStatus('idle');
|
|
const res = await writeJsonFile('notice.json', notice);
|
|
if (res.success) {
|
|
setSaveStatus('success');
|
|
setTimeout(() => setSaveStatus('idle'), 3000);
|
|
} else {
|
|
setSaveStatus('error');
|
|
}
|
|
setIsSaving(false);
|
|
};
|
|
|
|
if (isLoading) return <div className="flex justify-center py-10"><Loader2 className="animate-spin text-slate-teal" size={32} /></div>;
|
|
|
|
return (
|
|
<div className="space-y-6 animate-fade-in">
|
|
<div className="bg-eggshell border border-slate-teal/10 p-5 md:p-8 rounded-3xl shadow-sm">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h2 className="text-xl font-black text-ebony uppercase tracking-widest flex items-center">
|
|
<div className="bg-white p-2.5 rounded-xl mr-3 text-slate-teal shadow-sm">
|
|
<BellRing size={24} />
|
|
</div>
|
|
Meddelande på startsidan
|
|
</h2>
|
|
|
|
{/* Av/På Switch */}
|
|
<button
|
|
onClick={() => setNotice({ ...notice, isActive: !notice.isActive })}
|
|
disabled={isOffline}
|
|
className={`relative inline-flex h-7 w-12 shrink-0 items-center rounded-full transition-colors focus:outline-none shadow-inner disabled:opacity-50 ${notice.isActive ? 'bg-moss' : 'bg-slate-teal/20'}`}
|
|
>
|
|
<span className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform shadow-sm ${notice.isActive ? 'translate-x-6' : 'translate-x-1'}`} />
|
|
</button>
|
|
</div>
|
|
|
|
<div className={`transition-all duration-300 ${!notice.isActive ? 'opacity-40 grayscale pointer-events-none' : ''}`}>
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-black text-ebony/60 uppercase tracking-widest mb-2">Typ av meddelande</label>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|
<button onClick={() => setNotice({ ...notice, type: 'notice' })} className={`flex items-center p-3 rounded-xl border-2 transition-all font-bold text-sm ${notice.type === 'notice' ? 'bg-seafoam/10 border-seafoam text-slate-teal' : 'bg-white border-transparent text-ebony/60 hover:bg-white/80 shadow-sm'}`}>
|
|
<Info size={18} className="mr-2" /> Information
|
|
</button>
|
|
<button onClick={() => setNotice({ ...notice, type: 'warning' })} className={`flex items-center p-3 rounded-xl border-2 transition-all font-bold text-sm ${notice.type === 'warning' ? 'bg-goldenrod/10 border-goldenrod text-goldenrod' : 'bg-white border-transparent text-ebony/60 hover:bg-white/80 shadow-sm'}`}>
|
|
<AlertTriangle size={18} className="mr-2" /> Varning
|
|
</button>
|
|
<button onClick={() => setNotice({ ...notice, type: 'important' })} className={`flex items-center p-3 rounded-xl border-2 transition-all font-bold text-sm ${notice.type === 'important' ? 'bg-emergency/10 border-emergency text-emergency' : 'bg-white border-transparent text-ebony/60 hover:bg-white/80 shadow-sm'}`}>
|
|
<AlertCircle size={18} className="mr-2" /> Akut / Viktigt
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-black text-ebony/60 uppercase tracking-widest mb-2">Text</label>
|
|
<textarea
|
|
value={notice.message}
|
|
onChange={(e) => setNotice({ ...notice, message: e.target.value })}
|
|
placeholder="Skriv ditt meddelande här..."
|
|
className="w-full h-32 bg-white border border-slate-teal/10 p-4 rounded-2xl text-sm font-bold resize-none focus:outline-none focus:border-slate-teal shadow-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-8 flex items-center justify-between border-t border-slate-teal/10 pt-6">
|
|
<p className="text-xs font-bold text-slate-teal/60">
|
|
{saveStatus === 'success' && <span className="text-moss flex items-center"><CheckCircle size={14} className="mr-1" /> Sparat och live!</span>}
|
|
{saveStatus === 'error' && <span className="text-emergency flex items-center"><AlertTriangle size={14} className="mr-1" /> Kunde inte spara.</span>}
|
|
{isOffline && "Du är offline. Går ej att uppdatera."}
|
|
</p>
|
|
<button
|
|
onClick={handleSave}
|
|
disabled={isOffline || isSaving}
|
|
className="bg-slate-teal text-eggshell font-black uppercase tracking-widest text-xs px-6 py-3 rounded-xl hover:bg-ebony transition-colors disabled:opacity-50 shadow-sm flex items-center gap-2"
|
|
>
|
|
{isSaving ? <Loader2 size={16} className="animate-spin" /> : <Save size={16} />}
|
|
Spara ändringar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |