WorkshopFixesCard.tsx 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. 'use client'
  2. import { useLocale, useTranslations } from 'next-intl'
  3. import { Users } from 'lucide-react'
  4. import { AppCard } from '@/components/app-card'
  5. import { useServiceType } from '@/components/service-type-context'
  6. import { useFormatDate } from '@/lib/use-format-date'
  7. import type { SharedFix } from '../Lib/types'
  8. import { FixText } from './FixText'
  9. import { FixPhotos, fixVehicleLine } from './FixPhotos'
  10. /** "Norwegian" for nb, in the reader's language; null when it is the reader's own. */
  11. function languageName(lang: string, locale: string): string | null {
  12. if (lang.toLowerCase() === locale.toLowerCase()) return null
  13. try {
  14. return new Intl.DisplayNames([locale], { type: 'language' }).of(lang) ?? null
  15. } catch {
  16. return null
  17. }
  18. }
  19. /**
  20. * "Fixes from other workshops": what fixed this code elsewhere, as a person
  21. * at Torqvoice published it, newest first. Each shows the vehicle, the text,
  22. * the photos and when it was shared, and never which workshop sent it.
  23. */
  24. export function WorkshopFixesCard({
  25. code,
  26. label = code,
  27. meaning = null,
  28. fixes: allFixes,
  29. available,
  30. }: {
  31. code: string
  32. /** The code as people read it ("BMW 283700"); the code itself otherwise. */
  33. label?: string
  34. /** For a make's own code, the meaning on screen: only its fixes, and those for the code as a whole. */
  35. meaning?: string | null
  36. fixes: SharedFix[]
  37. available: boolean
  38. }) {
  39. const fixes = meaning
  40. ? allFixes.filter((f) => f.meaning === null || f.meaning === meaning)
  41. : allFixes
  42. const t = useTranslations('faultCodes.fixes')
  43. const locale = useLocale()
  44. const isMarine = useServiceType() === 'marine'
  45. const { formatDate } = useFormatDate()
  46. return (
  47. <AppCard
  48. icon={Users}
  49. title={t('title')}
  50. badge={fixes.length > 0 ? fixes.length : undefined}
  51. contentClassName={fixes.length > 0 ? 'p-0 py-1' : 'space-y-3'}
  52. >
  53. {!available ? (
  54. <p className="text-sm text-muted-foreground">{t('unavailable')}</p>
  55. ) : fixes.length === 0 ? (
  56. <p className="text-sm text-muted-foreground">{t('empty', { code: label })}</p>
  57. ) : (
  58. <ul className="divide-y divide-card-edge/60">
  59. {fixes.map((fix) => {
  60. const vehicle = fixVehicleLine(fix.vehicle)
  61. const language = languageName(fix.lang, locale)
  62. return (
  63. <li key={fix.id} className="space-y-2 px-5 py-4">
  64. <div className="flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1">
  65. <span className="text-sm font-medium">
  66. {vehicle ?? (isMarine ? t('unknownVehicleMarine') : t('unknownVehicle'))}
  67. </span>
  68. <span className="text-xs text-muted-foreground">
  69. {t('sharedOn', { date: formatDate(fix.createdAt) })}
  70. </span>
  71. </div>
  72. <FixText text={fix.text} />
  73. {language && (
  74. <p className="text-xs text-muted-foreground">{t('writtenIn', { language })}</p>
  75. )}
  76. <FixPhotos fixId={fix.id} photos={fix.photos} />
  77. </li>
  78. )
  79. })}
  80. </ul>
  81. )}
  82. </AppCard>
  83. )
  84. }