Skip to content

caeher/competitive-intelligence-dashboard

Competitive Intelligence Dashboard

Full-stack dashboard for monitoring competitors, products, and market signals from a single interface. The project centralizes metrics, news, and relevant events to compare entities, detect changes, and expose them through a REST API and real-time updates.

Repository: https://github.com/caeher/competitive-intelligence-dashboard

What Is This Project?

This repository includes:

  • A modular backend built with Express + TypeScript + Prisma.
  • A frontend built with React + Vite + Tailwind CSS.
  • JSON file-based configuration for entities, verticals, fetchers, scheduler, and thresholds.
  • Persistence with Prisma and a SQLite database for the local workflow and Docker setup included in the repo.

Main use cases:

  • Browse entities and their products.
  • Compare metrics across multiple entities.
  • Aggregate news and external signals from multiple sources.
  • Expose system operational status and health.

General Architecture

Frontend (React + Vite)
    |
    | HTTP -> /api/v1
    | WS   -> /ws
    v
Backend (Express + TypeScript)
    |
    +-> Prisma
    +-> JSON config in backend/config/
    +-> Scheduled jobs
    +-> Alerts and events

Repository Structure

Path Description
backend/ API, domain modules, Prisma, configuration, and jobs
backend/config/ Entities, verticals, fetchers, scheduler, and thresholds
frontend/ React web application
spec/ Project specifications and contracts
presentation/ Five-slide PDF deck (competitive-intelligence-dashboard.pdf) + regen script at repo root
CONTRIBUTING.md Contribution guide
CODE_OF_CONDUCT.md Code of conduct
SECURITY.md Vulnerability reporting process

Deploying for Your Organization

The dashboard is config-driven: swap Blockstream’s reference dataset for your own companies without changing application code.

  1. Verticals — Edit backend/config/verticals.json. Each vertical needs a unique id, name, and optional displayOrder.
  2. Entities — Edit backend/config/entities.json (see minimal skeleton backend/config/entities.json.example). For each entity set name, isPrimary, verticals, newsKeywords, and identifiers (GitHub org/repos, appStore.ios / appStore.android, website, Twitter, etc.). Add products for SKU-level apps/repos.
  3. Fetchers & schedule — Enable or disable sources in backend/config/fetchers.json and tune cron expressions in backend/config/scheduler.json.
  4. Thresholds — Edit backend/config/thresholds.json for alert sensitivity.
  5. Apply — Run npm run prisma:push --prefix backend (or your migration workflow) and start the app; the backend syncs config into Prisma when core rows are missing.

Candidate surfacing from news uses optional candidates settings in backend/config/app.json (domainKeywords, mentionThreshold, confidenceThreshold).

Main Stack

Layer Technologies
Frontend React 19, Vite 8, Tailwind CSS 4, TanStack Table, Recharts
Backend Node.js, Express 4, TypeScript 5, Prisma 5, Zod, Pino, ws, node-cron
Database SQLite in the repository's included setup
Containers Docker, Docker Compose, Nginx

Requirements

  • Node.js 20 or later
  • npm 10 or later
  • Docker Desktop + Docker Compose v2 if you plan to use containers

Running the Project Locally

1. Install dependencies

From the repository root:

npm install
npm run install:all

2. Create the backend environment file

In PowerShell:

Copy-Item backend/.env.example backend/.env

On macOS or Linux:

cp backend/.env.example backend/.env

3. Initialize Prisma and the local database

npm run prisma:generate --prefix backend
npm run prisma:push --prefix backend

The backend syncs and seeds base data from backend/config/*.json when it detects essential data is missing. If you need to force the seed manually:

npm run prisma:seed --prefix backend

4. Start the frontend and backend

npm run dev

5. Default development URLs

  • Frontend: http://localhost:5173
  • Backend: http://localhost:3001
  • API: http://localhost:3001/api/v1
  • WebSocket: ws://localhost:3001/ws

Deliverable deck: after npm install at the repo root, run npm run presentation:pdf to regenerate presentation/competitive-intelligence-dashboard.pdf (5 slides: problem, architecture, demo notes, implemented vs SPEC, next steps).

Useful notes:

  • In local development, Vite proxies /api to http://localhost:3001.
  • The backend exposes a root endpoint at / and health routes under /api/v1/health and /api/v1/status.

Running with Docker

The repository's Docker configuration starts:

  • backend on Node.js with Prisma and SQLite persisted in a Docker volume.
  • frontend served with Nginx, proxying requests to the backend API and WebSocket.

For local machine access through published ports, use the dedicated override file docker-compose.local.yml.

1. Optional variables

If you want to enable external integrations, you can define these variables before starting the containers:

  • GITHUB_TOKEN
  • NEWS_API_KEY

You can export them in your terminal or define them in a root .env file so Docker Compose reads them automatically.

2. Build and start the services

docker compose up --build

To run in the background:

docker compose up --build -d

For local access from your PC, start Docker Compose with the local override:

docker compose -f docker-compose.yml -f docker-compose.local.yml up --build

Background mode:

docker compose -f docker-compose.yml -f docker-compose.local.yml up --build -d

The local override publishes only on 127.0.0.1:

  • Frontend: 127.0.0.1:3000 -> 80
  • Backend: 127.0.0.1:3001 -> 3001

You can override those host ports with environment variables:

$env:LOCAL_FRONTEND_PORT = "8080"
$env:LOCAL_BACKEND_PORT = "3101"
docker compose -f docker-compose.yml -f docker-compose.local.yml up --build

3. Access in Docker

  • Web app: http://localhost:3000
  • API through Nginx proxy: http://localhost:3000/api/v1
  • WebSocket through Nginx proxy: ws://localhost:3000/ws
  • Backend direct access when using the local override: http://localhost:3001

Important:

  • docker-compose.yml keeps backend and frontend on the internal Docker network.
  • docker-compose.local.yml is the local-only override that publishes frontend and backend on 127.0.0.1.
  • The SQLite database is persisted in the backend-data volume.

4. Useful Docker commands

View logs:

docker compose logs -f

Stop services:

docker compose down

Also remove the data volume:

docker compose down -v

Useful Scripts

Root scripts

Script Description
npm run dev Starts backend and frontend in parallel
npm run dev:backend Starts only the backend
npm run dev:frontend Starts only the frontend
npm run build Builds backend and frontend
npm run install:all Installs dependencies for backend/ and frontend/

Backend scripts

Script Description
npm run dev --prefix backend Runs the backend in development mode
npm run build --prefix backend Compiles TypeScript
npm run start --prefix backend Runs the compiled backend
npm run typecheck --prefix backend Checks types without emitting files
npm run test --prefix backend Runs backend tests
npm run prisma:generate --prefix backend Generates the Prisma client
npm run prisma:push --prefix backend Syncs the schema to the database
npm run prisma:migrate --prefix backend Runs development migrations
npm run prisma:seed --prefix backend Seeds data from configuration

Frontend scripts

Script Description
npm run dev --prefix frontend Starts Vite
npm run build --prefix frontend Builds the frontend
npm run lint --prefix frontend Runs ESLint
npm run preview --prefix frontend Serves the Vite build for validation

Key Backend Environment Variables

The base file is backend/.env.example.

Variable Default Description
NODE_ENV development Execution environment
PORT 3001 Backend HTTP port
DATABASE_URL file:./dev.db Prisma connection string
LOG_LEVEL info Log level
CORS_ORIGIN http://localhost:5173 Allowed CORS origin
ENABLE_CRON true Enables scheduled jobs
MAIL_PROVIDER console Mail provider
GITHUB_TOKEN empty Optional token for the GitHub API
NEWS_API_KEY empty Optional API key for NewsAPI

Main Application Routes

Frontend

  • /entities
  • /entities/:entityId
  • /entities/:entityId/products/:productId
  • /compare

Backend

All business routes live under /api/v1.

Examples:

  • GET /api/v1/health
  • GET /api/v1/status
  • GET /api/v1/entities
  • GET /api/v1/entities/:entityId
  • GET /api/v1/entities/datatable
  • GET /api/v1/entities/select
  • GET /api/v1/compare
  • GET /api/v1/feed
  • GET /api/v1/feed/:feedItemId
  • PATCH /api/v1/feed/:feedItemId
  • GET /api/v1/alerts
  • GET /api/v1/alerts/:alertId
  • POST /api/v1/alerts/:alertId/acknowledge
  • GET /api/v1/candidates
  • GET /api/v1/candidates/:candidateId
  • POST /api/v1/candidates/:candidateId/confirm
  • POST /api/v1/candidates/:candidateId/dismiss
  • GET /api/v1/fetchers/health
  • POST /api/v1/fetchers/:sourceId/refresh

API Query Examples

Compare entities

GET /api/v1/compare?entities=blockstream,trezor&metrics=stars,forks,commits30d

List entities sorted by stars

GET /api/v1/entities?sort=stars&direction=desc&page=1&limit=10

Datatable for entities

GET /api/v1/entities/datatable?draw=1&start=0&length=20&search[value]=block

Response shape:

{
  "draw": 1,
  "recordsTotal": 30,
  "recordsFiltered": 3,
  "data": [...]
}

Select dropdown for entities

GET /api/v1/entities/select?search=block&page=1&limit=20

Response shape:

{
  "items": [{ "value": "blockstream", "label": "Blockstream" }],
  "pagination": { "page": 1, "limit": 20, "hasMore": false, "total": 1 }
}

Unified feed with filters

GET /api/v1/feed?entityId=blockstream&itemType=article&from=2026-04-01T00:00:00Z&page=1&limit=20

Alert feed filtered by severity

GET /api/v1/alerts?severity=high&isAcknowledged=false&page=1&limit=10

Acknowledge an alert

POST /api/v1/alerts/{alertId}/acknowledge
Content-Type: application/json

{ "acknowledgedBy": "analyst@example.com" }

List pending competitor candidates

GET /api/v1/candidates?status=pending&sort=confidenceScore&direction=desc

Confirm a candidate

POST /api/v1/candidates/{candidateId}/confirm
Content-Type: application/json

{ "reviewedBy": "analyst", "reviewNotes": "Valid competitor" }

Dismiss a candidate

POST /api/v1/candidates/{candidateId}/dismiss
Content-Type: application/json

{ "reviewedBy": "analyst", "reviewNotes": "Not relevant" }

WebSocket Channels

Connect to ws://localhost:3001/ws and subscribe to channels:

{ "type": "subscribe", "channels": ["alerts", "feed", "candidates"] }

The server broadcasts events on these channels:

  • alerts — New alert detections (type: alert)
  • feed — New feed items ingested (type: feed_item)
  • candidates — New competitor candidates detected (type: metric_update)

How to Contribute to This Repository

Before contributing, review:

Recommended workflow summary:

  1. Open an issue with context, expected scope, and impacted areas.
  2. Propose specification changes first if behavior is going to change.
  3. Keep code, contracts, configuration, and documentation in sync.
  4. Run local validations before opening a PR.
  5. Submit small, clear PRs with explicit risks.

Contribution conventions

  • Use branches in the format feature/<topic>, fix/<topic>, or docs/<topic>.
  • Use PR titles in the format [area] short summary.
  • Keep behavior configuration-driven when the repo already models it that way.
  • Avoid hardcoding ids, entity names, or thresholds in code.
  • If you change contracts, review the impact on backend, frontend, Prisma, and documentation.

Recommended validations before a PR

npm run typecheck --prefix backend
npm run test --prefix backend
npm run lint --prefix frontend
npm run build

If you change Prisma:

npm run prisma:generate --prefix backend
npm run prisma:push --prefix backend

Reference Files

  • Functional configuration: backend/config/
  • Specifications: spec/
  • Entity contract: spec/ENTITY-CONFIG.md
  • Implementation status and future development: spec/SPEC.md
  • Contribution guide: CONTRIBUTING.md

Quick Troubleshooting

Missing DATABASE_URL

  • Verify that backend/.env exists.
  • Check that DATABASE_URL has a valid value.
  • Restart npm run dev.

Prisma issues

Run again:

npm run prisma:generate --prefix backend
npm run prisma:push --prefix backend

The frontend cannot connect to the API

  • Confirm that the backend is running at http://localhost:3001.
  • Check CORS_ORIGIN in backend/.env.
  • In Docker, use the app from http://localhost:3000, not directly from the backend container.

License

This project is distributed under the license included in LICENSE.

About

A Competitive Intelligence Dashboard

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages