- dashboard.html: each section sits in OPTIONAL:<name> BEGIN/END blocks in the style, markup and script, with sample answers so a page with no bot shows it working. Core change: setUser records the role on <body>, so CSS hides admin-only parts from operators. - templates/sections.py keeps the chosen sections and deletes the rest, markers included; it stops on a broken marker. - templates/sections.md: what each section needs from the host (events, actions, desktop slot snippets, the role re-check, the login hand-off). - SKILL.md step 5 asks one multi-select question and runs sections.py. - Page check: checkpoint states, admin visibility by role and assign/remove, login theme and hand-off to auth or the stub. Cases skip on a generated page without the section; the template must carry all three. The harness signs in when the page has a login screen. - tests/test_sections.py: no sections leave no trace, and that page still passes the page check. - Eval runner: a-empty-sections variant (all three) and per-section checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.6 KiB
Optional sections
Three extras live in dashboard.html as marked blocks (OPTIONAL:<name> BEGIN ... END)
in the style, the markup and the script. Build only the ones the user picked:
python <skill>/templates/sections.py <project>/dashboard/dashboard.html checkpoints admin login
Name only the chosen sections; name none to remove all three. The script deletes every unchosen block (markup, script and style) and the marker lines of the chosen ones, and it stops if a marker is broken. Never cut the blocks by hand.
With SAMPLE_DATA = true, every section works on its own sample answers. Once the bot is
wired, the host must answer the section's actions as below. Desktop hosts add the slots to
the Bridge class in bridge.py. Web hosts serve POST {API_BASE}/<action-with-dashes>,
like the core actions, and send the events in the /run/status events list.
Checkpoint tracker (checkpoints)
Numbered boxes on the Run tab. Each one is pending, in progress, done or stuck.
-
Fill
{{CHECKPOINTS_JSON}}with the checkpoint names, in order, from the bot's real stages, for example["Open the portal","Load the list","Process items","Finish up"]. They can be finer thanSTEPS. -
Event
cpstate {kind: "pending"|"active"|"done"|"stuck", cp}.cpcounts from 1.activealso marks the earlier boxes done and the later ones pending, so the bot can sendactivefor each checkpoint as it starts, andstuckwhen one fails. -
Desktop: add a marker table next to
STEP_MARKERSinbridge.py, and send the event from_on_line, using specific substrings the bot really prints:CHECKPOINT_MARKERS = [("[cp 1]", "active", 1), ("[cp 1 failed]", "stuck", 1)] # (substring, kind, cp) # in _on_line, after the step markers: for marker, kind, cp in CHECKPOINT_MARKERS: if marker in low: self.send_to_js("cpstate", {"kind": kind, "cp": cp}) break
Admin/roles screen (admin)
An Admin tab where an admin assigns or removes admin and operator access. The Admin nav
item and the screen appear only while set_user says role: "admin"; operators never see
them. Hiding them is not security: the host re-checks that the signed-in user is an admin
on every admin action, and refuses otherwise.
-
Event
admin_data {rows: [{username, emp_id, role: "admin"|"operator"}], msg?}.msgis shown above the list, for example "Access assigned." or "Only admins can do that." -
Actions:
load_admin()when the tab opens,assign_role(who, role),remove_role(who).whois a username or employee ID. Answer each one with a freshadmin_data. -
Desktop slots, using the project's own user store:
@pyqtSlot() def load_admin(self): if self.role != "admin": # self.role: set when the user signs in return self.send_to_js("admin_data", {"rows": read_access_list()}) @pyqtSlot(str, str) def assign_role(self, who, role): if self.role == "admin" and role in ("admin", "operator"): save_access(who, role) self.load_admin() @pyqtSlot(str) def remove_role(self, who): if self.role == "admin": delete_access(who) self.load_admin()
Login page (login)
A sign-in screen in the page's own theme, shown over the dashboard until sign-in succeeds, plus a Sign out button in the top bar. It is UI only: it hands the username and password to the project's existing auth. Don't build an auth backend.
-
Desktop action
login(username, password). The host checks them and sendslogin_result {ok, msg?, user?: {username, emp_id, role}}. Onokthe screen closes anduserfills the sidebar (and the role, if the admin screen is built). Otherwisemsgshows under the form. -
Web:
POST {API_BASE}/loginwith JSON{username, password}, answered with the same{ok, msg?, user?}body. -
Action
logout()when the user signs out. The screen comes back. -
The page sits behind the screen, but the host must still refuse data actions until sign-in succeeds.
-
No auth in the project yet: keep a stub that lets any non-empty username in, and tell the user it is a stub. In sample mode the page has its own stub.
@pyqtSlot(str, str) def login(self, username, password): role = check_login(username, password) # the project's auth: a role, or None self.role = role or "" if role: self.send_to_js("login_result", {"ok": True, "user": {"username": username, "emp_id": "", "role": role}}) else: self.send_to_js("login_result", {"ok": False, "msg": "Wrong username or password."}) @pyqtSlot() def logout(self): self.role = ""