Files
warmbly/web/src/lib/api/models/app/crm/SearchDeals.ts
T
Matthew Meszaros 050d09c961 feat: add cross-pipeline deals search surface
Replace the client-side, 100-row-capped deals board as the default view
with a server-driven cross-pipeline table so totals stop lying at scale.

- POST /crm/deals/search: faceted filter body (query, status, pipeline,
  stage, owner, campaign, value range, close-date range), whitelisted
  sort, offset pagination, and Contact/Stage/campaign hydrated via JOIN.
- POST /crm/deals/summary: COUNT + SUM(value) per status and per stage
  over the same filter, with a mixed-currency guard, so header and board
  column totals are true server aggregates.
- DealsTable: cross-pipeline default view (infinite scroll, server
  filters/sort, honest summary stats) with a Table|Board toggle; the
  kanban stays as the single-pipeline working view.
- Deal attribution: campaign_id + source_mailbox_id columns + indexes
  (migration 000022) as groundwork for revenue lineage.
2026-06-06 19:42:20 +02:00

40 lines
1.0 KiB
TypeScript

// Filter body for POST /crm/deals/search and /crm/deals/summary. Every field
// is optional; an empty body matches every deal in the org (the cross-pipeline
// "All deals" default). The same body drives the rows and the summary totals,
// so a header number always reflects the exact filter shown below it.
export type DealSortBy =
| "created_at"
| "updated_at"
| "value"
| "expected_close_date"
| "name";
export default interface SearchDeals {
query: string;
statuses: ("open" | "won" | "lost")[];
pipeline_ids: string[];
stage_ids: string[];
assigned_to: string[];
campaign_ids: string[];
min_value?: number;
max_value?: number;
close_after?: string;
close_before?: string;
created_after?: string;
created_before?: string;
sort_by: DealSortBy;
reverse: boolean;
}
export const EMPTY_DEAL_SEARCH: SearchDeals = {
query: "",
statuses: [],
pipeline_ids: [],
stage_ids: [],
assigned_to: [],
campaign_ids: [],
sort_by: "created_at",
reverse: false,
};