Newer
Older
bugtrail / packages / shared / src / api.ts
/**
 * API types — hand-written mirror of the server Pydantic schemas (server/app/schemas.py).
 * Kept in sync manually; openapi-typescript generation is available via
 * `npm run generate-api-types --workspace @ltt/shared` once the server is running.
 */

export interface User {
  id: string;
  email: string;
  nickname: string;
  created_at: string;
}

export interface TokenOut {
  token: string;
  expires_at: string;
}

export interface Project {
  id: string;
  name: string;
  description: string | null;
  share_token: string;
  created_at: string;
}

export interface ProjectDetail extends Project {
  owner_nickname: string;
  report_counts: { total: number; open: number };
}

export interface Author {
  id: string;
  nickname: string;
}

export interface Attachment {
  id: string;
  file_id: string;
  kind: string;
  filename: string;
  mime: string;
  size: number;
  annotation_shapes: AnnotationShape[] | null;
}

export interface AnnotationShape {
  tool: "pen" | "arrow" | "rect" | "text";
  color: string;
  strokeWidth: number;
  points?: [number, number][];
  rect?: [number, number, number, number];
  pos?: [number, number];
  text?: string;
}

export interface RecordingStep {
  id: string;
  step_index: number;
  type: "click" | "input" | "url_change" | "navigation" | "note" | "screenshot" | "console";
  offset_ms: number;
  data: Record<string, unknown>;
  screenshot_attachment_id: string | null;
}

export interface MouseTrack {
  /** viewport the points were captured in, CSS px */
  viewport: { w: number; h: number };
  /** cursor samples; t = ms since recording start, x/y viewport-relative */
  points: { t: number; x: number; y: number }[];
}

export interface ElementContext {
  selector?: string | null;
  unique_selector?: string | null;
  tag?: string | null;
  id?: string | null;
  classes?: string[] | null;
  text_snippet?: string | null;
  aria_label?: string | null;
  test_attributes?: Record<string, string> | null;
  rect?: { x: number; y: number; w: number; h: number } | null;
  screenshot_rel?: { x: number; y: number; w: number; h: number } | null;
  extra?: Record<string, unknown> | null;
}

export interface Report {
  id: string;
  type: "element_note" | "recording";
  title: string;
  description: string | null;
  status: "open" | "fixed" | "wont_fix";
  page_url: string | null;
  page_title: string | null;
  environment: Record<string, unknown>;
  element: ElementContext | null;
  mouse_track: MouseTrack | null;
  share_token: string;
  created_at: string;
  updated_at: string;
}

export interface ReportListItem extends Report {
  author: Author;
}

export interface ReportDetail extends ReportListItem {
  attachments: Attachment[];
  steps: RecordingStep[];
}

export interface ReportList {
  items: ReportListItem[];
  total: number;
  limit: number;
  offset: number;
}

export interface UploadResult {
  file_id: string;
  mime: string;
  size: number;
  filename: string;
}

// request payloads

export interface StepIn {
  type: RecordingStep["type"];
  offset_ms: number;
  data?: Record<string, unknown>;
  attachment_id?: string | null;
}

export interface ReportCreateIn {
  project_id: string;
  type: Report["type"];
  title: string;
  description?: string | null;
  page_url?: string | null;
  page_title?: string | null;
  environment?: Record<string, unknown> | null;
  element?: ElementContext | null;
  mouse_track?: MouseTrack | null;
  attachment_ids?: string[] | null;
  annotation_shapes?: Record<string, AnnotationShape[]> | null;
  steps?: StepIn[] | null;
}

export interface ReportUpdateIn {
  title?: string;
  description?: string | null;
  status?: Report["status"];
}