'use client'; import { useState, useTransition } from 'react'; import { forgetDevice, regenerateDeviceToken, saveSettings, testTranslation, type SettingsInput, } from './actions'; export type DeviceRow = { id: string; friendlyId: string; macAddress: string; lastSeenAt: string | null; fwVersion: string | null; }; export default function SettingsForm({ canEdit, initial, devices, deviceBaseUrl, }: { canEdit: boolean; initial: SettingsInput; devices: DeviceRow[]; deviceBaseUrl: string; }) { const [form, setForm] = useState(initial); const [pending, startTransition] = useTransition(); const [message, setMessage] = useState<{ kind: 'ok' | 'error'; text: string } | null>(null); const [issuedToken, setIssuedToken] = useState<{ id: string; token: string } | null>(null); function set(key: K, value: SettingsInput[K]) { setMessage(null); setForm((current) => ({ ...current, [key]: value })); } function run(work: () => Promise<{ ok: true } | { ok: false; error: string }>, success: string) { setMessage(null); startTransition(async () => { const result = await work(); setMessage(result.ok ? { kind: 'ok', text: success } : { kind: 'error', text: result.error }); }); } return (
{message ? (

{message.text}

) : null}

Boutique

set('shopName', event.target.value)} className={inputClass} /> set('timezone', event.target.value)} className={inputClass} /> set('countryIsoCode', event.target.value)} className={inputClass} /> set('subdivisionCode', event.target.value)} className={inputClass} />

Écran

L’écran dort entre deux réveils. Un intervalle court garde l’affichage juste, un intervalle long ménage la batterie.

set('refreshRateOpenSec', Number(event.target.value))} className={inputClass} /> set('refreshRateClosedSec', Number(event.target.value))} className={inputClass} />
{canEdit ? ( ) : null}

Appareils

À saisir dans le portail captif de l’écran sous Advanced > Custom Server,{' '} sans barre oblique finale : {deviceBaseUrl}

{devices.length === 0 ? (

Aucun appareil appairé. Le premier écran qui appelle sera enregistré automatiquement.

) : (
    {devices.map((device) => (
  • {device.friendlyId} {device.macAddress} {device.lastSeenAt ? `vu ${device.lastSeenAt}` : 'jamais vu'} {device.fwVersion ? ` · firmware ${device.fwVersion}` : ''} {canEdit ? ( ) : null} {issuedToken?.id === device.id ? (

    Nouveau jeton — affiché une seule fois :

    {issuedToken.token}

    L’ancien ne fonctionne plus. Réappairez l’écran par le portail captif.

    ) : null}
  • ))}
)}
{canEdit ? (

Service de traduction

Traduit une phrase type pour vérifier la configuration, sans créer de message.

) : null}
); } const inputClass = 'mt-1 w-full rounded-[var(--radius-sm)] border border-[var(--line-strong)] bg-[var(--surface)] px-2 py-2 text-base disabled:opacity-60'; function Field({ label, hint, children, }: { label: string; hint?: string; children: React.ReactNode; }) { return ( ); }