import * as React from 'react'; import { Check } from 'lucide-react'; import { cn } from '@/lib/utils'; const clamp = (n) => Math.max(0, Math.min(100, Number(n) || 0)); const TRACK_TONES = { brand: 'bg-krow-blue', navy: 'bg-krow-navy', accent: 'bg-krow-yellow', success: 'bg-success', warning: 'bg-warning', destructive: 'bg-destructive', }; /** * Score-band colour, shared by every progress component. * * The app scores people constantly, and the band boundaries (80/60/40) are the * same ones the pipeline tiers use — so a 72 is the same colour on a bar, a ring * and a leaderboard row. */ export function toneForScore(score) { const s = clamp(score); if (s >= 80) return 'brand'; if (s >= 60) return 'success'; if (s >= 40) return 'warning'; return 'destructive'; } /** * ProgressBar — a horizontal track. * * `tone="score"` picks the colour from the value, which is what most usages in * this app want. */ /** @param {any} props */ export function ProgressBar({ value, max = 100, tone = 'brand', size = 'default', label, showValue = false, valueSuffix = '', className, trackClassName, }) { const pct = clamp((Number(value) / max) * 100); const resolvedTone = tone === 'score' ? toneForScore(pct) : tone; const heights = { xs: 'h-1', sm: 'h-1.5', default: 'h-2', lg: 'h-2.5' }; return (
{(label || showValue) && (
{label && {label}} {showValue && ( {value} {valueSuffix} )}
)}
); } /** * ProgressRing — a circular gauge. * * Supports the app's two scales: 0–100 percentages and the 300–850 career * score, via `min`/`max`, so the KROW ID gauge and a completion ring are the * same component. */ /** @param {any} props */ export function ProgressRing({ value, min = 0, max = 100, size = 88, strokeWidth = 8, tone = 'brand', /** Big text in the centre. Defaults to the value. */ label, /** Small text above the label. */ caption, /** Small text below the label. */ footnote, className, }) { const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const ratio = Math.max(0, Math.min(1, (Number(value) - min) / (max - min))); const resolvedTone = tone === 'score' ? toneForScore(ratio * 100) : tone; const strokeColor = { brand: '#0838E0', navy: '#333F48', accent: '#F9E547', success: 'hsl(142 71% 36%)', warning: 'hsl(32 95% 44%)', destructive: 'hsl(0 84% 60%)', }[resolvedTone]; return (
{caption && ( {caption} )} {label ?? value} {footnote && {footnote}}
); } /** * ProgressSteps — a linear multi-step indicator for wizards. * * Completed steps show a check rather than their number, so "done" is readable * at a glance without relying on colour. */ /** @param {any} props */ export function ProgressSteps({ steps = [], current = 0, className = '', onStepClick = null }) { const normalized = steps.map((s) => (typeof s === 'string' ? { label: s } : s)); return (
    {normalized.map((step, i) => { const done = i < current; const active = i === current; const clickable = onStepClick && i <= current; return (
  1. {i < normalized.length - 1 && (
  2. ); })}
); } /** * ScoreMeter — a labelled bar row used in score breakdowns, where many * dimensions are listed with a weight and a value. */ /** @param {any} props */ export function ScoreMeter({ label, value, weight, tone = 'score', className }) { return (
{label} {weight != null && {weight}% weight} {value}
); }