All files / apps/web/src/components QuotaBar.jsx

100% Statements 10/10
100% Branches 20/20
100% Functions 1/1
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78      2x                                               80x 80x 80x 80x 80x 80x   80x 76x                           4x                                                        
import { AlertTriangle } from 'lucide-react'
import { formatUsageValue, usagePercent, usageTone } from '../utils/usage'
 
const TONES = {
  green: { bar: 'bg-green-500', text: 'text-green-700' },
  orange: { bar: 'bg-orange-500', text: 'text-orange-700' },
  red: { bar: 'bg-red-500', text: 'text-red-700' },
  neutral: { bar: 'bg-gray-300', text: 'text-gray-500' },
}
 
/**
 * Affichage graphique d'un quota, partagé entre l'abonnement (variant "full")
 * et le tableau des organisations de l'admin (variant "compact").
 *
 * Aucun hook de routing interne : l'action éventuelle (ex. « Upgrader ») est
 * injectée par l'appelant via `action`.
 */
function QuotaBar({
  label,
  used,
  limit,
  type = 'count',
  unit = '',
  variant = 'full',
  action = null,
  className = '',
}) {
  const percent = usagePercent(used, limit)
  const tone = TONES[usageTone(percent)]
  const width = percent == null ? 0 : Math.min(100, Math.max(0, percent))
  const exceeded = percent != null && percent > 100
  const usedText = formatUsageValue(used, type, limit)
  const limitText = formatUsageValue(limit, type, limit)
 
  if (variant === 'compact') {
    return (
      <div className={`flex items-center gap-2 whitespace-nowrap ${className}`}>
        <span className="w-7 text-[11px] uppercase tracking-wide text-gray-400">{label}</span>
        <div className="w-16 bg-gray-200 rounded-full h-1.5">
          <div className={`h-1.5 rounded-full ${tone.bar}`} style={{ width: `${width}%` }} />
        </div>
        <span className={`text-[11px] font-medium ${tone.text}`}>
          {usedText} / {limitText} {unit}
          {exceeded && <span className="ml-1 font-semibold">dépassé</span>}
        </span>
      </div>
    )
  }
 
  return (
    <div className={`mb-4 ${className}`}>
      <div className="flex items-center justify-between mb-1">
        <span className="text-sm font-medium text-gray-700">{label}</span>
        <span className={`text-sm font-medium ${tone.text}`}>
          {usedText} / {limitText} {unit}
        </span>
      </div>
      <div className="w-full bg-gray-200 rounded-full h-2.5">
        <div
          className={`h-2.5 rounded-full transition-all duration-300 ${tone.bar}`}
          style={{ width: `${width}%` }}
        />
      </div>
      {percent != null && percent >= 90 && (
        <div className="flex items-center gap-2 mt-1">
          <AlertTriangle className={`w-4 h-4 ${tone.text}`} />
          <span className={`text-xs ${tone.text}`}>
            {exceeded ? 'Limite dépassée' : 'Vous approchez de votre limite'}
          </span>
          {action && <div className="ml-auto">{action}</div>}
        </div>
      )}
    </div>
  )
}
 
export default QuotaBar