import type { ChatAttachment } from '../../chat/utils/chat-attachment-markdown'; import { type AnyTicket, type MappedTicketActionError, type OptimisticTicket, type TicketActionErrorCode } from '../types'; interface SubmitTicketInput { subject: string; content: string; attachments?: ChatAttachment[]; } export interface UseTicketActionsOptions { /** Called when the parent should prepend an optimistic placeholder * to the local cache. Implementer mutates the QueryClient cache * directly so the row appears before the server roundtrip. */ prependOptimistic: (placeholder: OptimisticTicket) => void; /** Called when the optimistic placeholder should be removed * (mirror-sync failure, or replacement after real ticket arrives). */ removeOptimistic: (placeholderId: string) => void; /** Called when a ticket should be removed from the cache without * a refetch (TICKET_NOT_FOUND). */ removeTicketFromCache: (ticketId: string) => void; /** Toast helper from `@flamingo-stack/openframe-frontend-core/hooks`. * Passed in so the lib doesn't import the toast singleton itself * (test-friendly). */ toast: (input: { title: string; description?: string; variant?: 'success' | 'destructive' | 'default'; }) => void; /** Called when a 412 HUBSPOT_DISCONNECTED arrives so the parent can * flip its `supportSystemDown` flag. */ onSupportSystemDown: () => void; } /** * Identity bundle used by every row action: the LOCAL mirror UUID * (drives the React-side mutex + optimistic cache removal) AND the * HubSpot ticket id (`external_id` — drives the server call, the only * id HubSpot REST recognizes). Decoupling these is mandatory: passing * the UUID to HubSpot gets you a 404 "Object not found. objectId are * usually numeric"; passing the external id to the React-side mutex * breaks per-row disable when the cache differs. */ export interface TicketRef { id: string; external_id: string; } export interface UseTicketActionsReturn { submitTicket: (input: SubmitTicketInput) => Promise; /** Single combined "reply" action — text + optional attachments * delivered as ONE HubSpot Note engagement (one bubble in the * timeline). Server creates a merged note when both are present. */ sendMessage: (ticket: TicketRef, text: string, attachments: ChatAttachment[]) => Promise; closeTicket: (ticket: TicketRef, resolution?: string) => Promise; reopenTicket: (ticket: TicketRef) => Promise; /** `true` while the form-level submit is in flight. */ isSubmittingForm: boolean; /** Per-row in-flight set (read-only). UI uses `isRowBusy(localId)`. */ isRowBusy: (localId: string) => boolean; /** Most recent reply failure for a given ticket id (`external_id`). * Drives the inline "couldn't send" banner above the composer in * ``. Cleared on the next successful send OR * via `clearReplyError(ticketId)`. */ replyErrorFor: (ticketExternalId: string) => MappedTicketActionError | null; /** Clear the persisted reply-failure banner for a ticket (e.g. when * the user dismisses it or starts a new draft). */ clearReplyError: (ticketExternalId: string) => void; } export declare function useTicketActions(options: UseTicketActionsOptions): UseTicketActionsReturn; /** Exported so unit tests can construct an instance to exercise the * per-code branches of `mapTicketActionError`. Not part of the public * surface — kept out of `tickets/index.ts`. */ export declare class TicketActionFailure extends Error { code: TicketActionErrorCode; response?: Response; constructor(code: TicketActionErrorCode, message: string, response?: Response); } /** * Translate a server error envelope into user-facing copy. Exported so * a future chat refactor can adopt the same translation table. */ export declare function mapTicketActionError(err: unknown): MappedTicketActionError; export type { AnyTicket, OptimisticTicket }; //# sourceMappingURL=use-ticket-actions.d.ts.map