Development
Overview
Development guidelines, integration specifications, and technical implementation details for the Mind Measure platform.
Contributing Guidelines
Code of Conduct
We are committed to making participation in the Mind Measure project harassment-free for everyone. Expected behavior includes:
- Use welcoming and inclusive language
- Be respectful of differing viewpoints and experiences
- Gracefully accept constructive criticism
- Focus on what is best for the community
- Show empathy towards other community members
- Maintain confidentiality of user data and respect privacy
Getting Started
Prerequisites
- Node.js 18+ installed
- Git configured with your name and email
- Cursor IDE (recommended)
- Basic understanding of React, TypeScript, and Supabase
Repository Setup
# Clone the repository
git clone https://github.com/ColwallKeith/mind-measure-core.git
cd mind-measure-core
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env
cp apps/admin/.env.example apps/admin/.env
cp apps/mobile/.env.example apps/mobile/.env
# Start local development
npm run dev:admin # or npm run dev:mobileDevelopment Workflow
Branching Strategy
- main: Production-ready code
- develop: Integration branch for features
- feature/: Individual feature branches
- hotfix/: Critical production fixes
Feature Development Process
# Create feature branch
git checkout develop
git pull origin develop
git checkout -b feature/your-feature-name
# Make changes and commit
git add .
git commit -m "feat: add new feature description"
# Push and create PR
git push origin feature/your-feature-nameCommit Message Convention
Follow conventional commits format:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changesstyle:- Code style changesrefactor:- Code refactoringtest:- Test additions or changeschore:- Maintenance tasks
Coding Standards
TypeScript Guidelines
// Use explicit types for function parameters and returns
interface UserProfile {
id: string;
email: string;
institutionId?: string;
}
const updateProfile = async (
userId: string,
updates: Partial<UserProfile>
): Promise<UserProfile> => {
// Implementation
};
// Use strict null checks
const userName = user?.profile?.name ?? 'Unknown User';React Component Standards
// Use function components with TypeScript
interface ButtonProps {
variant: 'primary' | 'secondary';
children: React.ReactNode;
onClick: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant,
children,
onClick
}) => {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
};CSS and Styling Guidelines
/* Use Mind Measure brand colors from design system */
:root {
--color-primary: #2563eb;
--color-secondary: #7c3aed;
--color-accent: #f59e0b;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
}
/* Follow BEM naming convention for custom classes */
.dashboard-card {
/* Block */
}
.dashboard-card__header {
/* Element */
}
.dashboard-card--featured {
/* Modifier */
}Supabase Edge Functions
// Edge function template
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
interface RequestBody {
// Define request structure
}
serve(async (req) => {
try {
// CORS headers
if (req.method === 'OPTIONS') {
return new Response('ok', {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
});
}
// Initialize Supabase client
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
);
// Validate request
const { data } = await req.json() as RequestBody;
// Process request
const result = await processRequest(data, supabase);
return new Response(JSON.stringify(result), {
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
});Quality Assurance
Security Guards
# Check for hardcoded secrets
npm run guard:secrets
# Check for cross-package imports
npm run guard:cross
# Run all guards
npm run guard:allCode Quality
# Linting
npm run lint
# Type checking
npm run typecheck
# Build verification
npm run buildTesting Guidelines
// Unit test example
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button Component', () => {
it('renders with correct text', () => {
render(<Button variant="primary" onClick={() => {}}>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button variant="primary" onClick={handleClick}>Click me</Button>);
screen.getByText('Click me').click();
expect(handleClick).toHaveBeenCalledTimes(1);
});
});Code Review Checklist
- Code follows TypeScript and React best practices
- All functions have appropriate type annotations
- Components are properly tested
- Security guards pass
- Documentation is updated
- Commit messages follow convention
- No hardcoded secrets or sensitive data
- Cross-import rules are followed
Integrations
ElevenLabs Voice Integration
Setup and Configuration
// ElevenLabs client configuration
import { ElevenLabsApi } from 'elevenlabs';
const elevenlabs = new ElevenLabsApi({
apiKey: process.env.ELEVENLABS_API_KEY,
});
// Voice synthesis
const synthesizeVoice = async (text: string, voiceId: string) => {
const audio = await elevenlabs.generate({
voice: voiceId,
text: text,
model_id: "eleven_monolingual_v1"
});
return audio;
};Voice Processing Pipeline
- Text Input: User provides text for synthesis
- Voice Selection: Choose appropriate voice model
- Audio Generation: ElevenLabs API processing
- Audio Delivery: Stream audio to client
- Quality Control: Monitor synthesis quality
Integration Strategy
Third-Party Service Connections
- Supabase: Primary backend and database
- OpenAI: Audio transcription via Whisper API
- AWS: Temporary audio processing and analysis
- ElevenLabs: Voice synthesis and generation
- Vercel: Frontend hosting and deployment
API Integration Patterns
// Standardized API client pattern
class ApiClient {
private baseUrl: string;
private apiKey: string;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
}Technical Implementation
Debug Implementation
Development Debugging
// Debug utilities
const DEBUG = process.env.NODE_ENV === 'development';
export const debugLog = (message: string, data?: any) => {
if (DEBUG) {
console.log(`[DEBUG] ${message}`, data);
}
};
// Error boundary for development
export class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
if (DEBUG) {
console.error('Error Boundary caught an error:', error, errorInfo);
}
}
render() {
if (this.state.hasError) {
return <div>Something went wrong. Check console for details.</div>;
}
return this.props.children;
}
}Production Monitoring
// Error tracking and monitoring
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
});
// Performance monitoring
export const trackPerformance = (name: string, fn: () => Promise<any>) => {
const transaction = Sentry.startTransaction({ name });
return fn()
.finally(() => transaction.finish());
};Migration Guide
Database Migrations
-- Migration template
-- Migration: YYYY-MM-DD-HHMMSS_migration_name.sql
-- Add new column with default value
ALTER TABLE profiles
ADD COLUMN new_field VARCHAR(255) DEFAULT 'default_value';
-- Create index for performance
CREATE INDEX idx_profiles_new_field ON profiles(new_field);
-- Update existing data if needed
UPDATE profiles
SET new_field = 'updated_value'
WHERE condition = true;Application Migrations
// Version migration helper
interface MigrationScript {
version: string;
description: string;
up: () => Promise<void>;
down: () => Promise<void>;
}
const migrations: MigrationScript[] = [
{
version: '1.1.0',
description: 'Add user preferences table',
up: async () => {
// Forward migration
},
down: async () => {
// Rollback migration
}
}
];
export const runMigrations = async (targetVersion: string) => {
// Migration execution logic
};Localization Standards
British English Spelling
- Colour not Color
- Centre not Center
- Realise not Realize
- Organisation not Organization
- Behaviour not Behavior
Text Guidelines
// Localization helper
const text = {
en_GB: {
'welcome.message': 'Welcome to Mind Measure',
'colour.primary': 'Primary Colour',
'organisation.settings': 'Organisation Settings',
}
};
export const t = (key: string, locale: string = 'en_GB') => {
return text[locale]?.[key] || key;
};Pull Request Process
PR Requirements
- Builds successfully and passes all tests
- Code review by at least one team member
- Documentation updates for any new features
- Security guards pass all checks
- No breaking changes without major version bump
PR Template
### Summary
Brief description of what changed and why.
### Checklist
- [ ] Code builds and typechecks locally
- [ ] If backend or schema changed, docs updated under /docs
- [ ] If UX changed, screenshots added to PR
- [ ] Security guards pass
- [ ] Tests added/updated for new functionality
### Testing
How was this change tested?
### Screenshots
If UI changes, include before/after screenshots.Security Guidelines
Data Handling
- Never log sensitive data in production
- Encrypt all data in transit and at rest
- Use environment variables for secrets
- Implement proper authentication for all endpoints
- Follow principle of least privilege for database access
Code Security
// Input validation example
import { z } from 'zod';
const UserProfileSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
institutionId: z.string().uuid().optional(),
});
export const validateUserProfile = (data: unknown) => {
return UserProfileSchema.parse(data);
};Security Checklist
- No hardcoded secrets in code
- Input validation on all user inputs
- SQL injection prevention (use parameterized queries)
- XSS prevention (proper escaping)
- CSRF protection enabled
- Authentication required for protected routes
- Authorization checks in place
- Rate limiting implemented