245 lines
8.4 KiB
Markdown
245 lines
8.4 KiB
Markdown
# Coding Conventions
|
|
|
|
**Analysis Date:** 2026-02-15
|
|
|
|
## Naming Patterns
|
|
|
|
**Files:**
|
|
- Frontend pages: PascalCase with `.jsx` extension (e.g., `Dashboard.jsx`, `LoginPage.jsx`, `WorkoutPage.jsx`)
|
|
- Frontend components: PascalCase with `.jsx` extension (e.g., `Icons.jsx`)
|
|
- Backend routes: `index.js` for main server file
|
|
- CSS files: kebab-case or match component name (e.g., `index.css`, `App.css`)
|
|
- Context files: Named with `Context` suffix (e.g., `AuthContext.jsx`)
|
|
|
|
**Functions:**
|
|
- Async functions: verb + noun pattern (e.g., `fetchProgram`, `fetchLogs`, `handleSubmit`)
|
|
- Event handlers: `handle` prefix (e.g., `handleSubmit`, `handleSave`, `handleChange`)
|
|
- Helper/utility functions: descriptive names without prefixes (e.g., `getCoachGreeting`, `getMuscleGroups`, `getWeekStart`)
|
|
- Hook usage: Standard React hooks (e.g., `useState`, `useEffect`, `useContext`)
|
|
- Middleware functions: descriptive names (e.g., `authMiddleware`)
|
|
|
|
**Variables:**
|
|
- State variables: camelCase (e.g., `user`, `loading`, `program`, `selectedDay`)
|
|
- Constants (config): UPPER_SNAKE_CASE or camelCase (e.g., `API_URL`, `JWT_SECRET`, `PORT`)
|
|
- Local variables: camelCase (e.g., `dayOfWeek`, `todayWorkout`, `lastWeight`)
|
|
- Boolean variables: descriptive (e.g., `loading`, `editing`, `warmupDone`, `completedWarmups`)
|
|
- IDs: numeric or snake_case from database (e.g., `user_id`, `program_exercise_id`, `program_day_id`)
|
|
|
|
**Types:**
|
|
- Objects/interfaces: use descriptive structure without explicit types (e.g., `{ id, email, onboarding_complete }`)
|
|
- Database records: snake_case field names from schema (e.g., `password_hash`, `body_fat_pct`, `measured_at`)
|
|
|
|
## Code Style
|
|
|
|
**Formatting:**
|
|
- No explicit linter/formatter detected in config
|
|
- Indentation: 2 spaces (observed in code)
|
|
- Line length: typically under 100 characters
|
|
- Quotes: single quotes in most files, double quotes in some (inconsistent but not enforced)
|
|
- Semicolons: inconsistently used (some files omit, some include)
|
|
|
|
**Linting:**
|
|
- No ESLint, Prettier, or Biome config files detected
|
|
- No type checking (no TypeScript or JSDoc type annotations)
|
|
|
|
**Spacing:**
|
|
- Components separated by blank lines
|
|
- Function logic blocks separated by comments
|
|
- Import statements grouped: React/library imports first, then local imports
|
|
|
|
## Import Organization
|
|
|
|
**Order:**
|
|
1. React and core library imports (e.g., `import React from 'react'`)
|
|
2. External libraries (e.g., `react-router-dom`, Express packages)
|
|
3. Local imports (context, pages, components)
|
|
4. CSS/asset imports (e.g., `import './index.css'`)
|
|
|
|
**Path Aliases:**
|
|
- No path aliases configured (`@/` style paths not used)
|
|
- Relative imports used throughout (e.g., `'./context/AuthContext'`, `'../components/Icons'`)
|
|
- Relative paths: `../` for parent directory navigation in page imports
|
|
|
|
**Examples:**
|
|
```javascript
|
|
// Frontend (AuthContext.jsx)
|
|
import { createContext, useContext, useState, useEffect } from 'react';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { Icon } from '../components/Icons';
|
|
import './App.css';
|
|
|
|
// Backend (index.js)
|
|
const express = require('express');
|
|
const { Pool } = require('pg');
|
|
const bcrypt = require('bcryptjs');
|
|
const jwt = require('jsonwebtoken');
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
**Patterns:**
|
|
- **Frontend (React):** Try-catch blocks in async functions, error state managed with `useState`
|
|
```javascript
|
|
try {
|
|
const res = await fetch(`${API_URL}/auth/login`, { ... });
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.error);
|
|
// Handle success
|
|
} catch (err) {
|
|
setError(err.message);
|
|
}
|
|
```
|
|
|
|
- **Backend (Express):** Try-catch blocks in route handlers with status code responses
|
|
```javascript
|
|
try {
|
|
// Database query or logic
|
|
res.json(result);
|
|
} catch (err) {
|
|
if (err.code === '23505') return res.status(400).json({ error: 'Email already exists' });
|
|
console.error('Operation error:', err);
|
|
res.status(500).json({ error: 'Server error' });
|
|
}
|
|
```
|
|
|
|
- **Error Response Format:** JSON with `error` key: `{ error: 'Human-readable message' }`
|
|
- **Status Codes:** 400 (validation/conflict), 401 (auth), 404 (not found), 500 (server error)
|
|
- **Empty error catches:** Some empty catch blocks without logging (e.g., `catch { logout(); }` in AuthContext)
|
|
|
|
## Logging
|
|
|
|
**Framework:** Native `console.error()` only
|
|
|
|
**Patterns:**
|
|
- Error logging: `console.error('Context + error:', err)`
|
|
- Backend operations logged: `console.error('Register error:', err)`, `console.error('Profile error:', err)`
|
|
- Frontend operations: minimal logging (mostly silent failures)
|
|
- No structured logging or log levels (DEBUG, INFO, WARN)
|
|
- Log format: descriptive label + colon + error object
|
|
|
|
**Examples from code:**
|
|
```javascript
|
|
console.error('Failed to fetch program:', err);
|
|
console.error('Login error:', err);
|
|
console.error('Update profile error:', err);
|
|
```
|
|
|
|
## Comments
|
|
|
|
**When to Comment:**
|
|
- Section headers for major logical blocks (e.g., `// Coach section`, `// Today's action`, `// Quick stats`)
|
|
- Data structure explanations (e.g., `// Uppvärmningsövningar baserat på muskelgrupp`)
|
|
- Complex calculations or business logic
|
|
- Not applied to simple conditionals or obvious code
|
|
|
|
**JSDoc/TSDoc:**
|
|
- Not used - no type annotations or formal documentation
|
|
- Inline comments rare and minimal
|
|
|
|
**Examples:**
|
|
```javascript
|
|
// Mappa övningar till muskelgrupper
|
|
function getMuscleGroups(exercises) { ... }
|
|
|
|
// Beräkna progress
|
|
const completedExercises = exercises.filter(ex => { ... });
|
|
|
|
// Check if log exists for this set
|
|
const existing = await pool.query(...);
|
|
```
|
|
|
|
## Function Design
|
|
|
|
**Size:**
|
|
- Page/component functions: 40-250 lines (includes JSX)
|
|
- Helper functions: 5-30 lines
|
|
- Backend route handlers: 10-50 lines
|
|
|
|
**Parameters:**
|
|
- Named parameters for component props: `{ children, requireOnboarding = true }`
|
|
- Function parameters: individual arguments or destructured objects
|
|
- Query parameters: destructured from request (e.g., `const { user_id, date } = req.query`)
|
|
|
|
**Return Values:**
|
|
- React components return JSX directly
|
|
- Async functions return Promise<JSON | null>
|
|
- Helper functions return computed values or arrays
|
|
- Route handlers return via `res.json()` or `res.status().json()`
|
|
|
|
**Async/Await:**
|
|
- Preferred over `.then()` chains
|
|
- Used consistently in all async operations
|
|
- Combined with try-catch for error handling
|
|
|
|
**Examples:**
|
|
```javascript
|
|
const fetchProgram = async () => {
|
|
if (program) return; // Early return
|
|
try {
|
|
const res = await fetch(`${API_URL}/programs/1`);
|
|
const data = await res.json();
|
|
setProgram(data);
|
|
} catch (err) {
|
|
console.error('Failed to fetch program:', err);
|
|
}
|
|
};
|
|
|
|
// Helper function
|
|
function isSameDay(d1, d2) {
|
|
return d1.getDate() === d2.getDate() &&
|
|
d1.getMonth() === d2.getMonth() &&
|
|
d1.getFullYear() === d2.getFullYear();
|
|
}
|
|
```
|
|
|
|
## Module Design
|
|
|
|
**Exports:**
|
|
- Frontend: Default exports for pages/contexts: `export default Dashboard`
|
|
- Frontend: Named exports for utilities: `export const useAuth = () => useContext(AuthContext)`
|
|
- Backend: Direct route handlers with `app.get()`, `app.post()` etc. (not module exports)
|
|
- Contexts: `export function AuthProvider` + `export const useAuth`
|
|
|
|
**Barrel Files:**
|
|
- Icons component (`Icons.jsx`) exports multiple icon definitions and helper functions
|
|
- Most modules single-responsibility (one component/context per file)
|
|
|
|
**Examples:**
|
|
```javascript
|
|
// Context export pattern
|
|
export function AuthProvider({ children }) { ... }
|
|
export const useAuth = () => useContext(AuthContext);
|
|
|
|
// Component export pattern
|
|
export default function LoginPage() { ... }
|
|
|
|
// Backend (no module export pattern, direct app routing)
|
|
app.post('/api/auth/login', async (req, res) => { ... });
|
|
```
|
|
|
|
## State Management
|
|
|
|
**Frontend:**
|
|
- React `useState` hooks for local component state
|
|
- React Context API for global auth state (`AuthContext.jsx`)
|
|
- Parent component state passed down as props (e.g., `App.jsx` manages view, program, logs)
|
|
- No Redux, Zustand, or Jotai
|
|
|
|
**Backend:**
|
|
- In-memory database connections via `Pool` (pg package)
|
|
- No state persistence between requests
|
|
- Request-scoped data via middleware (e.g., `req.user` from JWT)
|
|
|
|
## CSS/Styling
|
|
|
|
**Approach:** Plain CSS with CSS variables
|
|
- CSS variables defined in `:root`: `--bg-primary`, `--text-primary`, `--accent`, etc.
|
|
- Dark theme with fitness-oriented color palette
|
|
- Classes: descriptive kebab-case (e.g., `dashboard-header`, `calendar-day`, `page-main`)
|
|
- Utility/modifier classes: `.active`, `.today`, `.has-workout`, `.loading`
|
|
- No CSS-in-JS or utility framework (no Tailwind, Styled Components)
|
|
|
|
---
|
|
|
|
*Convention analysis: 2026-02-15*
|