Development Guide¶
This guide covers the development workflow, testing, and contributing to Coalition Builder.
Development Workflow¶
Quick Setup¶
For most developers, the Installation guide provides everything needed. For detailed development environment setup including automated tooling installation, see the Development Setup guide.
Docker Compose Configurations¶
Coalition Builder uses two Docker Compose configurations:
Production Configuration (docker compose.yml
)¶
- Purpose: Production deployments and CI/CD pipelines
- Features: Optimized builds, production environment variables, no volume mounts
- Usage:
docker compose up -d
Development Configuration (docker compose.dev.yml
)¶
- Purpose: Local development with live code reload
- Features: Volume mounts for instant code changes, development build targets, debug mode
- Usage:
docker compose -f docker compose.yml -f docker compose.dev.yml up -d
Key Differences:
- Build Targets: Production uses full builds, development uses
target: deps
for faster rebuilds - Volume Mounts: Development mounts source code for live reload
- Environment Variables:
DEBUG=True
andNODE_ENV=development
in dev mode - Commands: Development uses
npm run dev
andrunserver
, production uses optimized startup commands
Code Style¶
- Python: Black formatting, Ruff linting
- TypeScript/React: Prettier formatting, ESLint
- Git: Conventional commit messages
CI/CD Pipeline¶
The Coalition Builder project uses GitHub Actions for continuous integration and deployment. The workflow automatically tests all components and deploys to AWS on successful builds.
Note: This diagram uses Mermaid syntax and will render automatically on GitHub and other platforms that support Mermaid.
%%{init: {'theme':'basic'}}%%
flowchart TD
%% Main trigger
push[Push/PR to main] --> check_app[Check App]
%% Check App branches
check_app --> frontend_check[Frontend Check]
check_app --> backend_check[Backend Check]
check_app --> terraform_check[Terraform Check]
check_app --> fullstack_tests[Full Stack Tests]
%% Frontend workflow
frontend_check --> ts_lint[TypeScript Lint]
frontend_check --> prettier[Prettier Format]
ts_lint --> frontend_lint_complete{"Frontend<br/>Linting<br/>Passed?"}
prettier --> frontend_lint_complete
frontend_lint_complete --> frontend_tests[Frontend Tests]
frontend_lint_complete --> ssr_tests[SSR Tests]
%% Backend workflow
backend_check --> python_lint[Python Lint]
python_lint --> backend_tests[Backend Tests]
%% Terraform workflow
terraform_check --> tf_lint[Terraform Lint]
terraform_check --> go_lint[Go Lint]
tf_lint --> terraform_lint_complete{"Terraform<br/>Linting<br/>Passed?"}
go_lint --> terraform_lint_complete
terraform_lint_complete --> terraform_tests[Terraform Tests]
%% All tests converge to deployment decision
frontend_tests --> tests_complete{"All Tests<br/>Passed?"}
ssr_tests --> tests_complete
backend_tests --> tests_complete
terraform_tests --> tests_complete
fullstack_tests --> tests_complete
%% Decision to deployment
tests_complete --> app_deploy[Application Deployment]
%% Deployment to AWS
app_deploy --> ecs[Amazon ECS]
The CI/CD pipeline includes:
- Parallel Testing: Frontend, backend, and infrastructure tests run simultaneously
- Code Quality: Automated linting and formatting checks
- Full Stack Integration: End-to-end testing across all components
- Automated Deployment: Successful builds deploy to AWS ECS
Running Tests¶
# Backend tests
cd backend
poetry run pytest
# Frontend tests
cd frontend
npm test
# Backend tests
cd backend && poetry run pytest
# Frontend tests
cd frontend && npm test
# Infrastructure tests
cd terraform && go test ./tests/...
For comprehensive testing documentation, see the Testing Guide.
Code Quality¶
Site Password Protection¶
Coalition Builder includes a flexible password protection system for securing your site during development and testing.
Architecture Overview¶
The system provides multiple protection layers:
Development Options:
- SSR Container (Port 3000): Next.js middleware with HTTP Basic Authentication
- nginx Proxy (Port 80): Optional reverse proxy with HTTP Basic Authentication
- API Container (Port 8000): django-lockdown middleware with session-based authentication
Production:
- ALB → SSR: Next.js middleware protects frontend routes
- ALB → API: django-lockdown middleware protects API endpoints
Quick Start¶
Enable password protection by setting environment variables in your .env
file:
# Enable password protection
SITE_PASSWORD_ENABLED=true
SITE_USERNAME=admin
SITE_PASSWORD=your-secure-password
# Restart containers
docker compose up -d
Access Options¶
# Direct SSR access with Next.js middleware protection
http://localhost:3000
# Through nginx proxy (if enabled)
http://localhost:80
# Direct API access with Django middleware protection
http://localhost:8000
Environment Variables¶
Variable | Description | Default | Used By |
---|---|---|---|
SITE_PASSWORD_ENABLED |
Enable protection | false |
All containers |
SITE_USERNAME |
HTTP Basic Auth username | admin |
SSR, nginx |
SITE_PASSWORD |
Site access password | changeme |
All containers |
Production Management¶
Set these GitHub repository secrets for production deployment:
SITE_PASSWORD_ENABLED
:true
orfalse
SITE_USERNAME
: Username for authenticationSITE_PASSWORD
: Secure password
Changes apply automatically when infrastructure is deployed via the deploy_infra.yml
workflow.
For detailed production setup, see the Site Password Protection Guide.
Project Structure¶
coalition-builder/
├── backend/ # Django API
│ ├── coalition/ # Main app
│ ├── docs/ # Sphinx API docs
│ └── scripts/ # Backend scripts
├── frontend/ # React frontend
├── ssr/ # Next.js SSR
├── terraform/ # Infrastructure
└── docs/ # Main documentation
Key Technologies¶
- Backend: Django 5.2, Django Ninja, PostGIS
- Frontend: React 19, TypeScript, Tailwind CSS
- Database: PostgreSQL with PostGIS extension
- Infrastructure: AWS, Terraform
- Documentation: Sphinx, TypeDoc, MkDocs
Contributing¶
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Ensure all tests pass
- Submit a pull request
Code Guidelines¶
- Write meaningful commit messages
- Add tests for new features
- Update documentation as needed
- Follow existing code patterns
API Development¶
The API is automatically documented from code. When adding new endpoints:
- Use proper type hints
- Add docstrings to views and models
- Update API tests
- Regenerate documentation
Data Models¶
Key models include:
- Bill: Tracks federal and state legislative bills with chamber-specific formatting
- Legislator: Manages federal and state legislators with appropriate validation
- PolicyCampaign: Organizes advocacy campaigns with endorsement support
- Stakeholder: Stores supporter information with geographic data
- Endorsement: Handles campaign endorsements with verification workflow
- Theme: Manages visual branding and customization settings
Theme System Development¶
Coalition Builder includes a comprehensive theme system for visual customization:
Architecture¶
- Backend: Django model with color validation and CSS generation
- Frontend: Styled-components with theme provider for React components
- SSR: Full theme support in Next.js server-side rendering
- API: REST endpoints for programmatic theme management
Key Components¶
Backend (/backend/coalition/core/
)¶
models.py
: Theme model with hex color validationtheme_service.py
: CSS variable generation service/api/themes.py
: Theme management API endpoints
Frontend (/frontend/src/
)¶
contexts/ThemeContext.tsx
: React context for theme datacontexts/StyledThemeProvider.tsx
: Styled-components theme providerstyles/theme.ts
: Theme type definitions and utilitiescomponents/styled/
: Reusable styled components library
Shared (/shared/
)¶
utils/theme.ts
: Common theme utilities for both frontend and SSRcomponents/
: Shared components with theme support
Development Guidelines¶
Adding Styled Components¶
import styled from "styled-components";
const MyComponent = styled.div<{ variant?: "primary" | "secondary" }>`
background-color: ${(props) =>
props.variant === "secondary"
? props.theme.colors.secondary
: props.theme.colors.primary};
color: ${(props) => props.theme.colors.white};
padding: ${(props) => props.theme.spacing[4]};
border-radius: ${(props) => props.theme.radii.md};
`;
Using Theme Context¶
import { useTheme } from '../contexts/ThemeContext';
const MyComponent = () => {
const { theme, loading } = useTheme();
if (loading) return <div>Loading theme...</div>;
return (
<div style={{ color: theme?.primary_color }}>
Themed content
</div>
);
};
API Integration¶
// Fetch active theme
const response = await fetch("/api/theme/active/");
const theme = await response.json();
// Update theme
const updatedTheme = await fetch(`/api/theme/${id}/`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(themeData),
});
Testing Themes¶
Backend Tests¶
- Model validation (hex colors, required fields)
- CSS generation functionality
- API endpoint responses
Frontend Tests¶
- Theme provider functionality
- Component rendering with different themes
- API integration tests
Integration Tests¶
- End-to-end theme switching
- SSR theme rendering
- CSS variable generation
Component Development¶
Shared Component Architecture¶
Coalition Builder uses a shared component architecture where:
- Components: All React components live in
/frontend/src/components/
- Types: Shared TypeScript interfaces are in
/frontend/src/types/
- SSR Integration: Next.js imports components from
/frontend
using@frontend
aliases - Testing: Unit tests are in
/frontend/src/components/__tests__/
Guidelines for React Components¶
- Create components in
/frontend/src/components/
- they'll be used by both SPA and SSR - Use TypeScript interfaces from
/frontend/src/types/
- Add JSDoc comments for better documentation
- Write Jest unit tests in
__tests__/
subdirectories - Export types for reuse across both frontend and SSR
- Handle errors gracefully with fallback UI and user-friendly messages
Error Handling Pattern¶
When creating components that fetch data:
// Use Promise.allSettled for parallel API calls
const [dataResult, otherResult] = await Promise.allSettled([
API.getData(),
API.getOtherData(),
]);
// Handle each result separately
if (dataResult.status === "fulfilled") {
setData(dataResult.value);
} else {
setError("Failed to load data");
// Show fallback UI, don't expose raw errors
}