File size: 3,407 Bytes
e327f0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import type { Part } from '@arac-hasar/types';
import { PART_STATUS_TR } from '@arac-hasar/types';
import { DamageBadge } from './DamageBadge';
import { cn } from '../utils/cn';

interface Props {
  part: Part;
  className?: string;
  onDamageClick?: (damageId: number) => void;
  onPartClick?: () => void;
}

const STATUS_RING: Record<Part['status'], string> = {
  clean: 'ring-emerald-200 bg-emerald-50/40',
  minor_damage: 'ring-amber-300 bg-amber-50/30',
  moderate_damage: 'ring-orange-300 bg-orange-50/30',
  severe_damage: 'ring-red-300 bg-red-50/30',
};

const STATUS_DOT: Record<Part['status'], string> = {
  clean: 'bg-emerald-500',
  minor_damage: 'bg-amber-500',
  moderate_damage: 'bg-orange-500',
  severe_damage: 'bg-red-500',
};

export function PartCard({ part, className, onDamageClick, onPartClick }: Props) {
  const isClean = part.status === 'clean';
  const interactive = !!onPartClick;
  return (
    <div
      role={interactive ? 'button' : undefined}
      tabIndex={interactive ? 0 : undefined}
      onClick={onPartClick}
      onKeyDown={(e) => {
        if (!interactive) return;
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onPartClick?.();
        }
      }}
      aria-label={interactive ? `${part.name_tr}, ${PART_STATUS_TR[part.status]}` : undefined}
      className={cn(
        'rounded-xl ring-1 ring-inset p-4 transition-shadow',
        STATUS_RING[part.status],
        interactive &&
          'cursor-pointer hover:shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-1',
        className,
      )}
    >
      <div className="flex items-start justify-between gap-3">
        <div className="flex items-center gap-2">
          <span className={cn('h-2 w-2 rounded-full', STATUS_DOT[part.status])} aria-hidden />
          <h3 className="font-semibold text-slate-900">{part.name_tr}</h3>
        </div>
        <span
          className={cn(
            'rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide',
            isClean ? 'bg-emerald-100 text-emerald-800' : 'bg-slate-900 text-white',
          )}
        >
          {isClean ? 'Hasarsız' : `${part.damage_count} hasar`}
        </span>
      </div>

      <div className="mt-1 text-[11px] uppercase tracking-wider text-slate-400">
        {PART_STATUS_TR[part.status]}
      </div>

      {!isClean && (
        <>
          <div className="mt-3 space-y-2">
            {part.damages.map((d) => (
              <DamageBadge
                key={d.id}
                damage={d}
                onClick={onDamageClick ? () => onDamageClick(d.id) : undefined}
              />
            ))}
          </div>

          {part.part_cost_max_tl > 0 && (
            <div className="mt-3 flex items-baseline justify-between border-t border-slate-200/70 pt-2">
              <span className="text-xs text-slate-500">Parça toplam</span>
              <span className="text-sm font-semibold text-slate-900 tabular-nums">
                {part.part_cost_min_tl.toLocaleString('tr-TR')} –{' '}
                {part.part_cost_max_tl.toLocaleString('tr-TR')} ₺
              </span>
            </div>
          )}

          {part.cost_note && (
            <p className="mt-1 text-[11px] italic text-slate-500">{part.cost_note}</p>
          )}
        </>
      )}
    </div>
  );
}