Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions packages/shared/src/inbox-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export type SignalRecordKind =
| "ticket"
| "scanner_finding"
| "feedback"
| "review";
| "review"
| "search_opportunity";

/**
* A warehouse data source the Self-driving inbox can watch. This is the single source of
Expand Down Expand Up @@ -48,6 +49,7 @@ const ERROR = "Surface new and reopened errors";
const FINDING = "Surface new security and code-quality findings";
const FEEDBACK = "Turn product feedback and feature requests into inputs";
const REVIEW = "Monitor new app and product reviews";
const SEARCH = "Fix pages that rank in Google but lose clicks";

/** Registry of warehouse-backed inbox sources, alphabetical within each category. */
export const EXTERNAL_INBOX_SOURCES = [
Expand Down Expand Up @@ -392,6 +394,16 @@ export const EXTERNAL_INBOX_SOURCES = [
recordKind: "review",
setup: "dynamic",
},
// Search analytics
{
product: "google_search_console",
label: "Google Search Console",
description: SEARCH,
dwSourceType: "GoogleSearchConsole",
requiredTables: ["search_analytics_by_query_page"],
recordKind: "search_opportunity",
setup: "dynamic",
},
] as const satisfies readonly ExternalInboxSource[];

/** Warehouse-backed source products, derived from the registry above. */
Expand Down Expand Up @@ -434,9 +446,13 @@ export type SourceType =
| "session_analysis_cluster"
| SignalRecordKind;

/** Issue-like records mutate (status/votes change), so their table needs full-refresh sync. */
/**
* Issue-like records mutate (status/votes change), so their table needs full-refresh sync.
* Tickets and search-analytics rows are append-only — existing rows never change once written —
* so they sync incrementally.
*/
export function sourceNeedsFullRefresh(recordKind: SignalRecordKind): boolean {
return recordKind !== "ticket";
return recordKind !== "ticket" && recordKind !== "search_opportunity";
}

export const EXTERNAL_INBOX_SOURCE_BY_PRODUCT: Partial<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
KanbanIcon,
LifebuoyIcon,
LightbulbIcon,
MagnifyingGlassIcon,
MegaphoneIcon,
ShieldIcon,
StarIcon,
Expand Down Expand Up @@ -178,4 +179,9 @@ export const SOURCE_PRODUCT_META: Partial<
},
intercom: { Icon: ChatsIcon, color: "var(--blue-9)", label: "Intercom" },
hubspot: { Icon: LifebuoyIcon, color: "var(--orange-9)", label: "HubSpot" },
google_search_console: {
Icon: MagnifyingGlassIcon,
color: "var(--sky-9)",
label: "Google Search Console",
},
};
Loading