479 lines
12 KiB
Markdown

# Contributing to Changemaker Lite
Thank you for your interest in contributing to Changemaker Lite! This guide will help you get started with contributing code, documentation, bug reports, and feature requests.
## Welcome!
Changemaker Lite is an open-source political campaign platform built by volunteers for organizers. We welcome contributions from developers, designers, writers, and community organizers of all experience levels.
**Our mission**: Provide free, self-hosted tools for grassroots political campaigns to compete with well-funded opponents.
## Ways to Contribute
### 1. Code Contributions
Help build new features or fix bugs in:
- **Backend API** (TypeScript + Express + Prisma)
- **Admin Frontend** (React + Vite + Ant Design)
- **Media API** (TypeScript + Fastify + Drizzle)
- **Infrastructure** (Docker, Nginx, PostgreSQL, Redis)
[→ Development Setup Guide](development-setup.md){ .md-button .md-button--primary }
### 2. Documentation
Improve guides, tutorials, and API documentation:
- **User guides** - Help organizers use the platform
- **Developer docs** - API reference, architecture guides
- **Tutorials** - Step-by-step walkthroughs
- **Translations** - Localize docs for other languages
[→ Documentation Guide](documentation.md){ .md-button }
### 3. Bug Reports
Found a bug? Help us fix it:
- Search existing issues first
- Provide clear reproduction steps
- Include error messages and logs
- Test on latest version
[→ Report a Bug](https://github.com/changemaker-lite/v2/issues/new?template=bug_report.md){ .md-button }
### 4. Feature Requests
Suggest new features or enhancements:
- Check roadmap first
- Describe the use case
- Explain why it's valuable
- Consider implementation complexity
[→ Request a Feature](https://github.com/changemaker-lite/v2/discussions/new?category=ideas){ .md-button }
### 5. Testing
Help test new features and releases:
- Test beta releases on staging
- Verify bug fixes
- Test migration procedures
- Report edge cases
### 6. Community Support
Help other users:
- Answer questions in Discussions
- Share your setup experiences
- Write blog posts or tutorials
- Present at community calls
### 7. Design
Improve user experience:
- UI/UX design mockups
- User flow improvements
- Accessibility enhancements
- Mobile responsiveness
## Code of Conduct
Changemaker Lite is committed to providing a welcoming and inclusive environment for all contributors.
**Our values**:
- **Respect**: Treat everyone with kindness and professionalism
- **Inclusivity**: Welcome contributors from all backgrounds
- **Collaboration**: Work together constructively
- **Constructive feedback**: Focus on improvement, not criticism
[→ Full Code of Conduct](code-of-conduct.md){ .md-button }
**Unacceptable behavior**:
- Harassment, discrimination, or hate speech
- Personal attacks or trolling
- Publishing private information
- Spam or self-promotion
**Enforcement**: Violations will result in warnings, temporary bans, or permanent bans depending on severity.
**Reporting**: Email conduct@cmlite.org to report violations confidentially.
## Getting Started
### Prerequisites
Before contributing code, ensure you have:
- **Node.js 20+** installed
- **Docker Desktop** (or Docker + Docker Compose)
- **Git** for version control
- **Code editor** (VSCode recommended)
- **GitHub account** for pull requests
### Quick Start
1. **Fork the repository** on GitHub
2. **Clone your fork** locally
3. **Set up development environment** ([guide](development-setup.md))
4. **Find an issue** to work on
5. **Create a branch** for your changes
6. **Make your changes** with tests
7. **Submit a pull request** ([guide](pull-requests.md))
### Finding Issues to Work On
**Good first issues**: Look for issues tagged `good-first-issue` in GitHub Issues.
**Help wanted**: Issues tagged `help-wanted` need contributors.
**By skill level**:
- `beginner` - Simple fixes, documentation
- `intermediate` - Feature enhancements, refactoring
- `advanced` - Architecture changes, performance optimization
**By area**:
- `backend` - API, database, services
- `frontend` - React components, UI/UX
- `infrastructure` - Docker, Nginx, deployment
- `documentation` - Guides, tutorials, API docs
[→ Browse Issues](https://github.com/changemaker-lite/v2/issues){ .md-button }
## Contribution Workflow
### 1. Claim an Issue
Before starting work:
1. **Comment on the issue**: "I'd like to work on this"
2. **Wait for assignment**: Maintainer will assign you
3. **Ask questions**: Clarify requirements before coding
!!! tip "Avoid Duplicate Work"
Always check if someone is already assigned before starting work.
### 2. Create a Branch
```bash
# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/campaign-export
# Or for bug fixes
git checkout -b fix/geocoding-error
```
**Branch naming**:
- `feature/description` - New features
- `fix/description` - Bug fixes
- `docs/description` - Documentation
- `refactor/description` - Code refactoring
- `test/description` - Test additions
### 3. Make Changes
Follow our coding standards:
- **TypeScript**: Strict mode, type all functions
- **ESLint**: Run `npm run lint` before committing
- **Prettier**: Auto-format with `npm run format`
- **Tests**: Add tests for new features
- **Comments**: Document complex logic
```typescript
// Good: Type-safe function with comments
/**
* Geocodes an address using the specified provider.
* Falls back to next provider if the first fails.
*
* @param address - Full address string
* @param provider - Geocoding provider (default: nominatim)
* @returns Promise resolving to { lat, lng, quality }
*/
async function geocodeAddress(
address: string,
provider: GeocodingProvider = 'nominatim'
): Promise<GeocodingResult> {
// Implementation
}
```
### 4. Test Your Changes
```bash
# Backend tests
cd api && npm test
# Frontend tests
cd admin && npm test
# Type checking
cd api && npx tsc --noEmit
cd admin && npx tsc --noEmit
# Linting
cd api && npm run lint
cd admin && npm run lint
# Integration tests
docker compose up -d
./scripts/test-integration.sh
```
### 5. Commit Your Changes
**Commit message format** (Conventional Commits):
```
type(scope): short description
Longer description (optional)
Fixes #123
```
**Types**:
- `feat` - New feature
- `fix` - Bug fix
- `docs` - Documentation
- `style` - Formatting, whitespace
- `refactor` - Code restructuring
- `test` - Test additions
- `chore` - Build, tooling
**Examples**:
```
feat(campaigns): add campaign export to CSV
Adds a new export button to the campaigns page that downloads
all campaigns as a CSV file.
Fixes #456
---
fix(geocoding): handle null responses from Nominatim
Prevents crash when Nominatim returns empty result for
invalid addresses.
Fixes #789
---
docs(api): document campaign endpoints
Adds comprehensive API documentation for all campaign endpoints
including request/response examples.
```
### 6. Push and Create Pull Request
```bash
# Push to your fork
git push origin feature/campaign-export
# Create pull request on GitHub
# Fill out the PR template
```
[→ Pull Request Guidelines](pull-requests.md){ .md-button }
### 7. Code Review
After submitting your PR:
1. **Automated checks** run (lint, tests, build)
2. **Maintainer review** provides feedback
3. **Address feedback** with new commits
4. **Request re-review** after changes
5. **Merge** after approval
**Be patient**: Reviews may take 1-3 business days. If no response after 5 days, politely ping the maintainer.
## Development Guidelines
### Code Style
**TypeScript**:
```typescript
// Use interfaces for object shapes
interface Campaign {
id: string;
title: string;
slug: string;
active: boolean;
}
// Use types for unions/aliases
type SupportLevel = 'STRONG_SUPPORT' | 'SUPPORT' | 'UNDECIDED' | 'OPPOSED' | 'STRONG_OPPOSED';
// Prefer async/await over promises
async function getCampaigns(): Promise<Campaign[]> {
const campaigns = await prisma.campaign.findMany();
return campaigns;
}
```
**React**:
```tsx
// Use functional components
const CampaignsPage: React.FC = () => {
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
useEffect(() => {
fetchCampaigns();
}, []);
return <Table dataSource={campaigns} />;
};
// Extract reusable components
const CampaignCard: React.FC<{ campaign: Campaign }> = ({ campaign }) => {
return <Card title={campaign.title} />;
};
```
**Prisma**:
```typescript
// Use type-safe queries
const campaigns = await prisma.campaign.findMany({
where: { active: true },
include: { createdBy: true },
orderBy: { createdAt: 'desc' }
});
// Use transactions for multi-step operations
await prisma.$transaction(async (tx) => {
await tx.campaign.update({ where: { id }, data: { active: false } });
await tx.campaignEmail.updateMany({ where: { campaignId: id }, data: { status: 'CANCELLED' } });
});
```
### Testing Guidelines
**Unit tests**:
```typescript
// api/src/modules/campaigns/campaigns.service.test.ts
describe('CampaignService', () => {
it('should create campaign with valid data', async () => {
const campaign = await campaignService.create({
title: 'Test Campaign',
slug: 'test-campaign',
createdByUserId: 'user-id'
});
expect(campaign).toHaveProperty('id');
expect(campaign.title).toBe('Test Campaign');
});
it('should throw error for duplicate slug', async () => {
await expect(
campaignService.create({ title: 'Test', slug: 'existing', createdByUserId: 'user-id' })
).rejects.toThrow('Slug already exists');
});
});
```
**Integration tests**:
```typescript
// api/tests/campaigns.integration.test.ts
describe('Campaigns API', () => {
it('GET /api/influence/campaigns returns campaigns', async () => {
const response = await request(app)
.get('/api/influence/campaigns')
.set('Authorization', `Bearer ${adminToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(Array.isArray(response.body.data)).toBe(true);
});
});
```
## Communication Channels
### GitHub
- **Issues**: Bug reports, feature requests
- **Discussions**: General questions, ideas
- **Pull Requests**: Code contributions
### Email
- **General**: hello@cmlite.org
- **Security**: security@cmlite.org
- **Code of Conduct**: conduct@cmlite.org
### Community Calls
- **Monthly Contributors Call**: First Tuesday of month, 7pm UTC
- **Quarterly Community Call**: Last Friday of quarter, 6pm UTC
[→ Join Calls](https://cmlite.org/community){ .md-button }
## Recognition
We appreciate all contributors! Your name will be:
- **Added to CONTRIBUTORS.md** after first merged PR
- **Listed in release notes** for significant contributions
- **Featured on website** for major features
- **Invited to community calls** as a contributor
### Hall of Fame
**Top Contributors** (all time):
1. @contributor1 - 234 commits
2. @contributor2 - 189 commits
3. @contributor3 - 156 commits
[→ Full Contributors List](https://github.com/changemaker-lite/v2/graphs/contributors)
## License
By contributing to Changemaker Lite, you agree that your contributions will be licensed under the **MIT License**.
This means:
- Your code can be used by anyone
- Attribution is required (copyright notice)
- No warranty is provided
See [LICENSE](https://github.com/changemaker-lite/v2/blob/main/LICENSE) for full terms.
## Questions?
- **Need help getting started?** Ask in [Discussions](https://github.com/changemaker-lite/v2/discussions)
- **Have a question about an issue?** Comment on the issue
- **Stuck on development setup?** Check [Development Setup Guide](development-setup.md)
- **Want to chat?** Join our monthly contributors call
## Related Documentation
- [Code of Conduct](code-of-conduct.md) - Community standards
- [Development Setup](development-setup.md) - Environment setup
- [Pull Request Guidelines](pull-requests.md) - PR process
- [Roadmap](roadmap.md) - Future plans
## Next Steps
Ready to contribute?
1. **[Read the Code of Conduct](code-of-conduct.md)** - Understand community standards
2. **[Set up your environment](development-setup.md)** - Install dependencies
3. **[Find an issue](https://github.com/changemaker-lite/v2/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)** - Pick something to work on
4. **[Submit your first PR](pull-requests.md)** - Make your contribution
Thank you for contributing to Changemaker Lite! Together, we're building tools for democratic change.