Files
Rpa_Skills/skills/rpa-dashboard-no-ai/templates/sections.md
T

107 lines
4.7 KiB
Markdown

# 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 in the run panel. 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 than `STEPS`.
- Event `cpstate {kind: "pending"|"active"|"done"|"stuck", cp}`. `cp` counts from 1.
`active` also marks the earlier boxes done and the later ones pending, so the bot can
send `active` for each checkpoint as it starts, and `stuck` when one fails.
- Desktop: add a marker table next to `STEP_MARKERS` in `bot_runner.py`, and add the event
in `events_for`, using specific substrings the bot really prints. The dry run then shows
the `cpstate` events too:
```python
# (specific substring, kind, cp). First match wins.
CHECKPOINT_MARKERS = [("[cp 1]", "active", 1), ("[cp 1 failed]", "stuck", 1)]
# in events_for, right after the step marker:
if cp := _first(CHECKPOINT_MARKERS, line):
out.append(("cpstate", {"kind": cp[1], "cp": cp[2]}))
```
## 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?}`. `msg` is
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)`.
`who` is a username or employee ID. Answer each one with a fresh `admin_data`.
- Desktop slots, using the project's own user store:
```python
@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 sends
`login_result {ok, msg?, user?: {username, emp_id, role}}`. On `ok` the screen closes and
`user` fills the sidebar (and the role, if the admin screen is built). Otherwise `msg`
shows under the form.
- Web: `POST {API_BASE}/login` with 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.
```python
@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 = ""
```