@foundrynorth/compass-schema 1.0.21 → 1.0.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foundrynorth/compass-schema",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Canonical Drizzle ORM schema for Foundry Compass (rough-waterfall database)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/schema.ts CHANGED
@@ -598,6 +598,35 @@ export const competitorSchema = z.object({
598
598
  lng: z.number(),
599
599
  })
600
600
  .optional(), // Detailed location information
601
+
602
+ // Ad library data from Apify deep enrichment
603
+ ads: z
604
+ .object({
605
+ facebook: z
606
+ .array(
607
+ z.object({
608
+ adImage: z.string().optional(),
609
+ headline: z.string().optional(),
610
+ adCopy: z.string().optional(),
611
+ callToAction: z.string().optional(),
612
+ publisherPlatforms: z.array(z.string()).optional(),
613
+ startDate: z.string().optional(),
614
+ })
615
+ )
616
+ .optional(),
617
+ google: z
618
+ .array(
619
+ z.object({
620
+ headline: z.string().optional(),
621
+ description: z.string().optional(),
622
+ landingPage: z.string().optional(),
623
+ adType: z.string().optional(),
624
+ imageUrl: z.string().optional(),
625
+ })
626
+ )
627
+ .optional(),
628
+ })
629
+ .optional(),
601
630
  });
602
631
 
603
632
  export type Competitor = z.infer<typeof competitorSchema>;
@@ -1244,6 +1273,7 @@ export const plans = pgTable("plans", {
1244
1273
  defaultTargeting: text("default_targeting"),
1245
1274
  defaultAudience: text("default_audience"),
1246
1275
  defaultPromoting: text("default_promoting"),
1276
+ defaultCreativeSource: varchar("default_creative_source"),
1247
1277
  defaultFlightStart: date("default_flight_start"),
1248
1278
  defaultFlightEnd: date("default_flight_end"),
1249
1279
 
@@ -1315,8 +1345,12 @@ export const proposals = pgTable(
1315
1345
  .default(sql`gen_random_uuid()`),
1316
1346
 
1317
1347
  // Optional plan reference (nullable — can be standalone)
1318
- planId: varchar("plan_id"),
1319
- engagementId: varchar("engagement_id"), // FK → engagements.id (nullable during transition)
1348
+ planId: varchar("plan_id").references(() => plans.id, {
1349
+ onDelete: "set null",
1350
+ }),
1351
+ engagementId: varchar("engagement_id").references(() => engagements.id, {
1352
+ onDelete: "set null",
1353
+ }), // nullable during transition
1320
1354
 
1321
1355
  // Configuration references (which plan_configurations to include)
1322
1356
  configurationIds: jsonb("configuration_ids").$type<string[]>(),
@@ -1387,6 +1421,41 @@ export const insertPlanSchema = createInsertSchema(plans).omit({
1387
1421
  createdAt: true,
1388
1422
  } as any);
1389
1423
 
1424
+ // ============================================================================
1425
+ // Proposal Engagement Tracking
1426
+ // ============================================================================
1427
+
1428
+ export const proposalEngagement = pgTable(
1429
+ "proposal_engagement",
1430
+ {
1431
+ id: varchar("id")
1432
+ .primaryKey()
1433
+ .default(sql`gen_random_uuid()`),
1434
+ proposalId: varchar("proposal_id"), // FK → proposals.id (nullable for legacy plan-based shares)
1435
+ planId: varchar("plan_id"), // FK → plans.id (fallback for legacy shares)
1436
+ shareToken: text("share_token").notNull(), // The share token used to access
1437
+ sessionId: text("session_id").notNull(), // Anonymous session ID (crypto.randomUUID on client)
1438
+ // Engagement data
1439
+ events: jsonb("events").notNull().default(sql`'[]'::jsonb`), // Array of engagement events
1440
+ engagementScore: integer("engagement_score").default(0), // Computed 0-100 score
1441
+ summary: text("summary"), // AI-generated follow-up brief
1442
+ // Device context
1443
+ device: text("device"), // "desktop" | "mobile" | "tablet"
1444
+ referrer: text("referrer"), // HTTP referrer
1445
+ userAgent: text("user_agent"),
1446
+ // Timestamps
1447
+ firstSeenAt: timestamp("first_seen_at").notNull().defaultNow(),
1448
+ lastSeenAt: timestamp("last_seen_at").notNull().defaultNow(),
1449
+ createdAt: timestamp("created_at").notNull().defaultNow(),
1450
+ },
1451
+ (table) => [
1452
+ index("proposal_engagement_proposal_id_idx").on(table.proposalId),
1453
+ index("proposal_engagement_plan_id_idx").on(table.planId),
1454
+ index("proposal_engagement_share_token_idx").on(table.shareToken),
1455
+ index("proposal_engagement_token_session_idx").on(table.shareToken, table.sessionId),
1456
+ ]
1457
+ );
1458
+
1390
1459
  export type InsertPlan = z.infer<typeof insertPlanSchema>;
1391
1460
  export type Plan = typeof plans.$inferSelect;
1392
1461
 
@@ -4805,22 +4874,134 @@ export type SalesInitiative = typeof salesInitiatives.$inferSelect;
4805
4874
  export type InsertSalesInitiative = z.infer<typeof insertSalesInitiativeSchema>;
4806
4875
  export type UpdateSalesInitiative = z.infer<typeof updateSalesInitiativeSchema>;
4807
4876
 
4877
+ export const salesPackages = pgTable(
4878
+ "sales_packages",
4879
+ {
4880
+ id: uuid("id").primaryKey().defaultRandom(),
4881
+ code: varchar("code").notNull().unique(),
4882
+ name: varchar("name").notNull(),
4883
+ description: text("description"),
4884
+ initiativeCode: varchar("initiative_code"),
4885
+ sellerSummary: text("seller_summary"),
4886
+ packagePrice: numeric("package_price", { precision: 12, scale: 2 }),
4887
+ discountPercent: numeric("discount_percent", { precision: 5, scale: 2 }),
4888
+ addedValueAmount: numeric("added_value_amount", { precision: 12, scale: 2 }),
4889
+ addedValueDescription: varchar("added_value_description", { length: 255 }),
4890
+ hubspotPackageValue: varchar("hubspot_package_value").notNull().unique(),
4891
+ startsAt: date("starts_at"),
4892
+ endsAt: date("ends_at"),
4893
+ status: varchar("status").notNull().default("draft"),
4894
+ recommendationMode: varchar("recommendation_mode").notNull().default("both"),
4895
+ recommendationPriority: integer("recommendation_priority").notNull().default(0),
4896
+ eligibilityRules: jsonb("eligibility_rules").$type<{
4897
+ goals?: string[];
4898
+ industries?: string[];
4899
+ geographyTags?: string[];
4900
+ minBudget?: number | null;
4901
+ maxBudget?: number | null;
4902
+ requireActiveWindow?: boolean;
4903
+ minProductOverlap?: number | null;
4904
+ allowSubsetMatch?: boolean;
4905
+ allowNearMatch?: boolean;
4906
+ compareSignals?: string[];
4907
+ }>(),
4908
+ assistantHints: jsonb("assistant_hints").$type<{
4909
+ recommendedWhen?: string[];
4910
+ upsellMessage?: string | null;
4911
+ compareHeadline?: string | null;
4912
+ compareBody?: string | null;
4913
+ }>(),
4914
+ taskTemplateMode: varchar("task_template_mode").notNull().default("standard"),
4915
+ displayOrder: integer("display_order").notNull().default(0),
4916
+ isActive: boolean("is_active").notNull().default(true),
4917
+ createdAt: timestamp("created_at").notNull().defaultNow(),
4918
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
4919
+ },
4920
+ (table) => [
4921
+ index("sales_packages_active_idx").on(table.isActive),
4922
+ index("sales_packages_status_idx").on(table.status),
4923
+ index("sales_packages_display_order_idx").on(table.displayOrder),
4924
+ index("sales_packages_initiative_code_idx").on(table.initiativeCode),
4925
+ ],
4926
+ );
4927
+
4928
+ export const insertSalesPackageSchema = createInsertSchema(salesPackages).omit({
4929
+ id: true,
4930
+ createdAt: true,
4931
+ updatedAt: true,
4932
+ });
4933
+ export const updateSalesPackageSchema = insertSalesPackageSchema.partial();
4934
+ export type SalesPackage = typeof salesPackages.$inferSelect;
4935
+ export type InsertSalesPackage = z.infer<typeof insertSalesPackageSchema>;
4936
+ export type UpdateSalesPackage = z.infer<typeof updateSalesPackageSchema>;
4937
+
4938
+ export const salesPackageProducts = pgTable(
4939
+ "sales_package_products",
4940
+ {
4941
+ id: uuid("id").primaryKey().defaultRandom(),
4942
+ salesPackageCode: varchar("sales_package_code").notNull(),
4943
+ stribProductId: uuid("strib_product_id")
4944
+ .notNull()
4945
+ .references(() => stribProducts.id, { onDelete: "cascade" }),
4946
+ displayOrder: integer("display_order").notNull().default(0),
4947
+ approvedUnitRate: numeric("approved_unit_rate", { precision: 12, scale: 4 }),
4948
+ approvedRateType: varchar("approved_rate_type"),
4949
+ allocationMode: varchar("allocation_mode").notNull().default("percent"),
4950
+ allocationValue: numeric("allocation_value", { precision: 12, scale: 4 }),
4951
+ required: boolean("required").notNull().default(true),
4952
+ defaultFulfillmentDetails: jsonb("default_fulfillment_details"),
4953
+ createdAt: timestamp("created_at").notNull().defaultNow(),
4954
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
4955
+ },
4956
+ (table) => [
4957
+ uniqueIndex("sales_package_products_unique_idx").on(
4958
+ table.salesPackageCode,
4959
+ table.stribProductId,
4960
+ ),
4961
+ index("sales_package_products_code_idx").on(table.salesPackageCode),
4962
+ index("sales_package_products_product_idx").on(table.stribProductId),
4963
+ ],
4964
+ );
4965
+
4966
+ export type SalesPackageProduct = typeof salesPackageProducts.$inferSelect;
4967
+
4808
4968
  export const mediaOrders = pgTable(
4809
4969
  "media_orders",
4810
4970
  {
4811
4971
  id: varchar("id")
4812
4972
  .primaryKey()
4813
4973
  .default(sql`gen_random_uuid()`),
4814
- planId: varchar("plan_id"), // FK → plans.id (nullable — can exist without a plan)
4815
- engagementId: varchar("engagement_id"), // FK → engagements.id (nullable during transition)
4974
+ planId: varchar("plan_id").references(() => plans.id, {
4975
+ onDelete: "set null",
4976
+ }), // nullable — can exist without a plan
4977
+ engagementId: varchar("engagement_id").references(() => engagements.id, {
4978
+ onDelete: "set null",
4979
+ }), // nullable during transition
4816
4980
  configurationId: varchar("configuration_id"), // FK → plan_configurations.id (named product mix)
4817
4981
  optionId: varchar("option_id"), // FK → plan_options.id (canonical selected option)
4818
- contractId: uuid("contract_id"), // FK → contracts.id (nullable — links to annual contract)
4982
+ contractId: uuid("contract_id").references(() => contracts.id, {
4983
+ onDelete: "set null",
4984
+ }), // nullable — links to annual contract
4819
4985
  partnerId: varchar("partner_id"),
4820
4986
  hubspotDealId: text("hubspot_deal_id"),
4821
4987
  hubspotCompanyId: text("hubspot_company_id"),
4822
4988
  parentHubspotCompanyId: text("parent_hubspot_company_id"),
4823
4989
  initiativeCode: varchar("initiative_code"),
4990
+ salesPackageCode: varchar("sales_package_code"),
4991
+ salesPackageSnapshot: jsonb("sales_package_snapshot").$type<Array<{
4992
+ code: string;
4993
+ name: string;
4994
+ packagePrice: number | null;
4995
+ initiativeCode: string | null;
4996
+ componentCount: number;
4997
+ appliedAt: string;
4998
+ packageSource: "direct_package" | "upgrade_from_selection" | "manual_products";
4999
+ compareSignals?: string[];
5000
+ sellerSummary?: string | null;
5001
+ discountPercent?: number | null;
5002
+ addedValueAmount?: number | null;
5003
+ addedValueDescription?: string | null;
5004
+ }>>(),
4824
5005
 
4825
5006
  // Client header
4826
5007
  clientName: text("client_name").notNull(),
@@ -4944,6 +5125,7 @@ export const mediaOrders = pgTable(
4944
5125
  index("media_orders_parent_company_idx").on(table.parentHubspotCompanyId),
4945
5126
  index("media_orders_contract_id_idx").on(table.contractId),
4946
5127
  index("media_orders_initiative_code_idx").on(table.initiativeCode),
5128
+ index("media_orders_sales_package_code_idx").on(table.salesPackageCode),
4947
5129
  ]
4948
5130
  );
4949
5131
 
@@ -5073,6 +5255,10 @@ export const mediaOrderLineItems = pgTable(
5073
5255
  fulfillmentDetails: jsonb("fulfillment_details"),
5074
5256
  creativeSource: varchar("creative_source"),
5075
5257
  initiativeCode: varchar("initiative_code"),
5258
+ salesPackageCode: varchar("sales_package_code"),
5259
+ salesPackageComponentRef: varchar("sales_package_component_ref"),
5260
+ isPackageDerived: boolean("is_package_derived").notNull().default(false),
5261
+ packagePricingSource: varchar("package_pricing_source"),
5076
5262
 
5077
5263
  // Ad trafficking reference key (auto-generated naming convention)
5078
5264
  trafficKey: text("traffic_key"),
@@ -5099,6 +5285,7 @@ export const mediaOrderLineItems = pgTable(
5099
5285
  index("media_order_line_items_location_id_idx").on(table.locationId),
5100
5286
  index("media_order_line_items_tracking_key_idx").on(table.trackingKey),
5101
5287
  index("media_order_line_items_initiative_code_idx").on(table.initiativeCode),
5288
+ index("media_order_line_items_sales_package_code_idx").on(table.salesPackageCode),
5102
5289
  ]
5103
5290
  );
5104
5291
 
@@ -8321,6 +8508,7 @@ export type InsertSemCampaign = typeof semCampaigns.$inferInsert;
8321
8508
 
8322
8509
  export const compassEventOutboxStatusEnum = pgEnum("compass_event_outbox_status", [
8323
8510
  "pending",
8511
+ "processing",
8324
8512
  "failed",
8325
8513
  "delivered",
8326
8514
  "dead_letter",
@@ -8422,6 +8610,8 @@ export const pendingActions = pgTable(
8422
8610
  },
8423
8611
  (table) => [
8424
8612
  index("pending_actions_order_idx").on(table.mediaOrderId),
8613
+ index("pending_actions_line_item_idx").on(table.lineItemId),
8614
+ index("pending_actions_activity_feed_idx").on(table.activityFeedId),
8425
8615
  index("pending_actions_unresolved_idx").on(table.mediaOrderId).where(
8426
8616
  sql`resolved_at IS NULL`,
8427
8617
  ),