Say Teams is switched off instead of showing an empty list

Teams is disabled server-side while it is redesigned: every endpoint answers
503 with an explanation. The view did not notice. The API helper resolves the
response body whatever the status, so the 503's object arrived where an array
was expected, `!teams.length` was true, and the page rendered "No teams yet —
Create a team to share devices with other users" beside a New Team button that
could only ever fail. An inviting empty state over a feature that is not there
is worse than an error: it invites someone into a dead end.

It now shows the server's own explanation, which stays accurate when the
feature returns, and removes the button that leads nowhere.

Also enlarges the help tip's hit area. The marker is 18px, which is fine to
look at and about half the touch guideline — and since tapping a tip is now how
touch users read it at all, that mattered. A transparent inset overlay makes
the target comfortable without inflating the marker in a heading; a tap 9px
outside the visible circle registers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
This commit is contained in:
ScreenTinker 2026-07-28 19:46:26 -05:00
parent 0a9a749475
commit a635120769
8 changed files with 35 additions and 0 deletions

View file

@ -1409,6 +1409,17 @@ body {
/* A help tip must be reachable without a mouse. :hover alone made every one of these
invisible on a tablet or phone which is a large share of the people administering a
signage install and unreachable by keyboard. Tapping toggles .is-open; focus shows it too. */
/* The visible marker is 18px, which is fine to look at and far too small to hit with a thumb
about half the ~44px touch guideline. Now that tapping a tip is how touch users read it at
all, extend the HIT AREA with a transparent inset overlay rather than inflating the circle,
so the target is comfortable without the marker dominating a heading. */
.help-tip::before {
content: '';
position: absolute;
inset: -13px;
border-radius: 50%;
}
.help-tip:hover::after,
.help-tip:focus-visible::after,
.help-tip.is-open::after {

View file

@ -2,6 +2,8 @@
// standard for B2B software in DACH). Native review recommended before
// publicizing as fully supported.
export default {
'team.unavailable_desc': 'Diese Funktion ist nicht verfügbar, während sie für die Zusammenarbeit mit Workspaces neu gestaltet wird. Vorhandene Teamdaten bleiben erhalten.',
'team.unavailable_title': 'Teams wird überarbeitet',
'settings.help_tip': 'Ihr Profil, Passwort und die Zwei-Faktor-Anmeldung sowie API-Tokens für Integrationen und Werkzeuge zum Export und Import Ihrer Daten.',
'activity.help_tip': 'Protokoll darüber, was sich geändert hat und wer es geändert hat: Anmeldungen, hochgeladene Inhalte, Gerätebefehle und Warnungen. Hilfreich, um nachzuvollziehen, warum ein Bildschirm etwas anderes angezeigt hat.',
'playlist.help_tip': 'Eine Playlist ist die Reihenfolge, die ein Bildschirm in einer Schleife abspielt. Fügen Sie Inhalte hinzu, legen Sie deren Anzeigedauer fest und weisen Sie die Playlist einem Bildschirm oder einer Gruppe zu. Ein Bildschirm spielt jeweils eine Playlist ab, sofern kein Zeitplan sie überschreibt.',

View file

@ -1,6 +1,8 @@
// English translations. This file is the source of truth for keys —
// every other locale should mirror its keys (or fall back to en).
export default {
'team.unavailable_desc': 'This feature is unavailable while it is redesigned to work with Workspaces. Existing team data is preserved.',
'team.unavailable_title': 'Teams is being rebuilt',
'settings.help_tip': 'Your profile, password and two-factor sign-in, plus API tokens for integrations and tools to export or import your data.',
'activity.help_tip': 'A record of what changed and who changed it — logins, content uploads, device commands and alerts. Useful for working out why a screen changed what it was showing.',
'playlist.help_tip': 'A playlist is the running order a screen loops through. Add content items, set how long each shows, then assign the playlist to a screen or group. A screen plays one playlist at a time unless a schedule overrides it.',

View file

@ -1,6 +1,8 @@
// Spanish translations. Reviewed for UI register (informal tú).
// Native review still recommended before publicizing as fully supported.
export default {
'team.unavailable_desc': 'Esta función no está disponible mientras se rediseña para funcionar con los Espacios de trabajo. Los datos de los equipos existentes se conservan.',
'team.unavailable_title': 'Equipos está en rediseño',
'settings.help_tip': 'Tu perfil, contraseña y verificación en dos pasos, además de tokens de API para integraciones y herramientas para exportar o importar tus datos.',
'activity.help_tip': 'Registro de qué cambió y quién lo cambió: inicios de sesión, subidas de contenido, comandos a dispositivos y alertas. Útil para averiguar por qué una pantalla cambió lo que mostraba.',
'playlist.help_tip': 'Una lista de reproducción es el orden que una pantalla repite en bucle. Añade contenidos, define cuánto dura cada uno y asigna la lista a una pantalla o a un grupo. Una pantalla reproduce una sola lista salvo que una programación la anule.',

View file

@ -2,6 +2,8 @@
// standard for software UIs in France; tu would feel underdressed for a B2B tool).
// Native review recommended before publicizing as fully supported.
export default {
'team.unavailable_desc': 'Cette fonctionnalité est indisponible pendant sa refonte pour fonctionner avec les espaces de travail. Les données des équipes existantes sont conservées.',
'team.unavailable_title': 'Les équipes sont en cours de refonte',
'settings.help_tip': 'Votre profil, votre mot de passe et la double authentification, ainsi que les jetons d\'API pour les intégrations et les outils d\'export ou d\'import de vos données.',
'activity.help_tip': 'Journal de ce qui a changé et par qui : connexions, envois de contenu, commandes aux appareils et alertes. Utile pour comprendre pourquoi un écran a changé d\'affichage.',
'playlist.help_tip': 'Une playlist est l\'ordre de lecture qu\'un écran répète en boucle. Ajoutez des contenus, définissez la durée de chacun, puis affectez la playlist à un écran ou à un groupe. Un écran lit une seule playlist à la fois, sauf si une programmation la remplace.',

View file

@ -1,6 +1,8 @@
// Italian translations. This file is the source of truth for keys —
// every other locale should mirror its keys (or fall back to en).
export default {
'team.unavailable_desc': 'Questa funzione non è disponibile mentre viene riprogettata per funzionare con gli Spazi di lavoro. I dati dei team esistenti vengono conservati.',
'team.unavailable_title': 'I team sono in fase di riprogettazione',
'settings.help_tip': 'Il tuo profilo, la password e l\'accesso a due fattori, oltre ai token API per le integrazioni e agli strumenti per esportare o importare i tuoi dati.',
'activity.help_tip': 'Registro di che cosa è cambiato e di chi lo ha cambiato: accessi, caricamenti di contenuti, comandi ai dispositivi e avvisi. Utile per capire perché uno schermo ha cambiato ciò che mostrava.',
'playlist.help_tip': 'Una playlist è l\'ordine di riproduzione che uno schermo ripete in ciclo. Aggiungi contenuti, imposta per quanto tempo ciascuno viene mostrato e assegna la playlist a uno schermo o a un gruppo. Uno schermo riproduce una sola playlist alla volta, a meno che una pianificazione non la sostituisca.',

View file

@ -2,6 +2,8 @@
// Reviewed for UI register (informal você). Native review recommended before
// publicizing as fully supported.
export default {
'team.unavailable_desc': 'Esta funcionalidade está indisponível enquanto é reformulada para funcionar com as Áreas de trabalho. Os dados das equipas existentes são preservados.',
'team.unavailable_title': 'As equipas estão a ser reformuladas',
'settings.help_tip': 'O seu perfil, palavra-passe e autenticação de dois fatores, além de tokens de API para integrações e ferramentas para exportar ou importar os seus dados.',
'activity.help_tip': 'Registo do que mudou e de quem o mudou: inícios de sessão, carregamentos de conteúdo, comandos a dispositivos e alertas. Útil para perceber por que razão um ecrã mudou o que estava a mostrar.',
'playlist.help_tip': 'Uma playlist é a ordem de reprodução que um ecrã repete em ciclo. Adicione conteúdos, defina quanto tempo cada um aparece e atribua a playlist a um ecrã ou a um grupo. Um ecrã reproduz uma playlist de cada vez, a não ser que um agendamento a substitua.',

View file

@ -37,6 +37,18 @@ async function renderList(container) {
const teams = await API('/teams');
const list = document.getElementById('teamsList');
// Teams is switched off server-side while it is redesigned: every endpoint answers 503 with
// an explanation. API() resolves the BODY whatever the status, so an object arrives where an
// array was expected — and `!teams.length` then rendered "No teams yet" over a feature that
// is not there, next to a New Team button that could only ever fail. Say what is actually
// happening, and take away the button that leads nowhere.
if (!Array.isArray(teams)) {
document.getElementById('newTeamBtn')?.remove();
list.innerHTML = `<div class="empty-state"><h3>${esc(t('team.unavailable_title'))}</h3>`
+ `<p>${esc(teams && teams.message ? teams.message : t('team.unavailable_desc'))}</p></div>`;
return;
}
if (!teams.length) {
list.innerHTML = `<div class="empty-state"><h3>${t('team.empty_title')}</h3><p>${t('team.empty_desc')}</p></div>`;
return;