Feat v0.2.8 team admin audit (#12)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m18s
CI/CD / test-sqlite (push) Successful in 2m40s
CI/CD / build-and-deploy (push) Successful in 1m43s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #12.
This commit is contained in:
2026-03-27 16:25:19 +00:00
committed by xcaliber
parent 82430a2cb2
commit babfe66721
12 changed files with 51 additions and 102 deletions

View File

@@ -10,8 +10,7 @@ const { html } = window;
const { useState, useEffect, useCallback } = hooks;
const ENTRY_MODES = ['public_link', 'team_only'];
const STAGE_MODES = ['chat_only', 'form_only', 'form_chat', 'review'];
const HISTORY_MODES = ['full', 'summary', 'fresh'];
const STAGE_MODES = ['form_only', 'form_chat', 'review', 'custom'];
const TABS = ['Workflows', 'Assignments', 'Monitor'];
function _timeAgo(ts) {
@@ -143,18 +142,13 @@ function WorkflowsTab({ teamId }) {
function WorkflowEditor({ teamId, workflow, onBack }) {
const [stages, setStages] = useState([]);
const [personas, setPersonas] = useState([]);
const [teams, setTeams] = useState([]);
const [editingStage, setEditingStage] = useState(null); // null | 'new' | stage_id
const loadStages = useCallback(async () => {
try {
const [s, p] = await Promise.all([
sw.api.teams.workflowStages(teamId, workflow.id),
sw.api.teams.personas(teamId),
]);
const s = await sw.api.teams.workflowStages(teamId, workflow.id);
setStages(s || []);
setPersonas(p || []);
setTeams(sw.auth?.teams || []);
} catch (e) { sw.toast(e.message, 'error'); }
}, [teamId, workflow.id]);
@@ -259,7 +253,6 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
<span class="text-muted" style="font-size:11px;width:24px;">#${i + 1}</span>
<strong style="flex:1;">${s.name || `Stage ${i + 1}`}</strong>
<span class="badge">${s.stage_mode || '\u2014'}</span>
${s.persona_id && html`<span class="badge badge-active">persona</span>`}
${s.assignment_team_id && html`<span class="badge">team assign</span>`}
<button class="btn-small" onClick=${() => setEditingStage(s.id)}>Edit</button>
<button class="btn-small btn-danger" onClick=${() => deleteStage(s.id)}>\u00d7</button>
@@ -271,7 +264,6 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
${editingStage && html`
<${StageForm}
stage=${editingStage === 'new' ? null : stages.find(s => s.id === editingStage)}
personas=${personas}
teams=${teams}
onSave=${(data) => editingStage === 'new' ? addStage(data) : updateStage(editingStage, data)}
onCancel=${() => setEditingStage(null)}
@@ -283,12 +275,10 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
// ── Stage Form ──────────────────────────────
function StageForm({ stage, personas, teams, onSave, onCancel }) {
function StageForm({ stage, teams, onSave, onCancel }) {
const [name, setName] = useState(stage?.name || '');
const [mode, setMode] = useState(stage?.stage_mode || 'chat_only');
const [personaId, setPersonaId] = useState(stage?.persona_id || '');
const [mode, setMode] = useState(stage?.stage_mode || 'form_only');
const [assignTeam, setAssignTeam] = useState(stage?.assignment_team_id || '');
const [historyMode, setHistoryMode] = useState(stage?.history_mode || 'full');
const [autoTransition, setAutoTransition] = useState(stage?.auto_transition || false);
const [sla, setSla] = useState(stage?.sla_seconds || '');
@@ -296,9 +286,7 @@ function StageForm({ stage, personas, teams, onSave, onCancel }) {
onSave({
name,
stage_mode: mode,
persona_id: personaId || null,
assignment_team_id: assignTeam || null,
history_mode: historyMode,
auto_transition: autoTransition,
sla_seconds: sla ? parseInt(sla, 10) : null,
});
@@ -319,13 +307,6 @@ function StageForm({ stage, personas, teams, onSave, onCancel }) {
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Persona (LLM)</label>
<select value=${personaId} onChange=${e => setPersonaId(e.target.value)}>
<option value="">\u2014 none \u2014</option>
${personas.map(p => html`<option key=${p.id} value=${p.id}>${p.name}</option>`)}
</select>
</div>
<div class="form-group">
<label>Queue to Team</label>
<select value=${assignTeam} onChange=${e => setAssignTeam(e.target.value)}>
@@ -333,14 +314,6 @@ function StageForm({ stage, personas, teams, onSave, onCancel }) {
${teams.map(t => html`<option key=${t.id || t.team_id} value=${t.id || t.team_id}>${t.name || t.team_name}</option>`)}
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>History Mode</label>
<select value=${historyMode} onChange=${e => setHistoryMode(e.target.value)}>
${HISTORY_MODES.map(m => html`<option key=${m} value=${m}>${m}</option>`)}
</select>
</div>
<div class="form-group">
<label>SLA (seconds)</label>
<input type="number" value=${sla} onInput=${e => setSla(e.target.value)} placeholder="e.g. 3600" />