Skip to main content

Coding Conventions

This page covers the coding standards and conventions used throughout the HostMetrics codebase. All contributors should follow these rules to maintain consistency.

File Naming

TypeConventionExample
ComponentsPascalCase.tsxStatCard.tsx, TripTable.tsx
HooksuseCamelCase.tsuseBookings.ts, useKPIs.ts
UtilitiescamelCase.tsdateUtils.ts, formatters.ts
Database modulescamelCase.ts or kebab-case.tspartners.ts, import-service.ts
Test filesName.test.tsx or name.test.tsStatCard.test.tsx, useKPIs.test.ts

Import Path Alias

All imports use the @/ alias which maps to src/:
// Correct - use the alias
import { cn } from "@/lib/utils";
import { trips } from "@/lib/db";
import { useAuth } from "@/components/auth/AuthProvider";
import { Button } from "@/components/ui/button";

// Incorrect - never use relative paths across module boundaries
import { cn } from "../../../lib/utils";

Client vs Server Components

Next.js App Router defaults to server components. Add the 'use client' directive only for interactive components that need browser APIs, hooks, or event handlers:
'use client';

import { useState } from "react";
import { Button } from "@/components/ui/button";

export function InteractiveWidget() {
  const [open, setOpen] = useState(false);
  return <Button onClick={() => setOpen(true)}>Open</Button>;
}
Server components (no directive needed) are used for pages that only fetch and render data.

Conditional Class Merging

Use the cn() utility from @/lib/utils for conditional Tailwind classes. This wraps clsx and tailwind-merge:
import { cn } from "@/lib/utils";

<div className={cn(
  "px-4 py-2 rounded-lg",
  isActive && "bg-[#f0f5ed] text-[#6b7c5c]",
  isDisabled && "opacity-50 cursor-not-allowed"
)}>
  {label}
</div>

TypeScript Strict Mode

The project uses TypeScript in strict mode. All new code must:
  • Define explicit types for function parameters and return values
  • Avoid any — use unknown with type narrowing when the type is truly unknown
  • Define interfaces/types in src/lib/db/schema/ for database entities
  • Use generics where appropriate for reusable utilities
// Correct
export async function getTrip(id: string): Promise<Trip | null> {
  // ...
}

// Incorrect
export async function getTrip(id: any) {
  // ...
}

Component Composition

Prefer composition over prop drilling. Pass children or render props instead of threading data through many layers:
// Preferred - composition
<Card>
  <CardHeader title="Fleet Stats" />
  <CardBody>
    <StatGrid stats={stats} />
  </CardBody>
</Card>

// Avoid - prop drilling
<Card title="Fleet Stats" stats={stats} renderStat={...} onStatClick={...} />

State Management

HostMetrics does not use a global state library. State is managed with:
  • React hooks (useState, useReducer) for local component state
  • React Context (useAuth, etc.) for cross-cutting concerns like auth
  • Custom hooks (useKPIs, useBookings, etc.) for data fetching and business logic
  • Supabase Realtime for live data updates where needed
// Auth context — available everywhere via useAuth()
const { user } = useAuth();

// Data hooks — fetch and cache per-component
const { kpis, isLoading } = useKPIs();
const { vehicles } = useVehicles();

Database Access Rules

All database operations live in src/lib/db/. Every query must filter by user_id to enforce row-level data isolation:
import { getClient, getCurrentUserId } from "./_client";

export async function listTrips() {
  const userId = await getCurrentUserId();
  const { data, error } = await getClient()
    .from("trips")
    .select("*")
    .eq("user_id", userId);

  if (error) throw error;
  return data;
}
Never call Supabase directly from components — always go through a src/lib/db/ module or a hook in src/hooks/.

Code Organization Summary

WhatWhere
Page routessrc/app/(dashboard)/ or src/app/(auth)/
React componentssrc/components/{feature}/
Base UI primitivessrc/components/ui/
Custom hookssrc/hooks/
Database operationssrc/lib/db/
Schema / typessrc/lib/db/schema/
Utility functionssrc/lib/
Testssrc/__tests__/

Error Handling

Database and API errors should be caught and surfaced with meaningful messages:
const { data, error } = await getClient()
  .from("vehicles")
  .insert(vehicle);

if (error) {
  throw new Error(`Failed to create vehicle: ${error.message}`);
}
Hooks should expose an error state so components can render appropriate error UI.