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.
- package/.ci/staging.yml +191 -0
- package/.dockerignore +117 -0
- package/.env +40 -0
- package/.env.staging +38 -0
- package/.gitlab-ci.yml +16 -0
- package/DEMO_STATUS.md +579 -0
- package/Dockerfile +61 -0
- package/Influence-MW-AdServer-12-02-2026/client/index.html +17 -0
- package/Influence-MW-AdServer-12-02-2026/client/public/favicon.png +0 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/App.tsx +91 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/advanced-map-drawer.tsx +1131 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ai-recommendation-panel.tsx +379 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/app-sidebar.tsx +183 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/auto-optimize-button.tsx +184 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/availability-drawer.tsx +385 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/brand-insights-panel.tsx +87 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/create-agency-drawer.tsx +198 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/create-brand-drawer.tsx +275 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/creative-assignment.tsx +526 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/data-table-toolbar.tsx +148 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/data-table.tsx +158 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/filter-drawer.tsx +356 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/form-insights-panel.tsx +82 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/geography-selector.tsx +699 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/header-user-menu.tsx +178 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/history-drawer.tsx +313 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-availability-section.tsx +176 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-format-drawer.tsx +173 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/inventory-selector.tsx +401 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/manual-inventory-drawer.tsx +368 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/mapbox-map.tsx +368 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/market-insights-panel.tsx +202 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/media-owner-drawer.tsx +217 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/metric-card.tsx +58 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/page-header.tsx +27 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/player-status-indicator.tsx +137 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/poi-targeting-drawer.tsx +298 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/recommendation-score-badge.tsx +102 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/recommended-inventories-panel.tsx +248 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/searchable-combobox.tsx +134 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/signal-visualizations.tsx +407 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/status-badge.tsx +35 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/theme-provider.tsx +73 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/theme-toggle.tsx +37 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/traffic-slider.tsx +75 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/accordion.tsx +56 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/alert-dialog.tsx +139 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/alert.tsx +59 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/aspect-ratio.tsx +5 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/avatar.tsx +51 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/badge.tsx +38 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/breadcrumb.tsx +115 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/button.tsx +62 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/calendar.tsx +68 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/card.tsx +85 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/carousel.tsx +260 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/chart.tsx +365 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/checkbox.tsx +28 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/collapsible.tsx +11 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/command.tsx +151 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/context-menu.tsx +198 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/dialog.tsx +122 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/drawer.tsx +118 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/dropdown-menu.tsx +198 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/form.tsx +178 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/hover-card.tsx +29 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/input-otp.tsx +69 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/input.tsx +23 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/label.tsx +24 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/menubar.tsx +256 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/navigation-menu.tsx +128 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/pagination.tsx +117 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/popover.tsx +29 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/progress.tsx +28 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/radio-group.tsx +42 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/resizable.tsx +45 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/scroll-area.tsx +46 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/select.tsx +160 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/separator.tsx +29 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/sheet.tsx +140 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/sidebar.tsx +727 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/skeleton.tsx +15 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/slider.tsx +26 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/switch.tsx +27 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/table.tsx +117 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/tabs.tsx +53 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/textarea.tsx +22 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toast.tsx +127 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toaster.tsx +33 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toggle-group.tsx +61 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/toggle.tsx +43 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/ui/tooltip.tsx +30 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/vendor-stores-modal.tsx +336 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/venue-type-drawer.tsx +359 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/components/venue-type-selector.tsx +436 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/hooks/use-mobile.tsx +19 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/hooks/use-toast.ts +191 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/index.css +244 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/lib/queryClient.ts +57 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/lib/utils.ts +39 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/lib/venue-taxonomy.ts +532 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/main.tsx +5 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/assign-creative.tsx +781 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/content-hub.tsx +995 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/custom-pois.tsx +431 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/dashboard.tsx +620 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/deal-detail.tsx +1062 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/deal-form.tsx +1570 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/deals.tsx +716 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/edit-creative-assignment.tsx +1051 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/geotargeting.tsx +675 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/integrations.tsx +425 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-item-creatives.tsx +622 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-item-form.tsx +3132 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/line-items.tsx +530 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/not-found.tsx +21 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/proof-of-play-upload.tsx +479 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/proof-of-play.tsx +880 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/reports.tsx +235 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/settings.tsx +652 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/signal-form.tsx +1117 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/signals.tsx +366 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/tags.tsx +332 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/pages/venues.tsx +381 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/types/mapbox-gl-draw.d.ts +37 -0
- package/Influence-MW-AdServer-12-02-2026/client/src/types/react-simple-maps.d.ts +57 -0
- package/Influence-MW-AdServer-12-02-2026/components.json +20 -0
- package/Influence-MW-AdServer-12-02-2026/docs/PRD.md +3373 -0
- package/Influence-MW-AdServer-12-02-2026/docs/influence-feature-mapping.csv +498 -0
- package/Influence-MW-AdServer-12-02-2026/drizzle.config.ts +14 -0
- package/Influence-MW-AdServer-12-02-2026/package-lock.json +9672 -0
- package/Influence-MW-AdServer-12-02-2026/package.json +118 -0
- package/Influence-MW-AdServer-12-02-2026/postcss.config.js +6 -0
- package/Influence-MW-AdServer-12-02-2026/replit.md +91 -0
- package/Influence-MW-AdServer-12-02-2026/script/build.ts +67 -0
- package/Influence-MW-AdServer-12-02-2026/scripts/create-miro-diagrams.cjs +318 -0
- package/Influence-MW-AdServer-12-02-2026/scripts/create-remaining-diagrams.cjs +270 -0
- package/Influence-MW-AdServer-12-02-2026/server/index.ts +103 -0
- package/Influence-MW-AdServer-12-02-2026/server/recommendation-service.ts +319 -0
- package/Influence-MW-AdServer-12-02-2026/server/routes.ts +1890 -0
- package/Influence-MW-AdServer-12-02-2026/server/static.ts +19 -0
- package/Influence-MW-AdServer-12-02-2026/server/storage.ts +2058 -0
- package/Influence-MW-AdServer-12-02-2026/server/vite.ts +58 -0
- package/Influence-MW-AdServer-12-02-2026/shared/schema.ts +1595 -0
- package/Influence-MW-AdServer-12-02-2026/tailwind.config.ts +107 -0
- package/Influence-MW-AdServer-12-02-2026/tsconfig.json +23 -0
- package/Influence-MW-AdServer-12-02-2026/vite.config.ts +40 -0
- package/LINE_ITEM_BUDGET_FIELD_MAPPING.md +178 -0
- package/PCM/.env.example +92 -0
- package/PCM/README.md +558 -0
- package/PCM/docs/TEST_CASES.md +422 -0
- package/PCM/index.js +106 -0
- package/PCM/package-lock.json +3282 -0
- package/PCM/package.json +32 -0
- package/PCM/replit.md +64 -0
- package/PCM/schema.sql +495 -0
- package/PCM/scripts/export-schema.js +183 -0
- package/PCM/scripts/seed-comprehensive.js +631 -0
- package/PCM/scripts/seed-production.js +477 -0
- package/PCM/src/config/db.js +56 -0
- package/PCM/src/config/swagger.js +5975 -0
- package/PCM/src/dto/EmailRequestDTO.js +166 -0
- package/PCM/src/middleware/errorHandler.js +52 -0
- package/PCM/src/middleware/logger.js +26 -0
- package/PCM/src/migrations/001_add_campaign_mode_fields.sql +36 -0
- package/PCM/src/migrations/002_create_deal_id_counters.sql +22 -0
- package/PCM/src/migrations/003_update_publishers_column.sql +15 -0
- package/PCM/src/migrations/004_add_direct_dealtype_and_advertiser.sql +5 -0
- package/PCM/src/migrations/005_add_programmatic_fields_and_update_enums.sql +31 -0
- package/PCM/src/migrations/006_add_line_item_programmatic_fields.sql +12 -0
- package/PCM/src/migrations/007_add_line_item_direct_fields.sql +15 -0
- package/PCM/src/migrations/008_add_inventory_fields.sql +45 -0
- package/PCM/src/migrations/009_move_inventory_fields_to_metadata.sql +32 -0
- package/PCM/src/migrations/010_add_draft_status_and_line_items_count.sql +23 -0
- package/PCM/src/migrations/011_add_planning_field.sql +21 -0
- package/PCM/src/migrations/012_fix_inventory_composite_pk.sql +17 -0
- package/PCM/src/migrations/013_make_external_id_optional.sql +3 -0
- package/PCM/src/migrations/014_create_change_history.sql +38 -0
- package/PCM/src/migrations/016_create_publisher_insertion_orders.sql +33 -0
- package/PCM/src/migrations/017_fix_line_item_id_fk_reference.sql +86 -0
- package/PCM/src/migrations/018_create_approval_tables.sql +44 -0
- package/PCM/src/migrations/019_add_encrypted_token_column.sql +2 -0
- package/PCM/src/migrations/020_add_rejection_reason_to_deals.sql +10 -0
- package/PCM/src/migrations/021_add_publisher_external_id_to_inventories.sql +12 -0
- package/PCM/src/migrations/022_add_line_item_extended_fields.sql +24 -0
- package/PCM/src/migrations/023_add_base_price_fields.sql +8 -0
- package/PCM/src/migrations/run-migrations.js +46 -0
- package/PCM/src/models/ApprovalOTP.js +51 -0
- package/PCM/src/models/ApprovalToken.js +79 -0
- package/PCM/src/models/ChangeHistory.js +107 -0
- package/PCM/src/models/Deal.js +186 -0
- package/PCM/src/models/DealIdCounter.js +28 -0
- package/PCM/src/models/LineItem.js +227 -0
- package/PCM/src/models/LineItemCreative.js +89 -0
- package/PCM/src/models/LineItemInventory.js +115 -0
- package/PCM/src/models/PublisherInsertionOrder.js +93 -0
- package/PCM/src/models/TransactionHistory.js +34 -0
- package/PCM/src/models/associations.js +81 -0
- package/PCM/src/routes/approval.js +321 -0
- package/PCM/src/routes/creatives.js +437 -0
- package/PCM/src/routes/deals.js +1638 -0
- package/PCM/src/routes/digitalSignage.js +242 -0
- package/PCM/src/routes/insertionOrders.js +380 -0
- package/PCM/src/routes/lineItems.js +926 -0
- package/PCM/src/routes/system.js +384 -0
- package/PCM/src/services/ApprovalService.js +885 -0
- package/PCM/src/services/CampaignImportConverter.js +631 -0
- package/PCM/src/services/CampaignModeService.js +273 -0
- package/PCM/src/services/CampaignStatusService.js +395 -0
- package/PCM/src/services/ChangeHistoryService.js +316 -0
- package/PCM/src/services/DealIdService.js +94 -0
- package/PCM/src/services/DealResponseFormatter.js +90 -0
- package/PCM/src/services/EmailNotificationService.js +315 -0
- package/PCM/src/services/LineItemResponseFormatter.js +122 -0
- package/PCM/src/services/LineItemStatusService.js +380 -0
- package/PCM/src/tests/comprehensiveTestRunner.js +360 -0
- package/PCM/src/tests/comprehensiveTests.js +1277 -0
- package/PCM/src/tests/dealTypeUnitTests.js +1058 -0
- package/PCM/src/tests/testRunner.js +248 -0
- package/PCM/src/utils/caseConverter.js +92 -0
- package/PCM/src/utils/dealCalculations.js +206 -0
- package/PCM/src/utils/lineItemPayloadNormalizer.js +41 -0
- package/PCM/src/utils/payloadNormalizer.js +34 -0
- package/PCM/src/utils/sourceNormalizer.js +56 -0
- package/PCM/src/validators/creativeValidator.js +27 -0
- package/PCM/src/validators/dealValidator.js +203 -0
- package/PCM/src/validators/lineItemValidator.js +489 -0
- package/PCM/tests/approval-flows.test.js +238 -0
- package/PCM/tests/approval-workflow.test.js +291 -0
- package/PCM/tests/campaign-import-converter.test.js +543 -0
- package/PCM/tests/campaign-import-e2e.test.js +520 -0
- package/PCM/tests/campaign-status.test.js +539 -0
- package/PCM/tests/direct-publisher-split-reimport.test.js +460 -0
- package/PCM/tests/e2e/digital-signage.test.js +145 -0
- package/PCM/tests/e2e/search-filter-pagination.test.js +399 -0
- package/PCM/tests/e2e-comprehensive.test.js +3446 -0
- package/PCM/tests/edge-cases.test.js +340 -0
- package/PCM/tests/line-item-status.test.js +340 -0
- package/PCM/tests/seller-account-external-ids.test.js +877 -0
- package/PCM/tests/source-validation.test.js +324 -0
- package/PRD.md +3373 -0
- package/README.md +186 -0
- package/client/index.html +35 -0
- package/client/public/DEMO_STATUS.md +579 -0
- package/client/public/img/MW-logo-trans_1754045676555.png +0 -0
- package/client/public/locales/ar/approval.json +144 -0
- package/client/public/locales/ar/buyer.json +61 -0
- package/client/public/locales/ar/campaigns.json +1 -0
- package/client/public/locales/ar/common.json +218 -0
- package/client/public/locales/ar/contentHub.json +266 -0
- package/client/public/locales/ar/creatives.json +79 -0
- package/client/public/locales/ar/dashboard.json +57 -0
- package/client/public/locales/ar/deals.json +886 -0
- package/client/public/locales/ar/dsp.json +131 -0
- package/client/public/locales/ar/inventory.json +201 -0
- package/client/public/locales/ar/lineItems.json +553 -0
- package/client/public/locales/ar/navigation.json +48 -0
- package/client/public/locales/ar/wizard.json +1 -0
- package/client/public/locales/en/approval.json +144 -0
- package/client/public/locales/en/buyer.json +65 -0
- package/client/public/locales/en/campaigns.json +1 -0
- package/client/public/locales/en/common.json +218 -0
- package/client/public/locales/en/contentHub.json +266 -0
- package/client/public/locales/en/creatives.json +79 -0
- package/client/public/locales/en/dashboard.json +57 -0
- package/client/public/locales/en/deals.json +886 -0
- package/client/public/locales/en/dsp.json +131 -0
- package/client/public/locales/en/inventory.json +201 -0
- package/client/public/locales/en/lineItems.json +659 -0
- package/client/public/locales/en/navigation.json +48 -0
- package/client/public/locales/en/wizard.json +1 -0
- package/client/public/locales/ja/approval.json +144 -0
- package/client/public/locales/ja/buyer.json +61 -0
- package/client/public/locales/ja/campaigns.json +1 -0
- package/client/public/locales/ja/common.json +218 -0
- package/client/public/locales/ja/contentHub.json +266 -0
- package/client/public/locales/ja/creatives.json +79 -0
- package/client/public/locales/ja/dashboard.json +57 -0
- package/client/public/locales/ja/deals.json +886 -0
- package/client/public/locales/ja/dsp.json +131 -0
- package/client/public/locales/ja/inventory.json +201 -0
- package/client/public/locales/ja/lineItems.json +553 -0
- package/client/public/locales/ja/navigation.json +48 -0
- package/client/public/locales/ja/wizard.json +1 -0
- package/client/public/locales/zh/approval.json +144 -0
- package/client/public/locales/zh/buyer.json +61 -0
- package/client/public/locales/zh/campaigns.json +1 -0
- package/client/public/locales/zh/common.json +218 -0
- package/client/public/locales/zh/contentHub.json +266 -0
- package/client/public/locales/zh/creatives.json +79 -0
- package/client/public/locales/zh/dashboard.json +57 -0
- package/client/public/locales/zh/deals.json +886 -0
- package/client/public/locales/zh/dsp.json +131 -0
- package/client/public/locales/zh/inventory.json +201 -0
- package/client/public/locales/zh/lineItems.json +553 -0
- package/client/public/locales/zh/navigation.json +48 -0
- package/client/public/locales/zh/wizard.json +1 -0
- package/client/public/manifest.json +36 -0
- package/client/src/App.tsx +464 -0
- package/client/src/components/app-sidebar.tsx +312 -0
- package/client/src/components/approval/approval-decision-form.test.tsx +294 -0
- package/client/src/components/approval/approval-decision-form.tsx +326 -0
- package/client/src/components/approval/approval-sheet.tsx +631 -0
- package/client/src/components/approval/line-item-details-sheet.tsx +371 -0
- package/client/src/components/approval/otp-verification.test.tsx +337 -0
- package/client/src/components/approval/otp-verification.tsx +180 -0
- package/client/src/components/content-hub/bulk-transcode-dialog.tsx +379 -0
- package/client/src/components/content-hub/content-hub-manager-v2.tsx +574 -0
- package/client/src/components/content-hub/content-hub-manager.tsx +330 -0
- package/client/src/components/content-hub/creative-card.tsx +456 -0
- package/client/src/components/content-hub/creative-detail-sheet.tsx +685 -0
- package/client/src/components/content-hub/creative-filters.tsx +457 -0
- package/client/src/components/content-hub/creative-grid.tsx +329 -0
- package/client/src/components/content-hub/creative-selector.tsx +415 -0
- package/client/src/components/content-hub/creative-upload.tsx +547 -0
- package/client/src/components/content-hub/folder-dialogs.tsx +445 -0
- package/client/src/components/content-hub/folder-list.tsx +280 -0
- package/client/src/components/content-hub/review-dialogs.tsx +268 -0
- package/client/src/components/content-hub/transcode-dialog.tsx +226 -0
- package/client/src/components/creative-library/creative-details-view.tsx +446 -0
- package/client/src/components/creative-library/creative-filters-panel.tsx +203 -0
- package/client/src/components/creative-library/creative-list.tsx +360 -0
- package/client/src/components/creative-library/creative-status-badge.tsx +71 -0
- package/client/src/components/creative-library/folder-card.tsx +78 -0
- package/client/src/components/creative-library/index.ts +27 -0
- package/client/src/components/creative-library/new-creative-card.tsx +211 -0
- package/client/src/components/creative-library/upload-creative-dialog.tsx +261 -0
- package/client/src/components/dashboard-overview.tsx +109 -0
- package/client/src/components/deals/approval-history-panel.test.tsx +240 -0
- package/client/src/components/deals/approval-history-panel.tsx +156 -0
- package/client/src/components/deals/deal-status-badge.tsx +92 -0
- package/client/src/components/deals/import-from-planner-dialog.tsx +399 -0
- package/client/src/components/deals/market-insights-panel.tsx +237 -0
- package/client/src/components/deals/reopen-deal-sheet.tsx +191 -0
- package/client/src/components/deals/request-approval-sheet.test.tsx +323 -0
- package/client/src/components/deals/request-approval-sheet.tsx +136 -0
- package/client/src/components/deals/resend-approval-sheet.tsx +201 -0
- package/client/src/components/direct-campaigns/campaign-card.tsx +283 -0
- package/client/src/components/direct-campaigns/deal-filter-panel.tsx +325 -0
- package/client/src/components/inventory/advanced-filters-panel.tsx +273 -0
- package/client/src/components/inventory/csv-upload-modal.tsx +639 -0
- package/client/src/components/inventory/inventory-availability-view.tsx +486 -0
- package/client/src/components/inventory/inventory-details-sheet.tsx +376 -0
- package/client/src/components/inventory/inventory-map-view.tsx +596 -0
- package/client/src/components/inventory/inventory-settings-menu.tsx +52 -0
- package/client/src/components/language-switcher.tsx +53 -0
- package/client/src/components/line-items/campaign-forecast-panel.tsx +138 -0
- package/client/src/components/line-items/form-insights.tsx +89 -0
- package/client/src/components/line-items/geofencing/LocationCsvUploadDrawer.tsx +100 -0
- package/client/src/components/line-items/geofencing/POIDropdown.tsx +379 -0
- package/client/src/components/line-items/geofencing/SelectedLocationsSidebar.tsx +436 -0
- package/client/src/components/line-items/geofencing/ViewFileLocationDrawer.tsx +199 -0
- package/client/src/components/line-items/geofencing/components/ExistingFilesTab.tsx +268 -0
- package/client/src/components/line-items/geofencing/components/TemplateDownloadSection.tsx +59 -0
- package/client/src/components/line-items/geofencing/components/UploadTab.tsx +215 -0
- package/client/src/components/line-items/geofencing-map.tsx +1270 -0
- package/client/src/components/line-items/inventory-availability-section.tsx +178 -0
- package/client/src/components/line-items/line-item-schedule-manager.tsx +313 -0
- package/client/src/components/line-items/manual-inventory-drawer.tsx +346 -0
- package/client/src/components/line-items/planner-inventory-card.tsx +495 -0
- package/client/src/components/line-items/planner-schedule-grid.tsx +495 -0
- package/client/src/components/line-items/schedule-rule-editor.tsx +649 -0
- package/client/src/components/line-items/schedule-rule-types.ts +122 -0
- package/client/src/components/line-items/steps/creatives-step.tsx +681 -0
- package/client/src/components/line-items/steps/inventory-schedule-step.tsx +1596 -0
- package/client/src/components/line-items/steps/inventory-step.tsx +1533 -0
- package/client/src/components/line-items/steps/line-item-details-step.tsx +916 -0
- package/client/src/components/line-items/steps/schedule-step.tsx +273 -0
- package/client/src/components/line-items/steps/summary-step.tsx +680 -0
- package/client/src/components/line-items/steps/targeting-step.tsx +1708 -0
- package/client/src/components/product-switcher.tsx +105 -0
- package/client/src/components/protected-route.tsx +49 -0
- package/client/src/components/skip-link.tsx +22 -0
- package/client/src/components/stat-card.tsx +53 -0
- package/client/src/components/status-badge.tsx +96 -0
- package/client/src/components/ui/hierarchical-venue-selector.tsx +389 -0
- package/client/src/components/ui/toaster.tsx +111 -0
- package/client/src/contexts/auth-context.tsx +181 -0
- package/client/src/contexts/sidebar-state.tsx +50 -0
- package/client/src/contexts/theme-context.tsx +66 -0
- package/client/src/data/campaign-data.json +107 -0
- package/client/src/data/countries.json +22 -0
- package/client/src/hooks/use-approval.ts +366 -0
- package/client/src/hooks/use-keyboard-shortcuts.ts +74 -0
- package/client/src/hooks/use-media-query.ts +46 -0
- package/client/src/hooks/use-mobile.tsx +19 -0
- package/client/src/hooks/use-page-title.ts +21 -0
- package/client/src/hooks/use-toast.ts +195 -0
- package/client/src/index.css +694 -0
- package/client/src/lib/__tests__/accessibility.test.ts +104 -0
- package/client/src/lib/__tests__/date-utils.test.ts +199 -0
- package/client/src/lib/__tests__/dsp-buyer-api.test.ts +127 -0
- package/client/src/lib/__tests__/dsp-buyer-integration.test.ts +247 -0
- package/client/src/lib/__tests__/storage-utils.test.ts +167 -0
- package/client/src/lib/__tests__/utils.test.ts +57 -0
- package/client/src/lib/accessibility.ts +141 -0
- package/client/src/lib/api-config.ts +9 -0
- package/client/src/lib/auth-service.ts +209 -0
- package/client/src/lib/campaign-creative-api.ts +82 -0
- package/client/src/lib/company-api.ts +61 -0
- package/client/src/lib/content-hub-api.ts +407 -0
- package/client/src/lib/creative-mapper.ts +61 -0
- package/client/src/lib/date-utils.ts +119 -0
- package/client/src/lib/deal-helpers.ts +220 -0
- package/client/src/lib/dsp-buyer-api.ts +196 -0
- package/client/src/lib/geo-import-api.ts +151 -0
- package/client/src/lib/google-poi-api.ts +305 -0
- package/client/src/lib/i18n/__tests__/formatting.test.ts +202 -0
- package/client/src/lib/i18n/formatting.ts +130 -0
- package/client/src/lib/i18n/index.ts +8 -0
- package/client/src/lib/i18n-compat.ts +76 -0
- package/client/src/lib/influence-deals-api.ts +896 -0
- package/client/src/lib/inventory-api.ts +399 -0
- package/client/src/lib/oauth-service.ts +678 -0
- package/client/src/lib/poi-types.ts +75 -0
- package/client/src/lib/queryClient.ts +144 -0
- package/client/src/lib/recommendation-api.ts +380 -0
- package/client/src/lib/storage-utils.ts +104 -0
- package/client/src/lib/tolgee.ts +85 -0
- package/client/src/lib/utils.ts +0 -0
- package/client/src/main.tsx +67 -0
- package/client/src/mapbox-draw-modes.d.ts +32 -0
- package/client/src/pages/all-folders.tsx +203 -0
- package/client/src/pages/auth-callback.tsx +115 -0
- package/client/src/pages/buyer-form.tsx +339 -0
- package/client/src/pages/buyer-list.tsx +622 -0
- package/client/src/pages/content-hub.tsx +1358 -0
- package/client/src/pages/create-deal.tsx +2093 -0
- package/client/src/pages/creative-assignment-page.tsx +548 -0
- package/client/src/pages/creatives.tsx +5 -0
- package/client/src/pages/custom-pois.tsx +425 -0
- package/client/src/pages/dashboard.tsx +615 -0
- package/client/src/pages/deal-history.tsx +434 -0
- package/client/src/pages/deal-line-items.tsx +1703 -0
- package/client/src/pages/demo-status.tsx +113 -0
- package/client/src/pages/direct-campaign-details.tsx +361 -0
- package/client/src/pages/direct-campaigns-new.tsx +824 -0
- package/client/src/pages/dsp-form.tsx +803 -0
- package/client/src/pages/dsp-list.tsx +239 -0
- package/client/src/pages/folder-content.tsx +336 -0
- package/client/src/pages/integrations.tsx +429 -0
- package/client/src/pages/line-item-creatives.tsx +789 -0
- package/client/src/pages/line-item-detail-page.tsx +684 -0
- package/client/src/pages/line-item-form-page.tsx +3261 -0
- package/client/src/pages/line-item-wizard.tsx +1207 -0
- package/client/src/pages/login.tsx +154 -0
- package/client/src/pages/not-found.tsx +23 -0
- package/client/src/pages/proof-of-play.tsx +397 -0
- package/client/src/pages/public-approval.tsx +551 -0
- package/client/src/pages/reports.tsx +231 -0
- package/client/src/pages/settings.tsx +760 -0
- package/client/src/pages/signals.tsx +389 -0
- package/client/src/pages/tags.tsx +318 -0
- package/client/src/pages/test-results.tsx +328 -0
- package/client/src/store/hooks.ts +5 -0
- package/client/src/store/index.ts +15 -0
- package/client/src/store/mapMarkerLocationsSlice.ts +241 -0
- package/client/src/styles/design-tokens.css +324 -0
- package/client/src/test/setup.ts +261 -0
- package/client/src/test/test-utils.tsx +40 -0
- package/client/src/types/approval.ts +221 -0
- package/client/src/types/content-hub.ts +209 -0
- package/client/src/types/geofencing.ts +67 -0
- package/client/src/types/transcoding.ts +140 -0
- package/client/src/vite-env.d.ts +18 -0
- package/components.json +20 -0
- package/creative-api.json +1 -0
- package/docs/AI_REFERENCE.md +459 -0
- package/docs/MWDesign-Prompt.md +132 -0
- package/docs/MWDesign-System.md +344 -0
- package/docs/test-plan.md +277 -0
- package/e2e/AUTONOMOUS-TESTING.md +406 -0
- package/e2e/README.md +219 -0
- package/e2e/autonomous-flow.spec.ts +308 -0
- package/e2e/debug-sso.spec.ts +163 -0
- package/e2e/direct-campaigns.spec.ts +219 -0
- package/e2e/explore-sso.spec.ts +149 -0
- package/e2e/fixtures/auth.ts +26 -0
- package/e2e/fixtures/enhanced-test.ts +331 -0
- package/e2e/pagination.spec.ts +280 -0
- package/e2e/view-toggle.spec.ts +312 -0
- package/generated-icon.png +0 -0
- package/i18next-scanner.config.cjs +46 -0
- package/package.json +141 -0
- package/playwright.config.ts +93 -0
- package/postcss.config.js +6 -0
- package/replit.md +196 -0
- package/screenshot-after-login.png +0 -0
- package/screenshot-contenthub-grid.png +0 -0
- package/screenshot-contenthub-list-fixed.png +0 -0
- package/screenshot-contenthub-list.png +0 -0
- package/screenshot-create-deal.png +0 -0
- package/screenshot-dashboard.png +0 -0
- package/screenshot-deals.png +0 -0
- package/screenshot-login-filled.png +0 -0
- package/screenshot-login.png +0 -0
- package/screenshot.mjs +24 -0
- package/scripts/deploy-stg.sh +185 -0
- package/shared/direct-io-schema.ts +383 -0
- package/shared/schema.ts +439 -0
- package/shared/screen-types.ts +149 -0
- package/springdocDefault.json +1 -0
- package/swagger-ui-bundle.js +2 -0
- package/swagger-ui-init.js +10316 -0
- package/tailwind.config.ts +282 -0
- package/terraform/README.md +306 -0
- package/terraform/cloudfront.tf +289 -0
- package/terraform/ecs.tf +727 -0
- package/terraform/environments/dev.tfvars +59 -0
- package/terraform/environments/production.tfvars +60 -0
- package/terraform/main.tf +47 -0
- package/terraform/outputs.tf +145 -0
- package/terraform/s3.tf +192 -0
- package/terraform/variables.tf +226 -0
- package/terraform/waf.tf +165 -0
- package/terraform-frontend/.terraform.lock.hcl +25 -0
- package/terraform-frontend/README.md +85 -0
- package/terraform-frontend/cloudfront.tf +125 -0
- package/terraform-frontend/main.tf +31 -0
- package/terraform-frontend/outputs.tf +24 -0
- package/terraform-frontend/terraform.tfvars +12 -0
- package/terraform-frontend/variables.tf +53 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +226 -0
- 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**
|