Просмотр исходного кода

Show the good, fair and poor counts on inspections in the customer portal (#520)

Bernt Christian Egeland 4 дней назад
Родитель
Сommit
f78ee146ef

+ 30 - 0
src/__tests__/features/portal/inspection-summary.test.ts

@@ -0,0 +1,30 @@
+import { describe, expect, it } from 'vitest'
+import { portalConditionCounts } from '@/features/portal/Lib/inspectionSummary'
+
+/**
+ * The portal sums up an inspection in plain words: "12 good, 2 fair, 1 poor".
+ * Items are stored on the grading scale, and the portal used to count them
+ * under the words themselves, so a customer saw no summary at all.
+ */
+describe('the inspection summary in the customer portal', () => {
+  it('counts the stored grades under the words the customer reads', () => {
+    const items = [
+      ...Array.from({ length: 12 }, () => ({ condition: 'pass' })),
+      { condition: 'attention' },
+      { condition: 'attention' },
+      { condition: 'fail' },
+      { condition: 'dangerous' },
+    ]
+    expect(portalConditionCounts(items)).toEqual({ good: 12, fair: 2, poor: 2 })
+  })
+
+  it('leaves out checks that were not done or do not apply', () => {
+    expect(
+      portalConditionCounts([
+        { condition: 'not_inspected' },
+        { condition: 'not_applicable' },
+        { condition: 'pass' },
+      ])
+    ).toEqual({ good: 1, fair: 0, poor: 0 })
+  })
+})

+ 9 - 20
src/app/(public)/portal/[orgId]/inspections/page.tsx

@@ -14,6 +14,7 @@ import Link from 'next/link'
 import { getTranslations } from 'next-intl/server'
 import { resolvePortalOrg } from '@/lib/portal-slug'
 import { workshopTimeZone } from '@/lib/workshop-timezone'
+import { portalConditionCounts } from '@/features/portal/Lib/inspectionSummary'
 
 export default async function PortalInspectionsPage({
   params,
@@ -55,13 +56,7 @@ export default async function PortalInspectionsPage({
             {/* Card list (phones + small tablets) */}
             <div className="space-y-2 md:hidden">
               {inspections.map((insp) => {
-                const conditions = insp.items.reduce(
-                  (acc, item) => {
-                    acc[item.condition] = (acc[item.condition] || 0) + 1
-                    return acc
-                  },
-                  {} as Record<string, number>
-                )
+                const conditions = portalConditionCounts(insp.items)
 
                 return (
                   <div key={insp.id} className="rounded-lg border bg-card p-3">
@@ -83,17 +78,17 @@ export default async function PortalInspectionsPage({
                     </div>
                     <p className="truncate text-xs text-muted-foreground">{insp.template.name}</p>
                     <div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs">
-                      {conditions.good && (
+                      {conditions.good > 0 && (
                         <span className="text-green-600">
                           {t('good', { count: conditions.good })}
                         </span>
                       )}
-                      {conditions.fair && (
+                      {conditions.fair > 0 && (
                         <span className="text-yellow-600">
                           {t('fair', { count: conditions.fair })}
                         </span>
                       )}
-                      {conditions.poor && (
+                      {conditions.poor > 0 && (
                         <span className="text-red-600">
                           {t('poor', { count: conditions.poor })}
                         </span>
@@ -130,13 +125,7 @@ export default async function PortalInspectionsPage({
                 </TableHeader>
                 <TableBody>
                   {inspections.map((insp) => {
-                    const conditions = insp.items.reduce(
-                      (acc, item) => {
-                        acc[item.condition] = (acc[item.condition] || 0) + 1
-                        return acc
-                      },
-                      {} as Record<string, number>
-                    )
+                    const conditions = portalConditionCounts(insp.items)
 
                     return (
                       <TableRow key={insp.id}>
@@ -159,17 +148,17 @@ export default async function PortalInspectionsPage({
                         </TableCell>
                         <TableCell>
                           <div className="flex gap-2 text-xs">
-                            {conditions.good && (
+                            {conditions.good > 0 && (
                               <span className="text-green-600">
                                 {t('good', { count: conditions.good })}
                               </span>
                             )}
-                            {conditions.fair && (
+                            {conditions.fair > 0 && (
                               <span className="text-yellow-600">
                                 {t('fair', { count: conditions.fair })}
                               </span>
                             )}
-                            {conditions.poor && (
+                            {conditions.poor > 0 && (
                               <span className="text-red-600">
                                 {t('poor', { count: conditions.poor })}
                               </span>

+ 5 - 10
src/app/(public)/portal/[orgId]/vehicles/[vehicleId]/page.tsx

@@ -13,6 +13,7 @@ import { db } from '@/lib/db'
 import { getWarrantyStatus, type WarrantyStatus } from '@/lib/warranty'
 import { ReportSoldButton } from '@/features/portal/Components/ReportSoldButton'
 import { formatTypeKey } from '@/features/vehicles/Lib/typeKey'
+import { portalConditionCounts } from '@/features/portal/Lib/inspectionSummary'
 
 const warrantyBadgeStyles: Record<WarrantyStatus, string> = {
   active: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
@@ -192,13 +193,7 @@ export default async function PortalVehicleDetailPage({
               ) : (
                 <div className="divide-y">
                   {v.inspections.map((insp) => {
-                    const conditions = insp.items.reduce(
-                      (acc, item) => {
-                        acc[item.condition] = (acc[item.condition] || 0) + 1
-                        return acc
-                      },
-                      {} as Record<string, number>
-                    )
+                    const conditions = portalConditionCounts(insp.items)
 
                     return (
                       <div
@@ -213,17 +208,17 @@ export default async function PortalVehicleDetailPage({
                               ` - ${t('completed', { date: new Date(insp.completedAt).toLocaleDateString(undefined, { timeZone }) })}`}
                           </p>
                           <div className="mt-1 flex gap-2">
-                            {conditions.good && (
+                            {conditions.good > 0 && (
                               <span className="text-xs text-green-600">
                                 {t('good', { count: conditions.good })}
                               </span>
                             )}
-                            {conditions.fair && (
+                            {conditions.fair > 0 && (
                               <span className="text-xs text-yellow-600">
                                 {t('fair', { count: conditions.fair })}
                               </span>
                             )}
-                            {conditions.poor && (
+                            {conditions.poor > 0 && (
                               <span className="text-xs text-red-600">
                                 {t('poor', { count: conditions.poor })}
                               </span>

+ 32 - 0
src/features/portal/Lib/inspectionSummary.ts

@@ -0,0 +1,32 @@
+import type { Condition } from '@/features/inspections/Lib/conditions'
+
+export interface PortalConditionCounts {
+  good: number
+  fair: number
+  poor: number
+}
+
+/** Which plain word each graded step is shown under in the portal. */
+const PORTAL_BUCKET: Partial<Record<Condition, keyof PortalConditionCounts>> = {
+  pass: 'good',
+  attention: 'fair',
+  fail: 'poor',
+  dangerous: 'poor',
+}
+
+/**
+ * An inspection's results as the customer reads them: good, fair and poor.
+ *
+ * Items are stored on the app's grading scale (pass, attention, fail and, on
+ * the EU scale, dangerous). The portal used to count the stored values under
+ * the words themselves, which never match, so every summary came out empty.
+ * Dangerous counts as poor; not applicable and not inspected are left out.
+ */
+export function portalConditionCounts(items: { condition: string }[]): PortalConditionCounts {
+  const counts: PortalConditionCounts = { good: 0, fair: 0, poor: 0 }
+  for (const item of items) {
+    const bucket = PORTAL_BUCKET[item.condition as Condition]
+    if (bucket) counts[bucket] += 1
+  }
+  return counts
+}