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
package/PCM/README.md ADDED
@@ -0,0 +1,558 @@
1
+ # DOOH AdServer API
2
+
3
+ A Digital Out-of-Home (DOOH) AdServer microservice built with Node.js and Express.js for managing programmatic advertising deals, line items, and creative assets.
4
+
5
+ ## Overview
6
+
7
+ The DOOH AdServer is a RESTful API that manages programmatic advertising campaigns for digital out-of-home displays. It provides comprehensive endpoints for creating and managing deals with various transaction types, cost models, and creative formats, complete with automatic metric calculations, robust validation, and comprehensive audit trails.
8
+
9
+ ## Features
10
+
11
+ ### Deal Management
12
+ - **Campaign Modes**: PROGRAMMATIC and DIRECT modes for different campaign types
13
+ - **Multiple Deal Types**: GUARANTEED, PREFERRED_DEAL, PRIVATE_AUCTION, EVERGREEN_PMP (Programmatic), DIRECT (Direct mode)
14
+ - **Cost Models**: CPD (Cost Per Day), CPM (Cost Per Mille), CPS (Cost Per Second - non-guaranteed only)
15
+ - **Auto-calculated Metrics**: Deal-level metrics (delivery goal, net cost, dates, timezone) automatically aggregated from line items
16
+ - **Soft Delete (Archiving)**: Deals and line items are archived instead of deleted, preserving data integrity
17
+ - **Dual ID Support**: Access resources by internal UUID or external identifier
18
+ - **Conflict Detection**: Prevents duplicate deals based on source + external_id
19
+ - **Auto-generated Deal IDs**: Format {PREFIX}-I-{10-digit-sequence} with prefixes GD, PD, PA, EG, DIR
20
+
21
+ ### Line Item Management
22
+ - **Creative Types**: DISPLAY, VIDEO, AUDIO
23
+ - **Schedule Types**: ALL_TIME, ON_SCHEDULE with day/time preferences
24
+ - **Impression Types**: SPOT, AUDIENCE
25
+ - **Inventory Management**: Associate multiple inventory locations per line item
26
+ - **Creative Rotation**: EVEN, OPTIMIZED, MANUAL, SEQUENTIAL rotation types
27
+
28
+ ### Creative Asset Management
29
+ - **Multiple Formats**: Support for video, display, and audio creatives
30
+ - **Inventory Targeting**: Associate creatives with specific inventory IDs
31
+ - **Metadata Support**: Flexible JSONB metadata storage
32
+
33
+ ### API Features
34
+ - **Bulk Import/Sync**: Atomic insertion of complete deal structures with nested entities
35
+ - **Pagination**: All list endpoints support page-based pagination
36
+ - **Comprehensive Filtering**: Filter deals by search, mode, dealType, status, currency, brand, advertiser, seller, publisher, source, and date ranges
37
+ - **Request Tracking**: Every API request logged with unique request ID
38
+ - **Comprehensive Validation**: Joi schema validation for all payloads
39
+ - **Case-insensitive**: Accepts both camelCase and snake_case payloads
40
+ - **Nested Request/Response**: Mode-specific fields grouped under `direct` and `programmatic` objects
41
+ - **Swagger Documentation**: Interactive API documentation at `/api-docs`
42
+
43
+ ### Testing & Monitoring
44
+ - **Health Check**: Service health status and uptime monitoring
45
+ - **127 Total Tests**: 62 unit tests + 65 comprehensive tests with 100% pass rate
46
+ - **Test Categories**: Deal types, auction types, cost type restrictions, campaign modes, schedules, CRUD operations, date validation, timezone consistency, aggregation, bulk import, validation
47
+ - **Automated Test Endpoints**: Execute and view test results via API
48
+
49
+ ## Technology Stack
50
+
51
+ - **Runtime**: Node.js 20 with ES Modules
52
+ - **Framework**: Express.js 5
53
+ - **Database**: PostgreSQL 12+ (via Sequelize ORM 6)
54
+ - **Validation**: Joi
55
+ - **Documentation**: Swagger/OpenAPI 3.0
56
+ - **Key Features**: UUID primary keys, JSONB metadata, ENUM types, CASCADE deletes
57
+
58
+ ## Getting Started
59
+
60
+ ### Prerequisites
61
+
62
+ - Node.js 20 or higher
63
+ - PostgreSQL 12 or higher
64
+
65
+ ### Installation
66
+
67
+ 1. Clone the repository:
68
+ ```bash
69
+ git clone <repository-url>
70
+ cd dooh-adserver
71
+ ```
72
+
73
+ 2. Install dependencies:
74
+ ```bash
75
+ npm install
76
+ ```
77
+
78
+ 3. Set up environment variables:
79
+ ```bash
80
+ # PostgreSQL database connection
81
+ DATABASE_URL=postgresql://user:password@localhost:5432/dooh_adserver
82
+
83
+ # Optional: Server port (default: 5000)
84
+ PORT=5000
85
+ ```
86
+
87
+ 4. Initialize the database:
88
+ ```bash
89
+ # Database tables are automatically created on first run
90
+ npm start
91
+ ```
92
+
93
+ ### Running the Server
94
+
95
+ **Development mode** (with auto-reload):
96
+ ```bash
97
+ npm run dev
98
+ ```
99
+
100
+ **Production mode**:
101
+ ```bash
102
+ npm start
103
+ ```
104
+
105
+ The API will be available at `http://localhost:5000/api/v1/rest`
106
+
107
+ ## API Documentation
108
+
109
+ ### Base URL
110
+ ```
111
+ http://localhost:5000/api/v1/rest
112
+ ```
113
+
114
+ ### Interactive Documentation
115
+ Access the Swagger UI at:
116
+ ```
117
+ http://localhost:5000/api-docs
118
+ ```
119
+
120
+ ### Download API Specification
121
+ Download the complete OpenAPI 3.0 specification as JSON:
122
+ ```
123
+ http://localhost:5000/api-docs.json
124
+ ```
125
+
126
+ Or via curl:
127
+ ```bash
128
+ curl -O http://localhost:5000/api-docs.json
129
+ ```
130
+
131
+ ### Core Endpoints
132
+
133
+ #### Deals
134
+ - `GET /deals` - List all deals (paginated with filters)
135
+ - Query params: `search`, `source`, `mode`, `dealType`, `status`, `currency`, `brand`, `advertiser`, `seller`, `publisher`, `startDate`, `endDate`, `page`, `limit`
136
+ - `POST /deals` - Create a new deal
137
+ - `GET /deals/{dealId}` - Get deal by ID
138
+ - `PUT /deals/{dealId}` - Update deal
139
+ - `DELETE /deals/{dealId}` - Archive deal (soft delete)
140
+ - `POST /deals/sync` - Bulk import/sync deals with nested entities
141
+
142
+ #### Line Items
143
+ - `GET /deals/{dealId}/line-items` - List line items for a deal
144
+ - `POST /deals/{dealId}/line-items` - Create line item
145
+ - `GET /deals/{dealId}/line-items/{lineItemId}` - Get line item by ID
146
+ - `PUT /deals/{dealId}/line-items/{lineItemId}` - Update line item
147
+ - `DELETE /deals/{dealId}/line-items/{lineItemId}` - Archive line item (soft delete)
148
+
149
+ #### Creatives
150
+ - `GET /deals/{dealId}/line-items/{lineItemId}/creatives` - List creatives
151
+ - `POST /deals/{dealId}/line-items/{lineItemId}/creatives` - Create creative
152
+ - `GET /deals/{dealId}/line-items/{lineItemId}/creatives/{creativeId}` - Get creative
153
+ - `PUT /deals/{dealId}/line-items/{lineItemId}/creatives/{creativeId}` - Update creative
154
+ - `DELETE /deals/{dealId}/line-items/{lineItemId}/creatives/{creativeId}` - Delete creative
155
+
156
+ #### System & Testing
157
+ - `GET /health` - Service health check
158
+ - `GET /test-results` - List unit test cases (13 tests)
159
+ - `POST /test-results/run` - Execute unit test suite
160
+ - `GET /comprehensive-tests` - List comprehensive tests (33 tests)
161
+ - `POST /comprehensive-tests/run` - Execute comprehensive test suite
162
+
163
+ ### Example Requests
164
+
165
+ #### Create a Deal
166
+ ```bash
167
+ curl -X POST http://localhost:5000/api/v1/rest/deals \
168
+ -H "Content-Type: application/json" \
169
+ -d '{
170
+ "source": "Hivestack",
171
+ "externalId": "deal-001",
172
+ "dealId": "GD-00001-12345",
173
+ "name": "Tokyo Metro Campaign",
174
+ "dealType": "GUARANTEED",
175
+ "transactionType": "SPOT",
176
+ "auctionType": 3,
177
+ "currency": "JPY",
178
+ "costType": "CPD",
179
+ "status": "LIVE"
180
+ }'
181
+ ```
182
+
183
+ #### Create a Line Item
184
+ ```bash
185
+ curl -X POST http://localhost:5000/api/v1/rest/deals/GD-00001-12345/line-items \
186
+ -H "Content-Type: application/json" \
187
+ -d '{
188
+ "externalId": "li-001",
189
+ "name": "Morning Rush Hour",
190
+ "dealType": "GUARANTEED",
191
+ "auctionType": 3,
192
+ "creativeType": "VIDEO",
193
+ "duration": 15,
194
+ "impressionType": "SPOT",
195
+ "timezoneId": "Asia/Tokyo",
196
+ "startDate": "2025-10-01",
197
+ "endDate": "2025-10-15",
198
+ "thresholdCountPerDay": 50,
199
+ "deliveryGoal": 750,
200
+ "currency": "JPY",
201
+ "costType": "CPD",
202
+ "netCost": 3750,
203
+ "schedule": [
204
+ {
205
+ "type": "WEEKDAY",
206
+ "daysOfWeek": [1, 2, 3, 4, 5],
207
+ "hours": [{ "start": 7, "end": 9 }],
208
+ "validity": { "startDate": "2025-01-01", "endDate": "2025-12-31" }
209
+ }
210
+ ]
211
+ }'
212
+ ```
213
+
214
+ #### Bulk Import with Sync
215
+ ```bash
216
+ curl -X POST http://localhost:5000/api/v1/rest/deals/sync \
217
+ -H "Content-Type: application/json" \
218
+ -d '[{
219
+ "deal": {
220
+ "source": "Hivestack",
221
+ "external_id": "deal-bulk-001",
222
+ "deal_id": "GD-00002-56789",
223
+ "name": "Bulk Import Campaign",
224
+ "deal_type": "GUARANTEED",
225
+ "transaction_type": "SPOT",
226
+ "auction_type": 3,
227
+ "currency": "JPY",
228
+ "cost_type": "CPD",
229
+ "status": "LIVE"
230
+ },
231
+ "line_items": [{
232
+ "line_item": {
233
+ "external_id": "li-bulk-001",
234
+ "name": "Bulk Line Item",
235
+ "deal_type": "GUARANTEED",
236
+ "auction_type": 3,
237
+ "creative_type": "VIDEO",
238
+ "duration": 15,
239
+ "impression_type": "SPOT",
240
+ "timezone_id": "Asia/Tokyo",
241
+ "start_date": "2025-10-01",
242
+ "end_date": "2025-10-15",
243
+ "delivery_goal": 1000,
244
+ "currency": "JPY",
245
+ "cost_type": "CPD",
246
+ "net_cost": 5000,
247
+ "schedule": [
248
+ {
249
+ "type": "DEFAULT",
250
+ "hours": [{ "start": 0, "end": 23 }],
251
+ "validity": { "startDate": "2025-01-01", "endDate": "2025-12-31" }
252
+ }
253
+ ]
254
+ },
255
+ "inventories": [],
256
+ "creatives": []
257
+ }]
258
+ }]'
259
+ ```
260
+
261
+ ### Bulk Sync/Import Behavior
262
+
263
+ The `/deals/sync` endpoint provides intelligent upsert functionality for bulk operations:
264
+
265
+ #### Upsert Logic (Match & Update or Create)
266
+
267
+ - **Deals**: Matches by `(source + externalId)` unique constraint
268
+ - If match found → Updates existing deal
269
+ - If no match → Creates new deal
270
+
271
+ - **Line Items**: Matches by `(source + externalId)` unique constraint
272
+ - If match found → Updates existing line item
273
+ - If no match → Creates new line item
274
+
275
+ - **Creatives**: Matches by `(creativeId + dealId)` unique constraint
276
+ - If match found → Updates existing creative
277
+ - If no match → Creates new creative
278
+
279
+ - **Inventories**: Uses standard upsert by primary key
280
+
281
+ #### Primary Key Protection (Oct 2025 Fix)
282
+
283
+ The sync endpoint automatically protects database primary keys:
284
+
285
+ - If `id` field is present in sync payload, it is **excluded from update operations**
286
+ - This prevents primary key mutation which would break foreign key relationships
287
+ - Primary keys remain stable across sync operations
288
+ - Safe to include `id` in sync payloads - used for creation, ignored for updates
289
+
290
+ #### Transaction Safety
291
+
292
+ - Each sync item is processed in an atomic database transaction
293
+ - If any operation within an item fails, the entire item is rolled back
294
+ - Partial success: Some items may succeed while others fail
295
+ - Response includes detailed success/failure status for each item
296
+
297
+ ## Business Logic
298
+
299
+ ### Auto-calculated Deal Metrics
300
+
301
+ Deal-level metrics are automatically calculated from associated **active (non-archived)** line items:
302
+
303
+ - **startDate**: Earliest line item start date
304
+ - **endDate**: Latest line item end date
305
+ - **deliveryGoal**: Sum of line item delivery goals
306
+ - **netCost**: Sum of line item net costs
307
+ - **netCostPerDay**: Sum of line item net cost per day
308
+ - **impressions**: Sum of line item impressions
309
+ - **timezoneId**: First non-null timezone from line items
310
+
311
+ These fields are **read-only** at the deal level and recalculated automatically when line items are created, updated, or deleted.
312
+
313
+ ### Soft Delete (Archiving)
314
+
315
+ The API implements soft delete for data integrity:
316
+
317
+ - **Line Items**: DELETE operations set `status = 'ARCHIVED'` instead of removing the record
318
+ - **Deals**: DELETE operations set `status = 'ARCHIVED'`
319
+ - **Archived line items** are excluded from deal metric calculations
320
+ - **Creatives**: Hard delete (no status field per specification)
321
+
322
+ ### Validation Rules
323
+
324
+ - **Deal Types**: GUARANTEED, PREFERRED_DEAL, PRIVATE_AUCTION, EVERGREEN_PMP (Programmatic) and DIRECT (Direct mode)
325
+ - **Schedule Types**: Only ALL_TIME and ON_SCHEDULE supported
326
+ - **Date Ranges**: startDate must be ≤ endDate
327
+ - **Timezone Consistency**: All line items in a deal should use the same timezone
328
+ - **Required Fields**: Validated per endpoint specification
329
+ - **Unique Constraints**: source + externalId must be unique for deals
330
+
331
+ ### Response Format
332
+
333
+ All API responses follow this structure:
334
+
335
+ ```json
336
+ {
337
+ "requestId": "req-abc123",
338
+ "status": 200,
339
+ "result": {
340
+ "data": { /* resource data */ }
341
+ }
342
+ }
343
+ ```
344
+
345
+ Error responses include:
346
+ ```json
347
+ {
348
+ "code": "VALIDATION_ERROR",
349
+ "message": "Invalid request payload",
350
+ "errors": [
351
+ {
352
+ "field": "dealType",
353
+ "message": "dealType must be one of [GUARANTEED, PREFERRED_DEAL, PRIVATE_AUCTION, EVERGREEN_PMP]"
354
+ }
355
+ ]
356
+ }
357
+ ```
358
+
359
+ ## Testing
360
+
361
+ ### Run Comprehensive Test Suite
362
+
363
+ Execute all tests via API:
364
+ ```bash
365
+ curl -X POST http://localhost:5000/api/v1/rest/comprehensive-tests/run \
366
+ -H "Content-Type: application/json" \
367
+ -d '{"cleanup": true}'
368
+ ```
369
+
370
+ ### Test Categories
371
+
372
+ **Unit Tests (62 tests):**
373
+ - Deal types, auction type rules (IAB OpenRTB), cost type restrictions
374
+ - Campaign modes (PROGRAMMATIC/DIRECT), business rule validation
375
+ - Schedule types, date validation, field constraints
376
+
377
+ **Comprehensive Tests (65 tests):**
378
+ - DEAL_TYPES - All supported deal types + negative tests
379
+ - SCHEDULE_TYPES - ALL_TIME, ON_SCHEDULE validation + negative tests
380
+ - LINE_ITEM_CRUD - Create, Read, Update, Delete operations
381
+ - DATE_VALIDATION - Valid/invalid date ranges
382
+ - TIMEZONE_CONSISTENCY - Timezone consistency validation
383
+ - AGGREGATION - Auto-calculated deal metrics
384
+ - BULK_IMPORT - Valid and invalid bulk scenarios
385
+ - VALIDATION - Missing fields, auto-calculated field rejection
386
+
387
+ ### View Test Results
388
+
389
+ List all test cases:
390
+ ```bash
391
+ curl http://localhost:5000/api/v1/rest/comprehensive-tests
392
+ ```
393
+
394
+ Current test status: **127/127 tests passing (100% success rate)**
395
+
396
+ ## Database Schema
397
+
398
+ ### Core Tables
399
+
400
+ - **deals**: Deal master records with UUID primary keys
401
+ - **line_items**: Line item details with deal_id foreign key
402
+ - **line_item_inventories**: JSONB array of inventory locations
403
+ - **line_item_creatives**: Creative assets per line item
404
+ - **transaction_history**: Audit log of all API requests
405
+
406
+ ### Key Design Choices
407
+
408
+ - UUID primary keys for all tables
409
+ - `deal_id` as unique business identifier
410
+ - Foreign keys with CASCADE delete
411
+ - JSONB fields for flexible metadata
412
+ - ENUM types for constrained values
413
+ - Composite uniqueness on `source + external_id`
414
+
415
+ ## Campaign Approval Workflow (DIRECT Mode)
416
+
417
+ The API includes a multi-approver approval workflow for DIRECT mode campaigns with OTP authentication.
418
+
419
+ ### Workflow Overview
420
+
421
+ 1. **AdOps triggers approval**: `POST /deals/{dealId}/approval/request`
422
+ 2. **System behavior**:
423
+ - If `deal.approvalEmails` is populated → Full approval workflow with emails to approvers
424
+ - If `deal.approvalEmails` is empty/missing → Auto-approves immediately
425
+ 3. **Approvers receive emails** with encrypted approval URLs
426
+ 4. **Public approval portal** (no auth required):
427
+ - View campaign: `GET /api/v1/public/approval/campaign?token=<encrypted>`
428
+ - View line items: `GET /api/v1/public/approval/line-items?token=<encrypted>`
429
+ - View inventories: `GET /api/v1/public/approval/inventories?token=<encrypted>`
430
+ 5. **OTP verification**: Approvers must verify via OTP before submitting decision
431
+ 6. **Decision submission**: Approve or reject with optional reason
432
+ 7. **Status cascade**: Decision cascades to all line items automatically
433
+
434
+ ### Approval Criteria
435
+
436
+ - **ALL approvers must approve** for campaign to become APPROVED
437
+ - **ANY rejection** immediately sets status to REJECTED
438
+ - Line item status changes cascade automatically (archived items skipped)
439
+
440
+ ### Campaign Reopen
441
+
442
+ DIRECT campaigns can be reopened from: APPROVED, LIVE, COMPLETED, EXPIRED, REJECTED, ARCHIVED
443
+
444
+ ```bash
445
+ POST /deals/{dealId}/reopen
446
+ { "reason": "Need to update inventory" }
447
+ ```
448
+
449
+ Both deal and line items cascade to REOPENED status.
450
+
451
+ ## Environment Configuration
452
+
453
+ Copy `.env.example` to `.env` and configure the required values.
454
+
455
+ ### Required Variables
456
+
457
+ | Variable | Description | Example |
458
+ |----------|-------------|---------|
459
+ | `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
460
+ | `APPROVAL_SECRET_KEY` | Secret for encrypting tokens (32+ chars) | `your-secret-key-min-32-chars` |
461
+
462
+ ### Server Configuration
463
+
464
+ | Variable | Default | Description |
465
+ |----------|---------|-------------|
466
+ | `PORT` | `5000` | Server port |
467
+
468
+ ### Approval Workflow Configuration
469
+
470
+ | Variable | Default | Description |
471
+ |----------|---------|-------------|
472
+ | `APPROVAL_TOKEN_EXPIRY_DAYS` | `30` | Token expiry in days |
473
+ | `APPROVAL_BASE_URL` | `http://localhost:5000/approve` | Base URL for approval links |
474
+ | `OTP_EXPIRY_MINUTES` | `10` | OTP code expiry |
475
+ | `MAX_OTP_ATTEMPTS` | `5` | Max OTP verification attempts |
476
+ | `MAX_OTP_REQUESTS` | `10` | Max OTP requests per token |
477
+ | `OTP_COOLDOWN_SECONDS` | `60` | Cooldown between OTP requests |
478
+ | `DEV_OTP_CODE` | - | Development only: Predictable OTP |
479
+
480
+ ### Email Notification Configuration
481
+
482
+ | Variable | Default | Description |
483
+ |----------|---------|-------------|
484
+ | `MAIL_ENABLED` | `false` | Enable email sending |
485
+ | `NOTIFICATION_URL` | - | External notification service URL |
486
+ | `MAIL_FROM` | `notification@movingwalls.com` | Sender email address |
487
+ | `NOTIFICATION_TIMEOUT_MS` | `10000` | Request timeout (ms) |
488
+
489
+ ### Email Templates
490
+
491
+ | Variable | Default | Description |
492
+ |----------|---------|-------------|
493
+ | `TEMPLATE_APPROVAL_REQUEST` | `lmx_influence_rfa.tpl` | Approval request email |
494
+ | `TEMPLATE_OTP` | `lmx_EmailVerificationCode.tpl` | OTP verification email |
495
+ | `TEMPLATE_DECISION_APPROVED` | `lmx_influence_public_approved.tpl` | Approved notification |
496
+ | `TEMPLATE_DECISION_REJECTED` | `lmx_influence_public_rejected.tpl` | Rejected notification |
497
+
498
+ ### Email Branding
499
+
500
+ | Variable | Default | Description |
501
+ |----------|---------|-------------|
502
+ | `LAYOUT_COMPANY_NAME` | `Influence` | Company name in emails |
503
+ | `LAYOUT_ADDRESS` | - | Company address in footer |
504
+ | `LAYOUT_PHONE_NUMBER` | - | Company phone number |
505
+ | `LAYOUT_EMAIL` | - | Company contact email |
506
+ | `DIRECT_APPROVAL_LINK` | - | Base URL for approval links in emails |
507
+
508
+ ## Deployment
509
+
510
+ ### Production Deployment
511
+
512
+ The API is ready for deployment on platforms like:
513
+ - **Replit Deployments**: Configured for autoscale or VM deployment
514
+ - **Heroku**: Include Procfile with `node index.js`
515
+ - **AWS/GCP/Azure**: Deploy as container or serverless function
516
+ - **Docker**: Create Dockerfile with Node.js 20 base image
517
+
518
+ ### Performance Considerations
519
+
520
+ - Use connection pooling for PostgreSQL (already configured in Sequelize)
521
+ - Enable CORS for frontend integration
522
+ - Add rate limiting for production (not included in current version)
523
+ - Consider read replicas for high-traffic scenarios
524
+ - Enable query logging for debugging (set `logging: console.log` in Sequelize config)
525
+
526
+ ## API Versioning
527
+
528
+ Current version: **v1.0.0**
529
+
530
+ All endpoints are prefixed with `/api/v1/rest` to support future API versions.
531
+
532
+ ## Error Handling
533
+
534
+ The API returns standard HTTP status codes:
535
+
536
+ - **200**: Success
537
+ - **201**: Created
538
+ - **400**: Bad Request (validation error)
539
+ - **404**: Not Found
540
+ - **409**: Conflict (duplicate resource)
541
+ - **500**: Internal Server Error
542
+
543
+ All errors include a consistent error structure with code, message, and detailed field-level errors when applicable.
544
+
545
+ ## License
546
+
547
+ This project is proprietary software. All rights reserved.
548
+
549
+ ## Support
550
+
551
+ For API support or questions:
552
+ - Review the Swagger documentation at `/api-docs`
553
+ - Check the comprehensive test suite for usage examples
554
+ - Contact the development team for technical assistance
555
+
556
+ ---
557
+
558
+ **Built with ❤️ for programmatic DOOH advertising**