feat(l1): real L1 dashboard with empty-state + resume widget
Replaces the T20 stub. L1 dashboard renders greeting, "Describe the problem" intake card (autofocus textarea, optional customer fields, primary "Start walk" CTA), open-tickets queue (Phase 1: display-only), and a "Resume in progress" widget listing the L1's active sessions ordered by last_step_at DESC. Empty-state card shows on accounts with no queue + no active sessions (first-run nudge to upload KB or auth flows). Adds /api/l1.ts (full L1 API client surface) and /types/l1.ts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
64
frontend/src/api/l1.ts
Normal file
64
frontend/src/api/l1.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { apiClient } from './client'
|
||||
import type {
|
||||
IntakeRequest,
|
||||
IntakeResponse,
|
||||
QueueRow,
|
||||
WalkSession,
|
||||
AdhocNote,
|
||||
} from '@/types/l1'
|
||||
|
||||
export const l1Api = {
|
||||
intake: (body: IntakeRequest) =>
|
||||
apiClient.post<IntakeResponse>('/l1/intake', body).then(r => r.data),
|
||||
|
||||
queue: (statusFilter?: string) =>
|
||||
apiClient.get<QueueRow[]>('/l1/queue', {
|
||||
params: statusFilter ? { status_filter: statusFilter } : {},
|
||||
}).then(r => r.data),
|
||||
|
||||
listActiveSessions: () =>
|
||||
apiClient.get<WalkSession[]>('/l1/sessions/active').then(r => r.data),
|
||||
|
||||
getSession: (sessionId: string) =>
|
||||
apiClient.get<WalkSession>(`/l1/sessions/${sessionId}`).then(r => r.data),
|
||||
|
||||
step: (
|
||||
sessionId: string,
|
||||
step: { node_id: string; question: string; answer: string; note?: string | null },
|
||||
) =>
|
||||
apiClient
|
||||
.post<WalkSession>(`/l1/sessions/${sessionId}/step`, step)
|
||||
.then(r => r.data),
|
||||
|
||||
notes: (sessionId: string, notes: AdhocNote[]) =>
|
||||
apiClient
|
||||
.post<WalkSession>(`/l1/sessions/${sessionId}/notes`, { notes })
|
||||
.then(r => r.data),
|
||||
|
||||
resolve: (
|
||||
sessionId: string,
|
||||
body: { helpful: boolean; resolution_notes: string },
|
||||
) =>
|
||||
apiClient
|
||||
.post<WalkSession>(`/l1/sessions/${sessionId}/resolve`, body)
|
||||
.then(r => r.data),
|
||||
|
||||
escalate: (
|
||||
sessionId: string,
|
||||
body: { reason: string; reason_category: string },
|
||||
) =>
|
||||
apiClient
|
||||
.post<WalkSession>(`/l1/sessions/${sessionId}/escalate`, body)
|
||||
.then(r => r.data),
|
||||
|
||||
escalateWithoutWalk: (body: {
|
||||
problem_statement: string
|
||||
customer_name?: string
|
||||
customer_contact?: string
|
||||
reason_category: string
|
||||
reason?: string
|
||||
}) =>
|
||||
apiClient
|
||||
.post<WalkSession>('/l1/escalate-without-walk', body)
|
||||
.then(r => r.data),
|
||||
}
|
||||
Reference in New Issue
Block a user