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

Send the technician app a contact line instead of a placeholder address (#541)

Bernt Christian Egeland 1 день назад
Родитель
Сommit
302a688472
2 измененных файлов с 92 добавлено и 2 удалено
  1. 79 0
      src/__tests__/api/tech-me-contact.test.ts
  2. 13 2
      src/app/api/v1/tech/me/route.ts

+ 79 - 0
src/__tests__/api/tech-me-contact.test.ts

@@ -0,0 +1,79 @@
+/**
+ * @vitest-environment node
+ *
+ * What the technician app shows under the person's name.
+ *
+ * A mechanic the desk sets up with a mobile number gets a placeholder address,
+ * because an account needs one. The app printed whatever `me` sent as the
+ * email, so the More screen read "tech-17db5cb0-…@technician.torqvoice.invalid".
+ */
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+const state = { user: null as Record<string, unknown> | null }
+
+vi.mock('@/lib/db', () => ({
+  db: {
+    user: { findUnique: vi.fn(async () => state.user) },
+    organization: { findUnique: vi.fn(async () => ({ id: 'org-1', name: 'Riverside Auto' })) },
+    technician: { findMany: vi.fn(async () => [{ id: 'tech-1', name: 'Sam Ortiz', color: null }]) },
+  },
+}))
+vi.mock('@/features/time-tracking/Lib/timeEntries', () => ({
+  getOpenEntry: vi.fn(async () => null),
+  loggedMinutes: vi.fn(async () => 0),
+}))
+vi.mock('@/lib/with-api-auth', async () => {
+  const actual = await vi.importActual<typeof import('@/lib/with-api-auth')>('@/lib/with-api-auth')
+  return {
+    ...actual,
+    withApiAuth: vi.fn(async (_request: Request, handler: (ctx: unknown) => Promise<Response>) =>
+      handler({
+        userId: 'user-1',
+        organizationId: 'org-1',
+        technicianIds: ['tech-1'],
+        isAdmin: false,
+      })
+    ),
+  }
+})
+
+import { GET } from '@/app/api/v1/tech/me/route'
+
+async function me() {
+  const res = await GET(new Request('https://app.test/api/v1/tech/me'))
+  const body = await res.json()
+  return (body.data ?? body).user
+}
+
+beforeEach(() => {
+  state.user = null
+})
+
+describe('the signed-in technician', () => {
+  it('shows the mobile number of someone set up without an email', async () => {
+    state.user = {
+      id: 'user-1',
+      name: 'Sam Ortiz',
+      email: 'tech-17db5cb0-dfe8-4a26-a153-891d3dc0a1b2@technician.torqvoice.invalid',
+      phone: '+15125550177',
+      image: null,
+    }
+    const user = await me()
+    expect(user.contact).toBe('+15125550177')
+    expect(user.email).toBeNull()
+    expect(JSON.stringify(user)).not.toContain('torqvoice.invalid')
+  })
+
+  it('shows the email of someone who has one', async () => {
+    state.user = {
+      id: 'user-1',
+      name: 'Marco Diaz',
+      email: 'marco@riversideauto.example',
+      phone: '+15125550188',
+      image: null,
+    }
+    const user = await me()
+    expect(user.contact).toBe('marco@riversideauto.example')
+    expect(user.email).toBe('marco@riversideauto.example')
+  })
+})

+ 13 - 2
src/app/api/v1/tech/me/route.ts

@@ -3,6 +3,7 @@ import { apiOk, withApiAuth } from '@/lib/with-api-auth'
 import { getOpenEntry, loggedMinutes } from '@/features/time-tracking/Lib/timeEntries'
 import { TECH_API_FEATURES } from '@/lib/tech-api-features'
 import { MIN_APP_VERSION } from '@/lib/tech-app-version'
+import { contactFor, isPlaceholderEmail } from '@/features/team/Lib/technicianRole'
 
 /**
  * Who the app is signed in as, which workshop it is looking at, and whether a
@@ -20,7 +21,7 @@ export async function GET(request: Request) {
       const [user, organization, technicians] = await Promise.all([
         db.user.findUnique({
           where: { id: ctx.userId },
-          select: { id: true, name: true, email: true, image: true },
+          select: { id: true, name: true, email: true, phone: true, image: true },
         }),
         db.organization.findUnique({
           where: { id: ctx.organizationId },
@@ -44,7 +45,17 @@ export async function GET(request: Request) {
         // What a newer app may rely on here. An app that finds a name missing
         // switches that part off rather than letting it fail.
         features: TECH_API_FEATURES,
-        user,
+        // A technician the desk set up by phone number has a placeholder
+        // address, which the app printed under their name. It gets null in
+        // its place, and `contact` is the line to show there instead: the
+        // email, or the number they sign in with.
+        user: user && {
+          id: user.id,
+          name: user.name,
+          image: user.image,
+          email: isPlaceholderEmail(user.email) ? null : user.email,
+          contact: contactFor(user),
+        },
         organization,
         technicians,
         isTechnician: technicians.length > 0,