Contribution Guide
Thank you for your interest in contributing to CheerKeeper! This guide explains how to get involved.
Getting Started
- Set up your local environment
- Familiarize yourself with the architecture
- Review the database schema
Types of Contributions
Bug Fixes
Found a bug? We appreciate fixes for:
- UI/UX issues
- Data handling errors
- Performance problems
- Mobile app crashes
Features
Have an idea? Consider:
- Is this something event organizers need?
- Does it help parents find their kids' performances?
- Is it simple enough for all user skill levels?
Documentation
Help improve our docs:
- Fix typos and unclear sections
- Add examples and screenshots
- Translate to other languages
Development Workflow
1. Create a Branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-description
2. Make Changes
- Write clean, readable code
- Follow existing patterns
- Add tests for new functionality
- Update documentation as needed
3. Test Locally
# Admin
npm run lint
npm run typecheck
npm run test
# Mobile
npx expo start --clear
4. Commit
Write clear commit messages:
# Good
git commit -m "Add session filtering to schedule view"
git commit -m "Fix notification timing on Android"
# Avoid
git commit -m "Fix stuff"
git commit -m "WIP"
5. Open a Pull Request
- Describe what changed and why
- Link related issues
- Include screenshots for UI changes
- Request review from maintainers
Code Style
TypeScript
// Use explicit types
function getEvent(id: string): Promise<Event> {
// ...
}
// Prefer interfaces for objects
interface EventInput {
name: string;
startDate: Date;
location?: Location;
}
// Use const for immutable values
const MAX_PERFORMANCES = 500;
React Components
// Functional components with typed props
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
export function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{label}
</button>
);
}
File Naming
- Components:
PascalCase.tsx→EventCard.tsx - Utilities:
camelCase.ts→formatDate.ts - Constants:
kebab-case.ts→competition-types.ts - Tests:
*.test.ts→formatDate.test.ts
Project Conventions
API Routes
// app/api/events/route.ts
export async function GET(request: Request) {
const session = await getUnifiedSession();
if (!session) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const events = await prisma.event.findMany({
where: { companyId: session.companyId },
});
return Response.json({ data: events });
} catch (error) {
return Response.json({ error: 'Internal error' }, { status: 500 });
}
}
Database Queries
// Use Prisma's type-safe queries
const event = await prisma.event.findUnique({
where: { id: eventId },
include: {
sessions: true,
eventUnits: {
orderBy: { performanceTime: 'asc' },
},
},
});
Error Handling
// Throw typed errors
throw new SessionNotFoundError(sessionId);
// Return appropriate HTTP codes
return Response.json(
{ error: { code: 'SESSION_NOT_FOUND', message: 'Session not found' } },
{ status: 409 }
);
Testing
Unit Tests
import { calculateCascade } from './cascade';
describe('calculateCascade', () => {
it('shifts performances after changed time', () => {
const performances = [
{ id: '1', time: '09:00' },
{ id: '2', time: '09:08' },
];
const result = calculateCascade(performances, '1', 15);
expect(result[0].time).toBe('09:15');
expect(result[1].time).toBe('09:23');
});
});
Integration Tests
describe('POST /api/events', () => {
it('creates an event', async () => {
const response = await fetch('/api/events', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ name: 'Test Event' }),
});
expect(response.status).toBe(201);
const data = await response.json();
expect(data.name).toBe('Test Event');
});
});
Documentation
When adding features, update:
- JSDoc comments for functions
- README if setup changes
- This docs site for user-facing features
Doc Style
- Use simple language
- Include code examples
- Add screenshots for UI features
- Link to related docs
Review Process
- Automated checks run on PR
- Maintainer review for code quality
- Testing in staging environment
- Merge after approval
Need Help?
- Open a GitHub issue for questions
- Email support@cheerkeeper.com
- Check existing issues and PRs
Recognition
Contributors are recognized in:
- Release notes
- Contributors list
- Annual thank-you post
Thank you for helping make CheerKeeper better for the cheer and dance community!