import { useEffect, useRef, useState } from 'react' import { api, message } from '../bridge.js' import * as Icon from '../ui/icons.jsx' import logo from '../assets/loyaly-mark.png' // The help. A conversation with the head-office assistant, which answers from // the shop's own data and knows how the product is set up - so "why is nobody // being recognised" and "how do I add my camera" are both answered here, by // the same thing, without leaving the app. // // The history lives in this component and is resent whole; nothing is stored // anywhere. A PC running on its own has no head office to ask and is told so. const SUGGESTED = [ 'Is my shop working right now?', 'How do I add my camera?', 'Why has nobody been recognised today?', 'Who came in this morning?', ] export default function Assistant({ session, onClose }) { const [turns, setTurns] = useState([]) const [draft, setDraft] = useState('') const [busy, setBusy] = useState(false) const [error, setError] = useState(null) const scroller = useRef(null) const standalone = session?.standalone useEffect(() => { scroller.current?.scrollTo({ top: 1e9, behavior: 'smooth' }) }, [turns, busy]) async function ask(text) { const q = (text ?? draft).trim() if (!q || busy) return const history = [...turns, { role: 'user', text: q }] setTurns(history); setDraft(''); setBusy(true); setError(null) try { const a = await api.ask(history.map(t => ({ role: t.role, text: t.text }))) setTurns([...history, { role: 'assistant', text: a.text, used: a.used ?? [] }]) } catch (e) { setError(message(e)) setTurns(turns) // the question stays in the box, not in the transcript setDraft(q) } finally { setBusy(false) } } return ( ) } function greeting(session) { const h = new Date().getHours() const part = h < 12 ? 'Morning' : h < 17 ? 'Afternoon' : 'Evening' const name = session?.user?.full_name?.split(' ')[0] return name ? `${part}, ${name}.` : `${part}.` } // What a PC with no head office can still be told. Static on purpose: there is // nobody to ask, and a chat box that always fails is worse than a short list. function QuickHelp() { return (
Adding a camera
Cameras → Add camera. The address is on a label on the camera; pick the make and the stream path fills itself in. Test, then save.
Proving it works
Press Check placement and walk past the camera like a customer for 25 seconds. Only “good” means it can recognise faces — otherwise move it to head height, facing the way people approach.
Nothing showing on Live
Check the camera is Connected and Proven. A camera aimed from above or the side streams fine and recognises nobody.
Linking to head office later
Use the link button at the bottom of the sidebar and type an installation code from head office. Nothing recorded here is lost.
) }