FixPhotos.tsx 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. 'use client'
  2. import { useState } from 'react'
  3. import { useTranslations } from 'next-intl'
  4. import { MediaLightbox } from '@/features/inspections/Components/MediaLightbox'
  5. import type { FixPhoto } from '../Lib/types'
  6. /** Where the app serves a shared fix's photo from the code service. */
  7. export function fixPhotoUrl(id: string, n: number, size: 'full' | 'thumb' = 'full'): string {
  8. const base = `/api/protected/fault-codes/fixes/${encodeURIComponent(id)}/photos/${n}`
  9. return size === 'thumb' ? `${base}?size=thumb` : base
  10. }
  11. /** "2014 Volkswagen Golf · CAXA", or null when the fix names no vehicle. */
  12. export function fixVehicleLine(
  13. vehicle: {
  14. make?: string | null
  15. model?: string | null
  16. year?: number | null
  17. engine?: string | null
  18. } | null
  19. ): string | null {
  20. if (!vehicle) return null
  21. const name = [vehicle.year, vehicle.make, vehicle.model].filter(Boolean).join(' ')
  22. const line = [name, vehicle.engine].filter(Boolean).join(' · ')
  23. return line || null
  24. }
  25. /** A fix's photos as small thumbnails; a tap opens them full size. */
  26. export function FixPhotos({ fixId, photos }: { fixId: string; photos: FixPhoto[] }) {
  27. const t = useTranslations('faultCodes.fixes')
  28. const [open, setOpen] = useState<number | null>(null)
  29. if (photos.length === 0) return null
  30. const images = photos.map((p, i) => ({
  31. url: fixPhotoUrl(fixId, p.n),
  32. caption: t('photo', { n: i + 1, count: photos.length }),
  33. }))
  34. return (
  35. <>
  36. <ul className="flex flex-wrap gap-2">
  37. {photos.map((p, i) => (
  38. <li key={p.n}>
  39. <button
  40. type="button"
  41. onClick={() => setOpen(i)}
  42. className="block overflow-hidden rounded-md border border-card-edge bg-muted transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
  43. aria-label={t('openPhoto', { n: i + 1 })}
  44. >
  45. <img
  46. src={fixPhotoUrl(fixId, p.n, 'thumb')}
  47. alt=""
  48. loading="lazy"
  49. width={p.width}
  50. height={p.height}
  51. className="size-16 object-cover sm:size-20"
  52. />
  53. </button>
  54. </li>
  55. ))}
  56. </ul>
  57. <MediaLightbox
  58. images={images}
  59. index={open}
  60. onClose={() => setOpen(null)}
  61. onNavigate={setOpen}
  62. />
  63. </>
  64. )
  65. }