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,113 @@
1
+ import { useState, useEffect } from 'react';
2
+ import ReactMarkdown from 'react-markdown';
3
+ import remarkGfm from 'remark-gfm';
4
+ import { Card, CardContent, CardHeader, CardTitle } from '@moving-walls/design-system';
5
+ import { Loader2, FileText, RefreshCw } from 'lucide-react';
6
+ import { Button } from '@moving-walls/design-system';
7
+ import { usePageTitle } from '@/hooks/use-page-title';
8
+
9
+ export default function DemoStatusPage() {
10
+ usePageTitle('Demo Status');
11
+ const [content, setContent] = useState<string>('');
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState<string | null>(null);
14
+ const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
15
+
16
+ const fetchContent = async () => {
17
+ setLoading(true);
18
+ setError(null);
19
+ try {
20
+ const response = await fetch('/DEMO_STATUS.md');
21
+ if (!response.ok) {
22
+ throw new Error('Failed to load demo status document');
23
+ }
24
+ const text = await response.text();
25
+ setContent(text);
26
+ setLastUpdated(new Date());
27
+ } catch (err) {
28
+ setError(err instanceof Error ? err.message : 'Unknown error occurred');
29
+ } finally {
30
+ setLoading(false);
31
+ }
32
+ };
33
+
34
+ useEffect(() => {
35
+ fetchContent();
36
+ }, []);
37
+
38
+ if (loading) {
39
+ return (
40
+ <div className="min-h-screen flex items-center justify-center bg-mw-neutral-50">
41
+ <div className="flex flex-col items-center gap-4">
42
+ <Loader2 className="h-8 w-8 animate-spin text-mw-primary-500" />
43
+ <p className="text-mw-neutral-600">Loading demo status...</p>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
48
+
49
+ if (error) {
50
+ return (
51
+ <div className="min-h-screen flex items-center justify-center bg-mw-neutral-50 p-4">
52
+ <Card className="max-w-md w-full shadow-lg">
53
+ <CardContent className="pt-6">
54
+ <div className="flex flex-col items-center gap-4 text-center">
55
+ <div className="h-12 w-12 rounded-full bg-red-100 flex items-center justify-center">
56
+ <FileText className="h-6 w-6 text-red-600" />
57
+ </div>
58
+ <h1 className="text-xl font-semibold text-mw-neutral-900">Error Loading Document</h1>
59
+ <p className="text-mw-neutral-600">{error}</p>
60
+ <Button onClick={fetchContent} variant="outline">
61
+ <RefreshCw className="mr-2 h-4 w-4" />
62
+ Retry
63
+ </Button>
64
+ </div>
65
+ </CardContent>
66
+ </Card>
67
+ </div>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <div className="min-h-screen bg-mw-neutral-50">
73
+ <div className="border-b border-mw-neutral-200 bg-white sticky top-0 z-10">
74
+ <div className="max-w-5xl mx-auto px-4 sm:px-6 py-4">
75
+ <div className="flex items-center justify-between">
76
+ <div className="flex items-center gap-3">
77
+ <div className="h-10 w-10 rounded-lg bg-mw-primary-100 flex items-center justify-center">
78
+ <FileText className="h-5 w-5 text-mw-primary-600" />
79
+ </div>
80
+ <div>
81
+ <h1 className="text-xl font-semibold text-mw-neutral-900">Demo Status</h1>
82
+ <p className="text-sm text-mw-neutral-500">Ad Server Platform Documentation</p>
83
+ </div>
84
+ </div>
85
+ <div className="flex items-center gap-3">
86
+ {lastUpdated && (
87
+ <span className="text-xs text-mw-neutral-400">
88
+ Loaded: {lastUpdated.toLocaleTimeString()}
89
+ </span>
90
+ )}
91
+ <Button onClick={fetchContent} variant="outline" size="sm">
92
+ <RefreshCw className="mr-2 h-3 w-3" />
93
+ Refresh
94
+ </Button>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <div className="max-w-5xl mx-auto px-4 sm:px-6 py-8">
101
+ <Card className="shadow-sm">
102
+ <CardContent className="p-8">
103
+ <article className="prose prose-neutral max-w-none prose-headings:text-mw-neutral-900 prose-h1:text-2xl prose-h1:font-bold prose-h1:border-b prose-h1:pb-2 prose-h2:text-xl prose-h2:font-semibold prose-h2:mt-8 prose-h2:mb-4 prose-h3:text-lg prose-h3:font-medium prose-h4:text-base prose-p:text-mw-neutral-700 prose-li:text-mw-neutral-700 prose-strong:text-mw-neutral-900 prose-code:bg-mw-neutral-100 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono prose-pre:bg-mw-neutral-900 prose-pre:text-mw-neutral-100 prose-table:w-full prose-th:bg-mw-neutral-100 prose-th:px-4 prose-th:py-2 prose-th:text-left prose-th:font-medium prose-th:text-mw-neutral-700 prose-th:border prose-th:border-mw-neutral-200 prose-td:px-4 prose-td:py-2 prose-td:border prose-td:border-mw-neutral-200 prose-td:text-sm prose-a:text-mw-primary-600 prose-a:no-underline hover:prose-a:underline prose-hr:border-mw-neutral-200">
104
+ <ReactMarkdown remarkPlugins={[remarkGfm]}>
105
+ {content}
106
+ </ReactMarkdown>
107
+ </article>
108
+ </CardContent>
109
+ </Card>
110
+ </div>
111
+ </div>
112
+ );
113
+ }
@@ -0,0 +1,361 @@
1
+ import { useState } from "react";
2
+ import { useParams, useLocation } from "wouter";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { InfluenceDealsAPI, influenceDealsRequest, Deal, LineItem } from "@/lib/influence-deals-api";
5
+ import { Button, cn } from "@moving-walls/design-system";
6
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@moving-walls/design-system";
7
+ import { Badge } from "@moving-walls/design-system";
8
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@moving-walls/design-system";
9
+ import { ArrowLeft, Calendar, Building2, FileText, Monitor, Image, Send } from "lucide-react";
10
+ import { RequestApprovalSheet } from "@/components/deals/request-approval-sheet";
11
+ import { ApprovalHistoryPanel } from "@/components/deals/approval-history-panel";
12
+ import { usePageTitle } from "@/hooks/use-page-title";
13
+
14
+ export default function DirectCampaignDetails() {
15
+ usePageTitle("Campaign Details");
16
+ const { id } = useParams<{ id: string }>();
17
+ const [, setLocation] = useLocation();
18
+ const [isApprovalSheetOpen, setIsApprovalSheetOpen] = useState(false);
19
+
20
+ const { data: deal, isLoading } = useQuery<Deal>({
21
+ queryKey: ['direct-campaigns', id],
22
+ queryFn: async () => {
23
+ const url = InfluenceDealsAPI.deals.get(id!);
24
+ return await influenceDealsRequest<Deal>(url, 'GET');
25
+ },
26
+ enabled: !!id,
27
+ });
28
+
29
+ const { data: lineItemsData } = useQuery<{ data: LineItem[] }>({
30
+ queryKey: ['direct-campaigns', id, 'line-items'],
31
+ queryFn: async () => {
32
+ const url = InfluenceDealsAPI.lineItems.list(id!);
33
+ return await influenceDealsRequest<{ data: LineItem[] }>(url, 'GET');
34
+ },
35
+ enabled: !!id,
36
+ });
37
+
38
+ const lineItems = lineItemsData?.data || [];
39
+
40
+ if (isLoading) {
41
+ return (
42
+ <div className="flex items-center justify-center h-full">
43
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-mw-primary-500"></div>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ if (!deal) {
49
+ return (
50
+ <div className="flex flex-col items-center justify-center h-full gap-4">
51
+ <div className="text-mw-neutral-500 dark:text-mw-neutral-400">Deal not found</div>
52
+ <Button
53
+ onClick={() => setLocation("/deals")}
54
+ className="bg-mw-primary-500 hover:bg-mw-primary-600"
55
+ >
56
+ <ArrowLeft className="h-4 w-4 mr-2" />
57
+ Back to Deals
58
+ </Button>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ const getStatusColor = (status: string) => {
64
+ switch (status?.toUpperCase()) {
65
+ case 'LIVE':
66
+ case 'APPROVED':
67
+ return 'bg-mw-success-500 text-white border-mw-success-500';
68
+ case 'PENDING':
69
+ case 'DRAFT':
70
+ return 'bg-mw-neutral-100 text-mw-neutral-600 border-mw-neutral-200';
71
+ case 'GENERATED':
72
+ case 'REVIEWING':
73
+ return 'bg-purple-100 text-purple-600 border-purple-200';
74
+ case 'PAUSED':
75
+ return 'bg-mw-warning-500 text-white border-mw-warning-500';
76
+ case 'COMPLETED':
77
+ return 'bg-mw-info-500 text-white border-mw-info-500';
78
+ case 'ARCHIVED':
79
+ case 'REJECTED':
80
+ return 'bg-mw-error-500 text-white border-mw-error-500';
81
+ default:
82
+ return 'bg-mw-neutral-100 text-mw-neutral-600 border-mw-neutral-200';
83
+ }
84
+ };
85
+
86
+ const showRequestApprovalButton = deal?.status?.toUpperCase() === 'GENERATED';
87
+ const showApprovalHistory = ['PENDING', 'APPROVED', 'REJECTED', 'REVIEWING'].includes(deal?.status?.toUpperCase() || '');
88
+
89
+ return (
90
+ <div className="flex flex-col gap-6 p-6 bg-mw-neutral-50 dark:bg-mw-neutral-900 min-h-screen">
91
+ <div className="flex items-center justify-between gap-4 flex-wrap">
92
+ <div className="flex items-center gap-4">
93
+ <Button
94
+ variant="ghost"
95
+ size="sm"
96
+ onClick={() => setLocation("/deals")}
97
+ className="hover:bg-mw-neutral-100 dark:hover:bg-mw-neutral-800 p-2"
98
+ data-testid="button-back"
99
+ >
100
+ <ArrowLeft className="h-4 w-4" />
101
+ </Button>
102
+ <div>
103
+ <div className="flex items-center gap-3 mb-1 flex-wrap">
104
+ <h1 className="text-3xl font-bold tracking-tight text-mw-neutral-900 dark:text-white" data-testid="text-deal-name">
105
+ {deal.name}
106
+ </h1>
107
+ <Badge className={getStatusColor(deal.status)}>
108
+ {deal.status}
109
+ </Badge>
110
+ <Badge
111
+ variant="outline"
112
+ className="bg-mw-primary-50 text-mw-primary-700 border-mw-primary-100"
113
+ >
114
+ {deal.dealType?.replace(/_/g, ' ')}
115
+ </Badge>
116
+ {deal.source && (
117
+ <Badge
118
+ variant="secondary"
119
+ className="bg-mw-info-50 text-mw-info-700 border-mw-info-100"
120
+ data-testid="badge-source"
121
+ >
122
+ {deal.source}
123
+ </Badge>
124
+ )}
125
+ </div>
126
+ <div className="flex items-center gap-4 text-sm text-mw-neutral-600 dark:text-mw-neutral-400 flex-wrap">
127
+ {deal.advertiser && (
128
+ <div className="flex items-center gap-1">
129
+ <Building2 className="h-4 w-4" />
130
+ <span>{typeof deal.advertiser === 'object' ? (deal.advertiser as any).name : deal.advertiser}</span>
131
+ </div>
132
+ )}
133
+ <div>Currency: {deal.currency}</div>
134
+ <div>Country: {deal.country}</div>
135
+ {deal.externalId && (
136
+ <div>External ID: {deal.externalId}</div>
137
+ )}
138
+ {deal.createdAt && (
139
+ <div className="flex items-center gap-1">
140
+ <Calendar className="h-4 w-4" />
141
+ <span>Created: {new Date(deal.createdAt).toLocaleDateString()}</span>
142
+ </div>
143
+ )}
144
+ {deal.publishers && deal.publishers.length > 0 && (
145
+ <div>Publishers: {deal.publishers.map(p => p.name).join(', ')}</div>
146
+ )}
147
+ </div>
148
+ </div>
149
+ </div>
150
+
151
+ {showRequestApprovalButton && (
152
+ <Button
153
+ onClick={() => setIsApprovalSheetOpen(true)}
154
+ className="bg-mw-primary-500 hover:bg-mw-primary-600"
155
+ data-testid="button-request-approval"
156
+ >
157
+ <Send className="h-4 w-4 mr-2" />
158
+ Request Approval
159
+ </Button>
160
+ )}
161
+ </div>
162
+
163
+ {showApprovalHistory && (
164
+ <ApprovalHistoryPanel dealId={id!} />
165
+ )}
166
+
167
+ <Tabs defaultValue="overview" className="w-full">
168
+ <TabsList className="grid w-full grid-cols-3 lg:w-auto bg-white dark:bg-mw-neutral-800 border border-mw-neutral-100">
169
+ <TabsTrigger
170
+ value="overview"
171
+ data-testid="tab-overview"
172
+ className="data-[state=active]:bg-mw-primary-50 data-[state=active]:text-mw-primary-700"
173
+ >
174
+ Overview
175
+ </TabsTrigger>
176
+ <TabsTrigger
177
+ value="line-items"
178
+ data-testid="tab-line-items"
179
+ className="gap-2 data-[state=active]:bg-mw-primary-50 data-[state=active]:text-mw-primary-700"
180
+ >
181
+ <FileText className="h-4 w-4" />
182
+ <span>Line Items</span>
183
+ </TabsTrigger>
184
+ <TabsTrigger
185
+ value="creatives"
186
+ data-testid="tab-creatives"
187
+ className="gap-2 data-[state=active]:bg-mw-primary-50 data-[state=active]:text-mw-primary-700"
188
+ >
189
+ <Image className="h-4 w-4" />
190
+ <span>Creatives</span>
191
+ </TabsTrigger>
192
+ </TabsList>
193
+
194
+ <TabsContent value="overview" className="space-y-4 mt-6">
195
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
196
+ <Card className="border-mw-neutral-100">
197
+ <CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
198
+ <CardTitle className="text-sm font-medium text-mw-neutral-900 dark:text-white">Line Items</CardTitle>
199
+ <div className="h-8 w-8 rounded-lg bg-mw-success-50 dark:bg-mw-success-900/20 flex items-center justify-center">
200
+ <FileText className="h-4 w-4 text-mw-success-600 dark:text-mw-success-400" />
201
+ </div>
202
+ </CardHeader>
203
+ <CardContent>
204
+ <div className="text-2xl font-bold text-mw-neutral-900 dark:text-white">
205
+ {lineItems.length}
206
+ </div>
207
+ <p className="text-xs text-mw-neutral-600 dark:text-mw-neutral-400">
208
+ {lineItems.length === 1 ? "line item" : "line items"}
209
+ </p>
210
+ </CardContent>
211
+ </Card>
212
+
213
+ <Card className="border-mw-neutral-100">
214
+ <CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
215
+ <CardTitle className="text-sm font-medium text-mw-neutral-900 dark:text-white">Inventories</CardTitle>
216
+ <div className="h-8 w-8 rounded-lg bg-mw-info-50 dark:bg-mw-info-900/20 flex items-center justify-center">
217
+ <Monitor className="h-4 w-4 text-mw-info-600 dark:text-mw-info-400" />
218
+ </div>
219
+ </CardHeader>
220
+ <CardContent>
221
+ <div className="text-2xl font-bold text-mw-neutral-900 dark:text-white">
222
+ {lineItems.reduce((sum, li) => sum + (li.inventories?.length || 0), 0)}
223
+ </div>
224
+ <p className="text-xs text-mw-neutral-600 dark:text-mw-neutral-400">
225
+ assigned screens
226
+ </p>
227
+ </CardContent>
228
+ </Card>
229
+
230
+ <Card className="border-mw-neutral-100">
231
+ <CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
232
+ <CardTitle className="text-sm font-medium text-mw-neutral-900 dark:text-white">Total Budget</CardTitle>
233
+ <div className="h-8 w-8 rounded-lg bg-mw-warning-50 dark:bg-mw-warning-900/20 flex items-center justify-center">
234
+ <Building2 className="h-4 w-4 text-mw-warning-600 dark:text-mw-warning-400" />
235
+ </div>
236
+ </CardHeader>
237
+ <CardContent>
238
+ <div className="text-2xl font-bold text-mw-neutral-900 dark:text-white">
239
+ {deal.currency} {lineItems.reduce((sum, li) => sum + (li.budget || 0), 0).toLocaleString()}
240
+ </div>
241
+ <p className="text-xs text-mw-neutral-600 dark:text-mw-neutral-400">
242
+ across all line items
243
+ </p>
244
+ </CardContent>
245
+ </Card>
246
+ </div>
247
+
248
+ <Card className="border-mw-neutral-100">
249
+ <CardHeader className="bg-mw-neutral-50 dark:bg-mw-neutral-900">
250
+ <CardTitle className="text-mw-neutral-900 dark:text-white">Deal Information</CardTitle>
251
+ <CardDescription className="text-mw-neutral-500 dark:text-mw-neutral-400">
252
+ Basic deal details and metadata
253
+ </CardDescription>
254
+ </CardHeader>
255
+ <CardContent className="space-y-4 p-6 pt-0">
256
+ <div className="grid gap-4 md:grid-cols-2">
257
+ <div>
258
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Deal ID</div>
259
+ <div className="text-sm text-mw-neutral-900 dark:text-white font-mono">{deal.id}</div>
260
+ </div>
261
+ <div>
262
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Status</div>
263
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{deal.status}</div>
264
+ </div>
265
+ <div>
266
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Deal Type</div>
267
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{deal.dealType?.replace(/_/g, ' ')}</div>
268
+ </div>
269
+ <div>
270
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Currency</div>
271
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{deal.currency}</div>
272
+ </div>
273
+ <div>
274
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Country</div>
275
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{deal.country}</div>
276
+ </div>
277
+ {deal.createdAt && (
278
+ <div>
279
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Created At</div>
280
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{new Date(deal.createdAt).toLocaleString()}</div>
281
+ </div>
282
+ )}
283
+ {deal.updatedAt && (
284
+ <div>
285
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400">Updated At</div>
286
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{new Date(deal.updatedAt).toLocaleString()}</div>
287
+ </div>
288
+ )}
289
+ {deal.publishers && deal.publishers.length > 0 && (
290
+ <div className="md:col-span-2">
291
+ <div className="text-sm font-medium text-mw-neutral-600 dark:text-mw-neutral-400 mb-1">Publishers</div>
292
+ <div className="flex flex-wrap gap-2">
293
+ <div className="text-sm text-mw-neutral-900 dark:text-white">{deal.publishers.map(p => p.name).join(', ')}</div>
294
+ </div>
295
+ </div>
296
+ )}
297
+ </div>
298
+ </CardContent>
299
+ </Card>
300
+ </TabsContent>
301
+
302
+ <TabsContent value="line-items" className="space-y-4 mt-6">
303
+ <Card className="border-mw-neutral-100">
304
+ <CardHeader className="bg-mw-neutral-50 dark:bg-mw-neutral-900">
305
+ <CardTitle className="text-mw-neutral-900 dark:text-white">Line Items</CardTitle>
306
+ <CardDescription className="text-mw-neutral-500 dark:text-mw-neutral-400">
307
+ All line items in this deal
308
+ </CardDescription>
309
+ </CardHeader>
310
+ <CardContent className="p-6 pt-0">
311
+ {lineItems.length === 0 ? (
312
+ <div className="text-center py-8 text-mw-neutral-500">No line items found</div>
313
+ ) : (
314
+ <div className="space-y-3">
315
+ {lineItems.map((lineItem) => (
316
+ <Card key={lineItem.id} className="border-mw-neutral-100">
317
+ <CardContent className="p-4">
318
+ <div className="flex items-center justify-between">
319
+ <div>
320
+ <h4 className="font-medium">{lineItem.name}</h4>
321
+ <p className="text-sm text-mw-neutral-500">
322
+ {lineItem.startDate} - {lineItem.endDate} | Budget: {deal.currency} {lineItem.budget?.toLocaleString()}
323
+ </p>
324
+ </div>
325
+ <Badge>{lineItem.status}</Badge>
326
+ </div>
327
+ </CardContent>
328
+ </Card>
329
+ ))}
330
+ </div>
331
+ )}
332
+ </CardContent>
333
+ </Card>
334
+ </TabsContent>
335
+
336
+ <TabsContent value="creatives" className="space-y-4 mt-6">
337
+ <Card className="border-mw-neutral-100">
338
+ <CardHeader className="bg-mw-neutral-50 dark:bg-mw-neutral-900">
339
+ <CardTitle className="text-mw-neutral-900 dark:text-white">Creatives</CardTitle>
340
+ <CardDescription className="text-mw-neutral-500 dark:text-mw-neutral-400">
341
+ Creatives assigned to line items
342
+ </CardDescription>
343
+ </CardHeader>
344
+ <CardContent className="p-6 pt-0">
345
+ <div className="text-center py-8 text-mw-neutral-500">
346
+ Creatives are managed within each line item. View line items to see assigned creatives.
347
+ </div>
348
+ </CardContent>
349
+ </Card>
350
+ </TabsContent>
351
+ </Tabs>
352
+
353
+ <RequestApprovalSheet
354
+ dealId={id!}
355
+ open={isApprovalSheetOpen}
356
+ onOpenChange={setIsApprovalSheetOpen}
357
+ existingEmails={deal.approvalEmails}
358
+ />
359
+ </div>
360
+ );
361
+ }