The assistant is Loya, and she lives in the top-right corner

A name, a voice and a door. The prompt now asks for a colleague on the
shop floor - answer first, one to three sentences, the shop's name and
the person's name, the one thing to do next - instead of a report with
headings. Both apps put her behind the Loyaly mark in the top-right
corner of every screen, because a buddy you have to find in a sidebar
is not around.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
2026-09-19 13:42:35 +05:30
parent 2835252bb3
commit 51b9cb9743
15 changed files with 82 additions and 47 deletions

View File

@@ -11,10 +11,10 @@ import logo from '../assets/loyaly-mark.png'
// 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?',
'Is everything working right now?',
'Why has nobody been recognised today?',
'Where do I get an installation code?',
'Who came in this morning?',
]
export default function Assistant({ session, onClose }) {
@@ -43,12 +43,12 @@ export default function Assistant({ session, onClose }) {
}
return (
<aside className="helper" role="dialog" aria-label="Ask Behavision">
<aside className="helper" role="dialog" aria-label="Loya">
<header className="helperhead">
<img src={logo} alt="" />
<div>
<b>Ask Behavision</b>
<span>Setup help, and what your shop is doing right now.</span>
<b>Loya</b>
<span>Your Behavision buddy — knows your cameras, your customers and your numbers.</span>
</div>
<button className="close" onClick={onClose} aria-label="Close"><Icon.Close size={16} /></button>
</header>
@@ -59,13 +59,15 @@ export default function Assistant({ session, onClose }) {
<Icon.CloudOff size={16} />
<div>
<b>This PC runs on its own</b>
<span>The assistant lives at head office. Link this PC to a shop to ask it questions; the basics are below.</span>
<span>Loya lives at head office. Link this PC to a shop to talk to her; the basics are below.</span>
</div>
</div>
)}
{turns.length === 0 && (
<div className="helperintro">
<p>Ask in plain words. It reads your shop’s own cameras and arrivals, and it knows how the setup works.</p>
<div className="turn assistant"><div className="bubble">
{greeting(session)} Ask me anything about {session?.site_name || 'this shop'} — what’s happening now, or how to set something up.
</div></div>
<div className="chips">
{SUGGESTED.map(s => <button key={s} className="chip" onClick={() => ask(s)} disabled={busy || standalone}>{s}</button>)}
</div>
@@ -84,13 +86,20 @@ export default function Assistant({ session, onClose }) {
<form className="helperask" onSubmit={e => { e.preventDefault(); ask() }}>
<input value={draft} onChange={e => setDraft(e.target.value)} disabled={busy || standalone}
placeholder={standalone ? 'Link this PC to head office to ask' : 'Ask anything about this shop…'} />
placeholder={standalone ? 'Link this PC to head office to talk to Loya' : 'Ask Loya…'} />
<button className="btn primary" disabled={busy || standalone || !draft.trim()} aria-label="Ask"><Icon.Chevron size={16} /></button>
</form>
</aside>
)
}
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() {