@foundrynorth/compass-schema 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/schema.js CHANGED
@@ -475,6 +475,30 @@ export const competitorSchema = z.object({
475
475
  lng: z.number(),
476
476
  })
477
477
  .optional(), // Detailed location information
478
+ // Ad library data from Apify deep enrichment
479
+ ads: z
480
+ .object({
481
+ facebook: z
482
+ .array(z.object({
483
+ adImage: z.string().optional(),
484
+ headline: z.string().optional(),
485
+ adCopy: z.string().optional(),
486
+ callToAction: z.string().optional(),
487
+ publisherPlatforms: z.array(z.string()).optional(),
488
+ startDate: z.string().optional(),
489
+ }))
490
+ .optional(),
491
+ google: z
492
+ .array(z.object({
493
+ headline: z.string().optional(),
494
+ description: z.string().optional(),
495
+ landingPage: z.string().optional(),
496
+ adType: z.string().optional(),
497
+ imageUrl: z.string().optional(),
498
+ }))
499
+ .optional(),
500
+ })
501
+ .optional(),
478
502
  });
479
503
  export const extractionResultSchema = z.object({
480
504
  brand: z.string(),
@@ -1016,6 +1040,7 @@ export const plans = pgTable("plans", {
1016
1040
  defaultTargeting: text("default_targeting"),
1017
1041
  defaultAudience: text("default_audience"),
1018
1042
  defaultPromoting: text("default_promoting"),
1043
+ defaultCreativeSource: varchar("default_creative_source"),
1019
1044
  defaultFlightStart: date("default_flight_start"),
1020
1045
  defaultFlightEnd: date("default_flight_end"),
1021
1046
  // Slack integration — per-plan client channel for alert routing
@@ -1069,8 +1094,12 @@ export const proposals = pgTable("proposals", {
1069
1094
  .primaryKey()
1070
1095
  .default(sql `gen_random_uuid()`),
1071
1096
  // Optional plan reference (nullable — can be standalone)
1072
- planId: varchar("plan_id"),
1073
- engagementId: varchar("engagement_id"), // FK → engagements.id (nullable during transition)
1097
+ planId: varchar("plan_id").references(() => plans.id, {
1098
+ onDelete: "set null",
1099
+ }),
1100
+ engagementId: varchar("engagement_id").references(() => engagements.id, {
1101
+ onDelete: "set null",
1102
+ }), // nullable during transition
1074
1103
  // Configuration references (which plan_configurations to include)
1075
1104
  configurationIds: jsonb("configuration_ids").$type(),
1076
1105
  optionIds: jsonb("option_ids").$type(),
@@ -1122,6 +1151,35 @@ export const insertPlanSchema = createInsertSchema(plans).omit({
1122
1151
  id: true,
1123
1152
  createdAt: true,
1124
1153
  });
1154
+ // ============================================================================
1155
+ // Proposal Engagement Tracking
1156
+ // ============================================================================
1157
+ export const proposalEngagement = pgTable("proposal_engagement", {
1158
+ id: varchar("id")
1159
+ .primaryKey()
1160
+ .default(sql `gen_random_uuid()`),
1161
+ proposalId: varchar("proposal_id"), // FK → proposals.id (nullable for legacy plan-based shares)
1162
+ planId: varchar("plan_id"), // FK → plans.id (fallback for legacy shares)
1163
+ shareToken: text("share_token").notNull(), // The share token used to access
1164
+ sessionId: text("session_id").notNull(), // Anonymous session ID (crypto.randomUUID on client)
1165
+ // Engagement data
1166
+ events: jsonb("events").notNull().default(sql `'[]'::jsonb`), // Array of engagement events
1167
+ engagementScore: integer("engagement_score").default(0), // Computed 0-100 score
1168
+ summary: text("summary"), // AI-generated follow-up brief
1169
+ // Device context
1170
+ device: text("device"), // "desktop" | "mobile" | "tablet"
1171
+ referrer: text("referrer"), // HTTP referrer
1172
+ userAgent: text("user_agent"),
1173
+ // Timestamps
1174
+ firstSeenAt: timestamp("first_seen_at").notNull().defaultNow(),
1175
+ lastSeenAt: timestamp("last_seen_at").notNull().defaultNow(),
1176
+ createdAt: timestamp("created_at").notNull().defaultNow(),
1177
+ }, (table) => [
1178
+ index("proposal_engagement_proposal_id_idx").on(table.proposalId),
1179
+ index("proposal_engagement_plan_id_idx").on(table.planId),
1180
+ index("proposal_engagement_share_token_idx").on(table.shareToken),
1181
+ index("proposal_engagement_token_session_idx").on(table.shareToken, table.sessionId),
1182
+ ]);
1125
1183
  // Plan Configurations — Named product mixes (replaces monolithic planAllocations)
1126
1184
  // A plan can have multiple configurations (e.g., "Digital Focus", "Full Mix", "Print Heavy")
1127
1185
  export const planConfigurations = pgTable("plan_configurations", {
@@ -3333,20 +3391,84 @@ export const insertSalesInitiativeSchema = createInsertSchema(salesInitiatives).
3333
3391
  updatedAt: true,
3334
3392
  });
3335
3393
  export const updateSalesInitiativeSchema = insertSalesInitiativeSchema.partial();
3394
+ export const salesPackages = pgTable("sales_packages", {
3395
+ id: uuid("id").primaryKey().defaultRandom(),
3396
+ code: varchar("code").notNull().unique(),
3397
+ name: varchar("name").notNull(),
3398
+ description: text("description"),
3399
+ initiativeCode: varchar("initiative_code"),
3400
+ sellerSummary: text("seller_summary"),
3401
+ packagePrice: numeric("package_price", { precision: 12, scale: 2 }),
3402
+ discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }),
3403
+ addedValueAmount: numeric("added_value_amount", { precision: 12, scale: 2 }),
3404
+ addedValueDescription: varchar("added_value_description", { length: 255 }),
3405
+ hubspotPackageValue: varchar("hubspot_package_value").notNull().unique(),
3406
+ startsAt: date("starts_at"),
3407
+ endsAt: date("ends_at"),
3408
+ status: varchar("status").notNull().default("draft"),
3409
+ recommendationMode: varchar("recommendation_mode").notNull().default("both"),
3410
+ recommendationPriority: integer("recommendation_priority").notNull().default(0),
3411
+ eligibilityRules: jsonb("eligibility_rules").$type(),
3412
+ assistantHints: jsonb("assistant_hints").$type(),
3413
+ taskTemplateMode: varchar("task_template_mode").notNull().default("standard"),
3414
+ displayOrder: integer("display_order").notNull().default(0),
3415
+ isActive: boolean("is_active").notNull().default(true),
3416
+ createdAt: timestamp("created_at").notNull().defaultNow(),
3417
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
3418
+ }, (table) => [
3419
+ index("sales_packages_active_idx").on(table.isActive),
3420
+ index("sales_packages_status_idx").on(table.status),
3421
+ index("sales_packages_display_order_idx").on(table.displayOrder),
3422
+ index("sales_packages_initiative_code_idx").on(table.initiativeCode),
3423
+ ]);
3424
+ export const insertSalesPackageSchema = createInsertSchema(salesPackages).omit({
3425
+ id: true,
3426
+ createdAt: true,
3427
+ updatedAt: true,
3428
+ });
3429
+ export const updateSalesPackageSchema = insertSalesPackageSchema.partial();
3430
+ export const salesPackageProducts = pgTable("sales_package_products", {
3431
+ id: uuid("id").primaryKey().defaultRandom(),
3432
+ salesPackageCode: varchar("sales_package_code").notNull(),
3433
+ stribProductId: uuid("strib_product_id")
3434
+ .notNull()
3435
+ .references(() => stribProducts.id, { onDelete: "cascade" }),
3436
+ displayOrder: integer("display_order").notNull().default(0),
3437
+ approvedUnitRate: numeric("approved_unit_rate", { precision: 12, scale: 4 }),
3438
+ approvedRateType: varchar("approved_rate_type"),
3439
+ allocationMode: varchar("allocation_mode").notNull().default("percent"),
3440
+ allocationValue: numeric("allocation_value", { precision: 12, scale: 4 }),
3441
+ required: boolean("required").notNull().default(true),
3442
+ defaultFulfillmentDetails: jsonb("default_fulfillment_details"),
3443
+ createdAt: timestamp("created_at").notNull().defaultNow(),
3444
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
3445
+ }, (table) => [
3446
+ uniqueIndex("sales_package_products_unique_idx").on(table.salesPackageCode, table.stribProductId),
3447
+ index("sales_package_products_code_idx").on(table.salesPackageCode),
3448
+ index("sales_package_products_product_idx").on(table.stribProductId),
3449
+ ]);
3336
3450
  export const mediaOrders = pgTable("media_orders", {
3337
3451
  id: varchar("id")
3338
3452
  .primaryKey()
3339
3453
  .default(sql `gen_random_uuid()`),
3340
- planId: varchar("plan_id"), // FK → plans.id (nullable — can exist without a plan)
3341
- engagementId: varchar("engagement_id"), // FK → engagements.id (nullable during transition)
3454
+ planId: varchar("plan_id").references(() => plans.id, {
3455
+ onDelete: "set null",
3456
+ }), // nullable — can exist without a plan
3457
+ engagementId: varchar("engagement_id").references(() => engagements.id, {
3458
+ onDelete: "set null",
3459
+ }), // nullable during transition
3342
3460
  configurationId: varchar("configuration_id"), // FK → plan_configurations.id (named product mix)
3343
3461
  optionId: varchar("option_id"), // FK → plan_options.id (canonical selected option)
3344
- contractId: uuid("contract_id"), // FK → contracts.id (nullable — links to annual contract)
3462
+ contractId: uuid("contract_id").references(() => contracts.id, {
3463
+ onDelete: "set null",
3464
+ }), // nullable — links to annual contract
3345
3465
  partnerId: varchar("partner_id"),
3346
3466
  hubspotDealId: text("hubspot_deal_id"),
3347
3467
  hubspotCompanyId: text("hubspot_company_id"),
3348
3468
  parentHubspotCompanyId: text("parent_hubspot_company_id"),
3349
3469
  initiativeCode: varchar("initiative_code"),
3470
+ salesPackageCode: varchar("sales_package_code"),
3471
+ salesPackageSnapshot: jsonb("sales_package_snapshot").$type(),
3350
3472
  // Client header
3351
3473
  clientName: text("client_name").notNull(),
3352
3474
  clientContactName: text("client_contact_name"),
@@ -3451,6 +3573,7 @@ export const mediaOrders = pgTable("media_orders", {
3451
3573
  index("media_orders_parent_company_idx").on(table.parentHubspotCompanyId),
3452
3574
  index("media_orders_contract_id_idx").on(table.contractId),
3453
3575
  index("media_orders_initiative_code_idx").on(table.initiativeCode),
3576
+ index("media_orders_sales_package_code_idx").on(table.salesPackageCode),
3454
3577
  ]);
3455
3578
  export const insertMediaOrderSchema = createInsertSchema(mediaOrders).omit({
3456
3579
  id: true,
@@ -3544,6 +3667,10 @@ export const mediaOrderLineItems = pgTable("media_order_line_items", {
3544
3667
  fulfillmentDetails: jsonb("fulfillment_details"),
3545
3668
  creativeSource: varchar("creative_source"),
3546
3669
  initiativeCode: varchar("initiative_code"),
3670
+ salesPackageCode: varchar("sales_package_code"),
3671
+ salesPackageComponentRef: varchar("sales_package_component_ref"),
3672
+ isPackageDerived: boolean("is_package_derived").notNull().default(false),
3673
+ packagePricingSource: varchar("package_pricing_source"),
3547
3674
  // Ad trafficking reference key (auto-generated naming convention)
3548
3675
  trafficKey: text("traffic_key"),
3549
3676
  // Canonical tracking fields for DSP/reporting consistency
@@ -3558,6 +3685,7 @@ export const mediaOrderLineItems = pgTable("media_order_line_items", {
3558
3685
  index("media_order_line_items_location_id_idx").on(table.locationId),
3559
3686
  index("media_order_line_items_tracking_key_idx").on(table.trackingKey),
3560
3687
  index("media_order_line_items_initiative_code_idx").on(table.initiativeCode),
3688
+ index("media_order_line_items_sales_package_code_idx").on(table.salesPackageCode),
3561
3689
  ]);
3562
3690
  export const insertMediaOrderLineItemSchema = createInsertSchema(mediaOrderLineItems).omit({
3563
3691
  id: true,
@@ -6040,10 +6168,19 @@ export const semCampaigns = pgTable("sem_campaigns", {
6040
6168
  index("sem_campaigns_status_idx").on(table.status),
6041
6169
  ]);
6042
6170
  // =============================================================================
6043
- // COMPASS EVENT OUTBOX (dead-letter queue for fn-flux webhook events)
6171
+ // COMPASS EVENT OUTBOX (durable outbox for cross-system writes)
6172
+ //
6173
+ // Generic claim/retry/DLQ pattern used by multiple producers. Events are
6174
+ // namespaced by `eventType` prefix so multiple workers can drain the same
6175
+ // table without contention:
6176
+ // • "flux.*" — fn-flux webhook delivery to external consumers
6177
+ // • "hubspot.*" — HubSpot ticket / Campaign Task writes from fn-legacy
6178
+ // (drained by fn-v2 hubspot-outbox-worker)
6179
+ // Workers filter on `event_type LIKE '{namespace}.%'` in their claim query.
6044
6180
  // =============================================================================
6045
6181
  export const compassEventOutboxStatusEnum = pgEnum("compass_event_outbox_status", [
6046
6182
  "pending",
6183
+ "processing",
6047
6184
  "failed",
6048
6185
  "delivered",
6049
6186
  "dead_letter",
@@ -6116,6 +6253,8 @@ export const pendingActions = pgTable("pending_actions", {
6116
6253
  createdAt: timestamp("created_at").notNull().defaultNow(),
6117
6254
  }, (table) => [
6118
6255
  index("pending_actions_order_idx").on(table.mediaOrderId),
6256
+ index("pending_actions_line_item_idx").on(table.lineItemId),
6257
+ index("pending_actions_activity_feed_idx").on(table.activityFeedId),
6119
6258
  index("pending_actions_unresolved_idx").on(table.mediaOrderId).where(sql `resolved_at IS NULL`),
6120
6259
  ]);
6121
6260
  // ─── Order Version Snapshots ────────────────────────────────────────────────