adserver-dashboard 1.0.0

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.
Files changed (525) hide show
  1. package/.ci/staging.yml +191 -0
  2. package/.dockerignore +117 -0
  3. package/.env +40 -0
  4. package/.env.staging +38 -0
  5. package/.gitlab-ci.yml +16 -0
  6. package/DEMO_STATUS.md +579 -0
  7. package/Dockerfile +61 -0
  8. package/Influence-MW-AdServer-12-02-2026/client/index.html +17 -0
  9. package/Influence-MW-AdServer-12-02-2026/client/public/favicon.png +0 -0
  10. package/Influence-MW-AdServer-12-02-2026/client/src/App.tsx +91 -0
  11. package/Influence-MW-AdServer-12-02-2026/client/src/components/advanced-map-drawer.tsx +1131 -0
  12. package/Influence-MW-AdServer-12-02-2026/client/src/components/ai-recommendation-panel.tsx +379 -0
  13. package/Influence-MW-AdServer-12-02-2026/client/src/components/app-sidebar.tsx +183 -0
  14. package/Influence-MW-AdServer-12-02-2026/client/src/components/auto-optimize-button.tsx +184 -0
  15. package/Influence-MW-AdServer-12-02-2026/client/src/components/availability-drawer.tsx +385 -0
  16. package/Influence-MW-AdServer-12-02-2026/client/src/components/brand-insights-panel.tsx +87 -0
  17. package/Influence-MW-AdServer-12-02-2026/client/src/components/create-agency-drawer.tsx +198 -0
  18. package/Influence-MW-AdServer-12-02-2026/client/src/components/create-brand-drawer.tsx +275 -0
  19. package/Influence-MW-AdServer-12-02-2026/client/src/components/creative-assignment.tsx +526 -0
  20. package/Influence-MW-AdServer-12-02-2026/client/src/components/data-table-toolbar.tsx +148 -0
  21. package/Influence-MW-AdServer-12-02-2026/client/src/components/data-table.tsx +158 -0
  22. package/Influence-MW-AdServer-12-02-2026/client/src/components/filter-drawer.tsx +356 -0
  23. package/Influence-MW-AdServer-12-02-2026/client/src/components/form-insights-panel.tsx +82 -0
  24. package/Influence-MW-AdServer-12-02-2026/client/src/components/geography-selector.tsx +699 -0
  25. package/Influence-MW-AdServer-12-02-2026/client/src/components/header-user-menu.tsx +178 -0
  26. package/Influence-MW-AdServer-12-02-2026/client/src/components/history-drawer.tsx +313 -0
  27. package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-availability-section.tsx +176 -0
  28. package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-format-drawer.tsx +173 -0
  29. package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-selector.tsx +401 -0
  30. package/Influence-MW-AdServer-12-02-2026/client/src/components/manual-inventory-drawer.tsx +368 -0
  31. package/Influence-MW-AdServer-12-02-2026/client/src/components/mapbox-map.tsx +368 -0
  32. package/Influence-MW-AdServer-12-02-2026/client/src/components/market-insights-panel.tsx +202 -0
  33. package/Influence-MW-AdServer-12-02-2026/client/src/components/media-owner-drawer.tsx +217 -0
  34. package/Influence-MW-AdServer-12-02-2026/client/src/components/metric-card.tsx +58 -0
  35. package/Influence-MW-AdServer-12-02-2026/client/src/components/page-header.tsx +27 -0
  36. package/Influence-MW-AdServer-12-02-2026/client/src/components/player-status-indicator.tsx +137 -0
  37. package/Influence-MW-AdServer-12-02-2026/client/src/components/poi-targeting-drawer.tsx +298 -0
  38. package/Influence-MW-AdServer-12-02-2026/client/src/components/recommendation-score-badge.tsx +102 -0
  39. package/Influence-MW-AdServer-12-02-2026/client/src/components/recommended-inventories-panel.tsx +248 -0
  40. package/Influence-MW-AdServer-12-02-2026/client/src/components/searchable-combobox.tsx +134 -0
  41. package/Influence-MW-AdServer-12-02-2026/client/src/components/signal-visualizations.tsx +407 -0
  42. package/Influence-MW-AdServer-12-02-2026/client/src/components/status-badge.tsx +35 -0
  43. package/Influence-MW-AdServer-12-02-2026/client/src/components/theme-provider.tsx +73 -0
  44. package/Influence-MW-AdServer-12-02-2026/client/src/components/theme-toggle.tsx +37 -0
  45. package/Influence-MW-AdServer-12-02-2026/client/src/components/traffic-slider.tsx +75 -0
  46. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/accordion.tsx +56 -0
  47. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/alert-dialog.tsx +139 -0
  48. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/alert.tsx +59 -0
  49. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/aspect-ratio.tsx +5 -0
  50. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/avatar.tsx +51 -0
  51. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/badge.tsx +38 -0
  52. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/breadcrumb.tsx +115 -0
  53. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/button.tsx +62 -0
  54. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/calendar.tsx +68 -0
  55. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/card.tsx +85 -0
  56. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/carousel.tsx +260 -0
  57. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/chart.tsx +365 -0
  58. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/checkbox.tsx +28 -0
  59. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/collapsible.tsx +11 -0
  60. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/command.tsx +151 -0
  61. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/context-menu.tsx +198 -0
  62. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/dialog.tsx +122 -0
  63. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/drawer.tsx +118 -0
  64. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/dropdown-menu.tsx +198 -0
  65. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/form.tsx +178 -0
  66. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/hover-card.tsx +29 -0
  67. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/input-otp.tsx +69 -0
  68. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/input.tsx +23 -0
  69. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/label.tsx +24 -0
  70. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/menubar.tsx +256 -0
  71. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/navigation-menu.tsx +128 -0
  72. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/pagination.tsx +117 -0
  73. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/popover.tsx +29 -0
  74. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/progress.tsx +28 -0
  75. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/radio-group.tsx +42 -0
  76. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/resizable.tsx +45 -0
  77. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/scroll-area.tsx +46 -0
  78. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/select.tsx +160 -0
  79. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/separator.tsx +29 -0
  80. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/sheet.tsx +140 -0
  81. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/sidebar.tsx +727 -0
  82. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/skeleton.tsx +15 -0
  83. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/slider.tsx +26 -0
  84. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/switch.tsx +27 -0
  85. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/table.tsx +117 -0
  86. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/tabs.tsx +53 -0
  87. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/textarea.tsx +22 -0
  88. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toast.tsx +127 -0
  89. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toaster.tsx +33 -0
  90. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toggle-group.tsx +61 -0
  91. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toggle.tsx +43 -0
  92. package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/tooltip.tsx +30 -0
  93. package/Influence-MW-AdServer-12-02-2026/client/src/components/vendor-stores-modal.tsx +336 -0
  94. package/Influence-MW-AdServer-12-02-2026/client/src/components/venue-type-drawer.tsx +359 -0
  95. package/Influence-MW-AdServer-12-02-2026/client/src/components/venue-type-selector.tsx +436 -0
  96. package/Influence-MW-AdServer-12-02-2026/client/src/hooks/use-mobile.tsx +19 -0
  97. package/Influence-MW-AdServer-12-02-2026/client/src/hooks/use-toast.ts +191 -0
  98. package/Influence-MW-AdServer-12-02-2026/client/src/index.css +244 -0
  99. package/Influence-MW-AdServer-12-02-2026/client/src/lib/queryClient.ts +57 -0
  100. package/Influence-MW-AdServer-12-02-2026/client/src/lib/utils.ts +39 -0
  101. package/Influence-MW-AdServer-12-02-2026/client/src/lib/venue-taxonomy.ts +532 -0
  102. package/Influence-MW-AdServer-12-02-2026/client/src/main.tsx +5 -0
  103. package/Influence-MW-AdServer-12-02-2026/client/src/pages/assign-creative.tsx +781 -0
  104. package/Influence-MW-AdServer-12-02-2026/client/src/pages/content-hub.tsx +995 -0
  105. package/Influence-MW-AdServer-12-02-2026/client/src/pages/custom-pois.tsx +431 -0
  106. package/Influence-MW-AdServer-12-02-2026/client/src/pages/dashboard.tsx +620 -0
  107. package/Influence-MW-AdServer-12-02-2026/client/src/pages/deal-detail.tsx +1062 -0
  108. package/Influence-MW-AdServer-12-02-2026/client/src/pages/deal-form.tsx +1570 -0
  109. package/Influence-MW-AdServer-12-02-2026/client/src/pages/deals.tsx +716 -0
  110. package/Influence-MW-AdServer-12-02-2026/client/src/pages/edit-creative-assignment.tsx +1051 -0
  111. package/Influence-MW-AdServer-12-02-2026/client/src/pages/geotargeting.tsx +675 -0
  112. package/Influence-MW-AdServer-12-02-2026/client/src/pages/integrations.tsx +425 -0
  113. package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-item-creatives.tsx +622 -0
  114. package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-item-form.tsx +3132 -0
  115. package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-items.tsx +530 -0
  116. package/Influence-MW-AdServer-12-02-2026/client/src/pages/not-found.tsx +21 -0
  117. package/Influence-MW-AdServer-12-02-2026/client/src/pages/proof-of-play-upload.tsx +479 -0
  118. package/Influence-MW-AdServer-12-02-2026/client/src/pages/proof-of-play.tsx +880 -0
  119. package/Influence-MW-AdServer-12-02-2026/client/src/pages/reports.tsx +235 -0
  120. package/Influence-MW-AdServer-12-02-2026/client/src/pages/settings.tsx +652 -0
  121. package/Influence-MW-AdServer-12-02-2026/client/src/pages/signal-form.tsx +1117 -0
  122. package/Influence-MW-AdServer-12-02-2026/client/src/pages/signals.tsx +366 -0
  123. package/Influence-MW-AdServer-12-02-2026/client/src/pages/tags.tsx +332 -0
  124. package/Influence-MW-AdServer-12-02-2026/client/src/pages/venues.tsx +381 -0
  125. package/Influence-MW-AdServer-12-02-2026/client/src/types/mapbox-gl-draw.d.ts +37 -0
  126. package/Influence-MW-AdServer-12-02-2026/client/src/types/react-simple-maps.d.ts +57 -0
  127. package/Influence-MW-AdServer-12-02-2026/components.json +20 -0
  128. package/Influence-MW-AdServer-12-02-2026/docs/PRD.md +3373 -0
  129. package/Influence-MW-AdServer-12-02-2026/docs/influence-feature-mapping.csv +498 -0
  130. package/Influence-MW-AdServer-12-02-2026/drizzle.config.ts +14 -0
  131. package/Influence-MW-AdServer-12-02-2026/package-lock.json +9672 -0
  132. package/Influence-MW-AdServer-12-02-2026/package.json +118 -0
  133. package/Influence-MW-AdServer-12-02-2026/postcss.config.js +6 -0
  134. package/Influence-MW-AdServer-12-02-2026/replit.md +91 -0
  135. package/Influence-MW-AdServer-12-02-2026/script/build.ts +67 -0
  136. package/Influence-MW-AdServer-12-02-2026/scripts/create-miro-diagrams.cjs +318 -0
  137. package/Influence-MW-AdServer-12-02-2026/scripts/create-remaining-diagrams.cjs +270 -0
  138. package/Influence-MW-AdServer-12-02-2026/server/index.ts +103 -0
  139. package/Influence-MW-AdServer-12-02-2026/server/recommendation-service.ts +319 -0
  140. package/Influence-MW-AdServer-12-02-2026/server/routes.ts +1890 -0
  141. package/Influence-MW-AdServer-12-02-2026/server/static.ts +19 -0
  142. package/Influence-MW-AdServer-12-02-2026/server/storage.ts +2058 -0
  143. package/Influence-MW-AdServer-12-02-2026/server/vite.ts +58 -0
  144. package/Influence-MW-AdServer-12-02-2026/shared/schema.ts +1595 -0
  145. package/Influence-MW-AdServer-12-02-2026/tailwind.config.ts +107 -0
  146. package/Influence-MW-AdServer-12-02-2026/tsconfig.json +23 -0
  147. package/Influence-MW-AdServer-12-02-2026/vite.config.ts +40 -0
  148. package/LINE_ITEM_BUDGET_FIELD_MAPPING.md +178 -0
  149. package/PCM/.env.example +92 -0
  150. package/PCM/README.md +558 -0
  151. package/PCM/docs/TEST_CASES.md +422 -0
  152. package/PCM/index.js +106 -0
  153. package/PCM/package-lock.json +3282 -0
  154. package/PCM/package.json +32 -0
  155. package/PCM/replit.md +64 -0
  156. package/PCM/schema.sql +495 -0
  157. package/PCM/scripts/export-schema.js +183 -0
  158. package/PCM/scripts/seed-comprehensive.js +631 -0
  159. package/PCM/scripts/seed-production.js +477 -0
  160. package/PCM/src/config/db.js +56 -0
  161. package/PCM/src/config/swagger.js +5975 -0
  162. package/PCM/src/dto/EmailRequestDTO.js +166 -0
  163. package/PCM/src/middleware/errorHandler.js +52 -0
  164. package/PCM/src/middleware/logger.js +26 -0
  165. package/PCM/src/migrations/001_add_campaign_mode_fields.sql +36 -0
  166. package/PCM/src/migrations/002_create_deal_id_counters.sql +22 -0
  167. package/PCM/src/migrations/003_update_publishers_column.sql +15 -0
  168. package/PCM/src/migrations/004_add_direct_dealtype_and_advertiser.sql +5 -0
  169. package/PCM/src/migrations/005_add_programmatic_fields_and_update_enums.sql +31 -0
  170. package/PCM/src/migrations/006_add_line_item_programmatic_fields.sql +12 -0
  171. package/PCM/src/migrations/007_add_line_item_direct_fields.sql +15 -0
  172. package/PCM/src/migrations/008_add_inventory_fields.sql +45 -0
  173. package/PCM/src/migrations/009_move_inventory_fields_to_metadata.sql +32 -0
  174. package/PCM/src/migrations/010_add_draft_status_and_line_items_count.sql +23 -0
  175. package/PCM/src/migrations/011_add_planning_field.sql +21 -0
  176. package/PCM/src/migrations/012_fix_inventory_composite_pk.sql +17 -0
  177. package/PCM/src/migrations/013_make_external_id_optional.sql +3 -0
  178. package/PCM/src/migrations/014_create_change_history.sql +38 -0
  179. package/PCM/src/migrations/016_create_publisher_insertion_orders.sql +33 -0
  180. package/PCM/src/migrations/017_fix_line_item_id_fk_reference.sql +86 -0
  181. package/PCM/src/migrations/018_create_approval_tables.sql +44 -0
  182. package/PCM/src/migrations/019_add_encrypted_token_column.sql +2 -0
  183. package/PCM/src/migrations/020_add_rejection_reason_to_deals.sql +10 -0
  184. package/PCM/src/migrations/021_add_publisher_external_id_to_inventories.sql +12 -0
  185. package/PCM/src/migrations/022_add_line_item_extended_fields.sql +24 -0
  186. package/PCM/src/migrations/023_add_base_price_fields.sql +8 -0
  187. package/PCM/src/migrations/run-migrations.js +46 -0
  188. package/PCM/src/models/ApprovalOTP.js +51 -0
  189. package/PCM/src/models/ApprovalToken.js +79 -0
  190. package/PCM/src/models/ChangeHistory.js +107 -0
  191. package/PCM/src/models/Deal.js +186 -0
  192. package/PCM/src/models/DealIdCounter.js +28 -0
  193. package/PCM/src/models/LineItem.js +227 -0
  194. package/PCM/src/models/LineItemCreative.js +89 -0
  195. package/PCM/src/models/LineItemInventory.js +115 -0
  196. package/PCM/src/models/PublisherInsertionOrder.js +93 -0
  197. package/PCM/src/models/TransactionHistory.js +34 -0
  198. package/PCM/src/models/associations.js +81 -0
  199. package/PCM/src/routes/approval.js +321 -0
  200. package/PCM/src/routes/creatives.js +437 -0
  201. package/PCM/src/routes/deals.js +1638 -0
  202. package/PCM/src/routes/digitalSignage.js +242 -0
  203. package/PCM/src/routes/insertionOrders.js +380 -0
  204. package/PCM/src/routes/lineItems.js +926 -0
  205. package/PCM/src/routes/system.js +384 -0
  206. package/PCM/src/services/ApprovalService.js +885 -0
  207. package/PCM/src/services/CampaignImportConverter.js +631 -0
  208. package/PCM/src/services/CampaignModeService.js +273 -0
  209. package/PCM/src/services/CampaignStatusService.js +395 -0
  210. package/PCM/src/services/ChangeHistoryService.js +316 -0
  211. package/PCM/src/services/DealIdService.js +94 -0
  212. package/PCM/src/services/DealResponseFormatter.js +90 -0
  213. package/PCM/src/services/EmailNotificationService.js +315 -0
  214. package/PCM/src/services/LineItemResponseFormatter.js +122 -0
  215. package/PCM/src/services/LineItemStatusService.js +380 -0
  216. package/PCM/src/tests/comprehensiveTestRunner.js +360 -0
  217. package/PCM/src/tests/comprehensiveTests.js +1277 -0
  218. package/PCM/src/tests/dealTypeUnitTests.js +1058 -0
  219. package/PCM/src/tests/testRunner.js +248 -0
  220. package/PCM/src/utils/caseConverter.js +92 -0
  221. package/PCM/src/utils/dealCalculations.js +206 -0
  222. package/PCM/src/utils/lineItemPayloadNormalizer.js +41 -0
  223. package/PCM/src/utils/payloadNormalizer.js +34 -0
  224. package/PCM/src/utils/sourceNormalizer.js +56 -0
  225. package/PCM/src/validators/creativeValidator.js +27 -0
  226. package/PCM/src/validators/dealValidator.js +203 -0
  227. package/PCM/src/validators/lineItemValidator.js +489 -0
  228. package/PCM/tests/approval-flows.test.js +238 -0
  229. package/PCM/tests/approval-workflow.test.js +291 -0
  230. package/PCM/tests/campaign-import-converter.test.js +543 -0
  231. package/PCM/tests/campaign-import-e2e.test.js +520 -0
  232. package/PCM/tests/campaign-status.test.js +539 -0
  233. package/PCM/tests/direct-publisher-split-reimport.test.js +460 -0
  234. package/PCM/tests/e2e/digital-signage.test.js +145 -0
  235. package/PCM/tests/e2e/search-filter-pagination.test.js +399 -0
  236. package/PCM/tests/e2e-comprehensive.test.js +3446 -0
  237. package/PCM/tests/edge-cases.test.js +340 -0
  238. package/PCM/tests/line-item-status.test.js +340 -0
  239. package/PCM/tests/seller-account-external-ids.test.js +877 -0
  240. package/PCM/tests/source-validation.test.js +324 -0
  241. package/PRD.md +3373 -0
  242. package/README.md +186 -0
  243. package/client/index.html +35 -0
  244. package/client/public/DEMO_STATUS.md +579 -0
  245. package/client/public/img/MW-logo-trans_1754045676555.png +0 -0
  246. package/client/public/locales/ar/approval.json +144 -0
  247. package/client/public/locales/ar/buyer.json +61 -0
  248. package/client/public/locales/ar/campaigns.json +1 -0
  249. package/client/public/locales/ar/common.json +218 -0
  250. package/client/public/locales/ar/contentHub.json +266 -0
  251. package/client/public/locales/ar/creatives.json +79 -0
  252. package/client/public/locales/ar/dashboard.json +57 -0
  253. package/client/public/locales/ar/deals.json +886 -0
  254. package/client/public/locales/ar/dsp.json +131 -0
  255. package/client/public/locales/ar/inventory.json +201 -0
  256. package/client/public/locales/ar/lineItems.json +553 -0
  257. package/client/public/locales/ar/navigation.json +48 -0
  258. package/client/public/locales/ar/wizard.json +1 -0
  259. package/client/public/locales/en/approval.json +144 -0
  260. package/client/public/locales/en/buyer.json +65 -0
  261. package/client/public/locales/en/campaigns.json +1 -0
  262. package/client/public/locales/en/common.json +218 -0
  263. package/client/public/locales/en/contentHub.json +266 -0
  264. package/client/public/locales/en/creatives.json +79 -0
  265. package/client/public/locales/en/dashboard.json +57 -0
  266. package/client/public/locales/en/deals.json +886 -0
  267. package/client/public/locales/en/dsp.json +131 -0
  268. package/client/public/locales/en/inventory.json +201 -0
  269. package/client/public/locales/en/lineItems.json +659 -0
  270. package/client/public/locales/en/navigation.json +48 -0
  271. package/client/public/locales/en/wizard.json +1 -0
  272. package/client/public/locales/ja/approval.json +144 -0
  273. package/client/public/locales/ja/buyer.json +61 -0
  274. package/client/public/locales/ja/campaigns.json +1 -0
  275. package/client/public/locales/ja/common.json +218 -0
  276. package/client/public/locales/ja/contentHub.json +266 -0
  277. package/client/public/locales/ja/creatives.json +79 -0
  278. package/client/public/locales/ja/dashboard.json +57 -0
  279. package/client/public/locales/ja/deals.json +886 -0
  280. package/client/public/locales/ja/dsp.json +131 -0
  281. package/client/public/locales/ja/inventory.json +201 -0
  282. package/client/public/locales/ja/lineItems.json +553 -0
  283. package/client/public/locales/ja/navigation.json +48 -0
  284. package/client/public/locales/ja/wizard.json +1 -0
  285. package/client/public/locales/zh/approval.json +144 -0
  286. package/client/public/locales/zh/buyer.json +61 -0
  287. package/client/public/locales/zh/campaigns.json +1 -0
  288. package/client/public/locales/zh/common.json +218 -0
  289. package/client/public/locales/zh/contentHub.json +266 -0
  290. package/client/public/locales/zh/creatives.json +79 -0
  291. package/client/public/locales/zh/dashboard.json +57 -0
  292. package/client/public/locales/zh/deals.json +886 -0
  293. package/client/public/locales/zh/dsp.json +131 -0
  294. package/client/public/locales/zh/inventory.json +201 -0
  295. package/client/public/locales/zh/lineItems.json +553 -0
  296. package/client/public/locales/zh/navigation.json +48 -0
  297. package/client/public/locales/zh/wizard.json +1 -0
  298. package/client/public/manifest.json +36 -0
  299. package/client/src/App.tsx +464 -0
  300. package/client/src/components/app-sidebar.tsx +312 -0
  301. package/client/src/components/approval/approval-decision-form.test.tsx +294 -0
  302. package/client/src/components/approval/approval-decision-form.tsx +326 -0
  303. package/client/src/components/approval/approval-sheet.tsx +631 -0
  304. package/client/src/components/approval/line-item-details-sheet.tsx +371 -0
  305. package/client/src/components/approval/otp-verification.test.tsx +337 -0
  306. package/client/src/components/approval/otp-verification.tsx +180 -0
  307. package/client/src/components/content-hub/bulk-transcode-dialog.tsx +379 -0
  308. package/client/src/components/content-hub/content-hub-manager-v2.tsx +574 -0
  309. package/client/src/components/content-hub/content-hub-manager.tsx +330 -0
  310. package/client/src/components/content-hub/creative-card.tsx +456 -0
  311. package/client/src/components/content-hub/creative-detail-sheet.tsx +685 -0
  312. package/client/src/components/content-hub/creative-filters.tsx +457 -0
  313. package/client/src/components/content-hub/creative-grid.tsx +329 -0
  314. package/client/src/components/content-hub/creative-selector.tsx +415 -0
  315. package/client/src/components/content-hub/creative-upload.tsx +547 -0
  316. package/client/src/components/content-hub/folder-dialogs.tsx +445 -0
  317. package/client/src/components/content-hub/folder-list.tsx +280 -0
  318. package/client/src/components/content-hub/review-dialogs.tsx +268 -0
  319. package/client/src/components/content-hub/transcode-dialog.tsx +226 -0
  320. package/client/src/components/creative-library/creative-details-view.tsx +446 -0
  321. package/client/src/components/creative-library/creative-filters-panel.tsx +203 -0
  322. package/client/src/components/creative-library/creative-list.tsx +360 -0
  323. package/client/src/components/creative-library/creative-status-badge.tsx +71 -0
  324. package/client/src/components/creative-library/folder-card.tsx +78 -0
  325. package/client/src/components/creative-library/index.ts +27 -0
  326. package/client/src/components/creative-library/new-creative-card.tsx +211 -0
  327. package/client/src/components/creative-library/upload-creative-dialog.tsx +261 -0
  328. package/client/src/components/dashboard-overview.tsx +109 -0
  329. package/client/src/components/deals/approval-history-panel.test.tsx +240 -0
  330. package/client/src/components/deals/approval-history-panel.tsx +156 -0
  331. package/client/src/components/deals/deal-status-badge.tsx +92 -0
  332. package/client/src/components/deals/import-from-planner-dialog.tsx +399 -0
  333. package/client/src/components/deals/market-insights-panel.tsx +237 -0
  334. package/client/src/components/deals/reopen-deal-sheet.tsx +191 -0
  335. package/client/src/components/deals/request-approval-sheet.test.tsx +323 -0
  336. package/client/src/components/deals/request-approval-sheet.tsx +136 -0
  337. package/client/src/components/deals/resend-approval-sheet.tsx +201 -0
  338. package/client/src/components/direct-campaigns/campaign-card.tsx +283 -0
  339. package/client/src/components/direct-campaigns/deal-filter-panel.tsx +325 -0
  340. package/client/src/components/inventory/advanced-filters-panel.tsx +273 -0
  341. package/client/src/components/inventory/csv-upload-modal.tsx +639 -0
  342. package/client/src/components/inventory/inventory-availability-view.tsx +486 -0
  343. package/client/src/components/inventory/inventory-details-sheet.tsx +376 -0
  344. package/client/src/components/inventory/inventory-map-view.tsx +596 -0
  345. package/client/src/components/inventory/inventory-settings-menu.tsx +52 -0
  346. package/client/src/components/language-switcher.tsx +53 -0
  347. package/client/src/components/line-items/campaign-forecast-panel.tsx +138 -0
  348. package/client/src/components/line-items/form-insights.tsx +89 -0
  349. package/client/src/components/line-items/geofencing/LocationCsvUploadDrawer.tsx +100 -0
  350. package/client/src/components/line-items/geofencing/POIDropdown.tsx +379 -0
  351. package/client/src/components/line-items/geofencing/SelectedLocationsSidebar.tsx +436 -0
  352. package/client/src/components/line-items/geofencing/ViewFileLocationDrawer.tsx +199 -0
  353. package/client/src/components/line-items/geofencing/components/ExistingFilesTab.tsx +268 -0
  354. package/client/src/components/line-items/geofencing/components/TemplateDownloadSection.tsx +59 -0
  355. package/client/src/components/line-items/geofencing/components/UploadTab.tsx +215 -0
  356. package/client/src/components/line-items/geofencing-map.tsx +1270 -0
  357. package/client/src/components/line-items/inventory-availability-section.tsx +178 -0
  358. package/client/src/components/line-items/line-item-schedule-manager.tsx +313 -0
  359. package/client/src/components/line-items/manual-inventory-drawer.tsx +346 -0
  360. package/client/src/components/line-items/planner-inventory-card.tsx +495 -0
  361. package/client/src/components/line-items/planner-schedule-grid.tsx +495 -0
  362. package/client/src/components/line-items/schedule-rule-editor.tsx +649 -0
  363. package/client/src/components/line-items/schedule-rule-types.ts +122 -0
  364. package/client/src/components/line-items/steps/creatives-step.tsx +681 -0
  365. package/client/src/components/line-items/steps/inventory-schedule-step.tsx +1596 -0
  366. package/client/src/components/line-items/steps/inventory-step.tsx +1533 -0
  367. package/client/src/components/line-items/steps/line-item-details-step.tsx +916 -0
  368. package/client/src/components/line-items/steps/schedule-step.tsx +273 -0
  369. package/client/src/components/line-items/steps/summary-step.tsx +680 -0
  370. package/client/src/components/line-items/steps/targeting-step.tsx +1708 -0
  371. package/client/src/components/product-switcher.tsx +105 -0
  372. package/client/src/components/protected-route.tsx +49 -0
  373. package/client/src/components/skip-link.tsx +22 -0
  374. package/client/src/components/stat-card.tsx +53 -0
  375. package/client/src/components/status-badge.tsx +96 -0
  376. package/client/src/components/ui/hierarchical-venue-selector.tsx +389 -0
  377. package/client/src/components/ui/toaster.tsx +111 -0
  378. package/client/src/contexts/auth-context.tsx +181 -0
  379. package/client/src/contexts/sidebar-state.tsx +50 -0
  380. package/client/src/contexts/theme-context.tsx +66 -0
  381. package/client/src/data/campaign-data.json +107 -0
  382. package/client/src/data/countries.json +22 -0
  383. package/client/src/hooks/use-approval.ts +366 -0
  384. package/client/src/hooks/use-keyboard-shortcuts.ts +74 -0
  385. package/client/src/hooks/use-media-query.ts +46 -0
  386. package/client/src/hooks/use-mobile.tsx +19 -0
  387. package/client/src/hooks/use-page-title.ts +21 -0
  388. package/client/src/hooks/use-toast.ts +195 -0
  389. package/client/src/index.css +694 -0
  390. package/client/src/lib/__tests__/accessibility.test.ts +104 -0
  391. package/client/src/lib/__tests__/date-utils.test.ts +199 -0
  392. package/client/src/lib/__tests__/dsp-buyer-api.test.ts +127 -0
  393. package/client/src/lib/__tests__/dsp-buyer-integration.test.ts +247 -0
  394. package/client/src/lib/__tests__/storage-utils.test.ts +167 -0
  395. package/client/src/lib/__tests__/utils.test.ts +57 -0
  396. package/client/src/lib/accessibility.ts +141 -0
  397. package/client/src/lib/api-config.ts +9 -0
  398. package/client/src/lib/auth-service.ts +209 -0
  399. package/client/src/lib/campaign-creative-api.ts +82 -0
  400. package/client/src/lib/company-api.ts +61 -0
  401. package/client/src/lib/content-hub-api.ts +407 -0
  402. package/client/src/lib/creative-mapper.ts +61 -0
  403. package/client/src/lib/date-utils.ts +119 -0
  404. package/client/src/lib/deal-helpers.ts +220 -0
  405. package/client/src/lib/dsp-buyer-api.ts +196 -0
  406. package/client/src/lib/geo-import-api.ts +151 -0
  407. package/client/src/lib/google-poi-api.ts +305 -0
  408. package/client/src/lib/i18n/__tests__/formatting.test.ts +202 -0
  409. package/client/src/lib/i18n/formatting.ts +130 -0
  410. package/client/src/lib/i18n/index.ts +8 -0
  411. package/client/src/lib/i18n-compat.ts +76 -0
  412. package/client/src/lib/influence-deals-api.ts +896 -0
  413. package/client/src/lib/inventory-api.ts +399 -0
  414. package/client/src/lib/oauth-service.ts +678 -0
  415. package/client/src/lib/poi-types.ts +75 -0
  416. package/client/src/lib/queryClient.ts +144 -0
  417. package/client/src/lib/recommendation-api.ts +380 -0
  418. package/client/src/lib/storage-utils.ts +104 -0
  419. package/client/src/lib/tolgee.ts +85 -0
  420. package/client/src/lib/utils.ts +0 -0
  421. package/client/src/main.tsx +67 -0
  422. package/client/src/mapbox-draw-modes.d.ts +32 -0
  423. package/client/src/pages/all-folders.tsx +203 -0
  424. package/client/src/pages/auth-callback.tsx +115 -0
  425. package/client/src/pages/buyer-form.tsx +339 -0
  426. package/client/src/pages/buyer-list.tsx +622 -0
  427. package/client/src/pages/content-hub.tsx +1358 -0
  428. package/client/src/pages/create-deal.tsx +2093 -0
  429. package/client/src/pages/creative-assignment-page.tsx +548 -0
  430. package/client/src/pages/creatives.tsx +5 -0
  431. package/client/src/pages/custom-pois.tsx +425 -0
  432. package/client/src/pages/dashboard.tsx +615 -0
  433. package/client/src/pages/deal-history.tsx +434 -0
  434. package/client/src/pages/deal-line-items.tsx +1703 -0
  435. package/client/src/pages/demo-status.tsx +113 -0
  436. package/client/src/pages/direct-campaign-details.tsx +361 -0
  437. package/client/src/pages/direct-campaigns-new.tsx +824 -0
  438. package/client/src/pages/dsp-form.tsx +803 -0
  439. package/client/src/pages/dsp-list.tsx +239 -0
  440. package/client/src/pages/folder-content.tsx +336 -0
  441. package/client/src/pages/integrations.tsx +429 -0
  442. package/client/src/pages/line-item-creatives.tsx +789 -0
  443. package/client/src/pages/line-item-detail-page.tsx +684 -0
  444. package/client/src/pages/line-item-form-page.tsx +3261 -0
  445. package/client/src/pages/line-item-wizard.tsx +1207 -0
  446. package/client/src/pages/login.tsx +154 -0
  447. package/client/src/pages/not-found.tsx +23 -0
  448. package/client/src/pages/proof-of-play.tsx +397 -0
  449. package/client/src/pages/public-approval.tsx +551 -0
  450. package/client/src/pages/reports.tsx +231 -0
  451. package/client/src/pages/settings.tsx +760 -0
  452. package/client/src/pages/signals.tsx +389 -0
  453. package/client/src/pages/tags.tsx +318 -0
  454. package/client/src/pages/test-results.tsx +328 -0
  455. package/client/src/store/hooks.ts +5 -0
  456. package/client/src/store/index.ts +15 -0
  457. package/client/src/store/mapMarkerLocationsSlice.ts +241 -0
  458. package/client/src/styles/design-tokens.css +324 -0
  459. package/client/src/test/setup.ts +261 -0
  460. package/client/src/test/test-utils.tsx +40 -0
  461. package/client/src/types/approval.ts +221 -0
  462. package/client/src/types/content-hub.ts +209 -0
  463. package/client/src/types/geofencing.ts +67 -0
  464. package/client/src/types/transcoding.ts +140 -0
  465. package/client/src/vite-env.d.ts +18 -0
  466. package/components.json +20 -0
  467. package/creative-api.json +1 -0
  468. package/docs/AI_REFERENCE.md +459 -0
  469. package/docs/MWDesign-Prompt.md +132 -0
  470. package/docs/MWDesign-System.md +344 -0
  471. package/docs/test-plan.md +277 -0
  472. package/e2e/AUTONOMOUS-TESTING.md +406 -0
  473. package/e2e/README.md +219 -0
  474. package/e2e/autonomous-flow.spec.ts +308 -0
  475. package/e2e/debug-sso.spec.ts +163 -0
  476. package/e2e/direct-campaigns.spec.ts +219 -0
  477. package/e2e/explore-sso.spec.ts +149 -0
  478. package/e2e/fixtures/auth.ts +26 -0
  479. package/e2e/fixtures/enhanced-test.ts +331 -0
  480. package/e2e/pagination.spec.ts +280 -0
  481. package/e2e/view-toggle.spec.ts +312 -0
  482. package/generated-icon.png +0 -0
  483. package/i18next-scanner.config.cjs +46 -0
  484. package/package.json +141 -0
  485. package/playwright.config.ts +93 -0
  486. package/postcss.config.js +6 -0
  487. package/replit.md +196 -0
  488. package/screenshot-after-login.png +0 -0
  489. package/screenshot-contenthub-grid.png +0 -0
  490. package/screenshot-contenthub-list-fixed.png +0 -0
  491. package/screenshot-contenthub-list.png +0 -0
  492. package/screenshot-create-deal.png +0 -0
  493. package/screenshot-dashboard.png +0 -0
  494. package/screenshot-deals.png +0 -0
  495. package/screenshot-login-filled.png +0 -0
  496. package/screenshot-login.png +0 -0
  497. package/screenshot.mjs +24 -0
  498. package/scripts/deploy-stg.sh +185 -0
  499. package/shared/direct-io-schema.ts +383 -0
  500. package/shared/schema.ts +439 -0
  501. package/shared/screen-types.ts +149 -0
  502. package/springdocDefault.json +1 -0
  503. package/swagger-ui-bundle.js +2 -0
  504. package/swagger-ui-init.js +10316 -0
  505. package/tailwind.config.ts +282 -0
  506. package/terraform/README.md +306 -0
  507. package/terraform/cloudfront.tf +289 -0
  508. package/terraform/ecs.tf +727 -0
  509. package/terraform/environments/dev.tfvars +59 -0
  510. package/terraform/environments/production.tfvars +60 -0
  511. package/terraform/main.tf +47 -0
  512. package/terraform/outputs.tf +145 -0
  513. package/terraform/s3.tf +192 -0
  514. package/terraform/variables.tf +226 -0
  515. package/terraform/waf.tf +165 -0
  516. package/terraform-frontend/.terraform.lock.hcl +25 -0
  517. package/terraform-frontend/README.md +85 -0
  518. package/terraform-frontend/cloudfront.tf +125 -0
  519. package/terraform-frontend/main.tf +31 -0
  520. package/terraform-frontend/outputs.tf +24 -0
  521. package/terraform-frontend/terraform.tfvars +12 -0
  522. package/terraform-frontend/variables.tf +53 -0
  523. package/tsconfig.json +23 -0
  524. package/vite.config.ts +226 -0
  525. package/vitest.config.ts +56 -0
@@ -0,0 +1,166 @@
1
+ export class ApprovalEmailBodyDTO {
2
+ constructor({
3
+ user = {},
4
+ approverEmail = '',
5
+ dealName,
6
+ dealId,
7
+ budget,
8
+ noLineItems,
9
+ approvalLink,
10
+ publisherName = '',
11
+ logoUrl = null,
12
+ layoutCompanyName = '',
13
+ layoutAddress = '',
14
+ layoutPhoneNumber = '',
15
+ layoutEmail = ''
16
+ }) {
17
+ const derivedFirstname = approverEmail ? approverEmail.split('@')[0] : '';
18
+ this.user = {
19
+ firstname: user.firstname || derivedFirstname,
20
+ lastname: user.lastname || ''
21
+ };
22
+ this.deal_name = dealName;
23
+ this.deal_id = dealId;
24
+ this.budget = budget;
25
+ this.no_line_items = String(noLineItems);
26
+ this.approval_link = approvalLink;
27
+ this.publisher_name = publisherName;
28
+ this.logoUrl = logoUrl;
29
+ this.layoutCompanyName = layoutCompanyName;
30
+ this.layoutAddress = layoutAddress;
31
+ this.layoutPhoneNumber = layoutPhoneNumber;
32
+ this.layoutEmail = layoutEmail;
33
+ }
34
+
35
+ toJSON() {
36
+ return JSON.stringify({
37
+ user: this.user,
38
+ deal_name: this.deal_name,
39
+ deal_id: this.deal_id,
40
+ budget: this.budget,
41
+ no_line_items: this.no_line_items,
42
+ approval_link: this.approval_link,
43
+ publisher_name: this.publisher_name,
44
+ logoUrl: this.logoUrl,
45
+ layoutCompanyName: this.layoutCompanyName,
46
+ layoutAddress: this.layoutAddress,
47
+ layoutPhoneNumber: this.layoutPhoneNumber,
48
+ layoutEmail: this.layoutEmail
49
+ });
50
+ }
51
+ }
52
+
53
+ export class OTPEmailBodyDTO {
54
+ constructor({
55
+ user = {},
56
+ approverEmail = '',
57
+ code,
58
+ mediaownerName = '',
59
+ logoUrl = null,
60
+ layoutCompanyName = '',
61
+ layoutAddress = '',
62
+ layoutPhoneNumber = '',
63
+ layoutEmail = ''
64
+ }) {
65
+ const derivedFirstname = approverEmail ? approverEmail.split('@')[0] : '';
66
+ this.user = {
67
+ firstname: user.firstname || derivedFirstname,
68
+ lastname: user.lastname || ''
69
+ };
70
+ this.code = code;
71
+ this.mediaownerName = mediaownerName;
72
+ this.logoUrl = logoUrl;
73
+ this.layoutCompanyName = layoutCompanyName;
74
+ this.layoutAddress = layoutAddress;
75
+ this.layoutPhoneNumber = layoutPhoneNumber;
76
+ this.layoutEmail = layoutEmail;
77
+ }
78
+
79
+ toJSON() {
80
+ return JSON.stringify({
81
+ user: this.user,
82
+ code: this.code,
83
+ mediaownerName: this.mediaownerName,
84
+ logoUrl: this.logoUrl,
85
+ layoutCompanyName: this.layoutCompanyName,
86
+ layoutAddress: this.layoutAddress,
87
+ layoutPhoneNumber: this.layoutPhoneNumber,
88
+ layoutEmail: this.layoutEmail
89
+ });
90
+ }
91
+ }
92
+
93
+ export class DecisionEmailBodyDTO {
94
+ constructor({
95
+ user = {},
96
+ sellerEmail = '',
97
+ dealName,
98
+ dealId,
99
+ budget,
100
+ noLineItems,
101
+ approverEmail,
102
+ logoUrl = null,
103
+ layoutCompanyName = '',
104
+ layoutAddress = '',
105
+ layoutPhoneNumber = '',
106
+ layoutEmail = ''
107
+ }) {
108
+ const derivedFirstname = sellerEmail ? sellerEmail.split('@')[0] : '';
109
+ this.user = {
110
+ firstname: user.firstname || derivedFirstname,
111
+ lastname: user.lastname || ''
112
+ };
113
+ this.deal_name = dealName;
114
+ this.deal_id = dealId;
115
+ this.budget = budget;
116
+ this.no_line_items = String(noLineItems);
117
+ this.approver = approverEmail;
118
+ this.logoUrl = logoUrl;
119
+ this.layoutCompanyName = layoutCompanyName;
120
+ this.layoutAddress = layoutAddress;
121
+ this.layoutPhoneNumber = layoutPhoneNumber;
122
+ this.layoutEmail = layoutEmail;
123
+ }
124
+
125
+ toJSON() {
126
+ return JSON.stringify({
127
+ user: this.user,
128
+ deal_name: this.deal_name,
129
+ deal_id: this.deal_id,
130
+ budget: this.budget,
131
+ no_line_items: this.no_line_items,
132
+ approver: this.approver,
133
+ logoUrl: this.logoUrl,
134
+ layoutCompanyName: this.layoutCompanyName,
135
+ layoutAddress: this.layoutAddress,
136
+ layoutPhoneNumber: this.layoutPhoneNumber,
137
+ layoutEmail: this.layoutEmail
138
+ });
139
+ }
140
+ }
141
+
142
+ export class EmailRequestDTO {
143
+ constructor({
144
+ from,
145
+ to,
146
+ subject,
147
+ body,
148
+ template
149
+ }) {
150
+ this.from = from;
151
+ this.to = Array.isArray(to) ? to : [to];
152
+ this.subject = subject;
153
+ this.body = body;
154
+ this.template = template;
155
+ }
156
+
157
+ toPayload() {
158
+ return {
159
+ from: this.from,
160
+ to: this.to,
161
+ subject: this.subject,
162
+ body: typeof this.body === 'string' ? this.body : this.body.toJSON(),
163
+ template: this.template
164
+ };
165
+ }
166
+ }
@@ -0,0 +1,52 @@
1
+ export const errorHandler = (err, req, res, next) => {
2
+ console.error('Error:', err);
3
+
4
+ if (err.name === 'ValidationError') {
5
+ const errors = err.details?.map(detail => ({
6
+ field: detail.path.join('.'),
7
+ message: detail.message
8
+ })) || [];
9
+
10
+ return res.status(400).json({
11
+ code: 'VALIDATION_ERROR',
12
+ message: 'Invalid request payload',
13
+ errors
14
+ });
15
+ }
16
+
17
+ if (err.name === 'SequelizeUniqueConstraintError') {
18
+ const fields = err.errors?.map(e => e.path).join(', ') || 'unknown';
19
+ return res.status(409).json({
20
+ code: 'CONFLICT',
21
+ message: `Duplicate value for: ${fields}`,
22
+ details: err.errors?.map(e => ({ field: e.path, value: e.value, message: e.message })) || []
23
+ });
24
+ }
25
+
26
+ if (err.name === 'SequelizeValidationError') {
27
+ return res.status(400).json({
28
+ code: 'VALIDATION_ERROR',
29
+ message: 'Database validation failed',
30
+ errors: err.errors?.map(e => ({ field: e.path, message: e.message, value: e.value })) || []
31
+ });
32
+ }
33
+
34
+ if (err.name === 'SequelizeForeignKeyConstraintError') {
35
+ return res.status(400).json({
36
+ code: 'REFERENCE_ERROR',
37
+ message: 'Referenced resource does not exist'
38
+ });
39
+ }
40
+
41
+ if (err.statusCode) {
42
+ return res.status(err.statusCode).json({
43
+ code: err.code || 'ERROR',
44
+ message: err.message
45
+ });
46
+ }
47
+
48
+ res.status(500).json({
49
+ code: 'INTERNAL_ERROR',
50
+ message: 'An unexpected error occurred'
51
+ });
52
+ };
@@ -0,0 +1,26 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import TransactionHistory from '../models/TransactionHistory.js';
3
+
4
+ export const requestLogger = async (req, res, next) => {
5
+ const requestId = uuidv4();
6
+ req.requestId = requestId;
7
+
8
+ const originalSend = res.send;
9
+ res.send = function (data) {
10
+ res.send = originalSend;
11
+
12
+ TransactionHistory.create({
13
+ request_id: requestId,
14
+ method: req.method,
15
+ endpoint: req.originalUrl || req.url,
16
+ payload: req.body || {},
17
+ response_status: res.statusCode
18
+ }).catch(err => {
19
+ console.error('Error logging transaction:', err);
20
+ });
21
+
22
+ return res.send(data);
23
+ };
24
+
25
+ next();
26
+ };
@@ -0,0 +1,36 @@
1
+ -- Migration: Add Campaign Mode fields to deals table
2
+ -- Version: 001
3
+ -- Date: December 2025
4
+ -- Description: Adds brand, client_type, mode, approval_emails, market_selection, budget_setup, campaign_goal fields
5
+ -- Note: Using TEXT for client_type and mode to match Sequelize model and avoid ENUM management complexity
6
+
7
+ -- Add new columns to deals table (all TEXT-based for flexibility)
8
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS brand TEXT;
9
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS client_type TEXT;
10
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS mode TEXT;
11
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS approval_emails TEXT[];
12
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS market_selection JSONB;
13
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS budget_setup JSONB;
14
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS campaign_goal JSONB;
15
+
16
+ -- Add check constraints for validation (instead of ENUMs)
17
+ DO $$
18
+ BEGIN
19
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'deals_client_type_check') THEN
20
+ ALTER TABLE deals ADD CONSTRAINT deals_client_type_check
21
+ CHECK (client_type IS NULL OR client_type IN ('DIRECT_ADVERTISER', 'AGENCY'));
22
+ END IF;
23
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'deals_mode_check') THEN
24
+ ALTER TABLE deals ADD CONSTRAINT deals_mode_check
25
+ CHECK (mode IS NULL OR mode IN ('PROGRAMMATIC', 'DIRECT'));
26
+ END IF;
27
+ END$$;
28
+
29
+ -- Add comments for documentation
30
+ COMMENT ON COLUMN deals.brand IS 'Brand name (dropdown selection)';
31
+ COMMENT ON COLUMN deals.client_type IS 'Client type: BUYER or AGENCY';
32
+ COMMENT ON COLUMN deals.mode IS 'Campaign mode: PROGRAMMATIC or DIRECT';
33
+ COMMENT ON COLUMN deals.approval_emails IS 'List of approval email addresses (required for DIRECT mode)';
34
+ COMMENT ON COLUMN deals.market_selection IS 'Market and currency selection (required for DIRECT mode)';
35
+ COMMENT ON COLUMN deals.budget_setup IS 'Budget configuration (required for DIRECT mode)';
36
+ COMMENT ON COLUMN deals.campaign_goal IS 'Campaign goal configuration (required for DIRECT mode)';
@@ -0,0 +1,22 @@
1
+ -- Migration: Create deal_id_counters table for sequence tracking
2
+ -- This table maintains sequence counters for auto-generating deal IDs
3
+
4
+ CREATE TABLE IF NOT EXISTS deal_id_counters (
5
+ id SERIAL PRIMARY KEY,
6
+ prefix VARCHAR(10) NOT NULL UNIQUE,
7
+ current_value BIGINT NOT NULL DEFAULT 0,
8
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
9
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
10
+ );
11
+
12
+ -- Initialize counters for each deal type prefix
13
+ INSERT INTO deal_id_counters (prefix, current_value) VALUES
14
+ ('GD', 0),
15
+ ('PD', 0),
16
+ ('PA', 0),
17
+ ('EG', 0),
18
+ ('DIR', 0)
19
+ ON CONFLICT (prefix) DO NOTHING;
20
+
21
+ -- Create index for faster lookups
22
+ CREATE INDEX IF NOT EXISTS idx_deal_id_counters_prefix ON deal_id_counters(prefix);
@@ -0,0 +1,15 @@
1
+ DO $$
2
+ BEGIN
3
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publisher_ids') THEN
4
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publishers') THEN
5
+ ALTER TABLE deals ADD COLUMN publishers JSONB;
6
+ UPDATE deals SET publishers = (
7
+ SELECT jsonb_agg(jsonb_build_object('id', elem, 'name', elem))
8
+ FROM unnest(publisher_ids) AS elem
9
+ ) WHERE publisher_ids IS NOT NULL AND array_length(publisher_ids, 1) > 0;
10
+ END IF;
11
+ ALTER TABLE deals DROP COLUMN publisher_ids;
12
+ ELSIF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publishers') THEN
13
+ ALTER TABLE deals ADD COLUMN publishers JSONB;
14
+ END IF;
15
+ END $$;
@@ -0,0 +1,5 @@
1
+ -- Add DIRECT to deal_type enum (using correct enum name)
2
+ ALTER TYPE deal_type_enum ADD VALUE IF NOT EXISTS 'DIRECT';
3
+
4
+ -- Add advertiser column if not exists
5
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS advertiser JSONB;
@@ -0,0 +1,31 @@
1
+ -- Add new status values to status enum
2
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'REQUESTED';
3
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'REJECTED';
4
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'CANCELED';
5
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'DELETED';
6
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'PENDING_CREATIVES';
7
+ ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'IN_ACTIVE';
8
+
9
+ -- Create transaction_type enum if not exists
10
+ DO $$ BEGIN
11
+ CREATE TYPE transaction_type_enum AS ENUM ('SPOT', 'AUDIENCE');
12
+ EXCEPTION
13
+ WHEN duplicate_object THEN null;
14
+ END $$;
15
+
16
+ -- Create imp_multiplier_type enum if not exists
17
+ DO $$ BEGIN
18
+ CREATE TYPE imp_multiplier_type_enum AS ENUM ('MAD', 'CUSTOM');
19
+ EXCEPTION
20
+ WHEN duplicate_object THEN null;
21
+ END $$;
22
+
23
+ -- Add new programmatic fields
24
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS published BOOLEAN DEFAULT false;
25
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS reopened BOOLEAN DEFAULT false;
26
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS non_billable BOOLEAN DEFAULT false;
27
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS stop_bid_requests BOOLEAN DEFAULT false;
28
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS imp_multiplier_type imp_multiplier_type_enum;
29
+
30
+ -- Set default status to GENERATED for new records
31
+ ALTER TABLE deals ALTER COLUMN status SET DEFAULT 'GENERATED';
@@ -0,0 +1,12 @@
1
+ -- Migration: Add programmatic fields to line_items table
2
+ -- Date: 2025-12-10
3
+
4
+ -- Add new columns to line_items table using TEXT types to avoid enum issues
5
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS mode TEXT;
6
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS resolutions TEXT[];
7
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS creative_source TEXT;
8
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS total_inventories INTEGER DEFAULT 0;
9
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS adm_creative_sync BOOLEAN DEFAULT false;
10
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS non_billable BOOLEAN DEFAULT false;
11
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS stop_bid_requests BOOLEAN DEFAULT false;
12
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS imp_multiplier JSONB;
@@ -0,0 +1,15 @@
1
+ -- Migration: Add DIRECT mode fields to line_items table
2
+ -- Date: 2025-01-10
3
+ -- Description: Adds budgetSetup, campaignGoal, pacing, and targeting fields for DIRECT mode line items
4
+
5
+ -- Add DIRECT mode specific fields
6
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS budget_setup JSONB;
7
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS campaign_goal JSONB;
8
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS pacing JSONB;
9
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS targeting JSONB;
10
+
11
+ -- Add comments for documentation
12
+ COMMENT ON COLUMN line_items.budget_setup IS 'Budget configuration for DIRECT mode line items';
13
+ COMMENT ON COLUMN line_items.campaign_goal IS 'Campaign goal settings for DIRECT mode line items';
14
+ COMMENT ON COLUMN line_items.pacing IS 'Pacing configuration for DIRECT mode line items';
15
+ COMMENT ON COLUMN line_items.targeting IS 'Targeting rules for DIRECT mode line items';
@@ -0,0 +1,45 @@
1
+ -- Migration: Add comprehensive inventory fields to line_item_inventories table
2
+ -- Date: 2025-01-10
3
+ -- Description: Adds new fields for ad unit identification, location, venue, display settings, and content controls
4
+
5
+ -- Identifier fields
6
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS ad_unit_code TEXT;
7
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS reference_id TEXT;
8
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS device_id TEXT;
9
+
10
+ -- Location/Country fields
11
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS country_iso2 TEXT;
12
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS country_iso3 TEXT;
13
+
14
+ -- Venue fields
15
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS venue_type_ids TEXT[];
16
+
17
+ -- Operational metrics
18
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS spots_per_hour INTEGER;
19
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS spot_duration INTEGER;
20
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS clients INTEGER;
21
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS "group" TEXT;
22
+
23
+ -- Content controls
24
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS bcat JSONB;
25
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS publisher_domain TEXT;
26
+
27
+ -- Display/Aspect ratio fields
28
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS enable_aspect_ratio BOOLEAN DEFAULT false;
29
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS display_aspect_ratio TEXT;
30
+
31
+ -- Add comments for documentation
32
+ COMMENT ON COLUMN line_item_inventories.ad_unit_code IS 'Ad unit code identifier';
33
+ COMMENT ON COLUMN line_item_inventories.reference_id IS 'External reference identifier';
34
+ COMMENT ON COLUMN line_item_inventories.device_id IS 'Device identifier';
35
+ COMMENT ON COLUMN line_item_inventories.country_iso2 IS 'ISO 3166-1 alpha-2 country code';
36
+ COMMENT ON COLUMN line_item_inventories.country_iso3 IS 'ISO 3166-1 alpha-3 country code';
37
+ COMMENT ON COLUMN line_item_inventories.venue_type_ids IS 'Array of venue type identifiers';
38
+ COMMENT ON COLUMN line_item_inventories.spots_per_hour IS 'Number of ad spots per hour';
39
+ COMMENT ON COLUMN line_item_inventories.spot_duration IS 'Duration of each spot in seconds';
40
+ COMMENT ON COLUMN line_item_inventories.clients IS 'Number of clients/advertisers';
41
+ COMMENT ON COLUMN line_item_inventories."group" IS 'Inventory grouping identifier';
42
+ COMMENT ON COLUMN line_item_inventories.bcat IS 'Blocked categories list [{name, code}]';
43
+ COMMENT ON COLUMN line_item_inventories.publisher_domain IS 'Publisher domain URL';
44
+ COMMENT ON COLUMN line_item_inventories.enable_aspect_ratio IS 'Whether aspect ratio constraints are enabled';
45
+ COMMENT ON COLUMN line_item_inventories.display_aspect_ratio IS 'Display aspect ratio (e.g., 16:9)';
@@ -0,0 +1,32 @@
1
+ -- Migration: Move inventory operational fields to metadata JSONB
2
+ -- Fields moved: spots_per_hour, spot_duration, clients, group
3
+ -- (transit was already stored in metadata, not as a column)
4
+ -- New fields added to metadata: networkId, packageId
5
+ -- This migration is idempotent - safe to run multiple times
6
+
7
+ -- Step 1: Migrate existing data from columns to metadata (only if columns exist)
8
+ DO $$
9
+ BEGIN
10
+ IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'line_item_inventories' AND column_name = 'spots_per_hour') THEN
11
+ EXECUTE '
12
+ UPDATE line_item_inventories
13
+ SET metadata = COALESCE(metadata, ''{}''::jsonb)
14
+ || jsonb_strip_nulls(jsonb_build_object(
15
+ ''spotsPerHour'', spots_per_hour,
16
+ ''spotDuration'', spot_duration,
17
+ ''clients'', clients,
18
+ ''group'', "group"
19
+ ))
20
+ WHERE spots_per_hour IS NOT NULL
21
+ OR spot_duration IS NOT NULL
22
+ OR clients IS NOT NULL
23
+ OR "group" IS NOT NULL
24
+ ';
25
+ END IF;
26
+ END $$;
27
+
28
+ -- Step 2: Drop the columns that are now in metadata
29
+ ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS spots_per_hour;
30
+ ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS spot_duration;
31
+ ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS clients;
32
+ ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS "group";
@@ -0,0 +1,23 @@
1
+ DO $$
2
+ BEGIN
3
+ IF NOT EXISTS (
4
+ SELECT 1 FROM pg_enum
5
+ WHERE enumlabel = 'DRAFT'
6
+ AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'enum_deals_status')
7
+ ) THEN
8
+ ALTER TYPE "enum_deals_status" ADD VALUE 'DRAFT' BEFORE 'REQUESTED';
9
+ END IF;
10
+ END$$;
11
+
12
+ DO $$
13
+ BEGIN
14
+ IF NOT EXISTS (
15
+ SELECT 1 FROM pg_enum
16
+ WHERE enumlabel = 'DRAFT'
17
+ AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'status_enum')
18
+ ) THEN
19
+ ALTER TYPE "status_enum" ADD VALUE 'DRAFT' BEFORE 'GENERATED';
20
+ END IF;
21
+ END$$;
22
+
23
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS line_items_count INTEGER DEFAULT 0;
@@ -0,0 +1,21 @@
1
+ -- Migration: Add planning field for DIRECT campaigns
2
+ -- The planning object contains capacity, allocation, estimates, and pricing data
3
+
4
+ -- Add planning column to deals table (aggregated from line items)
5
+ ALTER TABLE deals ADD COLUMN IF NOT EXISTS planning JSONB;
6
+
7
+ -- Add planning column to line_items table
8
+ ALTER TABLE line_items ADD COLUMN IF NOT EXISTS planning JSONB;
9
+
10
+ -- Add planning column to line_item_inventories table
11
+ ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS planning JSONB;
12
+
13
+ -- Create index for planning field on line_items (for querying DIRECT campaigns with planning data)
14
+ CREATE INDEX IF NOT EXISTS idx_line_items_planning ON line_items USING GIN (planning) WHERE planning IS NOT NULL;
15
+
16
+ -- Create index for planning field on line_item_inventories
17
+ CREATE INDEX IF NOT EXISTS idx_line_item_inventories_planning ON line_item_inventories USING GIN (planning) WHERE planning IS NOT NULL;
18
+
19
+ -- Comment on columns
20
+ COMMENT ON COLUMN line_items.planning IS 'Planning data for DIRECT campaigns: capacity, allocation, estimates, pricing';
21
+ COMMENT ON COLUMN line_item_inventories.planning IS 'Planning data for DIRECT inventory units: capacity, allocation, estimates, pricing';
@@ -0,0 +1,17 @@
1
+ -- Migration: Change line_item_inventories primary key to composite (id, line_item_id)
2
+ -- This allows the same inventory to be assigned to multiple line items
3
+
4
+ -- Step 1: Drop the existing primary key constraint
5
+ ALTER TABLE line_item_inventories DROP CONSTRAINT IF EXISTS line_item_inventories_pkey;
6
+
7
+ -- Step 2: Make line_item_id NOT NULL (required for composite PK)
8
+ ALTER TABLE line_item_inventories ALTER COLUMN line_item_id SET NOT NULL;
9
+
10
+ -- Step 3: Create composite primary key
11
+ ALTER TABLE line_item_inventories ADD PRIMARY KEY (id, line_item_id);
12
+
13
+ -- Step 4: Add index for faster lookups by inventory id alone
14
+ CREATE INDEX IF NOT EXISTS idx_line_item_inventories_id ON line_item_inventories(id);
15
+
16
+ -- Step 5: Add index for faster lookups by line_item_id
17
+ CREATE INDEX IF NOT EXISTS idx_line_item_inventories_line_item_id ON line_item_inventories(line_item_id);
@@ -0,0 +1,3 @@
1
+ -- Make external_id optional for deals and line_items
2
+ ALTER TABLE deals ALTER COLUMN external_id DROP NOT NULL;
3
+ ALTER TABLE line_items ALTER COLUMN external_id DROP NOT NULL;
@@ -0,0 +1,38 @@
1
+ -- Create change_history table for tracking all changes to deals and line items
2
+ DO $$ BEGIN
3
+ CREATE TYPE change_entity_type AS ENUM ('DEAL', 'LINE_ITEM');
4
+ EXCEPTION
5
+ WHEN duplicate_object THEN null;
6
+ END $$;
7
+
8
+ DO $$ BEGIN
9
+ CREATE TYPE change_action_type AS ENUM ('CREATE', 'UPDATE', 'DELETE', 'STATUS_CHANGE');
10
+ EXCEPTION
11
+ WHEN duplicate_object THEN null;
12
+ END $$;
13
+
14
+ CREATE TABLE IF NOT EXISTS change_history (
15
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
16
+ entity_type change_entity_type NOT NULL,
17
+ entity_id UUID NOT NULL,
18
+ deal_id TEXT,
19
+ line_item_id TEXT,
20
+ action change_action_type NOT NULL,
21
+ field_name TEXT,
22
+ previous_value JSONB,
23
+ current_value JSONB,
24
+ changed_by TEXT,
25
+ changed_by_email TEXT,
26
+ request_id TEXT,
27
+ ip_address TEXT,
28
+ user_agent TEXT,
29
+ summary TEXT,
30
+ metadata JSONB DEFAULT '{}',
31
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
32
+ );
33
+
34
+ CREATE INDEX IF NOT EXISTS idx_change_history_entity ON change_history(entity_type, entity_id);
35
+ CREATE INDEX IF NOT EXISTS idx_change_history_deal_id ON change_history(deal_id);
36
+ CREATE INDEX IF NOT EXISTS idx_change_history_line_item_id ON change_history(line_item_id);
37
+ CREATE INDEX IF NOT EXISTS idx_change_history_created_at ON change_history(created_at);
38
+ CREATE INDEX IF NOT EXISTS idx_change_history_action ON change_history(action);
@@ -0,0 +1,33 @@
1
+ -- Create publisher_insertion_orders table for minimal insertion order objects per publisher
2
+ -- Used for multiple publishers with common campaign budget
3
+
4
+ DO $$ BEGIN
5
+ CREATE TYPE insertion_order_status AS ENUM ('DRAFT', 'PENDING', 'APPROVED', 'LIVE', 'COMPLETED', 'ARCHIVED');
6
+ EXCEPTION
7
+ WHEN duplicate_object THEN null;
8
+ END $$;
9
+
10
+ CREATE TABLE IF NOT EXISTS publisher_insertion_orders (
11
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
12
+ deal_id TEXT NOT NULL REFERENCES deals(deal_id) ON DELETE CASCADE,
13
+ publisher_id TEXT NOT NULL,
14
+ publisher_name TEXT,
15
+ io_number TEXT,
16
+ status insertion_order_status DEFAULT 'DRAFT',
17
+ budget_amount DECIMAL(14, 2),
18
+ currency TEXT,
19
+ start_date DATE,
20
+ end_date DATE,
21
+ summary JSONB DEFAULT '{}',
22
+ external_sync_reference JSONB DEFAULT '{}',
23
+ published BOOLEAN DEFAULT false,
24
+ published_at TIMESTAMP WITH TIME ZONE,
25
+ metadata JSONB DEFAULT '{}',
26
+ version INTEGER DEFAULT 1,
27
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
28
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
29
+ );
30
+
31
+ CREATE INDEX IF NOT EXISTS idx_publisher_insertion_orders_deal_id ON publisher_insertion_orders(deal_id);
32
+ CREATE INDEX IF NOT EXISTS idx_publisher_insertion_orders_publisher_id ON publisher_insertion_orders(publisher_id);
33
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_publisher_insertion_orders_deal_publisher ON publisher_insertion_orders(deal_id, publisher_id);