Files
sales-trainer/frontend/src/api/index.js
Macky 5b167b4546 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.
2026-08-09 10:20:10 +07:00

68 lines
2.9 KiB
JavaScript

// API client with JWT auth injection.
const TOKEN_KEY = 'st_token'
export function getToken() {
return localStorage.getItem(TOKEN_KEY)
}
export function setToken(t) {
if (t) localStorage.setItem(TOKEN_KEY, t)
else localStorage.removeItem(TOKEN_KEY)
}
async function request(method, url, body, isForm = false) {
const headers = {}
const token = getToken()
if (token) headers['Authorization'] = `Bearer ${token}`
let payload = body
if (!isForm && body !== undefined && body !== null) {
headers['Content-Type'] = 'application/json'
payload = JSON.stringify(body)
}
const res = await fetch(url, { method, headers, body: payload })
let data = null
try {
data = await res.json()
} catch (e) {
/* ignore json parse errors */
}
if (!res.ok) {
const msg = (data && (data.error || data.message)) || `HTTP ${res.status}`
throw new Error(msg)
}
return data
}
export const api = {
login: (username, password) => request('POST', '/api/auth/login', { username, password }),
me: () => request('GET', '/api/auth/me'),
setup: (b) => request('POST', '/api/auth/setup', b),
updateProfile: (b) => request('PATCH', '/api/auth/profile', b),
adminCreateUser: (b) => request('POST', '/api/admin/users', b),
adminListUsers: () => request('GET', '/api/admin/users'),
adminUpdateUser: (username, b) => request('PUT', `/api/admin/users/${username}`, b),
createGroup: (formData) => request('POST', '/api/groups', formData, true),
listGroups: () => request('GET', '/api/groups'),
getGroup: (id) => request('GET', `/api/groups/${id}`),
deleteGroup: (id) => request('DELETE', `/api/groups/${id}`),
analyzeGroup: (id, opts = {}) => request('POST', `/api/groups/${id}/analyze${opts.append ? '?append=true' : ''}`),
listPersonas: (gid) => request('GET', `/api/groups/${gid}/personas`),
getPersona: (gid, pid) => request('GET', `/api/groups/${gid}/personas/${pid}`),
updatePersona: (gid, pid, b) => request('PUT', `/api/groups/${gid}/personas/${pid}`, b),
chatStart: (gid, pid, scenario, locale) => request('POST', `/api/chat/${gid}/personas/${pid}/chat/start`, { scenario, locale }),
chatResume: (gid, pid) => request('GET', `/api/chat/${gid}/personas/${pid}/chat/resume`),
chatSend: (gid, pid, text) => request('POST', `/api/chat/${gid}/personas/${pid}/chat/send`, { text }),
chatFinish: (gid, pid) => request('POST', `/api/chat/${gid}/personas/${pid}/chat/finish`),
mySessions: () => request('GET', '/api/chat/sessions'),
myBoard: () => request('GET', '/api/me/board'),
weakAreas: () => request('GET', '/api/me/weak-areas'),
myPersonas: () => request('GET', '/api/me/personas'),
generatePersona: (b) => request('POST', '/api/me/personas/generate', b),
analytics: (params = {}) => {
const qs = new URLSearchParams()
if (params.from) qs.set('from', params.from)
if (params.to) qs.set('to', params.to)
const q = qs.toString()
return request('GET', `/api/analytics${q ? `?${q}` : ''}`)
},
}