docs: map existing codebase
This commit is contained in:
257
.planning/codebase/CONVENTIONS.md
Normal file
257
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-03-16
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- React components: PascalCase with .tsx extension - `QuickAddPicker.tsx`, `LoginPage.tsx`
|
||||
- Custom hooks: camelCase starting with "use" with .ts extension - `useAuth.ts`, `useCategories.ts`, `useBudgets.ts`
|
||||
- Utilities and type files: camelCase with .ts extension - `utils.ts`, `types.ts`, `format.ts`, `palette.ts`
|
||||
- UI components: lowercase with hyphens for compound names - `button.tsx`, `dropdown-menu.tsx`, `alert-dialog.tsx`
|
||||
- Directories: lowercase with hyphens for multi-word names - `components/ui`, `pages`, `hooks`, `lib`
|
||||
|
||||
**Functions:**
|
||||
- React components: PascalCase - `QuickAddPicker`, `LoginPage`, `AppLayout`
|
||||
- Hook functions: camelCase with "use" prefix - `useAuth()`, `useIsMobile()`, `useBudgets()`
|
||||
- Utility functions: camelCase - `cn()`, `formatCurrency()`, `monthBounds()`
|
||||
- Event handlers: camelCase starting with "handle" - `handlePickItem()`, `handleSave()`, `handleDialogClose()`
|
||||
- Private/internal helpers: lowercase with underscore prefix when needed or nested as sub-functions
|
||||
|
||||
**Variables:**
|
||||
- State variables: camelCase - `session`, `user`, `loading`, `popoverOpen`, `selectedItem`
|
||||
- Constants: UPPER_SNAKE_CASE - `MOBILE_BREAKPOINT`, `CATEGORY_TYPES`, `BUDGETS_KEY`
|
||||
- Query keys: lowercase with underscores - `budgets`, `categories`, `templates`
|
||||
- Boolean variables: descriptive names - `isMobile`, `canSave`, `loading`, `isLoading`
|
||||
|
||||
**Types:**
|
||||
- Interfaces: PascalCase - `Profile`, `Category`, `Budget`, `BudgetItem`
|
||||
- Type unions: PascalCase or vertical bar notation - `type CategoryType = "income" | "bill" | ...`
|
||||
- Generic parameters: single uppercase letter or descriptive PascalCase - `T`, `Data`
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- No explicit formatter configured, but code follows consistent patterns
|
||||
- 2-space indentation (standard TypeScript/JavaScript practice)
|
||||
- Multiline imports organized by source type
|
||||
- Trailing commas in multiline arrays and objects
|
||||
- Semicolons at end of statements
|
||||
|
||||
**Linting:**
|
||||
- Tool: ESLint (version 9.39.4) with Flat Config
|
||||
- Config: `eslint.config.js`
|
||||
- Extends: `@eslint/js`, `typescript-eslint`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh`
|
||||
- Key rules enforced:
|
||||
- React hooks best practices (`react-hooks/rules-of-hooks`)
|
||||
- React refresh compatibility checks
|
||||
- TypeScript recommended rules
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order:**
|
||||
1. React imports and hooks - `import { useState } from "react"`
|
||||
2. Third-party libraries - `import { useQuery } from "@tanstack/react-query"`
|
||||
3. Supabase imports - `import { supabase } from "@/lib/supabase"`
|
||||
4. Internal types - `import type { Category } from "@/lib/types"`
|
||||
5. Internal utilities - `import { cn } from "@/lib/utils"`
|
||||
6. Components - `import { Button } from "@/components/ui/button"`
|
||||
7. Other imports - hooks, constants, etc.
|
||||
|
||||
**Path Aliases:**
|
||||
- `@/*` resolves to `./src/*` (configured in `tsconfig.app.json`)
|
||||
- Relative imports used only for co-located files
|
||||
- All absolute imports use the `@/` prefix
|
||||
|
||||
**Example pattern from `QuickAddPicker.tsx`:**
|
||||
```typescript
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Zap } from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
import { useQuickAdd } from "@/hooks/useQuickAdd"
|
||||
import { useCategories } from "@/hooks/useCategories"
|
||||
import { useBudgets } from "@/hooks/useBudgets"
|
||||
import type { QuickAddItem, CategoryType } from "@/lib/types"
|
||||
import { categoryColors } from "@/lib/palette"
|
||||
import { Button } from "@/components/ui/button"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Patterns:**
|
||||
- Supabase errors checked with `if (error) throw error` pattern
|
||||
- Async/await used for promises with try/catch at caller level
|
||||
- User-facing errors converted to toast notifications using `sonner`
|
||||
- Authentication errors throw and are caught in component try/catch blocks
|
||||
- Validation errors prevented via state checks before action (`canSave` boolean pattern)
|
||||
|
||||
**Example from `useCategories.ts`:**
|
||||
```typescript
|
||||
const { data, error } = await supabase.from("categories").select("*")
|
||||
if (error) throw error
|
||||
return data as Category[]
|
||||
```
|
||||
|
||||
**Example from `LoginPage.tsx`:**
|
||||
```typescript
|
||||
try {
|
||||
await signIn(email, password)
|
||||
navigate("/")
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : t("common.error"))
|
||||
}
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
**Framework:** No structured logging library. Uses standard browser console methods implicitly.
|
||||
|
||||
**Patterns:**
|
||||
- No verbose console.log calls in code
|
||||
- Relies on error boundaries and try/catch for debugging
|
||||
- Toast notifications (via `sonner`) for user feedback on async operations
|
||||
- Translation keys used for all user-facing messages
|
||||
|
||||
**Example from `QuickAddPicker.tsx`:**
|
||||
```typescript
|
||||
catch {
|
||||
toast.error(t("common.error"))
|
||||
}
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
**When to Comment:**
|
||||
- Complex business logic documented with block comments
|
||||
- Multi-step mutations explained with numbered sections
|
||||
- Props documented with JSDoc-style comments on interfaces
|
||||
- Section separators used to organize large components (see 80+ line files)
|
||||
|
||||
**JSDoc/TSDoc:**
|
||||
- Used on exported functions and interfaces
|
||||
- Describes purpose, parameters, and return values
|
||||
- Example from `useBudgets.ts`:
|
||||
```typescript
|
||||
/**
|
||||
* Given a 1-based month and a full year, return ISO date strings for the
|
||||
* first and last day of that month.
|
||||
*/
|
||||
function monthBounds(
|
||||
month: number,
|
||||
year: number
|
||||
): { start_date: string; end_date: string }
|
||||
```
|
||||
|
||||
**Documentation Comments on Props:**
|
||||
```typescript
|
||||
interface QuickAddPickerProps {
|
||||
/** The id of the current month's budget to add the item to. */
|
||||
budgetId: string
|
||||
}
|
||||
```
|
||||
|
||||
## Function Design
|
||||
|
||||
**Size:** Functions kept under 100 lines; larger components organized with comment sections
|
||||
|
||||
**Parameters:**
|
||||
- Single simple types preferred
|
||||
- Object destructuring for multiple related parameters
|
||||
- Type annotations always present for parameters
|
||||
- Optional parameters marked with `?:`
|
||||
|
||||
**Return Values:**
|
||||
- Hooks return objects with destructurable properties
|
||||
- Components return JSX.Element
|
||||
- Explicit return type annotations on typed functions
|
||||
- null/undefined handled explicitly in return statements
|
||||
|
||||
**Example pattern from `useBudgets.ts`:**
|
||||
```typescript
|
||||
return {
|
||||
budgets: budgetsQuery.data ?? [],
|
||||
loading: budgetsQuery.isLoading,
|
||||
getBudget,
|
||||
createBudget,
|
||||
generateFromTemplate,
|
||||
updateItem,
|
||||
createItem,
|
||||
deleteItem,
|
||||
deleteBudget,
|
||||
}
|
||||
```
|
||||
|
||||
## Module Design
|
||||
|
||||
**Exports:**
|
||||
- Named exports for utility functions - `export function cn(...)`
|
||||
- Default exports for React components - `export default function QuickAddPicker`
|
||||
- Type-only exports for TypeScript types - `export type CategoryType = ...`
|
||||
- Multiple related hooks exported from single file - `useBudgets()` and `useBudgetDetail()` from same file
|
||||
|
||||
**Barrel Files:**
|
||||
- Not used; imports reference specific files directly
|
||||
- Example: `import { Button } from "@/components/ui/button"` not from `@/components/ui`
|
||||
|
||||
**File Organization:**
|
||||
- One main export per file
|
||||
- Related utilities and helpers in same file with clear section comments
|
||||
- Query key constants defined at top of custom hooks before the hook itself
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
**Strict Mode:** Enabled (`"strict": true`)
|
||||
- All implicit `any` types caught
|
||||
- Null and undefined checking enforced
|
||||
- Type assertions allowed but discouraged
|
||||
|
||||
**Key Options (`tsconfig.app.json`):**
|
||||
- Target: ES2023
|
||||
- Module: ESNext
|
||||
- JSX: react-jsx (React 17+ transform)
|
||||
- Strict mode enabled
|
||||
- `noUnusedLocals` and `noUnusedParameters` enforced
|
||||
- Path aliases configured for `@/*`
|
||||
|
||||
## React Patterns
|
||||
|
||||
**Hooks:**
|
||||
- `useState` for local component state
|
||||
- `useEffect` for side effects with proper cleanup
|
||||
- Custom hooks for data fetching and logic reuse
|
||||
- `useTranslation` from react-i18next for i18n
|
||||
- `useQueryClient` from @tanstack/react-query for cache invalidation
|
||||
|
||||
**Component Structure:**
|
||||
- Functional components only
|
||||
- Props typed with TypeScript interfaces
|
||||
- Event handlers defined as functions (not inline)
|
||||
- Section comments separate concerns (Constants, Props, Render)
|
||||
|
||||
**Forms:**
|
||||
- Controlled inputs with onChange handlers
|
||||
- Form submission prevents default with `e.preventDefault()`
|
||||
- Validation done before mutation execution
|
||||
- Error state displayed to user via toast or error field
|
||||
|
||||
## Data Validation
|
||||
|
||||
**Approach:** Type safety first with TypeScript
|
||||
- All data from Supabase cast with `as Type` after type-safe query
|
||||
- Input validation in component state checks before action
|
||||
- Zod not used; relies on TypeScript types and Supabase schema
|
||||
- Client-side checks prevent invalid mutations from firing
|
||||
|
||||
**Example from `QuickAddPicker.tsx`:**
|
||||
```typescript
|
||||
const canSave =
|
||||
Boolean(categoryId) &&
|
||||
Boolean(amount) &&
|
||||
!isNaN(parseFloat(amount)) &&
|
||||
parseFloat(amount) >= 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-03-16*
|
||||
Reference in New Issue
Block a user