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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export class ApprovalEmailBodyDTO {
|
|
2
|
+
constructor({
|
|
3
|
+
user = {},
|
|
4
|
+
approverEmail = '',
|
|
5
|
+
dealName,
|
|
6
|
+
dealId,
|
|
7
|
+
budget,
|
|
8
|
+
noLineItems,
|
|
9
|
+
approvalLink,
|
|
10
|
+
publisherName = '',
|
|
11
|
+
logoUrl = null,
|
|
12
|
+
layoutCompanyName = '',
|
|
13
|
+
layoutAddress = '',
|
|
14
|
+
layoutPhoneNumber = '',
|
|
15
|
+
layoutEmail = ''
|
|
16
|
+
}) {
|
|
17
|
+
const derivedFirstname = approverEmail ? approverEmail.split('@')[0] : '';
|
|
18
|
+
this.user = {
|
|
19
|
+
firstname: user.firstname || derivedFirstname,
|
|
20
|
+
lastname: user.lastname || ''
|
|
21
|
+
};
|
|
22
|
+
this.deal_name = dealName;
|
|
23
|
+
this.deal_id = dealId;
|
|
24
|
+
this.budget = budget;
|
|
25
|
+
this.no_line_items = String(noLineItems);
|
|
26
|
+
this.approval_link = approvalLink;
|
|
27
|
+
this.publisher_name = publisherName;
|
|
28
|
+
this.logoUrl = logoUrl;
|
|
29
|
+
this.layoutCompanyName = layoutCompanyName;
|
|
30
|
+
this.layoutAddress = layoutAddress;
|
|
31
|
+
this.layoutPhoneNumber = layoutPhoneNumber;
|
|
32
|
+
this.layoutEmail = layoutEmail;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
toJSON() {
|
|
36
|
+
return JSON.stringify({
|
|
37
|
+
user: this.user,
|
|
38
|
+
deal_name: this.deal_name,
|
|
39
|
+
deal_id: this.deal_id,
|
|
40
|
+
budget: this.budget,
|
|
41
|
+
no_line_items: this.no_line_items,
|
|
42
|
+
approval_link: this.approval_link,
|
|
43
|
+
publisher_name: this.publisher_name,
|
|
44
|
+
logoUrl: this.logoUrl,
|
|
45
|
+
layoutCompanyName: this.layoutCompanyName,
|
|
46
|
+
layoutAddress: this.layoutAddress,
|
|
47
|
+
layoutPhoneNumber: this.layoutPhoneNumber,
|
|
48
|
+
layoutEmail: this.layoutEmail
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class OTPEmailBodyDTO {
|
|
54
|
+
constructor({
|
|
55
|
+
user = {},
|
|
56
|
+
approverEmail = '',
|
|
57
|
+
code,
|
|
58
|
+
mediaownerName = '',
|
|
59
|
+
logoUrl = null,
|
|
60
|
+
layoutCompanyName = '',
|
|
61
|
+
layoutAddress = '',
|
|
62
|
+
layoutPhoneNumber = '',
|
|
63
|
+
layoutEmail = ''
|
|
64
|
+
}) {
|
|
65
|
+
const derivedFirstname = approverEmail ? approverEmail.split('@')[0] : '';
|
|
66
|
+
this.user = {
|
|
67
|
+
firstname: user.firstname || derivedFirstname,
|
|
68
|
+
lastname: user.lastname || ''
|
|
69
|
+
};
|
|
70
|
+
this.code = code;
|
|
71
|
+
this.mediaownerName = mediaownerName;
|
|
72
|
+
this.logoUrl = logoUrl;
|
|
73
|
+
this.layoutCompanyName = layoutCompanyName;
|
|
74
|
+
this.layoutAddress = layoutAddress;
|
|
75
|
+
this.layoutPhoneNumber = layoutPhoneNumber;
|
|
76
|
+
this.layoutEmail = layoutEmail;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
toJSON() {
|
|
80
|
+
return JSON.stringify({
|
|
81
|
+
user: this.user,
|
|
82
|
+
code: this.code,
|
|
83
|
+
mediaownerName: this.mediaownerName,
|
|
84
|
+
logoUrl: this.logoUrl,
|
|
85
|
+
layoutCompanyName: this.layoutCompanyName,
|
|
86
|
+
layoutAddress: this.layoutAddress,
|
|
87
|
+
layoutPhoneNumber: this.layoutPhoneNumber,
|
|
88
|
+
layoutEmail: this.layoutEmail
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class DecisionEmailBodyDTO {
|
|
94
|
+
constructor({
|
|
95
|
+
user = {},
|
|
96
|
+
sellerEmail = '',
|
|
97
|
+
dealName,
|
|
98
|
+
dealId,
|
|
99
|
+
budget,
|
|
100
|
+
noLineItems,
|
|
101
|
+
approverEmail,
|
|
102
|
+
logoUrl = null,
|
|
103
|
+
layoutCompanyName = '',
|
|
104
|
+
layoutAddress = '',
|
|
105
|
+
layoutPhoneNumber = '',
|
|
106
|
+
layoutEmail = ''
|
|
107
|
+
}) {
|
|
108
|
+
const derivedFirstname = sellerEmail ? sellerEmail.split('@')[0] : '';
|
|
109
|
+
this.user = {
|
|
110
|
+
firstname: user.firstname || derivedFirstname,
|
|
111
|
+
lastname: user.lastname || ''
|
|
112
|
+
};
|
|
113
|
+
this.deal_name = dealName;
|
|
114
|
+
this.deal_id = dealId;
|
|
115
|
+
this.budget = budget;
|
|
116
|
+
this.no_line_items = String(noLineItems);
|
|
117
|
+
this.approver = approverEmail;
|
|
118
|
+
this.logoUrl = logoUrl;
|
|
119
|
+
this.layoutCompanyName = layoutCompanyName;
|
|
120
|
+
this.layoutAddress = layoutAddress;
|
|
121
|
+
this.layoutPhoneNumber = layoutPhoneNumber;
|
|
122
|
+
this.layoutEmail = layoutEmail;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
toJSON() {
|
|
126
|
+
return JSON.stringify({
|
|
127
|
+
user: this.user,
|
|
128
|
+
deal_name: this.deal_name,
|
|
129
|
+
deal_id: this.deal_id,
|
|
130
|
+
budget: this.budget,
|
|
131
|
+
no_line_items: this.no_line_items,
|
|
132
|
+
approver: this.approver,
|
|
133
|
+
logoUrl: this.logoUrl,
|
|
134
|
+
layoutCompanyName: this.layoutCompanyName,
|
|
135
|
+
layoutAddress: this.layoutAddress,
|
|
136
|
+
layoutPhoneNumber: this.layoutPhoneNumber,
|
|
137
|
+
layoutEmail: this.layoutEmail
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export class EmailRequestDTO {
|
|
143
|
+
constructor({
|
|
144
|
+
from,
|
|
145
|
+
to,
|
|
146
|
+
subject,
|
|
147
|
+
body,
|
|
148
|
+
template
|
|
149
|
+
}) {
|
|
150
|
+
this.from = from;
|
|
151
|
+
this.to = Array.isArray(to) ? to : [to];
|
|
152
|
+
this.subject = subject;
|
|
153
|
+
this.body = body;
|
|
154
|
+
this.template = template;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
toPayload() {
|
|
158
|
+
return {
|
|
159
|
+
from: this.from,
|
|
160
|
+
to: this.to,
|
|
161
|
+
subject: this.subject,
|
|
162
|
+
body: typeof this.body === 'string' ? this.body : this.body.toJSON(),
|
|
163
|
+
template: this.template
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const errorHandler = (err, req, res, next) => {
|
|
2
|
+
console.error('Error:', err);
|
|
3
|
+
|
|
4
|
+
if (err.name === 'ValidationError') {
|
|
5
|
+
const errors = err.details?.map(detail => ({
|
|
6
|
+
field: detail.path.join('.'),
|
|
7
|
+
message: detail.message
|
|
8
|
+
})) || [];
|
|
9
|
+
|
|
10
|
+
return res.status(400).json({
|
|
11
|
+
code: 'VALIDATION_ERROR',
|
|
12
|
+
message: 'Invalid request payload',
|
|
13
|
+
errors
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (err.name === 'SequelizeUniqueConstraintError') {
|
|
18
|
+
const fields = err.errors?.map(e => e.path).join(', ') || 'unknown';
|
|
19
|
+
return res.status(409).json({
|
|
20
|
+
code: 'CONFLICT',
|
|
21
|
+
message: `Duplicate value for: ${fields}`,
|
|
22
|
+
details: err.errors?.map(e => ({ field: e.path, value: e.value, message: e.message })) || []
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (err.name === 'SequelizeValidationError') {
|
|
27
|
+
return res.status(400).json({
|
|
28
|
+
code: 'VALIDATION_ERROR',
|
|
29
|
+
message: 'Database validation failed',
|
|
30
|
+
errors: err.errors?.map(e => ({ field: e.path, message: e.message, value: e.value })) || []
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (err.name === 'SequelizeForeignKeyConstraintError') {
|
|
35
|
+
return res.status(400).json({
|
|
36
|
+
code: 'REFERENCE_ERROR',
|
|
37
|
+
message: 'Referenced resource does not exist'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (err.statusCode) {
|
|
42
|
+
return res.status(err.statusCode).json({
|
|
43
|
+
code: err.code || 'ERROR',
|
|
44
|
+
message: err.message
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
res.status(500).json({
|
|
49
|
+
code: 'INTERNAL_ERROR',
|
|
50
|
+
message: 'An unexpected error occurred'
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import TransactionHistory from '../models/TransactionHistory.js';
|
|
3
|
+
|
|
4
|
+
export const requestLogger = async (req, res, next) => {
|
|
5
|
+
const requestId = uuidv4();
|
|
6
|
+
req.requestId = requestId;
|
|
7
|
+
|
|
8
|
+
const originalSend = res.send;
|
|
9
|
+
res.send = function (data) {
|
|
10
|
+
res.send = originalSend;
|
|
11
|
+
|
|
12
|
+
TransactionHistory.create({
|
|
13
|
+
request_id: requestId,
|
|
14
|
+
method: req.method,
|
|
15
|
+
endpoint: req.originalUrl || req.url,
|
|
16
|
+
payload: req.body || {},
|
|
17
|
+
response_status: res.statusCode
|
|
18
|
+
}).catch(err => {
|
|
19
|
+
console.error('Error logging transaction:', err);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return res.send(data);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
next();
|
|
26
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Migration: Add Campaign Mode fields to deals table
|
|
2
|
+
-- Version: 001
|
|
3
|
+
-- Date: December 2025
|
|
4
|
+
-- Description: Adds brand, client_type, mode, approval_emails, market_selection, budget_setup, campaign_goal fields
|
|
5
|
+
-- Note: Using TEXT for client_type and mode to match Sequelize model and avoid ENUM management complexity
|
|
6
|
+
|
|
7
|
+
-- Add new columns to deals table (all TEXT-based for flexibility)
|
|
8
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS brand TEXT;
|
|
9
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS client_type TEXT;
|
|
10
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS mode TEXT;
|
|
11
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS approval_emails TEXT[];
|
|
12
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS market_selection JSONB;
|
|
13
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS budget_setup JSONB;
|
|
14
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS campaign_goal JSONB;
|
|
15
|
+
|
|
16
|
+
-- Add check constraints for validation (instead of ENUMs)
|
|
17
|
+
DO $$
|
|
18
|
+
BEGIN
|
|
19
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'deals_client_type_check') THEN
|
|
20
|
+
ALTER TABLE deals ADD CONSTRAINT deals_client_type_check
|
|
21
|
+
CHECK (client_type IS NULL OR client_type IN ('DIRECT_ADVERTISER', 'AGENCY'));
|
|
22
|
+
END IF;
|
|
23
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'deals_mode_check') THEN
|
|
24
|
+
ALTER TABLE deals ADD CONSTRAINT deals_mode_check
|
|
25
|
+
CHECK (mode IS NULL OR mode IN ('PROGRAMMATIC', 'DIRECT'));
|
|
26
|
+
END IF;
|
|
27
|
+
END$$;
|
|
28
|
+
|
|
29
|
+
-- Add comments for documentation
|
|
30
|
+
COMMENT ON COLUMN deals.brand IS 'Brand name (dropdown selection)';
|
|
31
|
+
COMMENT ON COLUMN deals.client_type IS 'Client type: BUYER or AGENCY';
|
|
32
|
+
COMMENT ON COLUMN deals.mode IS 'Campaign mode: PROGRAMMATIC or DIRECT';
|
|
33
|
+
COMMENT ON COLUMN deals.approval_emails IS 'List of approval email addresses (required for DIRECT mode)';
|
|
34
|
+
COMMENT ON COLUMN deals.market_selection IS 'Market and currency selection (required for DIRECT mode)';
|
|
35
|
+
COMMENT ON COLUMN deals.budget_setup IS 'Budget configuration (required for DIRECT mode)';
|
|
36
|
+
COMMENT ON COLUMN deals.campaign_goal IS 'Campaign goal configuration (required for DIRECT mode)';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Migration: Create deal_id_counters table for sequence tracking
|
|
2
|
+
-- This table maintains sequence counters for auto-generating deal IDs
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS deal_id_counters (
|
|
5
|
+
id SERIAL PRIMARY KEY,
|
|
6
|
+
prefix VARCHAR(10) NOT NULL UNIQUE,
|
|
7
|
+
current_value BIGINT NOT NULL DEFAULT 0,
|
|
8
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
9
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
-- Initialize counters for each deal type prefix
|
|
13
|
+
INSERT INTO deal_id_counters (prefix, current_value) VALUES
|
|
14
|
+
('GD', 0),
|
|
15
|
+
('PD', 0),
|
|
16
|
+
('PA', 0),
|
|
17
|
+
('EG', 0),
|
|
18
|
+
('DIR', 0)
|
|
19
|
+
ON CONFLICT (prefix) DO NOTHING;
|
|
20
|
+
|
|
21
|
+
-- Create index for faster lookups
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_deal_id_counters_prefix ON deal_id_counters(prefix);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
DO $$
|
|
2
|
+
BEGIN
|
|
3
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publisher_ids') THEN
|
|
4
|
+
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publishers') THEN
|
|
5
|
+
ALTER TABLE deals ADD COLUMN publishers JSONB;
|
|
6
|
+
UPDATE deals SET publishers = (
|
|
7
|
+
SELECT jsonb_agg(jsonb_build_object('id', elem, 'name', elem))
|
|
8
|
+
FROM unnest(publisher_ids) AS elem
|
|
9
|
+
) WHERE publisher_ids IS NOT NULL AND array_length(publisher_ids, 1) > 0;
|
|
10
|
+
END IF;
|
|
11
|
+
ALTER TABLE deals DROP COLUMN publisher_ids;
|
|
12
|
+
ELSIF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'deals' AND column_name = 'publishers') THEN
|
|
13
|
+
ALTER TABLE deals ADD COLUMN publishers JSONB;
|
|
14
|
+
END IF;
|
|
15
|
+
END $$;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- Add new status values to status enum
|
|
2
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'REQUESTED';
|
|
3
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'REJECTED';
|
|
4
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'CANCELED';
|
|
5
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'DELETED';
|
|
6
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'PENDING_CREATIVES';
|
|
7
|
+
ALTER TYPE status_enum ADD VALUE IF NOT EXISTS 'IN_ACTIVE';
|
|
8
|
+
|
|
9
|
+
-- Create transaction_type enum if not exists
|
|
10
|
+
DO $$ BEGIN
|
|
11
|
+
CREATE TYPE transaction_type_enum AS ENUM ('SPOT', 'AUDIENCE');
|
|
12
|
+
EXCEPTION
|
|
13
|
+
WHEN duplicate_object THEN null;
|
|
14
|
+
END $$;
|
|
15
|
+
|
|
16
|
+
-- Create imp_multiplier_type enum if not exists
|
|
17
|
+
DO $$ BEGIN
|
|
18
|
+
CREATE TYPE imp_multiplier_type_enum AS ENUM ('MAD', 'CUSTOM');
|
|
19
|
+
EXCEPTION
|
|
20
|
+
WHEN duplicate_object THEN null;
|
|
21
|
+
END $$;
|
|
22
|
+
|
|
23
|
+
-- Add new programmatic fields
|
|
24
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS published BOOLEAN DEFAULT false;
|
|
25
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS reopened BOOLEAN DEFAULT false;
|
|
26
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS non_billable BOOLEAN DEFAULT false;
|
|
27
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS stop_bid_requests BOOLEAN DEFAULT false;
|
|
28
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS imp_multiplier_type imp_multiplier_type_enum;
|
|
29
|
+
|
|
30
|
+
-- Set default status to GENERATED for new records
|
|
31
|
+
ALTER TABLE deals ALTER COLUMN status SET DEFAULT 'GENERATED';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Migration: Add programmatic fields to line_items table
|
|
2
|
+
-- Date: 2025-12-10
|
|
3
|
+
|
|
4
|
+
-- Add new columns to line_items table using TEXT types to avoid enum issues
|
|
5
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS mode TEXT;
|
|
6
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS resolutions TEXT[];
|
|
7
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS creative_source TEXT;
|
|
8
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS total_inventories INTEGER DEFAULT 0;
|
|
9
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS adm_creative_sync BOOLEAN DEFAULT false;
|
|
10
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS non_billable BOOLEAN DEFAULT false;
|
|
11
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS stop_bid_requests BOOLEAN DEFAULT false;
|
|
12
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS imp_multiplier JSONB;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Migration: Add DIRECT mode fields to line_items table
|
|
2
|
+
-- Date: 2025-01-10
|
|
3
|
+
-- Description: Adds budgetSetup, campaignGoal, pacing, and targeting fields for DIRECT mode line items
|
|
4
|
+
|
|
5
|
+
-- Add DIRECT mode specific fields
|
|
6
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS budget_setup JSONB;
|
|
7
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS campaign_goal JSONB;
|
|
8
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS pacing JSONB;
|
|
9
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS targeting JSONB;
|
|
10
|
+
|
|
11
|
+
-- Add comments for documentation
|
|
12
|
+
COMMENT ON COLUMN line_items.budget_setup IS 'Budget configuration for DIRECT mode line items';
|
|
13
|
+
COMMENT ON COLUMN line_items.campaign_goal IS 'Campaign goal settings for DIRECT mode line items';
|
|
14
|
+
COMMENT ON COLUMN line_items.pacing IS 'Pacing configuration for DIRECT mode line items';
|
|
15
|
+
COMMENT ON COLUMN line_items.targeting IS 'Targeting rules for DIRECT mode line items';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- Migration: Add comprehensive inventory fields to line_item_inventories table
|
|
2
|
+
-- Date: 2025-01-10
|
|
3
|
+
-- Description: Adds new fields for ad unit identification, location, venue, display settings, and content controls
|
|
4
|
+
|
|
5
|
+
-- Identifier fields
|
|
6
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS ad_unit_code TEXT;
|
|
7
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS reference_id TEXT;
|
|
8
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS device_id TEXT;
|
|
9
|
+
|
|
10
|
+
-- Location/Country fields
|
|
11
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS country_iso2 TEXT;
|
|
12
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS country_iso3 TEXT;
|
|
13
|
+
|
|
14
|
+
-- Venue fields
|
|
15
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS venue_type_ids TEXT[];
|
|
16
|
+
|
|
17
|
+
-- Operational metrics
|
|
18
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS spots_per_hour INTEGER;
|
|
19
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS spot_duration INTEGER;
|
|
20
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS clients INTEGER;
|
|
21
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS "group" TEXT;
|
|
22
|
+
|
|
23
|
+
-- Content controls
|
|
24
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS bcat JSONB;
|
|
25
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS publisher_domain TEXT;
|
|
26
|
+
|
|
27
|
+
-- Display/Aspect ratio fields
|
|
28
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS enable_aspect_ratio BOOLEAN DEFAULT false;
|
|
29
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS display_aspect_ratio TEXT;
|
|
30
|
+
|
|
31
|
+
-- Add comments for documentation
|
|
32
|
+
COMMENT ON COLUMN line_item_inventories.ad_unit_code IS 'Ad unit code identifier';
|
|
33
|
+
COMMENT ON COLUMN line_item_inventories.reference_id IS 'External reference identifier';
|
|
34
|
+
COMMENT ON COLUMN line_item_inventories.device_id IS 'Device identifier';
|
|
35
|
+
COMMENT ON COLUMN line_item_inventories.country_iso2 IS 'ISO 3166-1 alpha-2 country code';
|
|
36
|
+
COMMENT ON COLUMN line_item_inventories.country_iso3 IS 'ISO 3166-1 alpha-3 country code';
|
|
37
|
+
COMMENT ON COLUMN line_item_inventories.venue_type_ids IS 'Array of venue type identifiers';
|
|
38
|
+
COMMENT ON COLUMN line_item_inventories.spots_per_hour IS 'Number of ad spots per hour';
|
|
39
|
+
COMMENT ON COLUMN line_item_inventories.spot_duration IS 'Duration of each spot in seconds';
|
|
40
|
+
COMMENT ON COLUMN line_item_inventories.clients IS 'Number of clients/advertisers';
|
|
41
|
+
COMMENT ON COLUMN line_item_inventories."group" IS 'Inventory grouping identifier';
|
|
42
|
+
COMMENT ON COLUMN line_item_inventories.bcat IS 'Blocked categories list [{name, code}]';
|
|
43
|
+
COMMENT ON COLUMN line_item_inventories.publisher_domain IS 'Publisher domain URL';
|
|
44
|
+
COMMENT ON COLUMN line_item_inventories.enable_aspect_ratio IS 'Whether aspect ratio constraints are enabled';
|
|
45
|
+
COMMENT ON COLUMN line_item_inventories.display_aspect_ratio IS 'Display aspect ratio (e.g., 16:9)';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- Migration: Move inventory operational fields to metadata JSONB
|
|
2
|
+
-- Fields moved: spots_per_hour, spot_duration, clients, group
|
|
3
|
+
-- (transit was already stored in metadata, not as a column)
|
|
4
|
+
-- New fields added to metadata: networkId, packageId
|
|
5
|
+
-- This migration is idempotent - safe to run multiple times
|
|
6
|
+
|
|
7
|
+
-- Step 1: Migrate existing data from columns to metadata (only if columns exist)
|
|
8
|
+
DO $$
|
|
9
|
+
BEGIN
|
|
10
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'line_item_inventories' AND column_name = 'spots_per_hour') THEN
|
|
11
|
+
EXECUTE '
|
|
12
|
+
UPDATE line_item_inventories
|
|
13
|
+
SET metadata = COALESCE(metadata, ''{}''::jsonb)
|
|
14
|
+
|| jsonb_strip_nulls(jsonb_build_object(
|
|
15
|
+
''spotsPerHour'', spots_per_hour,
|
|
16
|
+
''spotDuration'', spot_duration,
|
|
17
|
+
''clients'', clients,
|
|
18
|
+
''group'', "group"
|
|
19
|
+
))
|
|
20
|
+
WHERE spots_per_hour IS NOT NULL
|
|
21
|
+
OR spot_duration IS NOT NULL
|
|
22
|
+
OR clients IS NOT NULL
|
|
23
|
+
OR "group" IS NOT NULL
|
|
24
|
+
';
|
|
25
|
+
END IF;
|
|
26
|
+
END $$;
|
|
27
|
+
|
|
28
|
+
-- Step 2: Drop the columns that are now in metadata
|
|
29
|
+
ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS spots_per_hour;
|
|
30
|
+
ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS spot_duration;
|
|
31
|
+
ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS clients;
|
|
32
|
+
ALTER TABLE line_item_inventories DROP COLUMN IF EXISTS "group";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
DO $$
|
|
2
|
+
BEGIN
|
|
3
|
+
IF NOT EXISTS (
|
|
4
|
+
SELECT 1 FROM pg_enum
|
|
5
|
+
WHERE enumlabel = 'DRAFT'
|
|
6
|
+
AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'enum_deals_status')
|
|
7
|
+
) THEN
|
|
8
|
+
ALTER TYPE "enum_deals_status" ADD VALUE 'DRAFT' BEFORE 'REQUESTED';
|
|
9
|
+
END IF;
|
|
10
|
+
END$$;
|
|
11
|
+
|
|
12
|
+
DO $$
|
|
13
|
+
BEGIN
|
|
14
|
+
IF NOT EXISTS (
|
|
15
|
+
SELECT 1 FROM pg_enum
|
|
16
|
+
WHERE enumlabel = 'DRAFT'
|
|
17
|
+
AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'status_enum')
|
|
18
|
+
) THEN
|
|
19
|
+
ALTER TYPE "status_enum" ADD VALUE 'DRAFT' BEFORE 'GENERATED';
|
|
20
|
+
END IF;
|
|
21
|
+
END$$;
|
|
22
|
+
|
|
23
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS line_items_count INTEGER DEFAULT 0;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Migration: Add planning field for DIRECT campaigns
|
|
2
|
+
-- The planning object contains capacity, allocation, estimates, and pricing data
|
|
3
|
+
|
|
4
|
+
-- Add planning column to deals table (aggregated from line items)
|
|
5
|
+
ALTER TABLE deals ADD COLUMN IF NOT EXISTS planning JSONB;
|
|
6
|
+
|
|
7
|
+
-- Add planning column to line_items table
|
|
8
|
+
ALTER TABLE line_items ADD COLUMN IF NOT EXISTS planning JSONB;
|
|
9
|
+
|
|
10
|
+
-- Add planning column to line_item_inventories table
|
|
11
|
+
ALTER TABLE line_item_inventories ADD COLUMN IF NOT EXISTS planning JSONB;
|
|
12
|
+
|
|
13
|
+
-- Create index for planning field on line_items (for querying DIRECT campaigns with planning data)
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_line_items_planning ON line_items USING GIN (planning) WHERE planning IS NOT NULL;
|
|
15
|
+
|
|
16
|
+
-- Create index for planning field on line_item_inventories
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_line_item_inventories_planning ON line_item_inventories USING GIN (planning) WHERE planning IS NOT NULL;
|
|
18
|
+
|
|
19
|
+
-- Comment on columns
|
|
20
|
+
COMMENT ON COLUMN line_items.planning IS 'Planning data for DIRECT campaigns: capacity, allocation, estimates, pricing';
|
|
21
|
+
COMMENT ON COLUMN line_item_inventories.planning IS 'Planning data for DIRECT inventory units: capacity, allocation, estimates, pricing';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Migration: Change line_item_inventories primary key to composite (id, line_item_id)
|
|
2
|
+
-- This allows the same inventory to be assigned to multiple line items
|
|
3
|
+
|
|
4
|
+
-- Step 1: Drop the existing primary key constraint
|
|
5
|
+
ALTER TABLE line_item_inventories DROP CONSTRAINT IF EXISTS line_item_inventories_pkey;
|
|
6
|
+
|
|
7
|
+
-- Step 2: Make line_item_id NOT NULL (required for composite PK)
|
|
8
|
+
ALTER TABLE line_item_inventories ALTER COLUMN line_item_id SET NOT NULL;
|
|
9
|
+
|
|
10
|
+
-- Step 3: Create composite primary key
|
|
11
|
+
ALTER TABLE line_item_inventories ADD PRIMARY KEY (id, line_item_id);
|
|
12
|
+
|
|
13
|
+
-- Step 4: Add index for faster lookups by inventory id alone
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_line_item_inventories_id ON line_item_inventories(id);
|
|
15
|
+
|
|
16
|
+
-- Step 5: Add index for faster lookups by line_item_id
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_line_item_inventories_line_item_id ON line_item_inventories(line_item_id);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
-- Create change_history table for tracking all changes to deals and line items
|
|
2
|
+
DO $$ BEGIN
|
|
3
|
+
CREATE TYPE change_entity_type AS ENUM ('DEAL', 'LINE_ITEM');
|
|
4
|
+
EXCEPTION
|
|
5
|
+
WHEN duplicate_object THEN null;
|
|
6
|
+
END $$;
|
|
7
|
+
|
|
8
|
+
DO $$ BEGIN
|
|
9
|
+
CREATE TYPE change_action_type AS ENUM ('CREATE', 'UPDATE', 'DELETE', 'STATUS_CHANGE');
|
|
10
|
+
EXCEPTION
|
|
11
|
+
WHEN duplicate_object THEN null;
|
|
12
|
+
END $$;
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS change_history (
|
|
15
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
16
|
+
entity_type change_entity_type NOT NULL,
|
|
17
|
+
entity_id UUID NOT NULL,
|
|
18
|
+
deal_id TEXT,
|
|
19
|
+
line_item_id TEXT,
|
|
20
|
+
action change_action_type NOT NULL,
|
|
21
|
+
field_name TEXT,
|
|
22
|
+
previous_value JSONB,
|
|
23
|
+
current_value JSONB,
|
|
24
|
+
changed_by TEXT,
|
|
25
|
+
changed_by_email TEXT,
|
|
26
|
+
request_id TEXT,
|
|
27
|
+
ip_address TEXT,
|
|
28
|
+
user_agent TEXT,
|
|
29
|
+
summary TEXT,
|
|
30
|
+
metadata JSONB DEFAULT '{}',
|
|
31
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_change_history_entity ON change_history(entity_type, entity_id);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_change_history_deal_id ON change_history(deal_id);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_change_history_line_item_id ON change_history(line_item_id);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_change_history_created_at ON change_history(created_at);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_change_history_action ON change_history(action);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- Create publisher_insertion_orders table for minimal insertion order objects per publisher
|
|
2
|
+
-- Used for multiple publishers with common campaign budget
|
|
3
|
+
|
|
4
|
+
DO $$ BEGIN
|
|
5
|
+
CREATE TYPE insertion_order_status AS ENUM ('DRAFT', 'PENDING', 'APPROVED', 'LIVE', 'COMPLETED', 'ARCHIVED');
|
|
6
|
+
EXCEPTION
|
|
7
|
+
WHEN duplicate_object THEN null;
|
|
8
|
+
END $$;
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS publisher_insertion_orders (
|
|
11
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
12
|
+
deal_id TEXT NOT NULL REFERENCES deals(deal_id) ON DELETE CASCADE,
|
|
13
|
+
publisher_id TEXT NOT NULL,
|
|
14
|
+
publisher_name TEXT,
|
|
15
|
+
io_number TEXT,
|
|
16
|
+
status insertion_order_status DEFAULT 'DRAFT',
|
|
17
|
+
budget_amount DECIMAL(14, 2),
|
|
18
|
+
currency TEXT,
|
|
19
|
+
start_date DATE,
|
|
20
|
+
end_date DATE,
|
|
21
|
+
summary JSONB DEFAULT '{}',
|
|
22
|
+
external_sync_reference JSONB DEFAULT '{}',
|
|
23
|
+
published BOOLEAN DEFAULT false,
|
|
24
|
+
published_at TIMESTAMP WITH TIME ZONE,
|
|
25
|
+
metadata JSONB DEFAULT '{}',
|
|
26
|
+
version INTEGER DEFAULT 1,
|
|
27
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
28
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_publisher_insertion_orders_deal_id ON publisher_insertion_orders(deal_id);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_publisher_insertion_orders_publisher_id ON publisher_insertion_orders(publisher_id);
|
|
33
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_publisher_insertion_orders_deal_publisher ON publisher_insertion_orders(deal_id, publisher_id);
|