feat(training): delete group button (admin only) on Training page

- Backend DELETE /api/groups/<gid> (admin, own-org enforced via _get_owned_group);
  cascades removal of the group's sessions. GroupStore.delete added.
- Training.vue: each group card gets a trash (Trash2 line icon) delete button shown only
  to admins (auth.isAdmin); confirm dialog; @click.stop so it doesn't navigate. Card
  restructured so the delete button is not inside the clickable router-link.
- test_user_journey: trainee cannot delete (403), admin deletes, group gone (404).
All suites pass. Rebuilt dist.
This commit is contained in:
Macky
2026-08-09 10:20:10 +07:00
parent 0b92430aab
commit 5b167b4546
30 changed files with 114 additions and 43 deletions

View File

@@ -324,6 +324,28 @@ def update_persona(gid: str, pid: str):
return jsonify({"persona": strip_secret_fields(full)})
@groups_bp.delete("/<gid>")
@require_auth
@require_roles("admin")
def delete_group(gid: str):
"""Delete a persona group (admin only, own org)."""
s = _stores()
group = _get_owned_group(s, gid) # also enforces tenant org (super_admin global)
s["groups"].delete(gid)
# cascade: also remove finished/active sessions for this group's personas
try:
sess = s.get("session_store")
if sess and hasattr(sess, "sessions"):
for r in sess.sessions.all():
if r.get("group_id") == gid:
key = r.get("id") or r.get("sid")
if key:
sess.sessions.delete(key)
except Exception:
pass
return jsonify({"ok": True, "deleted": gid})
@groups_bp.post("/<gid>/reanalyze")
@require_auth
@require_roles("admin")

View File

@@ -95,3 +95,7 @@ class GroupStore:
if not found:
raise ValueError("persona not found")
return self.groups.replace(gid, group)
def delete(self, gid: str) -> None:
"""Hard-delete a group (personas/report included)."""
self.groups.delete(gid)

View File

@@ -93,4 +93,14 @@ after = len(r.get_json()["personas"])
assert after > before, f"append should add personas (before={before}, after={after})"
print(f"[ok] append adds personas ({before} -> {after}); existing kept")
# 12. delete group: trainee cannot, admin can
r = C.delete(f"/api/groups/{gid}", headers=TH)
assert r.status_code == 403, ("trainee must not delete group", r.status_code)
print("[ok] trainee cannot delete group (403)")
r = C.delete(f"/api/groups/{gid}", headers=AH)
assert r.status_code == 200, r.get_json()
gone = C.get(f"/api/groups/{gid}", headers=AH)
assert gone.status_code == 404, "deleted group should be gone"
print("[ok] admin deletes group; group no longer reachable")
print("ALL USER-JOURNEY FLOW TESTS PASSED")