Generate, list, and cancel attended-support codes (XXX-XXX-XXX), built on the v2 codes API and existing UI primitives. - Codes table: code in mono, status badge (pending+pulse/connected/ completed/cancelled), bound client/machine, created-by, created (relative + absolute tooltip). Sticky header, skeleton load, actionable empty/error states. - Generate opens a focused reveal modal showing the code large in JetBrains Mono with copy and a read-aloud instruction; the code is announced character-by-character for screen readers. Mint is ref- guarded so it creates exactly one code per open (no StrictMode dupe). - Cancel via confirm dialog (POST /api/codes/:code/cancel), disabled for non-cancellable statuses; invalidates the codes query. List polls 7s. - Shared API client now tolerates non-JSON 200 bodies, so the cancel endpoint's plain-text "Code cancelled" success no longer surfaces as a failure. Error-envelope handling unchanged. Passed Code Review (no blockers after fixes) and local gates (tsc/lint/build green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
import {
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
} from "@tanstack/react-query";
|
|
import * as codesApi from "../../api/codes";
|
|
import type { CreateCodeRequest } from "../../api/types";
|
|
|
|
const CODES_KEY = ["codes"] as const;
|
|
|
|
/**
|
|
* List the active support codes. Polls on a short interval because codes are
|
|
* short-lived: a `pending` code can be redeemed (-> `connected`) or expire out
|
|
* of the active set at any moment, and a tech who just read a code aloud is
|
|
* watching for exactly that transition. The interval is tight (like the
|
|
* sessions poll) so the redeem shows up on its own without a manual refresh.
|
|
*/
|
|
export function useSupportCodes() {
|
|
return useQuery({
|
|
queryKey: CODES_KEY,
|
|
queryFn: ({ signal }) => codesApi.listCodes(signal),
|
|
refetchInterval: 7_000,
|
|
staleTime: 3_500,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generate a new support code, then invalidate the list so the new `pending`
|
|
* code appears in the table. The created code is returned to the caller so the
|
|
* generate flow can surface it prominently (it is read to the end user).
|
|
*/
|
|
export function useGenerateCode() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (body: CreateCodeRequest) => codesApi.createCode(body),
|
|
onSuccess: () => {
|
|
void qc.invalidateQueries({ queryKey: CODES_KEY });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Cancel (revoke) a support code, then invalidate the list so the row drops out
|
|
* of the active set. Cancelling an un-redeemed code is irreversible, so the UI
|
|
* confirms first; this hook is the action behind that confirmation.
|
|
*/
|
|
export function useCancelCode() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (code: string) => codesApi.cancelCode(code),
|
|
onSuccess: () => {
|
|
void qc.invalidateQueries({ queryKey: CODES_KEY });
|
|
},
|
|
});
|
|
}
|