# 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 { // 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 { const campaigns = await prisma.campaign.findMany(); return campaigns; } ``` **React**: ```tsx // Use functional components const CampaignsPage: React.FC = () => { const [campaigns, setCampaigns] = useState([]); useEffect(() => { fetchCampaigns(); }, []); return ; }; // Extract reusable components const CampaignCard: React.FC<{ campaign: Campaign }> = ({ campaign }) => { return ; }; ``` **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.