Files
resolutionflow/frontend/src/api/sessions.ts
chihlasm 8db34f07ee feat: maintenance flow UX redesign — batch status hub, context strip, detail page upgrades (#85)
- Add BatchStatusPage (/flows/:id/batches/:batchId): per-target Start/Resume/View cards, progress bar, 5s polling while in-progress, completion outcome summary
- Add BatchStatusCard: handles not-started/in-progress/complete states with step progress for in-progress targets
- Add ActiveBatchBanner: amber banner on detail page when a batch is running, links to BatchStatusPage
- Add MaintenanceContextStrip: amber strip in ProceduralNavigationPage for maintenance flows showing target name, batch progress (X/Y complete), and Back to Batch nav
- Update MaintenanceFlowDetailPage: active batch banner, clickable run history rows with mini progress dots and outcome summaries, Run button loading state, post-launch navigates to BatchStatusPage
- Update ProceduralNavigationPage: renders MaintenanceContextStrip between top bar and content when tree_type === 'maintenance'; fetches batch progress once on mount
- Add batch_id filter to GET /sessions backend endpoint and SessionListParams frontend type
- Add /flows/:id/batches/:batchId route to router

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 18:51:42 -05:00

104 lines
3.4 KiB
TypeScript

import apiClient from './client'
import type { Session, SessionCreate, SessionUpdate, SessionExport, SaveAsTreeRequest, SaveAsTreeResponse, SessionComplete, RedactionSummary, SessionShareCreate, SessionShare, SharedSessionView } from '@/types'
export interface SessionListParams {
page?: number
size?: number
tree_id?: string
batch_id?: string
completed?: boolean
ticket_number?: string
client_name?: string
tree_name?: string
started_after?: string // ISO datetime string
started_before?: string
completed_after?: string
completed_before?: string
}
export const sessionsApi = {
async list(params?: SessionListParams): Promise<Session[]> {
const response = await apiClient.get<Session[]>('/sessions', { params })
return response.data
},
async get(id: string): Promise<Session> {
const response = await apiClient.get<Session>(`/sessions/${id}`)
return response.data
},
async create(data: SessionCreate): Promise<Session> {
const response = await apiClient.post<Session>('/sessions', data)
return response.data
},
async update(id: string, data: SessionUpdate): Promise<Session> {
const response = await apiClient.put<Session>(`/sessions/${id}`, data)
return response.data
},
async complete(id: string, data: SessionComplete): Promise<Session> {
const response = await apiClient.post<Session>(`/sessions/${id}/complete`, data)
return response.data
},
async export(id: string, options: SessionExport): Promise<string> {
const response = await apiClient.post<string>(`/sessions/${id}/export`, options)
return response.data
},
async exportWithMeta(
id: string,
options: SessionExport
): Promise<{
content: string
redactionMode: 'none' | 'mask'
redactionSummary: RedactionSummary | null
}> {
const response = await apiClient.post<string>(`/sessions/${id}/export`, options)
const redactionMode = (response.headers['x-redaction-mode'] as 'none' | 'mask') || 'none'
let redactionSummary: RedactionSummary | null = null
const summaryHeader = response.headers['x-redaction-summary']
if (summaryHeader) {
try {
redactionSummary = JSON.parse(summaryHeader)
} catch {
// Ignore malformed header
}
}
return { content: response.data, redactionMode, redactionSummary }
},
async updateScratchpad(id: string, content: string): Promise<Session> {
const response = await apiClient.patch<Session>(`/sessions/${id}/scratchpad`, { scratchpad: content })
return response.data
},
async saveAsTree(id: string, data: SaveAsTreeRequest): Promise<SaveAsTreeResponse> {
const response = await apiClient.post<SaveAsTreeResponse>(`/sessions/${id}/save-as-tree`, data)
return response.data
},
// Session Sharing
async createShare(sessionId: string, data: SessionShareCreate): Promise<SessionShare> {
const response = await apiClient.post<SessionShare>(`/sessions/${sessionId}/shares`, data)
return response.data
},
async listMyShares(): Promise<SessionShare[]> {
const response = await apiClient.get<SessionShare[]>('/shares/my-shares')
return response.data
},
async revokeShare(shareId: string): Promise<void> {
await apiClient.delete(`/shares/${shareId}`)
},
async getSharedSession(shareToken: string): Promise<SharedSessionView> {
const response = await apiClient.get<SharedSessionView>(`/share/${shareToken}`)
return response.data
},
}
export default sessionsApi