Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6eb32e7
feat: Add Campaign Management System with full CRUD, analytics dashbo…
devin-ai-integration[bot] May 4, 2026
df4581a
fix: Handle invalid campaign state transitions with 409 instead of 50…
devin-ai-integration[bot] May 5, 2026
2ddb200
fix: Add state validation to Campaign.end(), use Link for client-side…
devin-ai-integration[bot] May 5, 2026
4bd7245
fix: Pass includeArchived to findByStatus, add error handling for del…
devin-ai-integration[bot] May 5, 2026
8e49369
fix: Fix app.spec.tsx tests, optimize dashboard with aggregate query …
devin-ai-integration[bot] May 5, 2026
f394964
fix: Change seed data passwords to complex password (C@mp4ign!Mngr#2026)
devin-ai-integration[bot] May 5, 2026
6e3f375
style: Redesign UI to Fiserv Admin Tool theme; fix campaign deletion …
devin-ai-integration[bot] May 5, 2026
ebfa264
fix: Address Devin Review findings - e2e test text, valueOf validation
devin-ai-integration[bot] May 5, 2026
a6652e6
fix: Validate enum/date inputs and force UTC date parsing
devin-ai-integration[bot] May 5, 2026
39f99b9
feat: add Stories 2-4 features - targeting, fulfillment, dashboard fi…
devin-ai-integration[bot] May 5, 2026
9e14430
fix: update test constructors for DashboardSummary and CampaignAnalytics
devin-ai-integration[bot] May 5, 2026
b6893f7
fix: wire up decision type filter and fix date boundary comparison
devin-ai-integration[bot] May 5, 2026
9498e05
fix: add @Transactional to deleteCampaign for atomic multi-table delete
devin-ai-integration[bot] May 5, 2026
15ff7b7
fix: wrap recordDecision valueOf in try-catch, use Jackson UTC serial…
devin-ai-integration[bot] May 5, 2026
ead659d
fix: CSV export uses UTF-8 charset and UTC timezone for dates
devin-ai-integration[bot] May 5, 2026
35a43c1
feat: add Fiserv logo to header and login page
devin-ai-integration[bot] May 5, 2026
53ee2f9
fix: prevent CSV formula injection in escapeCsv method
devin-ai-integration[bot] May 5, 2026
e5de1ad
fix: validate campaign status for decisions and status transitions
devin-ai-integration[bot] May 5, 2026
2d6f27c
fix: activate campaign in test before recording decision
devin-ai-integration[bot] May 5, 2026
055c5e1
fix: use nullish coalescing for numeric form fields
devin-ai-integration[bot] May 5, 2026
b82773a
fix: exclude null-date campaigns from date range filters
devin-ai-integration[bot] May 5, 2026
13a9fd3
feat: Add industry-standard campaign features
devin-ai-integration[bot] May 5, 2026
3e12fab
fix: Address Devin Review findings
devin-ai-integration[bot] May 5, 2026
9da0504
fix: Address Devin Review round 2 findings
devin-ai-integration[bot] May 5, 2026
260e21d
fix: Address Devin Review round 3 findings
devin-ai-integration[bot] May 5, 2026
1f9e18d
fix: Complete hasFieldParams check with all 24 UpdateCampaignParam fi…
devin-ai-integration[bot] May 5, 2026
677f742
fix: Replace raw Map @RequestBody with typed DTOs for UNWRAP_ROOT_VAL…
devin-ai-integration[bot] May 5, 2026
9b1deae
fix: Add @Transactional to updateCampaign and fix misleading error fo…
devin-ai-integration[bot] May 5, 2026
6a28f2f
fix: Add @Transactional to createCampaign/cloneCampaign and populate …
devin-ai-integration[bot] May 5, 2026
95e3158
fix: Wrap CampaignStatus.valueOf in try-catch in bulkUpdateStatus
devin-ai-integration[bot] May 5, 2026
988d27c
feat: add Market/Competitor Intelligence Agent (evidence-pack builder)
devin-ai-integration[bot] May 5, 2026
daa72b5
fix: ensure on_status callback saves intermediate progress under corr…
devin-ai-integration[bot] May 5, 2026
467229e
fix: use numeric subtraction for stable sort comparator in calendar page
devin-ai-integration[bot] May 5, 2026
4383e03
fix: use type-only imports for interfaces (verbatimModuleSyntax compl…
devin-ai-integration[bot] May 5, 2026
bee8716
fix: address Devin Review findings (round 8)
devin-ai-integration[bot] May 5, 2026
9658b19
fix: wrap error handler load_pack/save_pack in asyncio.to_thread
devin-ai-integration[bot] May 5, 2026
6302d19
fix: address Devin Review findings (round 9)
devin-ai-integration[bot] May 5, 2026
80cfc07
feat: integrate Market Intelligence Agent into Campaign Admin
devin-ai-integration[bot] May 5, 2026
fce0b24
fix: address Devin Review findings (round 10)
devin-ai-integration[bot] May 5, 2026
9a498e4
fix: prevent SSRF bypass via redirect — validate each hop
devin-ai-integration[bot] May 5, 2026
7a6a15b
fix: eliminate DNS rebinding TOCTOU and blocking DNS in scraper
devin-ai-integration[bot] May 5, 2026
3a1f207
fix: drop IP-pinning approach that broke TLS, use validate-then-fetch
devin-ai-integration[bot] May 5, 2026
555e838
fix: revert V2 migration, add V7 for password update
devin-ai-integration[bot] May 5, 2026
4922200
feat: add 5-step campaign creation wizard with multi-step form
devin-ai-integration[bot] May 6, 2026
aabc8f6
fix: correct priority mapping and add missing channel labels
devin-ai-integration[bot] May 6, 2026
7189cbc
fix: wrap synchronous SQLite calls in asyncio.to_thread in async hand…
devin-ai-integration[bot] May 6, 2026
d393221
fix: CSV injection newline bypass and SSRF DNS rebinding TOCTOU
devin-ai-integration[bot] May 6, 2026
4459087
fix: SSRF redirect hostname loss and tighten CORS/rate limiting
devin-ai-integration[bot] May 6, 2026
55b9ae7
fix: restore hostname-based fetch for TLS compatibility, fix redirect…
devin-ai-integration[bot] May 6, 2026
ed24864
fix: add SOCIAL and ADS channels to detail/list/form pages, remove pl…
devin-ai-integration[bot] May 6, 2026
62d7bb9
fix: use regex for underscore replacement in audit log, clarify migra…
devin-ai-integration[bot] May 6, 2026
ec23ed5
fix: add threshold-based sweep to prevent unbounded rate limiter memo…
devin-ai-integration[bot] May 6, 2026
0cff880
fix: prevent CSV delivery window corruption when start time is null
devin-ai-integration[bot] May 6, 2026
5931428
fix: add @AllArgsConstructor to CloneCampaignParam and BulkStatusUpda…
devin-ai-integration[bot] May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ frontend/.next/
frontend/out/
frontend/.env*.local
frontend/.git/

### Campaign Admin (Nx React) ###
campaign-admin/node_modules/
campaign-admin/dist/
campaign-admin/.nx/
campaign-admin/.env*.local
101 changes: 101 additions & 0 deletions campaign-admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# CampaignAdmin

<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>

✨ Your new, shiny [Nx workspace](https://nx.dev) is ready ✨.

[Learn more about this workspace setup and its capabilities](https://nx.dev/getting-started/tutorials/react-standalone-tutorial?utm_source=nx_project&amp;utm_medium=readme&amp;utm_campaign=nx_projects) or run `npx nx graph` to visually explore what was created. Now, let's get you up to speed!

## Run tasks

To run the dev server for your app, use:

```sh
npx nx serve campaign-admin
```

To create a production bundle:

```sh
npx nx build campaign-admin
```

To see all available targets to run for a project, run:

```sh
npx nx show project campaign-admin
```

These targets are either [inferred automatically](https://nx.dev/concepts/inferred-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) or defined in the `project.json` or `package.json` files.

[More about running tasks in the docs &raquo;](https://nx.dev/features/run-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

## Add new projects

While you could add new projects to your workspace manually, you might want to leverage [Nx plugins](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) and their [code generation](https://nx.dev/features/generate-code?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) feature.

Use the plugin's generator to create new projects.

To generate a new application, use:

```sh
npx nx g @nx/react:app demo
```

To generate a new library, use:

```sh
npx nx g @nx/react:lib mylib
```

You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx list <plugin-name>` to learn about more specific capabilities of a particular plugin. Alternatively, [install Nx Console](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) to browse plugins and generators in your IDE.

[Learn more about Nx plugins &raquo;](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) | [Browse the plugin registry &raquo;](https://nx.dev/plugin-registry?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

## Set up CI!

### Step 1

To connect to Nx Cloud, run the following command:

```sh
npx nx connect
```

Connecting to Nx Cloud ensures a [fast and scalable CI](https://nx.dev/ci/intro/why-nx-cloud?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) pipeline. It includes features such as:

- [Remote caching](https://nx.dev/ci/features/remote-cache?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Task distribution across multiple machines](https://nx.dev/ci/features/distribute-task-execution?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Automated e2e test splitting](https://nx.dev/ci/features/split-e2e-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Task flakiness detection and rerunning](https://nx.dev/ci/features/flaky-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

### Step 2

Use the following command to configure a CI workflow for your workspace:

```sh
npx nx g ci-workflow
```

[Learn more about Nx on CI](https://nx.dev/ci/intro/ci-with-nx#ready-get-started-with-your-provider?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

## Install Nx Console

Nx Console is an editor extension that enriches your developer experience. It lets you run tasks, generate code, and improves code autocompletion in your IDE. It is available for VSCode and IntelliJ.

[Install Nx Console &raquo;](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

## Useful links

Learn more:

- [Learn more about this workspace setup](https://nx.dev/getting-started/tutorials/react-standalone-tutorial?utm_source=nx_project&amp;utm_medium=readme&amp;utm_campaign=nx_projects)
- [Learn about Nx on CI](https://nx.dev/ci/intro/ci-with-nx?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Releasing Packages with Nx release](https://nx.dev/features/manage-releases?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [What are Nx plugins?](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)

And join the Nx community:
- [Discord](https://go.nx.dev/community)
- [Follow us on X](https://twitter.com/nxdevtools) or [LinkedIn](https://www.linkedin.com/company/nrwl)
- [Our Youtube channel](https://www.youtube.com/@nxdevtools)
- [Our blog](https://nx.dev/blog?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
10 changes: 10 additions & 0 deletions campaign-admin/e2e/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import playwright from 'eslint-plugin-playwright';

export default [
playwright.configs['flat/recommended'],
{
files: ['**/*.ts', '**/*.js'],
// Override or add rules here
rules: {},
},
];
68 changes: 68 additions & 0 deletions campaign-admin/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { defineConfig, devices } from '@playwright/test';
import { nxE2EPreset } from '@nx/playwright/preset';
import { workspaceRoot } from '@nx/devkit';

// For CI, you may want to set BASE_URL to the deployed application.
const baseURL = process.env['BASE_URL'] || 'http://localhost:4300';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
...nxE2EPreset(__filename, { testDir: './src' }),
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
baseURL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Run your local dev server before starting the tests */
webServer: {
command: 'npx nx run campaign-admin:preview',
url: 'http://localhost:4300',
reuseExistingServer: true,
cwd: workspaceRoot,
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},

// Uncomment for mobile browsers support
/* {
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
}, */

// Uncomment for branded browsers
/* {
name: 'Microsoft Edge',
use: { ...devices['Desktop Edge'], channel: 'msedge' },
},
{
name: 'Google Chrome',
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
} */
],
});
10 changes: 10 additions & 0 deletions campaign-admin/e2e/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "e2e",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "e2e/src",
"tags": [],
"implicitDependencies": ["campaign-admin"],
"// targets": "to see all targets run: nx show project e2e --web",
"targets": {}
}
36 changes: 36 additions & 0 deletions campaign-admin/e2e/src/example.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { test, expect } from '@playwright/test';

test.describe('Campaign Manager', () => {
test('should redirect unauthenticated users to login', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveURL(/\/login/);
});

test('should display login form', async ({ page }) => {
await page.goto('/login');
await expect(page.locator('h1')).toContainText('Welcome Back');
await expect(page.locator('input[type="email"]')).toBeVisible();
await expect(page.locator('input[type="password"]')).toBeVisible();
await expect(page.locator('button[type="submit"]')).toContainText(
'Sign In'
);
});

test('should show error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('input[type="email"]', 'invalid@test.com');
await page.fill('input[type="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
await expect(page.locator('text=Invalid credentials')).toBeVisible({
timeout: 10000,
});
});

test('should show access denied page', async ({ page }) => {
await page.goto('/access-denied');
await expect(page.locator('text=Access Denied')).toBeVisible();
await expect(
page.locator('text=Marketing')
).toBeVisible();
});
});
19 changes: 19 additions & 0 deletions campaign-admin/e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"allowJs": true,
"outDir": "../dist/out-tsc",
"sourceMap": false,
"module": "commonjs"
},
"include": [
"**/*.ts",
"**/*.js",
"playwright.config.ts",
"src/**/*.spec.ts",
"src/**/*.spec.js",
"src/**/*.test.ts",
"src/**/*.test.js",
"src/**/*.d.ts"
]
}
35 changes: 35 additions & 0 deletions campaign-admin/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import nx from '@nx/eslint-plugin';

export default [
...nx.configs['flat/base'],
...nx.configs['flat/typescript'],
...nx.configs['flat/javascript'],
{
ignores: [
'**/dist',
'**/out-tsc',
'**/vite.config.*.timestamp*',
'**/vitest.config.*.timestamp*',
],
},
{
files: [
'**/*.ts',
'**/*.tsx',
'**/*.cts',
'**/*.mts',
'**/*.js',
'**/*.jsx',
'**/*.cjs',
'**/*.mjs',
],
// Override or add rules here
rules: {},
},
...nx.configs['flat/react'],
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
// Override or add rules here
rules: {},
},
];
16 changes: 16 additions & 0 deletions campaign-admin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>CampaignAdmin</title>
<base href="/" />

<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
70 changes: 70 additions & 0 deletions campaign-admin/nx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"analytics": false,
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": [
"default",
"!{projectRoot}/.eslintrc.json",
"!{projectRoot}/eslint.config.mjs",
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/src/test-setup.[jt]s"
],
"sharedGlobals": []
},
"plugins": [
{
"plugin": "@nx/eslint/plugin",
"options": {
"targetName": "lint"
}
},
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build",
"testTargetName": "test",
"serveTargetName": "serve",
"devTargetName": "dev",
"previewTargetName": "preview",
"serveStaticTargetName": "serve-static",
"typecheckTargetName": "typecheck",
"buildDepsTargetName": "build-deps",
"watchDepsTargetName": "watch-deps"
}
},
{
"plugin": "@nx/vitest",
"options": {
"testTargetName": "test",
"ciTargetName": "test-ci",
"testMode": "watch"
}
},
{
"plugin": "@nx/playwright/plugin",
"options": {
"targetName": "e2e"
}
}
],
"defaultProject": "campaign-admin",
"generators": {
"@nx/react": {
"application": {
"babel": true,
"style": "css",
"linter": "eslint",
"bundler": "vite"
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint"
}
}
}
}
Loading
Loading