Skip to content

BenjaminBini/boamp

Repository files navigation

BOAMP Viewer

A full-stack React application for tracking and managing French public procurement announcements (Appels d'Offres) from the BOAMP (Bulletin Officiel des Annonces des Marchés Publics) database.

Features

  • AO Tracking System: Tag and track announcements through their lifecycle (new → analyzing → in_progress → won/lost, or analyzing → no_go)
  • Kanban-Style Interface: Organize AOs in columns by status for visual project management
  • Real-time Data: Fetches live data from the BOAMP OpenDataSoft API
  • Smart Filtering: Multi-dimensional filtering by publication date, response date, and status
  • Expandable Cards: Modern card-based display with smooth expand/collapse animations
  • Persistent Storage: SQLite database tracks your AO status and notes locally
  • Responsive Design: Built with Tailwind CSS for mobile and desktop
  • Type-Safe: Full TypeScript coverage with proper interfaces
  • Background Jobs: Automatic AO expiration and sync processes

Architecture

  • Frontend: React 18 + TypeScript + Tailwind CSS + Vite (port 5173)
  • Backend: Express.js + SQLite REST API (port 3001)
  • Data Source: BOAMP OpenDataSoft API with ODSQL filtering
  • State Management: Tag-based organization with separate state arrays

Tech Stack

Frontend

  • React 18.3 - Modern UI library with hooks
  • Vite 6.0 - Lightning-fast build tool and dev server
  • TypeScript 5.7 - Type-safe JavaScript
  • Tailwind CSS 3.4 - Utility-first CSS framework
  • Framer Motion 12 - Smooth animations and transitions

Backend

  • Express.js 4.18 - Web application framework
  • SQLite3 5.1 - Embedded database for tracking
  • CORS - Cross-origin resource sharing
  • Body Parser - Request body parsing middleware

Project Structure

boamp/
├── src/                        # Frontend React application
│   ├── App.tsx                 # Main app with tag-based state management
│   ├── components/
│   │   ├── ao/                 # AO-specific components
│   │   │   ├── GridCard.tsx    # Main record display card
│   │   │   ├── AnalyzeButton.tsx, InProgressButton.tsx, etc.
│   │   │   └── ActionButton.tsx # Base action button component
│   │   ├── ui/                 # Reusable UI components
│   │   │   ├── FilterBar.tsx   # Multi-dimensional filtering
│   │   │   ├── Badge.tsx       # Status badges with colors
│   │   │   └── LoadingSpinner.tsx, Toast.tsx, etc.
│   │   └── layout/             # Layout components
│   │       ├── ColumnContainer.tsx # Kanban-style columns
│   │       ├── Header.tsx, Footer.tsx
│   │       └── FilterBanner.tsx
│   ├── services/
│   │   └── trackingApi.ts      # Backend API client for tracking
│   ├── types/
│   │   └── boamp.ts            # TypeScript interfaces
│   ├── utils/
│   │   ├── dateFilters.ts      # Date filtering logic
│   │   ├── dateUtils.ts        # Date formatting utilities
│   │   └── statusColors.ts     # Status color mapping
│   └── constants/
│       └── filters.ts          # BOAMP filter constants
├── server/                     # Backend Express.js application
│   ├── server.js               # Main server with job scheduling
│   ├── db.js                   # SQLite database setup
│   ├── routes/
│   │   └── ao.js              # AO tracking REST endpoints
│   └── jobs/                   # Background processes
│       ├── expireAOs.js        # Auto-expire old AOs
│       └── syncBOAMP.js        # Sync with BOAMP API
├── docs/                       # Documentation
├── .github/
│   └── copilot-instructions.md # AI agent guidance
└── Configuration files (vite, typescript, tailwind, etc.)

Getting Started

Prerequisites

  • Node.js 18+ and npm

Installation

Install dependencies for both frontend and backend:

# Frontend dependencies
npm install

# Backend dependencies
cd server && npm install

Development

The application requires both frontend and backend servers:

Option 1: Run both simultaneously

# Terminal 1: Frontend (Vite dev server)
npm run dev

# Terminal 2: Backend (Express server with --watch)
cd server && npm run dev

Option 2: One-liner (runs both in background)

npm run dev & cd server && npm run dev
  • Frontend: http://localhost:5173
  • Backend API: http://localhost:3001
  • Health check: http://localhost:3001/health

Database

The SQLite database (server/boamp.db) is automatically created on first run with the following tables:

  • ao_tracking - AO status and tags
  • ao_blacklist - Discarded AOs

Build

Create a production build:

npm run build

Linting

npm run lint

API Integration

External BOAMP API

  • Endpoint: https://boamp-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/boamp/records
  • Query Language: ODSQL (Opendatasoft Query Language)
  • Default Filters:
    • descripteur_code:(163 OR 186 OR 183) - Specific procurement categories
    • Date filters for publication and response deadlines

Internal Tracking API

  • Base URL: http://localhost:3001/api
  • Endpoints:
    • GET /ao/:idweb - Get tracking info for an AO
    • POST /ao/:idweb/tag - Set tag/status for an AO
    • POST /ao/:idweb/discard - Soft delete an AO
    • GET /ao/status/:tag - Get all AOs with specific tag
    • GET /health - Server health check

Key Components

App.tsx

Main application with tag-based state management:

const [newRecords, setNewRecords] = useState<BOAMPRecord[]>([]);
const [analyzingRecords, setAnalyzingRecords] = useState<BOAMPRecord[]>([]);
// ... separate state for each tag

GridCard

Modern card component featuring:

  • Smooth expand/collapse with Framer Motion
  • Status badges with color coding
  • Action buttons for status transitions
  • JSON data tooltip for debugging

FilterBar

Multi-dimensional filtering:

  • Publication date (today, yesterday, last week, etc.)
  • Response date (today, tomorrow, next week, etc.)
  • Status filter (all, new, analyzing, etc.)
  • Real-time count display for each filter option

Action Buttons

Individual components for each status transition:

  • AnalyzeButton - Move to analyzing
  • InProgressButton - Move to in_progress
  • WonButton, LostButton, NoGoButton - Final states
  • RemoveTagButton - Remove status/tag
  • DeleteButton - Soft delete (discard)

AO Lifecycle & Status Flow

### Status Flow

AO announcements progress through specific state transitions:

- **new** → **analyzing** (research/evaluation) OR **archive** (discard)
- **analyzing** → **in_progress** (active pursuit) OR **no_go** (decision not to pursue)
- **in_progress** → **won** (successful) OR **lost** (unsuccessful)
- Final states (**won/lost/no_go**) → **remove tag** (return to untracked)
 ↓         ↓           ↓            ↓
Remove tag at any stage, or discard (soft delete)

Status Definitions

  • new: Recently imported AOs awaiting review
  • analyzing: AOs under evaluation for relevance/fit
  • in_progress: AOs being actively pursued (proposal preparation)
  • won: Successfully awarded contracts
  • lost: Unsuccessful bids
  • no_go: AOs determined not worth pursuing

Database Schema

ao_tracking Table

CREATE TABLE ao_tracking (
  idweb TEXT PRIMARY KEY,           -- BOAMP record ID
  tag TEXT CHECK(tag IN (...)),     -- Status enum
  discarded INTEGER DEFAULT 0,     -- Soft delete flag
  record_data TEXT,                -- Full BOAMP JSON record
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

The application stores the complete BOAMP record in record_data for offline access and to avoid re-fetching data.

Data Fields

Each BOAMP record includes:

  • idweb: Unique identifier
  • objet: Announcement title/object
  • nomacheteur: Buyer organization name
  • datelimitereponse: Response deadline
  • dateparution: Publication date
  • datefindiffusion: End of distribution date
  • etat: Status (publié, archivé, annulé)
  • descripteur_code/libelle: Category descriptors (163, 186, 183)
  • type_marche: Contract type
  • procedure_libelle: Procedure type
  • nature_libelle: Nature of the contract
  • url_avis: Link to full announcement
  • And more...

Background Jobs

  • expireAOs.js: Automatically moves old AOs out of active status
  • syncBOAMP.js: Periodic sync with BOAMP API for new announcements
  • Jobs are scheduled in server.js using setInterval

Development Notes

  • Uses ES modules (import/export) throughout
  • Tailwind for all styling with custom color schemes
  • TypeScript strict mode enabled
  • Framer Motion for smooth animations
  • CORS enabled for localhost development

Data Source

Data is provided by BOAMP DataDila, the official source for French public procurement announcements.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •