← back to Homesonspec

apps/web/src/lib/trades.ts

63 lines

// CSLB (California Contractors State License Board) classification → trade metadata.
//
// A spec-home builder staffing a job thinks in TRADES ("who does my framing /
// electrical / roofing"), not in raw CSLB class codes. This map turns each class
// code the contractor API groups results under into a human trade label so the
// "Find subs near this home" surface reads like a build schedule, not a code list.
//
// SCOPE: labels only — this is display metadata, no data is fabricated. The
// contractor facts themselves come from the shared usre contractor API (which is
// sourced from the public CSLB licensed-contractor list).

export interface TradeMeta {
  /** CSLB class code, e.g. "C-10". */
  code: string;
  /** Human trade label, e.g. "Electrical". */
  label: string;
  /** Emoji used as a lightweight per-trade icon on the card header. */
  icon: string;
  /** Rough order a spec home is staffed in (used as a sensible default sort). */
  order: number;
}

// The default trade set a spec-home builder wants surfaced, in build order.
// Mirrors the brief's default set: framing, concrete, electrical, plumbing,
// HVAC, roofing, paint, plaster, flooring, tile, carpentry (+ general "B").
export const DEFAULT_TRADES: TradeMeta[] = [
  { code: "B", label: "General building", icon: "🏗️", order: 0 },
  { code: "C-8", label: "Concrete", icon: "🧱", order: 1 },
  { code: "C-5", label: "Framing & rough carpentry", icon: "🪵", order: 2 },
  { code: "C-6", label: "Cabinet & finish carpentry", icon: "🪚", order: 3 },
  { code: "C-10", label: "Electrical", icon: "⚡", order: 4 },
  { code: "C-36", label: "Plumbing", icon: "🚰", order: 5 },
  { code: "C-20", label: "HVAC", icon: "❄️", order: 6 },
  { code: "C-39", label: "Roofing", icon: "🏠", order: 7 },
  { code: "C-35", label: "Lathing & plaster", icon: "🪵", order: 8 },
  { code: "C-33", label: "Painting & decorating", icon: "🎨", order: 9 },
  { code: "C-15", label: "Flooring", icon: "🪟", order: 10 },
  { code: "C-54", label: "Tile (ceramic & mosaic)", icon: "🔲", order: 11 },
];

/** Comma-joined class codes for the contractor API `trades=` param, in build order. */
export const DEFAULT_TRADES_PARAM: string = DEFAULT_TRADES.map((t) => t.code).join(",");

const BY_CODE: Record<string, TradeMeta> = Object.fromEntries(
  DEFAULT_TRADES.map((t) => [t.code, t]),
);

/**
 * Resolve display metadata for a CSLB class code. Unknown codes (the API may
 * return a class outside the default set) get an honest generic label rather
 * than being dropped — never fabricate a trade name, but never dead-end either.
 */
export function tradeMeta(code: string): TradeMeta {
  return (
    BY_CODE[code] ?? {
      code,
      label: `Class ${code}`,
      icon: "🔧",
      order: 999,
    }
  );
}