4
0
Эх сурвалжийг харах

Add a Teams or Meet meeting to one work order from its page, and say what the calendar switch really does (#336)

Bernt Christian Egeland 2 сар өмнө
parent
commit
21c279a85c

+ 4 - 4
messages/de/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Kalender",
         "pushEnabled": "Geplante Aufträge in diesen Kalender eintragen",
         "includeCustomer": "Name und Telefonnummer des Kunden in den Termin aufnehmen",
-        "addConference": "Jedem Termin einen Google Meet-Link hinzufügen",
-        "addConferenceHelp": "Der Link wird im Auftrag angezeigt, damit Sie ihn mit dem Kunden teilen können.",
+        "addConference": "Jedem geplanten Auftrag ein Google Meet-Meeting hinzufügen",
+        "addConferenceHelp": "Ein: jeder in den Kalender übertragene Auftrag bekommt ein Google Meet-Meeting, und der Link erscheint auf dem Auftrag. Aus: ein Google Meet-Meeting einem einzelnen Auftrag von dessen Seite aus hinzufügen. In beiden Fällen bekommt der Kunde den Link erst, wenn Sie ihn senden.",
         "pullEnabled": "Belegte Zeiten dieses Kalenders im Torqvoice-Kalender anzeigen",
         "pullEnabledHelp": "Nur lesend. Wird alle 15 Minuten aktualisiert, eine Woche zurück und zwei Monate voraus."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Kalender",
         "pushEnabled": "Geplante Aufträge in diesen Kalender eintragen",
         "includeCustomer": "Name und Telefonnummer des Kunden in den Termin aufnehmen",
-        "addConference": "Jedem Termin einen Microsoft Teams-Link hinzufügen",
-        "addConferenceHelp": "Der Link wird im Auftrag angezeigt, damit Sie ihn mit dem Kunden teilen können.",
+        "addConference": "Jedem geplanten Auftrag ein Microsoft Teams-Meeting hinzufügen",
+        "addConferenceHelp": "Ein: jeder in den Kalender übertragene Auftrag bekommt ein Microsoft Teams-Meeting, und der Link erscheint auf dem Auftrag. Aus: ein Microsoft Teams-Meeting einem einzelnen Auftrag von dessen Seite aus hinzufügen. In beiden Fällen bekommt der Kunde den Link erst, wenn Sie ihn senden.",
         "pullEnabled": "Belegte Zeiten dieses Kalenders im Torqvoice-Kalender anzeigen",
         "pullEnabledHelp": "Nur lesend. Wird alle 15 Minuten aktualisiert, eine Woche zurück und zwei Monate voraus."
       }

+ 4 - 4
messages/en/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Calendar",
         "pushEnabled": "Add scheduled work orders to this calendar",
         "includeCustomer": "Include the customer's name and phone in the event",
-        "addConference": "Add a Google Meet link to each event",
-        "addConferenceHelp": "The link is shown on the work order so you can share it with the customer.",
+        "addConference": "Add a Google Meet meeting to every scheduled work order",
+        "addConferenceHelp": "On: every work order pushed to the calendar gets a Google Meet meeting, and the join link shows on the work order. Off: add a Google Meet meeting to a single work order from its page. Either way the customer only gets the link when you send it.",
         "pullEnabled": "Show this calendar's busy time on the Torqvoice calendar",
         "pullEnabledHelp": "Read-only. Refreshed every 15 minutes, one week back and two months ahead."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Calendar",
         "pushEnabled": "Add scheduled work orders to this calendar",
         "includeCustomer": "Include the customer's name and phone in the event",
-        "addConference": "Add a Microsoft Teams link to each event",
-        "addConferenceHelp": "The link is shown on the work order so you can share it with the customer.",
+        "addConference": "Add a Microsoft Teams meeting to every scheduled work order",
+        "addConferenceHelp": "On: every work order pushed to the calendar gets a Microsoft Teams meeting, and the join link shows on the work order. Off: add a Microsoft Teams meeting to a single work order from its page. Either way the customer only gets the link when you send it.",
         "pullEnabled": "Show this calendar's busy time on the Torqvoice calendar",
         "pullEnabledHelp": "Read-only. Refreshed every 15 minutes, one week back and two months ahead."
       }

+ 4 - 4
messages/es/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Calendario",
         "pushEnabled": "Añadir las órdenes de trabajo programadas a este calendario",
         "includeCustomer": "Incluir el nombre y el teléfono del cliente en el evento",
-        "addConference": "Añadir un enlace de Google Meet a cada evento",
-        "addConferenceHelp": "El enlace se muestra en la orden de trabajo para que pueda compartirlo con el cliente.",
+        "addConference": "Añadir una reunión de Google Meet a cada orden de trabajo programada",
+        "addConferenceHelp": "Activado: cada orden de trabajo enviada al calendario recibe una reunión de Google Meet, y el enlace se muestra en la orden. Desactivado: añade una reunión de Google Meet a una orden concreta desde su página. En ambos casos el cliente solo recibe el enlace cuando lo envías.",
         "pullEnabled": "Mostrar el tiempo ocupado de este calendario en el calendario de Torqvoice",
         "pullEnabledHelp": "Solo lectura. Se actualiza cada 15 minutos, una semana hacia atrás y dos meses hacia adelante."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Calendario",
         "pushEnabled": "Añadir las órdenes de trabajo programadas a este calendario",
         "includeCustomer": "Incluir el nombre y el teléfono del cliente en el evento",
-        "addConference": "Añadir un enlace de Microsoft Teams a cada evento",
-        "addConferenceHelp": "El enlace se muestra en la orden de trabajo para que pueda compartirlo con el cliente.",
+        "addConference": "Añadir una reunión de Microsoft Teams a cada orden de trabajo programada",
+        "addConferenceHelp": "Activado: cada orden de trabajo enviada al calendario recibe una reunión de Microsoft Teams, y el enlace se muestra en la orden. Desactivado: añade una reunión de Microsoft Teams a una orden concreta desde su página. En ambos casos el cliente solo recibe el enlace cuando lo envías.",
         "pullEnabled": "Mostrar el tiempo ocupado de este calendario en el calendario de Torqvoice",
         "pullEnabledHelp": "Solo lectura. Se actualiza cada 15 minutos, una semana hacia atrás y dos meses hacia adelante."
       }

+ 4 - 4
messages/fr/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Calendrier",
         "pushEnabled": "Ajouter les ordres de travail planifiés à ce calendrier",
         "includeCustomer": "Inclure le nom et le téléphone du client dans l'événement",
-        "addConference": "Ajouter un lien Google Meet à chaque événement",
-        "addConferenceHelp": "Le lien est affiché sur l'ordre de travail pour que vous puissiez le partager avec le client.",
+        "addConference": "Ajouter une réunion Google Meet à chaque ordre de travail planifié",
+        "addConferenceHelp": "Activé : chaque ordre de travail envoyé au calendrier reçoit une réunion Google Meet, et le lien apparaît sur l'ordre. Désactivé : ajoutez une réunion Google Meet à un ordre précis depuis sa page. Dans les deux cas, le client ne reçoit le lien que lorsque vous l'envoyez.",
         "pullEnabled": "Afficher les plages occupées de ce calendrier dans le calendrier Torqvoice",
         "pullEnabledHelp": "Lecture seule. Actualisé toutes les 15 minutes, une semaine en arrière et deux mois en avant."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Calendrier",
         "pushEnabled": "Ajouter les ordres de travail planifiés à ce calendrier",
         "includeCustomer": "Inclure le nom et le téléphone du client dans l'événement",
-        "addConference": "Ajouter un lien Microsoft Teams à chaque événement",
-        "addConferenceHelp": "Le lien est affiché sur l'ordre de travail pour que vous puissiez le partager avec le client.",
+        "addConference": "Ajouter une réunion Microsoft Teams à chaque ordre de travail planifié",
+        "addConferenceHelp": "Activé : chaque ordre de travail envoyé au calendrier reçoit une réunion Microsoft Teams, et le lien apparaît sur l'ordre. Désactivé : ajoutez une réunion Microsoft Teams à un ordre précis depuis sa page. Dans les deux cas, le client ne reçoit le lien que lorsque vous l'envoyez.",
         "pullEnabled": "Afficher les plages occupées de ce calendrier dans le calendrier Torqvoice",
         "pullEnabledHelp": "Lecture seule. Actualisé toutes les 15 minutes, une semaine en arrière et deux mois en avant."
       }

+ 4 - 4
messages/it/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Calendario",
         "pushEnabled": "Aggiungi gli ordini di lavoro pianificati a questo calendario",
         "includeCustomer": "Includi nome e telefono del cliente nell'evento",
-        "addConference": "Aggiungi un link Google Meet a ogni evento",
-        "addConferenceHelp": "Il link viene mostrato nell'ordine di lavoro, così puoi condividerlo con il cliente.",
+        "addConference": "Aggiungi una riunione Google Meet a ogni ordine di lavoro pianificato",
+        "addConferenceHelp": "Attivo: ogni ordine di lavoro inviato al calendario riceve una riunione Google Meet, e il link compare sull'ordine. Spento: aggiungi una riunione Google Meet a un singolo ordine dalla sua pagina. In entrambi i casi il cliente riceve il link solo quando lo invii.",
         "pullEnabled": "Mostra gli impegni di questo calendario nel calendario di Torqvoice",
         "pullEnabledHelp": "Sola lettura. Aggiornato ogni 15 minuti, da una settimana indietro a due mesi avanti."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Calendario",
         "pushEnabled": "Aggiungi gli ordini di lavoro pianificati a questo calendario",
         "includeCustomer": "Includi nome e telefono del cliente nell'evento",
-        "addConference": "Aggiungi un link Microsoft Teams a ogni evento",
-        "addConferenceHelp": "Il link viene mostrato nell'ordine di lavoro, così puoi condividerlo con il cliente.",
+        "addConference": "Aggiungi una riunione Microsoft Teams a ogni ordine di lavoro pianificato",
+        "addConferenceHelp": "Attivo: ogni ordine di lavoro inviato al calendario riceve una riunione Microsoft Teams, e il link compare sull'ordine. Spento: aggiungi una riunione Microsoft Teams a un singolo ordine dalla sua pagina. In entrambi i casi il cliente riceve il link solo quando lo invii.",
         "pullEnabled": "Mostra gli impegni di questo calendario nel calendario di Torqvoice",
         "pullEnabledHelp": "Sola lettura. Aggiornato ogni 15 minuti, da una settimana indietro a due mesi avanti."
       }

+ 4 - 4
messages/lt/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Kalendorius",
         "pushEnabled": "Įtraukti suplanuotus darbo užsakymus į šį kalendorių",
         "includeCustomer": "Įtraukti į įvykį kliento vardą ir telefoną",
-        "addConference": "Pridėti Google Meet nuorodą prie kiekvieno įvykio",
-        "addConferenceHelp": "Nuoroda rodoma darbo užsakyme, kad galėtumėte ja pasidalinti su klientu.",
+        "addConference": "Pridėti Google Meet susitikimą prie kiekvieno suplanuoto darbo užsakymo",
+        "addConferenceHelp": "Įjungta: kiekvienas į kalendorių įkeltas darbo užsakymas gauna Google Meet susitikimą, o nuoroda rodoma užsakyme. Išjungta: pridėkite Google Meet susitikimą prie vieno užsakymo iš jo puslapio. Bet kuriuo atveju klientas nuorodą gauna tik tada, kai ją išsiunčiate.",
         "pullEnabled": "Rodyti šio kalendoriaus užimtą laiką Torqvoice kalendoriuje",
         "pullEnabledHelp": "Tik skaitymui. Atnaujinama kas 15 minučių, viena savaitė atgal ir du mėnesiai į priekį."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Kalendorius",
         "pushEnabled": "Įtraukti suplanuotus darbo užsakymus į šį kalendorių",
         "includeCustomer": "Įtraukti į įvykį kliento vardą ir telefoną",
-        "addConference": "Pridėti Microsoft Teams nuorodą prie kiekvieno įvykio",
-        "addConferenceHelp": "Nuoroda rodoma darbo užsakyme, kad galėtumėte ja pasidalinti su klientu.",
+        "addConference": "Pridėti Microsoft Teams susitikimą prie kiekvieno suplanuoto darbo užsakymo",
+        "addConferenceHelp": "Įjungta: kiekvienas į kalendorių įkeltas darbo užsakymas gauna Microsoft Teams susitikimą, o nuoroda rodoma užsakyme. Išjungta: pridėkite Microsoft Teams susitikimą prie vieno užsakymo iš jo puslapio. Bet kuriuo atveju klientas nuorodą gauna tik tada, kai ją išsiunčiate.",
         "pullEnabled": "Rodyti šio kalendoriaus užimtą laiką Torqvoice kalendoriuje",
         "pullEnabledHelp": "Tik skaitymui. Atnaujinama kas 15 minučių, viena savaitė atgal ir du mėnesiai į priekį."
       }

+ 4 - 4
messages/nb/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Kalender",
         "pushEnabled": "Legg planlagte arbeidsordre i denne kalenderen",
         "includeCustomer": "Ta med kundens navn og telefonnummer i hendelsen",
-        "addConference": "Legg til en Google Meet-lenke i hver hendelse",
-        "addConferenceHelp": "Lenken vises på arbeidsordren slik at du kan dele den med kunden.",
+        "addConference": "Legg til et Google Meet-møte på hver planlagte arbeidsordre",
+        "addConferenceHelp": "På: hver arbeidsordre som legges i kalenderen får et Google Meet-møte, og lenken vises på arbeidsordren. Av: legg til et Google Meet-møte på en enkelt arbeidsordre fra siden dens. Uansett får kunden lenken bare når du sender den.",
         "pullEnabled": "Vis denne kalenderens opptatte tid i Torqvoice-kalenderen",
         "pullEnabledHelp": "Skrivebeskyttet. Oppdateres hvert 15. minutt, én uke bakover og to måneder fremover."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Kalender",
         "pushEnabled": "Legg planlagte arbeidsordre i denne kalenderen",
         "includeCustomer": "Ta med kundens navn og telefonnummer i hendelsen",
-        "addConference": "Legg til en Microsoft Teams-lenke i hver hendelse",
-        "addConferenceHelp": "Lenken vises på arbeidsordren slik at du kan dele den med kunden.",
+        "addConference": "Legg til et Microsoft Teams-møte på hver planlagte arbeidsordre",
+        "addConferenceHelp": "På: hver arbeidsordre som legges i kalenderen får et Microsoft Teams-møte, og lenken vises på arbeidsordren. Av: legg til et Microsoft Teams-møte på en enkelt arbeidsordre fra siden dens. Uansett får kunden lenken bare når du sender den.",
         "pullEnabled": "Vis denne kalenderens opptatte tid i Torqvoice-kalenderen",
         "pullEnabledHelp": "Skrivebeskyttet. Oppdateres hvert 15. minutt, én uke bakover og to måneder fremover."
       }

+ 4 - 4
messages/nl/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Agenda",
         "pushEnabled": "Geplande werkorders aan deze agenda toevoegen",
         "includeCustomer": "Naam en telefoonnummer van de klant in de afspraak opnemen",
-        "addConference": "Een Google Meet-link aan elke afspraak toevoegen",
-        "addConferenceHelp": "De link wordt op de werkorder getoond, zodat u die met de klant kunt delen.",
+        "addConference": "Een Google Meet-vergadering toevoegen aan elke geplande werkorder",
+        "addConferenceHelp": "Aan: elke werkorder die naar de agenda gaat krijgt een Google Meet-vergadering, en de link staat op de werkorder. Uit: voeg een Google Meet-vergadering toe aan één werkorder vanaf zijn pagina. In beide gevallen krijgt de klant de link pas als u die verstuurt.",
         "pullEnabled": "Bezette tijd van deze agenda in de Torqvoice-agenda tonen",
         "pullEnabledHelp": "Alleen-lezen. Elke 15 minuten vernieuwd, één week terug en twee maanden vooruit."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Agenda",
         "pushEnabled": "Geplande werkorders aan deze agenda toevoegen",
         "includeCustomer": "Naam en telefoonnummer van de klant in de afspraak opnemen",
-        "addConference": "Een Microsoft Teams-link aan elke afspraak toevoegen",
-        "addConferenceHelp": "De link wordt op de werkorder getoond, zodat u die met de klant kunt delen.",
+        "addConference": "Een Microsoft Teams-vergadering toevoegen aan elke geplande werkorder",
+        "addConferenceHelp": "Aan: elke werkorder die naar de agenda gaat krijgt een Microsoft Teams-vergadering, en de link staat op de werkorder. Uit: voeg een Microsoft Teams-vergadering toe aan één werkorder vanaf zijn pagina. In beide gevallen krijgt de klant de link pas als u die verstuurt.",
         "pullEnabled": "Bezette tijd van deze agenda in de Torqvoice-agenda tonen",
         "pullEnabledHelp": "Alleen-lezen. Elke 15 minuten vernieuwd, één week terug en twee maanden vooruit."
       }

+ 4 - 4
messages/pl/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Kalendarz",
         "pushEnabled": "Dodawaj zaplanowane zlecenia do tego kalendarza",
         "includeCustomer": "Umieść w wydarzeniu imię i nazwisko oraz telefon klienta",
-        "addConference": "Dodaj link Google Meet do każdego wydarzenia",
-        "addConferenceHelp": "Link jest widoczny na zleceniu, więc możesz go przekazać klientowi.",
+        "addConference": "Dodawaj spotkanie Google Meet do każdego zaplanowanego zlecenia",
+        "addConferenceHelp": "Włączone: każde zlecenie wysłane do kalendarza dostaje spotkanie Google Meet, a link pojawia się na zleceniu. Wyłączone: dodaj spotkanie Google Meet do pojedynczego zlecenia z jego strony. W obu przypadkach klient dostaje link dopiero, gdy go wyślesz.",
         "pullEnabled": "Pokazuj zajęty czas z tego kalendarza w kalendarzu Torqvoice",
         "pullEnabledHelp": "Tylko do odczytu. Odświeżane co 15 minut, tydzień wstecz i dwa miesiące do przodu."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Kalendarz",
         "pushEnabled": "Dodawaj zaplanowane zlecenia do tego kalendarza",
         "includeCustomer": "Umieść w wydarzeniu imię i nazwisko oraz telefon klienta",
-        "addConference": "Dodaj link Microsoft Teams do każdego wydarzenia",
-        "addConferenceHelp": "Link jest widoczny na zleceniu, więc możesz go przekazać klientowi.",
+        "addConference": "Dodawaj spotkanie Microsoft Teams do każdego zaplanowanego zlecenia",
+        "addConferenceHelp": "Włączone: każde zlecenie wysłane do kalendarza dostaje spotkanie Microsoft Teams, a link pojawia się na zleceniu. Wyłączone: dodaj spotkanie Microsoft Teams do pojedynczego zlecenia z jego strony. W obu przypadkach klient dostaje link dopiero, gdy go wyślesz.",
         "pullEnabled": "Pokazuj zajęty czas z tego kalendarza w kalendarzu Torqvoice",
         "pullEnabledHelp": "Tylko do odczytu. Odświeżane co 15 minut, tydzień wstecz i dwa miesiące do przodu."
       }

+ 4 - 4
messages/pt-BR/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Calendário",
         "pushEnabled": "Adicionar ordens de serviço agendadas a este calendário",
         "includeCustomer": "Incluir o nome e o telefone do cliente no evento",
-        "addConference": "Adicionar um link do Google Meet a cada evento",
-        "addConferenceHelp": "O link aparece na ordem de serviço para que você possa compartilhá-lo com o cliente.",
+        "addConference": "Adicionar uma reunião do Google Meet a cada ordem de serviço agendada",
+        "addConferenceHelp": "Ligado: cada ordem de serviço enviada à agenda recebe uma reunião do Google Meet, e o link aparece na ordem. Desligado: adicione uma reunião do Google Meet a uma ordem específica pela página dela. De qualquer forma o cliente só recebe o link quando você o envia.",
         "pullEnabled": "Mostrar os horários ocupados deste calendário no calendário do Torqvoice",
         "pullEnabledHelp": "Somente leitura. Atualizado a cada 15 minutos, uma semana para trás e dois meses para a frente."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Calendário",
         "pushEnabled": "Adicionar ordens de serviço agendadas a este calendário",
         "includeCustomer": "Incluir o nome e o telefone do cliente no evento",
-        "addConference": "Adicionar um link do Microsoft Teams a cada evento",
-        "addConferenceHelp": "O link aparece na ordem de serviço para que você possa compartilhá-lo com o cliente.",
+        "addConference": "Adicionar uma reunião do Microsoft Teams a cada ordem de serviço agendada",
+        "addConferenceHelp": "Ligado: cada ordem de serviço enviada à agenda recebe uma reunião do Microsoft Teams, e o link aparece na ordem. Desligado: adicione uma reunião do Microsoft Teams a uma ordem específica pela página dela. De qualquer forma o cliente só recebe o link quando você o envia.",
         "pullEnabled": "Mostrar os horários ocupados deste calendário no calendário do Torqvoice",
         "pullEnabledHelp": "Somente leitura. Atualizado a cada 15 minutos, uma semana para trás e dois meses para a frente."
       }

+ 4 - 4
messages/ru/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Календарь",
         "pushEnabled": "Добавлять запланированные заказ-наряды в этот календарь",
         "includeCustomer": "Включать имя и телефон клиента в событие",
-        "addConference": "Добавлять ссылку Google Meet к каждому событию",
-        "addConferenceHelp": "Ссылка отображается в заказ-наряде, чтобы вы могли поделиться ею с клиентом.",
+        "addConference": "Добавлять встречу Google Meet к каждому запланированному заказу",
+        "addConferenceHelp": "Вкл.: каждый заказ, отправленный в календарь, получает встречу Google Meet, а ссылка показывается в заказе. Выкл.: добавьте встречу Google Meet к отдельному заказу с его страницы. В любом случае клиент получает ссылку только когда вы её отправите.",
         "pullEnabled": "Показывать занятое время этого календаря в календаре Torqvoice",
         "pullEnabledHelp": "Только чтение. Обновляется каждые 15 минут: неделя назад и два месяца вперёд."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Календарь",
         "pushEnabled": "Добавлять запланированные заказ-наряды в этот календарь",
         "includeCustomer": "Включать имя и телефон клиента в событие",
-        "addConference": "Добавлять ссылку Microsoft Teams к каждому событию",
-        "addConferenceHelp": "Ссылка отображается в заказ-наряде, чтобы вы могли поделиться ею с клиентом.",
+        "addConference": "Добавлять встречу Microsoft Teams к каждому запланированному заказу",
+        "addConferenceHelp": "Вкл.: каждый заказ, отправленный в календарь, получает встречу Microsoft Teams, а ссылка показывается в заказе. Выкл.: добавьте встречу Microsoft Teams к отдельному заказу с его страницы. В любом случае клиент получает ссылку только когда вы её отправите.",
         "pullEnabled": "Показывать занятое время этого календаря в календаре Torqvoice",
         "pullEnabledHelp": "Только чтение. Обновляется каждые 15 минут: неделя назад и два месяца вперёд."
       }

+ 4 - 4
messages/tr/integrations.json

@@ -209,8 +209,8 @@
         "calendarId": "Takvim",
         "pushEnabled": "Planlanmış iş emirlerini bu takvime ekle",
         "includeCustomer": "Etkinliğe müşterinin adını ve telefonunu ekle",
-        "addConference": "Her etkinliğe bir Google Meet bağlantısı ekle",
-        "addConferenceHelp": "Bağlantı iş emrinde gösterilir, böylece müşteriyle paylaşabilirsiniz.",
+        "addConference": "Planlanan her iş emrine Google Meet toplantısı ekle",
+        "addConferenceHelp": "Açık: takvime gönderilen her iş emri bir Google Meet toplantısı alır ve bağlantı iş emrinde görünür. Kapalı: tek bir iş emrine kendi sayfasından Google Meet toplantısı ekleyin. Her durumda müşteri bağlantıyı yalnızca siz gönderdiğinizde alır.",
         "pullEnabled": "Bu takvimin meşgul zamanlarını Torqvoice takviminde göster",
         "pullEnabledHelp": "Salt okunur. 15 dakikada bir yenilenir; bir hafta geriye ve iki ay ileriye."
       }
@@ -226,8 +226,8 @@
         "calendarId": "Takvim",
         "pushEnabled": "Planlanmış iş emirlerini bu takvime ekle",
         "includeCustomer": "Etkinliğe müşterinin adını ve telefonunu ekle",
-        "addConference": "Her etkinliğe bir Microsoft Teams bağlantısı ekle",
-        "addConferenceHelp": "Bağlantı iş emrinde gösterilir, böylece müşteriyle paylaşabilirsiniz.",
+        "addConference": "Planlanan her iş emrine Microsoft Teams toplantısı ekle",
+        "addConferenceHelp": "Açık: takvime gönderilen her iş emri bir Microsoft Teams toplantısı alır ve bağlantı iş emrinde görünür. Kapalı: tek bir iş emrine kendi sayfasından Microsoft Teams toplantısı ekleyin. Her durumda müşteri bağlantıyı yalnızca siz gönderdiğinizde alır.",
         "pullEnabled": "Bu takvimin meşgul zamanlarını Torqvoice takviminde göster",
         "pullEnabledHelp": "Salt okunur. 15 dakikada bir yenilenir; bir hafta geriye ve iki ay ileriye."
       }

+ 17 - 13
src/features/integrations/Actions/integrationActions.ts

@@ -965,16 +965,16 @@ export interface ServiceVideoCall {
     /** True when it can be removed from the work order: the connector owns the meeting. */
     removable: boolean
   } | null
-  /** Connected video call services a meeting can be added from. */
-  providers: { connectorId: string; name: string }[]
+  /** Connected services a meeting can be added from, with the meeting product's label key. */
+  providers: { connectorId: string; name: string; provider: string }[]
 }
 
 /**
  * Video call state for one work order: the link a connection attached, and
- * the connected conferencing services that could add one. Calendar
- * connectors attach links as part of their event (Google Meet, Teams); a
- * conferencing connector such as Zoom owns the meeting outright, which is
- * the only kind a person can add or remove from the work order.
+ * the connected services that could add one. Calendar connectors attach
+ * the meeting to their event (Google Meet, Teams) and can do so on request;
+ * a conferencing connector such as Zoom owns the meeting outright, which is
+ * the only kind a person can remove again from the work order.
  */
 export async function getServiceVideoCall(serviceRecordId: string) {
   return withAuth(
@@ -1009,8 +1009,10 @@ export async function getServiceVideoCall(serviceRecordId: string) {
       }
       const providers = connections
         .map((c) => getManifest(c.connectorId))
-        .filter((m): m is ConnectorManifest => Boolean(m && m.category === 'conferencing'))
-        .map((m) => ({ connectorId: m.id, name: m.name }))
+        .filter((m): m is ConnectorManifest & { meetingProvider: string } =>
+          Boolean(m?.meetingProvider)
+        )
+        .map((m) => ({ connectorId: m.id, name: m.name, provider: m.meetingProvider }))
       return { link, providers }
     },
     { requiredPermissions: READ_PERMISSION }
@@ -1018,10 +1020,10 @@ export async function getServiceVideoCall(serviceRecordId: string) {
 }
 
 /**
- * Run a conferencing connector's sync for one work order right now, rather
- * than within the minute the cron would take, and report what it left
- * behind. The job still goes through the queue so it is logged, retried and
- * visible on the integration page like any other.
+ * Run a connector's sync for one work order right now, rather than within
+ * the minute the cron would take, and report what it left behind. The job
+ * still goes through the queue so it is logged, retried and visible on the
+ * integration page like any other.
  */
 async function syncServiceConference(
   organizationId: string,
@@ -1030,7 +1032,9 @@ async function syncServiceConference(
   action: 'create' | 'remove'
 ) {
   const manifest = getManifest(connectorId)
-  if (!manifest || manifest.category !== 'conferencing') throw new Error('Unknown integration')
+  if (!manifest?.meetingProvider) throw new Error('Unknown integration')
+  if (action === 'remove' && manifest.category !== 'conferencing')
+    throw new Error('This meeting is part of the calendar event and is removed with it')
   const job = manifest.subscriptions?.find((s) => s.event === 'service.update')?.job
   if (!job) throw new Error('This integration cannot add video calls')
   const row = await db.integrationConnection.findUnique({

+ 6 - 0
src/features/integrations/Lib/types.ts

@@ -113,6 +113,12 @@ export interface ConnectorManifest {
   auth: AuthSpec
   /** Capability ids such as 'calendar.push'. Shown as badges; used by the app to find a provider. */
   capabilities: string[]
+  /**
+   * The video call product a person can add to a work order from its page,
+   * as a key under integrations.meeting: 'teams', 'google-meet', 'zoom'.
+   * Only connectors whose service.update job honours a `create` action.
+   */
+  meetingProvider?: string
   settings: SettingField[]
   /** App events (webhook event names) that enqueue a job of the given kind with the event's entityId. */
   subscriptions?: { event: string; job: string }[]

+ 1 - 1
src/features/vehicles/Components/service-page/VideoCallSection.tsx

@@ -232,7 +232,7 @@ export function VideoCallSection({
                 ) : (
                   <Video className="mr-1 h-3.5 w-3.5" />
                 )}
-                {t('add', { provider: p.name })}
+                {t('add', { provider: providerLabel(p.provider) })}
               </Button>
             ))}
           </div>

+ 1 - 0
src/integrations/google-calendar/manifest.ts

@@ -37,6 +37,7 @@ export const manifest: ConnectorManifest = {
     ],
     tenantHelp: 'tenantHelp',
   },
+  meetingProvider: 'google-meet',
   capabilities: ['calendar.push', 'calendar.pull', 'calendar.conference'],
   settings: [
     {

+ 28 - 6
src/integrations/google-calendar/server.ts

@@ -43,7 +43,17 @@ function eventsUrl(calendarId: string, eventId?: string): string {
   return eventId ? `${base}/${encodeURIComponent(eventId)}` : base
 }
 
-async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
+/**
+ * Put the work order on the calendar, with a Meet link when the setting
+ * says so or a person asked for one from the work order (`action:
+ * 'create'`, remembered as `manual` on the link). A meeting added by hand
+ * keeps its event even with the automatic push switched off.
+ */
+async function pushService(
+  ctx: ConnectorContext,
+  serviceRecordId: string,
+  action: 'create' | null = null
+) {
   const settings = settingsOf(ctx)
   if (!settings.calendarId) throw new Error('No calendar chosen in the integration settings')
   const link = await ctx.links.get(SERVICE_ENTITY, serviceRecordId)
@@ -51,8 +61,10 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
   const draft = record
     ? draftCalendarEvent(record, { appUrl: ctx.appUrl, includeCustomer: settings.includeCustomer })
     : null
+  const manual = action === 'create' || link?.metadata?.manual === true
+  const wantMeeting = settings.addConference || manual
 
-  if (!draft || !settings.pushEnabled) {
+  if (!draft || !(settings.pushEnabled || manual)) {
     if (!link) return { summary: 'nothing to do' }
     try {
       await ctx.http.fetch(eventsUrl(settings.calendarId, link.remoteId), { method: 'DELETE' })
@@ -64,7 +76,7 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     return { summary: 'event removed' }
   }
 
-  if (link?.checksum === draft.checksum && (!settings.addConference || link.metadata?.meetingUrl)) {
+  if (link?.checksum === draft.checksum && (!wantMeeting || link.metadata?.meetingUrl)) {
     return { summary: 'unchanged' }
   }
 
@@ -76,7 +88,7 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     source: { title: 'Torqvoice', url: draft.url },
     extendedProperties: { private: { torqvoiceServiceId: serviceRecordId } },
   }
-  if (settings.addConference && !link?.metadata?.meetingUrl) {
+  if (wantMeeting && !link?.metadata?.meetingUrl) {
     body.conferenceData = {
       createRequest: {
         requestId: `torqvoice-${serviceRecordId}-${Date.now()}`,
@@ -116,13 +128,23 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     )
   }
 
+  // Google creates the Meet room after the write returns; one read back
+  // catches the link rather than leaving the work order without it.
+  if (wantMeeting && !link?.metadata?.meetingUrl && !saved.hangoutLink) {
+    saved = await ctx.http.json<GoogleEvent>(eventsUrl(settings.calendarId, saved.id))
+  }
+  if (action === 'create' && !saved.hangoutLink && !link?.metadata?.meetingUrl) {
+    throw new Error('Google did not add a Meet link to the event. Try again in a moment.')
+  }
+
   await ctx.links.set(SERVICE_ENTITY, serviceRecordId, {
     remoteId: saved.id,
-    remoteUrl: null,
+    remoteUrl: saved.htmlLink ?? null,
     checksum: draft.checksum,
     metadata: {
       ...(link?.metadata ?? {}),
       calendarId: settings.calendarId,
+      ...(manual && { manual: true }),
       ...(saved.hangoutLink && { meetingUrl: saved.hangoutLink, meetingProvider: 'google-meet' }),
     },
   })
@@ -211,7 +233,7 @@ export const connector: ConnectorServer = {
     'calendar.push': async (ctx, payload) => {
       const id = typeof payload.entityId === 'string' ? payload.entityId : null
       if (!id) return { summary: 'no record id' }
-      return pushService(ctx, id)
+      return pushService(ctx, id, payload.action === 'create' ? 'create' : null)
     },
     'calendar.pull': pullBusy,
   },

+ 1 - 0
src/integrations/microsoft-365/manifest.ts

@@ -30,6 +30,7 @@ export const manifest: ConnectorManifest = {
     ],
     tenantHelp: 'tenantHelp',
   },
+  meetingProvider: 'teams',
   capabilities: ['calendar.push', 'calendar.pull', 'calendar.conference'],
   settings: [
     {

+ 40 - 12
src/integrations/microsoft-365/server.ts

@@ -51,7 +51,18 @@ function fromGraphTime(t: { dateTime: string; timeZone: string } | undefined): D
   return Number.isNaN(d.getTime()) ? null : d
 }
 
-async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
+/**
+ * Put the work order on the calendar, as a Teams meeting when the setting
+ * says so or a person asked for one from the work order (`action:
+ * 'create'`, remembered as `manual` on the link). A meeting added by hand
+ * keeps its event even with the automatic push switched off, since the
+ * meeting is the event.
+ */
+async function pushService(
+  ctx: ConnectorContext,
+  serviceRecordId: string,
+  action: 'create' | null = null
+) {
   const settings = settingsOf(ctx)
   if (!settings.calendarId) throw new Error('No calendar chosen in the integration settings')
   const link = await ctx.links.get(SERVICE_ENTITY, serviceRecordId)
@@ -59,8 +70,10 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
   const draft = record
     ? draftCalendarEvent(record, { appUrl: ctx.appUrl, includeCustomer: settings.includeCustomer })
     : null
+  const manual = action === 'create' || link?.metadata?.manual === true
+  const wantMeeting = settings.addConference || manual
 
-  if (!draft || !settings.pushEnabled) {
+  if (!draft || !(settings.pushEnabled || manual)) {
     if (!link) return { summary: 'nothing to do' }
     try {
       const res = await ctx.http.fetch(`${GRAPH}/me/events/${encodeURIComponent(link.remoteId)}`, {
@@ -75,19 +88,22 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     return { summary: 'event removed' }
   }
 
-  if (link?.checksum === draft.checksum && (!settings.addConference || link.metadata?.meetingUrl)) {
+  if (link?.checksum === draft.checksum && (!wantMeeting || link.metadata?.meetingUrl)) {
     return { summary: 'unchanged' }
   }
 
+  // Graph does not let an online meeting be switched off again, so it is
+  // only asked for while the event has none.
   const body: Record<string, unknown> = {
     subject: draft.title,
     body: { contentType: 'text', content: draft.description },
     start: graphTime(draft.start),
     end: graphTime(draft.end),
-    ...(settings.addConference && {
-      isOnlineMeeting: true,
-      onlineMeetingProvider: 'teamsForBusiness',
-    }),
+    ...(wantMeeting &&
+      !link?.metadata?.meetingUrl && {
+        isOnlineMeeting: true,
+        onlineMeetingProvider: 'teamsForBusiness',
+      }),
   }
 
   let saved: GraphEvent
@@ -121,6 +137,20 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     )
   }
 
+  // Graph fills the join link a moment after an update returns; one read
+  // back catches it rather than leaving the work order without a link.
+  if (wantMeeting && !link?.metadata?.meetingUrl && !saved.onlineMeeting?.joinUrl) {
+    saved = await ctx.http.json<GraphEvent>(
+      `${GRAPH}/me/events/${encodeURIComponent(saved.id)}?$select=id,webLink,onlineMeeting`
+    )
+  }
+  const meetingUrl = saved.onlineMeeting?.joinUrl
+  if (manual && action === 'create' && !meetingUrl && !link?.metadata?.meetingUrl) {
+    throw new Error(
+      'Microsoft did not add a Teams meeting to the event. Teams meetings need a work or school account with Teams.'
+    )
+  }
+
   await ctx.links.set(SERVICE_ENTITY, serviceRecordId, {
     remoteId: saved.id,
     remoteUrl: saved.webLink ?? null,
@@ -128,10 +158,8 @@ async function pushService(ctx: ConnectorContext, serviceRecordId: string) {
     metadata: {
       ...(link?.metadata ?? {}),
       calendarId: settings.calendarId,
-      ...(saved.onlineMeeting?.joinUrl && {
-        meetingUrl: saved.onlineMeeting.joinUrl,
-        meetingProvider: 'teams',
-      }),
+      ...(manual && { manual: true }),
+      ...(meetingUrl && { meetingUrl, meetingProvider: 'teams' }),
     },
   })
   return { summary: link ? 'event updated' : 'event created' }
@@ -222,7 +250,7 @@ export const connector: ConnectorServer = {
     'calendar.push': async (ctx, payload) => {
       const id = typeof payload.entityId === 'string' ? payload.entityId : null
       if (!id) return { summary: 'no record id' }
-      return pushService(ctx, id)
+      return pushService(ctx, id, payload.action === 'create' ? 'create' : null)
     },
     'calendar.pull': pullBusy,
   },

+ 1 - 0
src/integrations/zoom/manifest.ts

@@ -37,6 +37,7 @@ export const manifest: ConnectorManifest = {
     ],
     tenantHelp: 'tenantHelp',
   },
+  meetingProvider: 'zoom',
   capabilities: ['calendar.conference'],
   settings: [
     {