|
|
@@ -259,6 +259,13 @@ export default function AdminDashboard() { |
|
|
const [savingPortal, setSavingPortal] = useState(false);
|
|
|
const [savingPortal, setSavingPortal] = useState(false);
|
|
|
const [uploading, setUploading] = useState<{ [key: string]: boolean }>({});
|
|
|
const [uploading, setUploading] = useState<{ [key: string]: boolean }>({});
|
|
|
|
|
|
|
|
|
|
|
|
// Hex input per il theme color: stato locale che resta libero durante la digitazione,
|
|
|
|
|
|
// committa su portal.themeColor solo quando la stringa e' un hex completo valido.
|
|
|
|
|
|
const [hexInput, setHexInput] = useState<string>('#1e3a8a');
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (portal.themeColor) setHexInput(portal.themeColor);
|
|
|
|
|
|
}, [portal.themeColor]);
|
|
|
|
|
|
|
|
|
// NEW UI STATES: Toast and Confirm Dialog
|
|
|
// NEW UI STATES: Toast and Confirm Dialog
|
|
|
const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
|
|
|
const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
|
|
|
const [confirmDialog, setConfirmDialog] = useState<{ message: string, onConfirm: () => void } | null>(null);
|
|
|
const [confirmDialog, setConfirmDialog] = useState<{ message: string, onConfirm: () => void } | null>(null);
|
|
|
@@ -836,7 +843,25 @@ export default function AdminDashboard() { |
|
|
<label className="block text-sm font-semibold text-gray-700 mb-1">Theme Color</label>
|
|
|
<label className="block text-sm font-semibold text-gray-700 mb-1">Theme Color</label>
|
|
|
<div className="flex items-center gap-4">
|
|
|
<div className="flex items-center gap-4">
|
|
|
<input type="color" value={portal.themeColor || '#1e3a8a'} onChange={e => setPortal({...portal, themeColor: e.target.value})} className="h-12 w-12 rounded cursor-pointer border-0 p-0" />
|
|
|
<input type="color" value={portal.themeColor || '#1e3a8a'} onChange={e => setPortal({...portal, themeColor: e.target.value})} className="h-12 w-12 rounded cursor-pointer border-0 p-0" />
|
|
|
<span className="text-gray-900 font-mono font-medium">{portal.themeColor || '#1e3a8a'}</span>
|
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={hexInput}
|
|
|
|
|
|
onChange={e => {
|
|
|
|
|
|
const v = e.target.value;
|
|
|
|
|
|
setHexInput(v);
|
|
|
|
|
|
// Commit a portal.themeColor solo se la stringa e' un hex pieno e valido.
|
|
|
|
|
|
if (/^#[0-9a-fA-F]{6}$/.test(v)) setPortal({ ...portal, themeColor: v.toLowerCase() });
|
|
|
|
|
|
}}
|
|
|
|
|
|
onBlur={() => {
|
|
|
|
|
|
// Se l'utente esce dal campo con un valore non valido, ripristina l'ultimo valore noto.
|
|
|
|
|
|
if (!/^#[0-9a-fA-F]{6}$/.test(hexInput)) setHexInput(portal.themeColor || '#1e3a8a');
|
|
|
|
|
|
}}
|
|
|
|
|
|
maxLength={7}
|
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
|
placeholder="#RRGGBB"
|
|
|
|
|
|
aria-label="Theme color hex"
|
|
|
|
|
|
className={`font-mono text-sm px-3 py-2 rounded border outline-none focus:ring-2 focus:ring-blue-500 w-32 ${/^#[0-9a-fA-F]{6}$/.test(hexInput) ? 'border-gray-300 text-gray-900' : 'border-red-500 text-red-600'}`}
|
|
|
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|