@foundrynorth/compass-schema 1.0.20 → 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/LICENSE +4 -3
- package/dist/schema.d.ts +5819 -1953
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +318 -8
- package/dist/schema.js.map +1 -1
- package/dist/types/activity-feed.d.ts +37 -0
- package/dist/types/activity-feed.d.ts.map +1 -0
- package/dist/types/activity-feed.js +63 -0
- package/dist/types/activity-feed.js.map +1 -0
- package/package.json +2 -2
- package/src/schema.ts +491 -9
- package/src/types/activity-feed.ts +97 -0
package/src/schema.ts
CHANGED
|
@@ -105,6 +105,12 @@ import {
|
|
|
105
105
|
import { createInsertSchema } from "drizzle-zod";
|
|
106
106
|
import { z } from "zod";
|
|
107
107
|
import type { IntelligenceData } from "./analyzeTypes.js";
|
|
108
|
+
import {
|
|
109
|
+
ACTIVITY_EVENT_TYPES,
|
|
110
|
+
ACTIVITY_SEVERITIES,
|
|
111
|
+
ACTIVITY_SOURCE_SYSTEMS,
|
|
112
|
+
PENDING_ACTION_TYPES,
|
|
113
|
+
} from "./types/activity-feed.js";
|
|
108
114
|
|
|
109
115
|
export const users = pgTable("users", {
|
|
110
116
|
id: varchar("id")
|
|
@@ -124,6 +130,38 @@ export type User = typeof users.$inferSelect;
|
|
|
124
130
|
|
|
125
131
|
export const avatarRequestStatusEnum = pgEnum("avatar_request_status", ["pending", "processed", "rejected"]);
|
|
126
132
|
|
|
133
|
+
// Activity feed enums (order activity feed + pending actions)
|
|
134
|
+
export const activityEventTypeEnum = pgEnum(
|
|
135
|
+
"activity_event_type",
|
|
136
|
+
ACTIVITY_EVENT_TYPES as unknown as [string, ...string[]],
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
export const activitySeverityEnum = pgEnum(
|
|
140
|
+
"activity_severity",
|
|
141
|
+
ACTIVITY_SEVERITIES as unknown as [string, ...string[]],
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
export const activitySourceSystemEnum = pgEnum(
|
|
145
|
+
"activity_source_system",
|
|
146
|
+
ACTIVITY_SOURCE_SYSTEMS as unknown as [string, ...string[]],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
export const pendingActionTypeEnum = pgEnum(
|
|
150
|
+
"pending_action_type",
|
|
151
|
+
PENDING_ACTION_TYPES as unknown as [string, ...string[]],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
export const amendmentReasonEnum = pgEnum("amendment_reason", [
|
|
155
|
+
"budget_adjustment",
|
|
156
|
+
"flight_date_change",
|
|
157
|
+
"product_swap",
|
|
158
|
+
"add_line_items",
|
|
159
|
+
"remove_line_items",
|
|
160
|
+
"rate_renegotiation",
|
|
161
|
+
"client_request",
|
|
162
|
+
"other",
|
|
163
|
+
]);
|
|
164
|
+
|
|
127
165
|
// Vendor enums (must be declared before clerkUsers which references them)
|
|
128
166
|
export const vendorFulfillmentStatusEnum = pgEnum("vendor_fulfillment_status", [
|
|
129
167
|
"assigned",
|
|
@@ -208,6 +246,19 @@ export const userProfiles = pgTable("user_profiles", {
|
|
|
208
246
|
photoUrl: text("photo_url"), // Profile photo (can sync from Clerk or custom upload)
|
|
209
247
|
agencyAvatarUrl: text("agency_avatar_url"), // Hand-sketched avatar for proposals (Fal.ai)
|
|
210
248
|
lastAvatarGeneratedAt: timestamp("last_avatar_generated_at"), // Rate limiting: non-admins can only generate 1/day
|
|
249
|
+
proposalPortraitRenderUrl: text("proposal_portrait_render_url"),
|
|
250
|
+
proposalPortraitCutoutUrl: text("proposal_portrait_cutout_url"),
|
|
251
|
+
proposalPortraitStatus: text("proposal_portrait_status"),
|
|
252
|
+
proposalPortraitPromptVersion: text("proposal_portrait_prompt_version"),
|
|
253
|
+
proposalPortraitSeed: integer("proposal_portrait_seed"),
|
|
254
|
+
proposalPortraitGeneratedAt: timestamp("proposal_portrait_generated_at"),
|
|
255
|
+
proposalPortraitSourcePhotoHash: text("proposal_portrait_source_photo_hash"),
|
|
256
|
+
proposalPortraitPreviewRenderUrl: text("proposal_portrait_preview_render_url"),
|
|
257
|
+
proposalPortraitPreviewCutoutUrl: text("proposal_portrait_preview_cutout_url"),
|
|
258
|
+
proposalPortraitPreviewPromptVersion: text("proposal_portrait_preview_prompt_version"),
|
|
259
|
+
proposalPortraitPreviewSeed: integer("proposal_portrait_preview_seed"),
|
|
260
|
+
proposalPortraitPreviewGeneratedAt: timestamp("proposal_portrait_preview_generated_at"),
|
|
261
|
+
proposalPortraitPreviewSourcePhotoHash: text("proposal_portrait_preview_source_photo_hash"),
|
|
211
262
|
|
|
212
263
|
// Active Market Preference (overrides partner default)
|
|
213
264
|
preferredMarketId: varchar("preferred_market_id"), // User's preferred market for planning
|
|
@@ -247,6 +298,19 @@ export const externalTeamMembers = pgTable("external_team_members", {
|
|
|
247
298
|
title: text("title"),
|
|
248
299
|
photoUrl: text("photo_url"),
|
|
249
300
|
agencyAvatarUrl: text("agency_avatar_url"),
|
|
301
|
+
proposalPortraitRenderUrl: text("proposal_portrait_render_url"),
|
|
302
|
+
proposalPortraitCutoutUrl: text("proposal_portrait_cutout_url"),
|
|
303
|
+
proposalPortraitStatus: text("proposal_portrait_status"),
|
|
304
|
+
proposalPortraitPromptVersion: text("proposal_portrait_prompt_version"),
|
|
305
|
+
proposalPortraitSeed: integer("proposal_portrait_seed"),
|
|
306
|
+
proposalPortraitGeneratedAt: timestamp("proposal_portrait_generated_at"),
|
|
307
|
+
proposalPortraitSourcePhotoHash: text("proposal_portrait_source_photo_hash"),
|
|
308
|
+
proposalPortraitPreviewRenderUrl: text("proposal_portrait_preview_render_url"),
|
|
309
|
+
proposalPortraitPreviewCutoutUrl: text("proposal_portrait_preview_cutout_url"),
|
|
310
|
+
proposalPortraitPreviewPromptVersion: text("proposal_portrait_preview_prompt_version"),
|
|
311
|
+
proposalPortraitPreviewSeed: integer("proposal_portrait_preview_seed"),
|
|
312
|
+
proposalPortraitPreviewGeneratedAt: timestamp("proposal_portrait_preview_generated_at"),
|
|
313
|
+
proposalPortraitPreviewSourcePhotoHash: text("proposal_portrait_preview_source_photo_hash"),
|
|
250
314
|
linkedinUrl: text("linkedin_url"),
|
|
251
315
|
displayOrder: integer("display_order").default(0),
|
|
252
316
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
@@ -534,6 +598,35 @@ export const competitorSchema = z.object({
|
|
|
534
598
|
lng: z.number(),
|
|
535
599
|
})
|
|
536
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(),
|
|
537
630
|
});
|
|
538
631
|
|
|
539
632
|
export type Competitor = z.infer<typeof competitorSchema>;
|
|
@@ -1029,7 +1122,7 @@ export const plans = pgTable("plans", {
|
|
|
1029
1122
|
contractId: uuid("contract_id"), // FK → contracts.id (nullable — links to annual contract)
|
|
1030
1123
|
|
|
1031
1124
|
// Workflow mode: determines which UI sections and features are available
|
|
1032
|
-
// 'research_backed' =
|
|
1125
|
+
// 'research_backed' = discovery mode — creates plan, runs analysis in background
|
|
1033
1126
|
// 'direct_config' = configure products directly without research
|
|
1034
1127
|
// 'aor_pitch' = agency-of-record pitch with proposal focus
|
|
1035
1128
|
// 'io_entry' = direct insertion order entry from client PDF/XLSX
|
|
@@ -1180,6 +1273,7 @@ export const plans = pgTable("plans", {
|
|
|
1180
1273
|
defaultTargeting: text("default_targeting"),
|
|
1181
1274
|
defaultAudience: text("default_audience"),
|
|
1182
1275
|
defaultPromoting: text("default_promoting"),
|
|
1276
|
+
defaultCreativeSource: varchar("default_creative_source"),
|
|
1183
1277
|
defaultFlightStart: date("default_flight_start"),
|
|
1184
1278
|
defaultFlightEnd: date("default_flight_end"),
|
|
1185
1279
|
|
|
@@ -1251,8 +1345,12 @@ export const proposals = pgTable(
|
|
|
1251
1345
|
.default(sql`gen_random_uuid()`),
|
|
1252
1346
|
|
|
1253
1347
|
// Optional plan reference (nullable — can be standalone)
|
|
1254
|
-
planId: varchar("plan_id"),
|
|
1255
|
-
|
|
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
|
|
1256
1354
|
|
|
1257
1355
|
// Configuration references (which plan_configurations to include)
|
|
1258
1356
|
configurationIds: jsonb("configuration_ids").$type<string[]>(),
|
|
@@ -1323,6 +1421,41 @@ export const insertPlanSchema = createInsertSchema(plans).omit({
|
|
|
1323
1421
|
createdAt: true,
|
|
1324
1422
|
} as any);
|
|
1325
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
|
+
|
|
1326
1459
|
export type InsertPlan = z.infer<typeof insertPlanSchema>;
|
|
1327
1460
|
export type Plan = typeof plans.$inferSelect;
|
|
1328
1461
|
|
|
@@ -1762,6 +1895,7 @@ export const clerkUsers = pgTable("clerk_users", {
|
|
|
1762
1895
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
1763
1896
|
approvedAt: timestamp("approved_at"),
|
|
1764
1897
|
approvedBy: text("approved_by"), // Admin who approved
|
|
1898
|
+
compassVoiceWarmth: numeric("compass_voice_warmth").default("0.6"),
|
|
1765
1899
|
});
|
|
1766
1900
|
|
|
1767
1901
|
export const insertClerkUserSchema = createInsertSchema(clerkUsers).omit({
|
|
@@ -4707,21 +4841,167 @@ export const mediaOrderStatusEnum = pgEnum("media_order_status", [
|
|
|
4707
4841
|
"completed",
|
|
4708
4842
|
]);
|
|
4709
4843
|
|
|
4844
|
+
export const salesInitiatives = pgTable(
|
|
4845
|
+
"sales_initiatives",
|
|
4846
|
+
{
|
|
4847
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
4848
|
+
code: varchar("code").notNull().unique(),
|
|
4849
|
+
name: varchar("name").notNull(),
|
|
4850
|
+
description: text("description"),
|
|
4851
|
+
hubspotValue: varchar("hubspot_value").notNull().unique(),
|
|
4852
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
4853
|
+
startsAt: date("starts_at"),
|
|
4854
|
+
endsAt: date("ends_at"),
|
|
4855
|
+
isActive: boolean("is_active").notNull().default(true),
|
|
4856
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
4857
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
4858
|
+
},
|
|
4859
|
+
(table) => [
|
|
4860
|
+
index("sales_initiatives_active_idx").on(table.isActive),
|
|
4861
|
+
index("sales_initiatives_display_order_idx").on(table.displayOrder),
|
|
4862
|
+
],
|
|
4863
|
+
);
|
|
4864
|
+
|
|
4865
|
+
export const insertSalesInitiativeSchema = createInsertSchema(
|
|
4866
|
+
salesInitiatives,
|
|
4867
|
+
).omit({
|
|
4868
|
+
id: true,
|
|
4869
|
+
createdAt: true,
|
|
4870
|
+
updatedAt: true,
|
|
4871
|
+
});
|
|
4872
|
+
export const updateSalesInitiativeSchema = insertSalesInitiativeSchema.partial();
|
|
4873
|
+
export type SalesInitiative = typeof salesInitiatives.$inferSelect;
|
|
4874
|
+
export type InsertSalesInitiative = z.infer<typeof insertSalesInitiativeSchema>;
|
|
4875
|
+
export type UpdateSalesInitiative = z.infer<typeof updateSalesInitiativeSchema>;
|
|
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
|
+
|
|
4710
4968
|
export const mediaOrders = pgTable(
|
|
4711
4969
|
"media_orders",
|
|
4712
4970
|
{
|
|
4713
4971
|
id: varchar("id")
|
|
4714
4972
|
.primaryKey()
|
|
4715
4973
|
.default(sql`gen_random_uuid()`),
|
|
4716
|
-
planId: varchar("plan_id")
|
|
4717
|
-
|
|
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
|
|
4718
4980
|
configurationId: varchar("configuration_id"), // FK → plan_configurations.id (named product mix)
|
|
4719
4981
|
optionId: varchar("option_id"), // FK → plan_options.id (canonical selected option)
|
|
4720
|
-
contractId: uuid("contract_id")
|
|
4982
|
+
contractId: uuid("contract_id").references(() => contracts.id, {
|
|
4983
|
+
onDelete: "set null",
|
|
4984
|
+
}), // nullable — links to annual contract
|
|
4721
4985
|
partnerId: varchar("partner_id"),
|
|
4722
4986
|
hubspotDealId: text("hubspot_deal_id"),
|
|
4723
4987
|
hubspotCompanyId: text("hubspot_company_id"),
|
|
4724
4988
|
parentHubspotCompanyId: text("parent_hubspot_company_id"),
|
|
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
|
+
}>>(),
|
|
4725
5005
|
|
|
4726
5006
|
// Client header
|
|
4727
5007
|
clientName: text("client_name").notNull(),
|
|
@@ -4749,6 +5029,8 @@ export const mediaOrders = pgTable(
|
|
|
4749
5029
|
activatedAt: timestamp("activated_at"),
|
|
4750
5030
|
completedAt: timestamp("completed_at"),
|
|
4751
5031
|
returnReason: text("return_reason"),
|
|
5032
|
+
amendmentReason: amendmentReasonEnum("amendment_reason"),
|
|
5033
|
+
amendmentNotes: text("amendment_notes"),
|
|
4752
5034
|
slaHold: boolean("sla_hold").default(false),
|
|
4753
5035
|
clientTier: text("client_tier"),
|
|
4754
5036
|
|
|
@@ -4780,9 +5062,6 @@ export const mediaOrders = pgTable(
|
|
|
4780
5062
|
// Order-level notes
|
|
4781
5063
|
orderNotes: text("order_notes"),
|
|
4782
5064
|
|
|
4783
|
-
// Sales initiative (links to sales_initiatives table)
|
|
4784
|
-
initiativeCode: varchar("initiative_code"),
|
|
4785
|
-
|
|
4786
5065
|
// Billing fields
|
|
4787
5066
|
navigaAdvertiserId: text("naviga_advertiser_id"),
|
|
4788
5067
|
purchaseOrderNumber: text("purchase_order_number"),
|
|
@@ -4839,12 +5118,14 @@ export const mediaOrders = pgTable(
|
|
|
4839
5118
|
index("media_orders_engagement_id_idx").on(table.engagementId),
|
|
4840
5119
|
index("media_orders_partner_id_idx").on(table.partnerId),
|
|
4841
5120
|
index("media_orders_hubspot_company_id_idx").on(table.hubspotCompanyId),
|
|
5121
|
+
index("media_orders_hubspot_deal_id_idx").on(table.hubspotDealId),
|
|
4842
5122
|
index("media_orders_status_idx").on(table.status),
|
|
4843
5123
|
index("media_orders_vendor_id_idx").on(table.vendorId),
|
|
4844
5124
|
index("media_orders_vendor_fulfillment_status_idx").on(table.vendorFulfillmentStatus),
|
|
4845
5125
|
index("media_orders_parent_company_idx").on(table.parentHubspotCompanyId),
|
|
4846
5126
|
index("media_orders_contract_id_idx").on(table.contractId),
|
|
4847
5127
|
index("media_orders_initiative_code_idx").on(table.initiativeCode),
|
|
5128
|
+
index("media_orders_sales_package_code_idx").on(table.salesPackageCode),
|
|
4848
5129
|
]
|
|
4849
5130
|
);
|
|
4850
5131
|
|
|
@@ -4973,6 +5254,11 @@ export const mediaOrderLineItems = pgTable(
|
|
|
4973
5254
|
// Fulfillment details (product-specific fields for HubSpot sync)
|
|
4974
5255
|
fulfillmentDetails: jsonb("fulfillment_details"),
|
|
4975
5256
|
creativeSource: varchar("creative_source"),
|
|
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"),
|
|
4976
5262
|
|
|
4977
5263
|
// Ad trafficking reference key (auto-generated naming convention)
|
|
4978
5264
|
trafficKey: text("traffic_key"),
|
|
@@ -4994,9 +5280,12 @@ export const mediaOrderLineItems = pgTable(
|
|
|
4994
5280
|
},
|
|
4995
5281
|
(table) => [
|
|
4996
5282
|
index("media_order_line_items_section_id_idx").on(table.sectionId),
|
|
5283
|
+
index("media_order_line_items_sync_status_idx").on(table.syncStatus),
|
|
4997
5284
|
index("idx_line_items_parent").on(table.parentLineItemId),
|
|
4998
5285
|
index("media_order_line_items_location_id_idx").on(table.locationId),
|
|
4999
5286
|
index("media_order_line_items_tracking_key_idx").on(table.trackingKey),
|
|
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),
|
|
5000
5289
|
]
|
|
5001
5290
|
);
|
|
5002
5291
|
|
|
@@ -5217,6 +5506,30 @@ export const updateStribProductSchema = insertStribProductSchema.partial();
|
|
|
5217
5506
|
export type StribProduct = typeof stribProducts.$inferSelect;
|
|
5218
5507
|
export type InsertStribProduct = z.infer<typeof insertStribProductSchema>;
|
|
5219
5508
|
|
|
5509
|
+
export const salesInitiativeProducts = pgTable(
|
|
5510
|
+
"sales_initiative_products",
|
|
5511
|
+
{
|
|
5512
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5513
|
+
initiativeCode: varchar("initiative_code").notNull(),
|
|
5514
|
+
stribProductId: uuid("strib_product_id")
|
|
5515
|
+
.notNull()
|
|
5516
|
+
.references(() => stribProducts.id, { onDelete: "cascade" }),
|
|
5517
|
+
displayOrder: integer("display_order").notNull().default(0),
|
|
5518
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
5519
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
5520
|
+
},
|
|
5521
|
+
(table) => [
|
|
5522
|
+
uniqueIndex("sales_initiative_products_unique_idx").on(
|
|
5523
|
+
table.initiativeCode,
|
|
5524
|
+
table.stribProductId,
|
|
5525
|
+
),
|
|
5526
|
+
index("sales_initiative_products_code_idx").on(table.initiativeCode),
|
|
5527
|
+
index("sales_initiative_products_product_idx").on(table.stribProductId),
|
|
5528
|
+
],
|
|
5529
|
+
);
|
|
5530
|
+
|
|
5531
|
+
export type SalesInitiativeProduct = typeof salesInitiativeProducts.$inferSelect;
|
|
5532
|
+
|
|
5220
5533
|
/**
|
|
5221
5534
|
* strib_product_rates — tier-based pricing per product.
|
|
5222
5535
|
* Different tiers depending on family: Digital uses open/advocacy/tier_1/tier_2/tier_3;
|
|
@@ -5555,6 +5868,7 @@ export const rateExceptionRequests = pgTable(
|
|
|
5555
5868
|
escalationHistory: jsonb("escalation_history"), // Array of {level, at, by, reason}
|
|
5556
5869
|
expiresAt: timestamp("expires_at"),
|
|
5557
5870
|
approvalScope: varchar("approval_scope").notNull().default("request"), // "request" = affects this exception only; "catalog" = also updates global product rate
|
|
5871
|
+
batchId: uuid("batch_id"), // Groups bulk rate exception requests submitted together
|
|
5558
5872
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
5559
5873
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
5560
5874
|
},
|
|
@@ -5563,6 +5877,8 @@ export const rateExceptionRequests = pgTable(
|
|
|
5563
5877
|
index("rate_exception_status_idx").on(table.status),
|
|
5564
5878
|
index("rate_exception_requester_idx").on(table.requestedByUserId),
|
|
5565
5879
|
index("rate_exception_plan_idx").on(table.planId),
|
|
5880
|
+
index("rate_exception_batch_idx").on(table.batchId),
|
|
5881
|
+
index("rate_exception_order_status_idx").on(table.mediaOrderId, table.status),
|
|
5566
5882
|
]
|
|
5567
5883
|
);
|
|
5568
5884
|
|
|
@@ -5633,6 +5949,41 @@ export const slaConfig = pgTable("sla_config", {
|
|
|
5633
5949
|
export type SlaConfig = typeof slaConfig.$inferSelect;
|
|
5634
5950
|
export type InsertSlaConfig = typeof slaConfig.$inferInsert;
|
|
5635
5951
|
|
|
5952
|
+
// ─── SLA Snapshots ───────────────────────────────────────────────────────────
|
|
5953
|
+
|
|
5954
|
+
/**
|
|
5955
|
+
* sla_snapshots — Point-in-time SLA state from fn-flux evaluations.
|
|
5956
|
+
* Each row captures the hold status, deadline, milestones, and profile
|
|
5957
|
+
* for a given media order + fulfillment ticket pair.
|
|
5958
|
+
*/
|
|
5959
|
+
export const slaHoldStatusEnum = pgEnum("sla_hold_status", ["clear", "hold", "override"]);
|
|
5960
|
+
|
|
5961
|
+
export const slaSnapshots = pgTable(
|
|
5962
|
+
"sla_snapshots",
|
|
5963
|
+
{
|
|
5964
|
+
id: serial("id").primaryKey(),
|
|
5965
|
+
mediaOrderId: text("media_order_id").notNull().references(() => mediaOrders.id),
|
|
5966
|
+
ticketId: text("ticket_id").notNull(),
|
|
5967
|
+
holdStatus: slaHoldStatusEnum("hold_status").notNull().default("clear"),
|
|
5968
|
+
holdReason: text("hold_reason"),
|
|
5969
|
+
slaDays: integer("sla_days").notNull(),
|
|
5970
|
+
availableDays: numeric("available_days"),
|
|
5971
|
+
slaDeadline: timestamp("sla_deadline"),
|
|
5972
|
+
campaignStartDate: date("campaign_start_date"),
|
|
5973
|
+
milestones: jsonb("milestones"),
|
|
5974
|
+
profileName: text("profile_name"),
|
|
5975
|
+
lastUpdatedAt: timestamp("last_updated_at").defaultNow(),
|
|
5976
|
+
},
|
|
5977
|
+
(table) => [
|
|
5978
|
+
index("sla_snapshots_media_order_id_idx").on(table.mediaOrderId),
|
|
5979
|
+
index("sla_snapshots_ticket_id_idx").on(table.ticketId),
|
|
5980
|
+
uniqueIndex("sla_snapshots_order_ticket_uniq").on(table.mediaOrderId, table.ticketId),
|
|
5981
|
+
]
|
|
5982
|
+
);
|
|
5983
|
+
|
|
5984
|
+
export type SlaSnapshot = typeof slaSnapshots.$inferSelect;
|
|
5985
|
+
export type InsertSlaSnapshot = typeof slaSnapshots.$inferInsert;
|
|
5986
|
+
|
|
5636
5987
|
// ─── Inventory Sync Audit Trail ─────────────────────────────────────────────
|
|
5637
5988
|
|
|
5638
5989
|
/**
|
|
@@ -8157,6 +8508,7 @@ export type InsertSemCampaign = typeof semCampaigns.$inferInsert;
|
|
|
8157
8508
|
|
|
8158
8509
|
export const compassEventOutboxStatusEnum = pgEnum("compass_event_outbox_status", [
|
|
8159
8510
|
"pending",
|
|
8511
|
+
"processing",
|
|
8160
8512
|
"failed",
|
|
8161
8513
|
"delivered",
|
|
8162
8514
|
"dead_letter",
|
|
@@ -8205,3 +8557,133 @@ export const creativeDeliveries = pgTable("creative_deliveries", {
|
|
|
8205
8557
|
assetCount: integer("asset_count").notNull().default(0),
|
|
8206
8558
|
receivedAt: timestamp("received_at").defaultNow().notNull(),
|
|
8207
8559
|
});
|
|
8560
|
+
|
|
8561
|
+
// ─── Order Activity Feed ────────────────────────────────────────────────────
|
|
8562
|
+
|
|
8563
|
+
export const orderActivityFeed = pgTable(
|
|
8564
|
+
"order_activity_feed",
|
|
8565
|
+
{
|
|
8566
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
8567
|
+
mediaOrderId: varchar("media_order_id")
|
|
8568
|
+
.notNull()
|
|
8569
|
+
.references(() => mediaOrders.id),
|
|
8570
|
+
lineItemId: varchar("line_item_id").references(() => mediaOrderLineItems.id),
|
|
8571
|
+
eventType: activityEventTypeEnum("event_type").notNull(),
|
|
8572
|
+
title: text("title").notNull(),
|
|
8573
|
+
detail: text("detail"),
|
|
8574
|
+
severity: activitySeverityEnum("severity").notNull(),
|
|
8575
|
+
sourceSystem: activitySourceSystemEnum("source_system").notNull(),
|
|
8576
|
+
externalId: text("external_id").notNull().unique(), // idempotency key — prevents duplicate event ingestion
|
|
8577
|
+
status: text("status"), // intentionally untyped — values vary by eventType (creative vs fulfillment vs contract)
|
|
8578
|
+
occurredAt: timestamp("occurred_at").notNull(),
|
|
8579
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
8580
|
+
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
|
|
8581
|
+
},
|
|
8582
|
+
(table) => [
|
|
8583
|
+
index("order_activity_feed_order_occurred_idx").on(
|
|
8584
|
+
table.mediaOrderId,
|
|
8585
|
+
table.occurredAt,
|
|
8586
|
+
),
|
|
8587
|
+
index("order_activity_feed_line_item_idx").on(table.lineItemId),
|
|
8588
|
+
],
|
|
8589
|
+
);
|
|
8590
|
+
|
|
8591
|
+
export type OrderActivityFeedEntry = typeof orderActivityFeed.$inferSelect;
|
|
8592
|
+
|
|
8593
|
+
// ─── Pending Actions ────────────────────────────────────────────────────────
|
|
8594
|
+
|
|
8595
|
+
export const pendingActions = pgTable(
|
|
8596
|
+
"pending_actions",
|
|
8597
|
+
{
|
|
8598
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
8599
|
+
mediaOrderId: varchar("media_order_id")
|
|
8600
|
+
.notNull()
|
|
8601
|
+
.references(() => mediaOrders.id),
|
|
8602
|
+
lineItemId: varchar("line_item_id").references(() => mediaOrderLineItems.id),
|
|
8603
|
+
actionType: pendingActionTypeEnum("action_type").notNull(),
|
|
8604
|
+
message: text("message").notNull(),
|
|
8605
|
+
activityFeedId: uuid("activity_feed_id")
|
|
8606
|
+
.notNull()
|
|
8607
|
+
.references(() => orderActivityFeed.id),
|
|
8608
|
+
resolvedAt: timestamp("resolved_at"),
|
|
8609
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
8610
|
+
},
|
|
8611
|
+
(table) => [
|
|
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),
|
|
8615
|
+
index("pending_actions_unresolved_idx").on(table.mediaOrderId).where(
|
|
8616
|
+
sql`resolved_at IS NULL`,
|
|
8617
|
+
),
|
|
8618
|
+
],
|
|
8619
|
+
);
|
|
8620
|
+
|
|
8621
|
+
export type PendingAction = typeof pendingActions.$inferSelect;
|
|
8622
|
+
|
|
8623
|
+
// ─── Order Version Snapshots ────────────────────────────────────────────────
|
|
8624
|
+
|
|
8625
|
+
export const orderVersionSnapshots = pgTable(
|
|
8626
|
+
"order_version_snapshots",
|
|
8627
|
+
{
|
|
8628
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
8629
|
+
mediaOrderId: varchar("media_order_id")
|
|
8630
|
+
.notNull()
|
|
8631
|
+
.references(() => mediaOrders.id),
|
|
8632
|
+
version: integer("version").notNull(),
|
|
8633
|
+
amendmentReason: text("amendment_reason"),
|
|
8634
|
+
amendmentNotes: text("amendment_notes"),
|
|
8635
|
+
amendedBy: varchar("amended_by"),
|
|
8636
|
+
snapshot: jsonb("snapshot").notNull().$type<Record<string, unknown>>(),
|
|
8637
|
+
netInvestment: numeric("net_investment", { precision: 12, scale: 2 }),
|
|
8638
|
+
lineItemCount: integer("line_item_count"),
|
|
8639
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
8640
|
+
},
|
|
8641
|
+
(table) => [
|
|
8642
|
+
index("order_version_snapshots_order_version_idx").on(
|
|
8643
|
+
table.mediaOrderId,
|
|
8644
|
+
table.version,
|
|
8645
|
+
),
|
|
8646
|
+
],
|
|
8647
|
+
);
|
|
8648
|
+
|
|
8649
|
+
export type OrderVersionSnapshot = typeof orderVersionSnapshots.$inferSelect;
|
|
8650
|
+
|
|
8651
|
+
// ─── User Milestones ────────────────────────────────────────────────────────
|
|
8652
|
+
|
|
8653
|
+
export const userMilestones = pgTable(
|
|
8654
|
+
"user_milestones",
|
|
8655
|
+
{
|
|
8656
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
8657
|
+
userId: varchar("user_id").notNull(),
|
|
8658
|
+
milestoneKey: text("milestone_key").notNull(),
|
|
8659
|
+
shownAt: timestamp("shown_at").notNull().defaultNow(),
|
|
8660
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
8661
|
+
},
|
|
8662
|
+
(table) => [
|
|
8663
|
+
index("user_milestones_user_id_idx").on(table.userId),
|
|
8664
|
+
unique("user_milestones_user_key_unique").on(table.userId, table.milestoneKey),
|
|
8665
|
+
],
|
|
8666
|
+
);
|
|
8667
|
+
|
|
8668
|
+
export type UserMilestone = typeof userMilestones.$inferSelect;
|
|
8669
|
+
|
|
8670
|
+
/** Shape of the ad_activity JSONB column in business_intel_cache */
|
|
8671
|
+
export interface BusinessIntelAdActivity {
|
|
8672
|
+
google?: { active: boolean; adCount: number; lastSeen?: string };
|
|
8673
|
+
meta?: { active: boolean; adCount: number; lastSeen?: string };
|
|
8674
|
+
}
|
|
8675
|
+
|
|
8676
|
+
/** Shape of the seo_snapshot JSONB column in business_intel_cache */
|
|
8677
|
+
export interface BusinessIntelSeoSnapshot {
|
|
8678
|
+
organicTraffic?: number;
|
|
8679
|
+
topKeywords?: string[];
|
|
8680
|
+
paidTrafficCost?: number;
|
|
8681
|
+
}
|
|
8682
|
+
|
|
8683
|
+
/** Shape of the tech_signals JSONB column in business_intel_cache */
|
|
8684
|
+
export interface BusinessIntelTechSignals {
|
|
8685
|
+
analytics?: string[];
|
|
8686
|
+
advertising?: string[];
|
|
8687
|
+
crm?: string[];
|
|
8688
|
+
social?: string[];
|
|
8689
|
+
}
|