Переглянути джерело

Make the work order agree with the team page about who people are (#273)

* Say what the phone button does, and who fixes it

* Let a work order put somebody on the board

* Call a technician by the name on their account
Bernt Christian Egeland 1 місяць тому
батько
коміт
bacfe518d6

+ 3 - 1
messages/de/service.json

@@ -348,7 +348,9 @@
     "technicians": "Techniker",
     "technicians": "Techniker",
     "otherTeamMembers": "Weitere Teammitglieder",
     "otherTeamMembers": "Weitere Teammitglieder",
     "makeTechnician": "Macht sie zum Techniker",
     "makeTechnician": "Macht sie zum Techniker",
-    "addSomeoneNew": "Jemanden hinzufügen"
+    "addSomeoneNew": "Jemanden hinzufügen",
+    "notOnBoard": "Noch nicht auf der Tafel",
+    "putOnBoard": "Auf die Tafel"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Kundenbenachrichtigungen",
     "title": "Kundenbenachrichtigungen",

+ 3 - 1
messages/en/service.json

@@ -348,7 +348,9 @@
     "technicians": "Technicians",
     "technicians": "Technicians",
     "otherTeamMembers": "Other team members",
     "otherTeamMembers": "Other team members",
     "makeTechnician": "Makes them a technician",
     "makeTechnician": "Makes them a technician",
-    "addSomeoneNew": "Add someone new"
+    "addSomeoneNew": "Add someone new",
+    "notOnBoard": "Not on the board yet",
+    "putOnBoard": "Put on board"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Customer Notifications",
     "title": "Customer Notifications",

+ 3 - 1
messages/es/service.json

@@ -348,7 +348,9 @@
     "technicians": "Técnicos",
     "technicians": "Técnicos",
     "otherTeamMembers": "Otros miembros del equipo",
     "otherTeamMembers": "Otros miembros del equipo",
     "makeTechnician": "Lo convierte en técnico",
     "makeTechnician": "Lo convierte en técnico",
-    "addSomeoneNew": "Añadir a alguien nuevo"
+    "addSomeoneNew": "Añadir a alguien nuevo",
+    "notOnBoard": "Aún no está en el tablero",
+    "putOnBoard": "Poner en el tablero"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Notificaciones al cliente",
     "title": "Notificaciones al cliente",

+ 3 - 1
messages/fr/service.json

@@ -348,7 +348,9 @@
     "technicians": "Techniciens",
     "technicians": "Techniciens",
     "otherTeamMembers": "Autres membres de l’équipe",
     "otherTeamMembers": "Autres membres de l’équipe",
     "makeTechnician": "En fait un technicien",
     "makeTechnician": "En fait un technicien",
-    "addSomeoneNew": "Ajouter quelqu’un"
+    "addSomeoneNew": "Ajouter quelqu’un",
+    "notOnBoard": "Pas encore sur le planning",
+    "putOnBoard": "Mettre au planning"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Notifications client",
     "title": "Notifications client",

+ 3 - 1
messages/it/service.json

@@ -348,7 +348,9 @@
     "technicians": "Tecnici",
     "technicians": "Tecnici",
     "otherTeamMembers": "Altri membri del team",
     "otherTeamMembers": "Altri membri del team",
     "makeTechnician": "Lo rende un tecnico",
     "makeTechnician": "Lo rende un tecnico",
-    "addSomeoneNew": "Aggiungi qualcuno"
+    "addSomeoneNew": "Aggiungi qualcuno",
+    "notOnBoard": "Non ancora sulla lavagna",
+    "putOnBoard": "Metti sulla lavagna"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Notifiche al cliente",
     "title": "Notifiche al cliente",

+ 3 - 1
messages/lt/service.json

@@ -348,7 +348,9 @@
     "technicians": "Technikai",
     "technicians": "Technikai",
     "otherTeamMembers": "Kiti komandos nariai",
     "otherTeamMembers": "Kiti komandos nariai",
     "makeTechnician": "Padarys jį techniku",
     "makeTechnician": "Padarys jį techniku",
-    "addSomeoneNew": "Pridėti naują žmogų"
+    "addSomeoneNew": "Pridėti naują žmogų",
+    "notOnBoard": "Dar nėra lentoje",
+    "putOnBoard": "Įtraukti į lentą"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Kliento pranešimai",
     "title": "Kliento pranešimai",

+ 3 - 1
messages/nb/service.json

@@ -348,7 +348,9 @@
     "technicians": "Teknikere",
     "technicians": "Teknikere",
     "otherTeamMembers": "Andre i teamet",
     "otherTeamMembers": "Andre i teamet",
     "makeTechnician": "Gjør dem til tekniker",
     "makeTechnician": "Gjør dem til tekniker",
-    "addSomeoneNew": "Legg til noen ny"
+    "addSomeoneNew": "Legg til noen ny",
+    "notOnBoard": "Ikke på tavlen ennå",
+    "putOnBoard": "Sett på tavlen"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Kundevarsler",
     "title": "Kundevarsler",

+ 3 - 1
messages/nl/service.json

@@ -348,7 +348,9 @@
     "technicians": "Monteurs",
     "technicians": "Monteurs",
     "otherTeamMembers": "Andere teamleden",
     "otherTeamMembers": "Andere teamleden",
     "makeTechnician": "Maakt hen monteur",
     "makeTechnician": "Maakt hen monteur",
-    "addSomeoneNew": "Iemand nieuw toevoegen"
+    "addSomeoneNew": "Iemand nieuw toevoegen",
+    "notOnBoard": "Nog niet op het bord",
+    "putOnBoard": "Op het bord zetten"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Klantmeldingen",
     "title": "Klantmeldingen",

+ 3 - 1
messages/pl/service.json

@@ -348,7 +348,9 @@
     "technicians": "Technicy",
     "technicians": "Technicy",
     "otherTeamMembers": "Pozostali członkowie zespołu",
     "otherTeamMembers": "Pozostali członkowie zespołu",
     "makeTechnician": "Uczyni tę osobę technikiem",
     "makeTechnician": "Uczyni tę osobę technikiem",
-    "addSomeoneNew": "Dodaj nową osobę"
+    "addSomeoneNew": "Dodaj nową osobę",
+    "notOnBoard": "Jeszcze nie na tablicy",
+    "putOnBoard": "Dodaj do tablicy"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Powiadomienia klienta",
     "title": "Powiadomienia klienta",

+ 3 - 1
messages/pt-BR/service.json

@@ -348,7 +348,9 @@
     "technicians": "Técnicos",
     "technicians": "Técnicos",
     "otherTeamMembers": "Outros membros da equipe",
     "otherTeamMembers": "Outros membros da equipe",
     "makeTechnician": "Torna essa pessoa técnica",
     "makeTechnician": "Torna essa pessoa técnica",
-    "addSomeoneNew": "Adicionar alguém novo"
+    "addSomeoneNew": "Adicionar alguém novo",
+    "notOnBoard": "Ainda não está no quadro",
+    "putOnBoard": "Colocar no quadro"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Notificações ao cliente",
     "title": "Notificações ao cliente",

+ 3 - 1
messages/ru/service.json

@@ -348,7 +348,9 @@
     "technicians": "Механики",
     "technicians": "Механики",
     "otherTeamMembers": "Другие участники",
     "otherTeamMembers": "Другие участники",
     "makeTechnician": "Сделает механиком",
     "makeTechnician": "Сделает механиком",
-    "addSomeoneNew": "Добавить человека"
+    "addSomeoneNew": "Добавить человека",
+    "notOnBoard": "Ещё не на доске",
+    "putOnBoard": "Добавить на доску"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Уведомления клиента",
     "title": "Уведомления клиента",

+ 3 - 1
messages/tr/service.json

@@ -348,7 +348,9 @@
     "technicians": "Teknisyenler",
     "technicians": "Teknisyenler",
     "otherTeamMembers": "Diğer ekip üyeleri",
     "otherTeamMembers": "Diğer ekip üyeleri",
     "makeTechnician": "Onu teknisyen yapar",
     "makeTechnician": "Onu teknisyen yapar",
-    "addSomeoneNew": "Yeni birini ekle"
+    "addSomeoneNew": "Yeni birini ekle",
+    "notOnBoard": "Henüz panoda değil",
+    "putOnBoard": "Panoya ekle"
   },
   },
   "notifications": {
   "notifications": {
     "title": "Müşteri bildirimleri",
     "title": "Müşteri bildirimleri",

+ 9 - 3
src/app/(authenticated)/settings/team/team-settings.tsx

@@ -561,7 +561,7 @@ export function TeamSettings({
                 )}
                 )}
                 {/* The way back off. Setting somebody up and signing them out
                 {/* The way back off. Setting somebody up and signing them out
                     are both one click, and neither hides inside the other. */}
                     are both one click, and neither hides inside the other. */}
-                {isAdmin && member.role !== 'owner' && technicians.has(member.user.id) && (
+                {isAdmin && technicians.has(member.user.id) && (
                   <Tooltip>
                   <Tooltip>
                     <TooltipTrigger asChild>
                     <TooltipTrigger asChild>
                       <Button
                       <Button
@@ -577,8 +577,14 @@ export function TeamSettings({
                     <TooltipContent>{t('team.revokeTechnicianTitle')}</TooltipContent>
                     <TooltipContent>{t('team.revokeTechnicianTitle')}</TooltipContent>
                   </Tooltip>
                   </Tooltip>
                 )}
                 )}
-                {isAdmin && member.role !== 'owner' && (
-                  /* Shown for everybody, because "can this person have the
+                {isAdmin && (
+                  /* Shown for everybody, the owner included: a one-person
+                     workshop is the desk and the bay, and the owner is the
+                     likeliest technician in it. Only role changes and removal
+                     exclude the owner, because those are the two things that
+                     would lock the workshop out of itself.
+
+                     Shown for everybody else too, because "can this person have the
                      app" is a question about their role, and hiding the
                      app" is a question about their role, and hiding the
                      button left no way to find out the answer. Disabled with
                      button left no way to find out the answer. Disabled with
                      the reason rather than absent. */
                      the reason rather than absent. */

+ 26 - 5
src/features/vehicles/Components/service-edit/ScheduleTimesSection.tsx

@@ -22,7 +22,7 @@ import {
   CommandItem,
   CommandItem,
   CommandList,
   CommandList,
 } from '@/components/ui/command'
 } from '@/components/ui/command'
-import { Check, ChevronsUpDown, Clock, Plus, Settings } from 'lucide-react'
+import { Check, ChevronsUpDown, Clock, Plus, Settings, UserPlus } from 'lucide-react'
 import { toast } from 'sonner'
 import { toast } from 'sonner'
 import { DateTimePicker } from '@/components/ui/datetime-picker'
 import { DateTimePicker } from '@/components/ui/datetime-picker'
 import {
 import {
@@ -309,11 +309,32 @@ export function ScheduleTimesSection({
                     ))}
                     ))}
                   </CommandGroup>
                   </CommandGroup>
                 )}
                 )}
+                {/* Colleagues who are not on the board yet.
+                    This list was computed and then not rendered, which left no
+                    way at all to put somebody on a job from here: the only
+                    route was a phone icon on the team page, labelled as
+                    something else. It was dropped because choosing a colleague
+                    quietly turned them into a technician, and that is the part
+                    worth keeping fixed, so the heading says what will happen
+                    rather than the click doing it silently. */}
+                {unlinkedMembers.length > 0 && (
+                  <CommandGroup heading={t('notOnBoard')}>
+                    {unlinkedMembers.map((member) => (
+                      <CommandItem
+                        key={member.id}
+                        value={member.name ?? ''}
+                        disabled={creating}
+                        onSelect={() => handleMemberSelect(member)}
+                      >
+                        <UserPlus className="mr-2 h-4 w-4 text-muted-foreground" />
+                        <span className="flex-1">{member.name}</span>
+                        <span className="text-muted-foreground text-xs">{t('putOnBoard')}</span>
+                      </CommandItem>
+                    ))}
+                  </CommandGroup>
+                )}
                 {/* Adding somebody is one workflow, and this is a door into
                 {/* Adding somebody is one workflow, and this is a door into
-                    it rather than a fourth way of doing it. The picker used to
-                    create people itself: choosing an office colleague quietly
-                    made them a technician, and typing a name made a second
-                    kind nobody had asked about. */}
+                    it rather than a fourth way of doing it. */}
                 <CommandGroup>
                 <CommandGroup>
                   <CommandItem value="__add_person__" onSelect={() => setAddingPerson(true)}>
                   <CommandItem value="__add_person__" onSelect={() => setAddingPerson(true)}>
                     <Plus className="mr-2 h-4 w-4" />
                     <Plus className="mr-2 h-4 w-4" />

+ 18 - 1
src/features/workboard/Actions/technicianActions.ts

@@ -25,9 +25,26 @@ export async function getTechnicians() {
           organizationId: true,
           organizationId: true,
           createdAt: true,
           createdAt: true,
           updatedAt: true,
           updatedAt: true,
+          user: { select: { name: true, email: true } },
         },
         },
       })
       })
-      return technicians
+
+      /**
+       * The account's name wins over the copy stored on the row.
+       *
+       * A technician row carries its own name, written once when the row was
+       * made and never since. Rename somebody, or attach an existing row to a
+       * different account, and the two drift: the work board went on calling
+       * one member "Petter" while the team page called the same person John
+       * Doe, and nobody could tell they were looking at one person.
+       *
+       * Only when a row belongs to an account. A board-only technician has no
+       * account to ask, and the stored name is the only name there is.
+       */
+      return technicians.map(({ user, ...technician }) => ({
+        ...technician,
+        name: user?.name || user?.email || technician.name,
+      }))
     },
     },
     {
     {
       requiredPermissions: [
       requiredPermissions: [