Thank you for your interest in contributing to Family Vault! This document provides guidelines and instructions for contributing.
By participating in this project, you agree to maintain a respectful and inclusive environment for everyone.
Before creating bug reports, please check existing issues to avoid duplicates. When creating a bug report, include:
- Clear title and description
- Steps to reproduce the behavior
- Expected vs actual behavior
- Screenshots if applicable
- Environment details (OS, browser, Docker version, etc.)
Enhancement suggestions are welcome! Please provide:
- Clear title and description
- Use case - Why would this be useful?
- Mockups or examples if applicable
- Fork the repository and create your branch from
main - Follow the coding standards outlined below
- Write tests for new functionality
- Update documentation if you're changing behavior
- Ensure tests pass before submitting
- Write clear commit messages
- Docker and Docker Compose
- Node.js 22+
- Python 3.13+
- Git
-
Clone your fork:
git clone https://github.com/YOUR_USERNAME/family-vault.git cd family-vault -
Create a development environment:
cp .env.example .env # Edit .env with your development settings -
Start development services:
docker-compose up -d postgres minio
-
Set up the backend:
cd backend python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt alembic upgrade head uvicorn app.main:app --reload
-
Set up the frontend (in a new terminal):
cd frontend npm install npm run dev -
Access the app at
http://localhost:3000
family-vault/
├── backend/ # Python FastAPI backend
│ ├── app/
│ │ ├── main.py # FastAPI app entry point
│ │ ├── config.py # Environment configuration
│ │ ├── database.py # SQLAlchemy setup
│ │ ├── auth/ # Authentication module
│ │ ├── items/ # Items CRUD
│ │ ├── categories/ # Category definitions
│ │ ├── files/ # File upload/encryption
│ │ ├── reminders/ # Reminder system
│ │ └── ... # Other modules
│ ├── alembic/ # Database migrations
│ └── requirements.txt # Python dependencies
│
├── frontend/ # Next.js frontend
│ ├── src/
│ │ ├── app/ # Next.js App Router pages
│ │ ├── components/ # React components
│ │ │ ├── ui/ # shadcn/ui components
│ │ │ ├── layout/ # Layout components
│ │ │ └── items/ # Item-specific components
│ │ └── lib/ # Utilities and API client
│ └── package.json # Node dependencies
│
├── docker-compose.yml # Docker services
├── .env.example # Environment template
└── README.md # Main documentation
- Style: Follow PEP 8
- Type hints: Use type hints for function signatures
- Imports: Organize imports (stdlib, third-party, local)
- Docstrings: Use docstrings for modules and complex functions
Example:
from typing import Optional
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
def get_item(db: Session, item_id: str, org_id: str) -> Optional[Item]:
"""Fetch an item by ID if it belongs to the given org.
Args:
db: Database session
item_id: UUID of the item
org_id: UUID of the organization
Returns:
Item if found and belongs to org, None otherwise
"""
return db.query(Item).filter(
Item.id == item_id,
Item.org_id == org_id
).first()- Style: Prettier + ES Lint configs
- TypeScript: Use strict mode, avoid
any - Components: Functional components with hooks
- Naming:
- Components: PascalCase (
ItemCard.tsx) - Utilities: camelCase (
formatDate.ts) - Constants: UPPER_SNAKE_CASE
- Components: PascalCase (
Example:
interface ItemCardProps {
item: Item;
onClick: (id: string) => void;
}
export function ItemCard({ item, onClick }: ItemCardProps) {
return (
<Card onClick={() => onClick(item.id)}>
<CardHeader>
<CardTitle>{item.name}</CardTitle>
</CardHeader>
</Card>
);
}When changing the database schema:
-
Create a new migration:
cd backend alembic revision -m "descriptive_name"
-
Edit the generated file in
backend/alembic/versions/ -
Test the migration:
alembic upgrade head # Apply alembic downgrade -1 # Test rollback alembic upgrade head # Reapply
-
Import new models in
backend/app/main.py(in the lifespan function)
cd backend
pytest
pytest tests/test_items.py # Specific test file
pytest -v # Verbose outputcd frontend
npm test
npm test -- --coverage # With coverage reportTo add a new item category (e.g., "Medical Records"):
-
Define the category in
backend/app/categories/definitions.py:CATEGORIES["medical"] = { "key": "medical", "label": "Medical", "icon": "heartbeat", "description": "Medical records and health documents", "subcategories": { "prescription": { "key": "prescription", "label": "Prescription", "fields": [ {"key": "medication_name", "label": "Medication", "type": "text", "required": True}, {"key": "dosage", "label": "Dosage", "type": "text", "required": False}, # ... more fields ], "file_slots": ["prescription_image", "label_image"] } } }
-
Add icon in
frontend/src/components/items/SubcategoryIcon.tsx:// Add to SUBCATEGORY_ICONS: prescription: { icon: Pill, bgColor: "bg-green-100", iconColor: "text-green-600" },
And add a category default to
CATEGORY_DEFAULTSif it's a new top-level category. -
Create route in
frontend/src/app/(app)/medical/ -
Add to sidebar in
frontend/src/components/layout/Sidebar.tsx -
Test thoroughly with create/edit/delete operations
When contributing code, leverage existing shared utilities to avoid duplication:
- Org helpers: Use
get_active_org_id(user, db)fromapp.orgs.serviceinstead of writing per-router helpers:Usefrom app.orgs.service import get_active_org_id @router.get("/my-endpoint") def my_endpoint(user = Depends(get_current_user), db = Depends(get_db)): org_id = get_active_org_id(user, db)
get_active_org(user, db)when you need the full org object (e.g., for encryption key access).
-
Formatting: Import from
@/lib/formatinstead of writing local helpers:import { humanize, titleCase, formatDate, getFieldValue, repeatLabel } from "@/lib/format";
-
Icons: Use
SubcategoryIconcomponent instead of writing icon switch statements:import { SubcategoryIcon } from "@/components/items/SubcategoryIcon"; <SubcategoryIcon subcategory="auto_insurance" category="insurance" />
-
Reminders: Use
ReminderCardcomponent with the appropriate variant:import { ReminderCard } from "@/components/items/ReminderCard"; <ReminderCard reminder={r} variant="compact" /> // RemindersPanel <ReminderCard reminder={r} variant="sidebar" /> // RightSidebar <ReminderCard reminder={r} /> // Full page (default)
Use conventional commits format:
type(scope): subject
body (optional)
footer (optional)
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(items): add passport selector for visa forms
fix(auth): resolve session token expiration issue
docs(readme): update deployment instructions
- Update documentation if you're changing functionality
- Add tests for new features
- Ensure all tests pass
- Update CHANGELOG.md (if applicable)
- Request review from maintainers
- Address feedback promptly
- Squash commits if requested before merging
Use conventional commits format:
feat(category): add vehicle selector to auto insurance
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
Describe how you tested your changes
## Checklist
- [ ] Tests pass locally
- [ ] Code follows project style guidelines
- [ ] Documentation updated
- [ ] No breaking changes (or documented)- Questions? Open a Discussion
- Bug reports Use Issues
- Chat Join our community chat (link TBD)
By contributing, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to Family Vault! 🎉