diff --git a/frontend/js/i18n/de.js b/frontend/js/i18n/de.js index 8e0435b..be0aed9 100644 --- a/frontend/js/i18n/de.js +++ b/frontend/js/i18n/de.js @@ -687,4 +687,85 @@ export default { 'playlist.add_btn': 'Hinzufügen', 'playlist.adding': 'Wird hinzugefügt...', 'playlist.added': 'Hinzugefügt', + + // Onboarding + 'onboarding.back': 'Zurück', + 'onboarding.next': 'Weiter', + 'onboarding.skip': 'Assistent überspringen', + 'onboarding.go_to_dashboard': 'Zum Dashboard', + 'onboarding.pair_display': 'Bildschirm koppeln', + 'onboarding.step.welcome.title': 'Willkommen bei ScreenTinker!', + 'onboarding.step.welcome.intro': 'Lassen Sie uns alles in unter 5 Minuten einrichten.', + 'onboarding.step.welcome.guide_through': 'Dieser Assistent führt Sie durch:', + 'onboarding.step.welcome.bullet_download': 'Player-App herunterladen', + 'onboarding.step.welcome.bullet_pair': 'Ersten Bildschirm koppeln', + 'onboarding.step.welcome.bullet_upload': 'Inhalte hochladen und zuweisen', + 'onboarding.step.player.title': 'Schritt 1: Player-App holen', + 'onboarding.step.player.intro': 'Installieren Sie den Player auf Ihrem Anzeigegerät.', + 'onboarding.step.player.android_label': 'Android-APK', + 'onboarding.step.player.android_desc': 'TV-Boxen, Tablets, Fire TV', + 'onboarding.step.player.web_label': 'Web-Player', + 'onboarding.step.player.web_desc': 'Beliebiger Browser, Pi, ChromeOS', + 'onboarding.step.player.url_hint': 'Öffnen Sie die App auf Ihrem Bildschirm und geben Sie diese Server-URL ein:', + 'onboarding.step.pair.title': 'Schritt 2: Bildschirm koppeln', + 'onboarding.step.pair.intro': 'Geben Sie den 6-stelligen Code ein, der auf Ihrem Bildschirm angezeigt wird.', + 'onboarding.step.pair.name_placeholder': 'Anzeigename (z. B. Lobby-TV)', + 'onboarding.step.upload.title': 'Schritt 3: Inhalt hochladen', + 'onboarding.step.upload.intro': 'Laden Sie ein Video oder Bild zur Anzeige hoch.', + 'onboarding.step.upload.click_to_select': 'Klicken, um eine Datei auszuwählen', + 'onboarding.step.upload.formats': 'MP4, WebM, JPEG, PNG, GIF', + 'onboarding.step.upload.uploading': 'Wird hochgeladen...', + 'onboarding.step.done.title': 'Alles bereit!', + 'onboarding.step.done.intro': 'Ihr Bildschirm ist gekoppelt und der Inhalt läuft!', + 'onboarding.step.done.whats_next': 'Wie geht es weiter?', + 'onboarding.step.done.next_content': 'Mehr Inhalte in der Inhaltsbibliothek hinzufügen', + 'onboarding.step.done.next_layouts': 'Multi-Zonen-Layouts in Layouts erstellen', + 'onboarding.step.done.next_schedule': 'Zeitplan im Zeitplan-Kalender einrichten', + 'onboarding.step.done.next_widgets': 'Live-Widgets (Uhr, Wetter, Ticker) in Widgets hinzufügen', + 'onboarding.step.done.next_kiosk': 'Interaktive Bildschirme in Kiosk erstellen', + 'onboarding.step.done.next_designer': 'Individuelle Inhalte im Designer entwerfen', + 'onboarding.toast.invalid_code': 'Geben Sie einen gültigen 6-stelligen Code ein', + 'onboarding.toast.pairing': 'Wird gekoppelt...', + 'onboarding.toast.pair_failed': 'Kopplung fehlgeschlagen', + 'onboarding.toast.pair_failed_with_error': 'Kopplung fehlgeschlagen: {error}', + 'onboarding.toast.paired': 'Bildschirm gekoppelt!', + 'onboarding.toast.uploaded_assigning': 'Hochgeladen! Wird dem Bildschirm zugewiesen...', + 'onboarding.toast.content_assigned': 'Inhalt hochgeladen und zugewiesen!', + 'onboarding.toast.upload_failed': 'Upload fehlgeschlagen', + 'onboarding.toast.error_with_error': 'Fehler: {error}', + + // Admin + 'admin.title': 'Plattform-Admin', + 'admin.subtitle': 'Superadmin-Steuerung - nur Sie sehen dies', + 'admin.access_denied': 'Zugriff verweigert', + 'admin.access_denied_desc': 'Plattform-Admin-Zugriff erforderlich.', + 'admin.all_users': 'Alle Benutzer', + 'admin.plans': 'Abonnementpläne', + 'admin.system': 'System', + 'admin.col.user': 'Benutzer', + 'admin.col.auth': 'Auth', + 'admin.col.last_login': 'Letzte Anmeldung', + 'admin.col.role': 'Rolle', + 'admin.col.plan': 'Plan', + 'admin.col.actions': 'Aktionen', + 'admin.col.devices': 'Geräte', + 'admin.col.storage': 'Speicher', + 'admin.col.monthly': 'Monatlich', + 'admin.col.yearly': 'Jährlich', + 'admin.role.user': 'Benutzer', + 'admin.role.admin': 'Admin', + 'admin.role.superadmin': 'Superadmin', + 'admin.remove': 'Entfernen', + 'admin.owner': 'Eigentümer', + 'admin.confirm': 'Bestätigen?', + 'admin.total_users': '{n} Benutzer insgesamt', + 'admin.unlimited': 'Unbegrenzt', + 'admin.free': 'Kostenlos', + 'admin.version': 'Version', + 'admin.frontend_hash': 'Frontend-Hash', + 'admin.download_db_backup': 'DB-Backup herunterladen', + 'admin.server_status': 'Serverstatus', + 'admin.toast.role_updated': 'Rolle aktualisiert', + 'admin.toast.plan_updated': 'Plan aktualisiert', + 'admin.toast.user_removed': 'Benutzer entfernt', }; diff --git a/frontend/js/i18n/en.js b/frontend/js/i18n/en.js index 086c260..36199ea 100644 --- a/frontend/js/i18n/en.js +++ b/frontend/js/i18n/en.js @@ -723,4 +723,85 @@ export default { 'playlist.add_btn': 'Add', 'playlist.adding': 'Adding...', 'playlist.added': 'Added', + + // Onboarding + 'onboarding.back': 'Back', + 'onboarding.next': 'Next', + 'onboarding.skip': 'Skip Wizard', + 'onboarding.go_to_dashboard': 'Go to Dashboard', + 'onboarding.pair_display': 'Pair Display', + 'onboarding.step.welcome.title': 'Welcome to ScreenTinker!', + 'onboarding.step.welcome.intro': "Let's get you set up in under 5 minutes.", + 'onboarding.step.welcome.guide_through': 'This wizard will guide you through:', + 'onboarding.step.welcome.bullet_download': 'Downloading the player app', + 'onboarding.step.welcome.bullet_pair': 'Pairing your first display', + 'onboarding.step.welcome.bullet_upload': 'Uploading and assigning content', + 'onboarding.step.player.title': 'Step 1: Get the Player App', + 'onboarding.step.player.intro': 'Install the player on your display device.', + 'onboarding.step.player.android_label': 'Android APK', + 'onboarding.step.player.android_desc': 'TV boxes, tablets, Fire TV', + 'onboarding.step.player.web_label': 'Web Player', + 'onboarding.step.player.web_desc': 'Any browser, Pi, ChromeOS', + 'onboarding.step.player.url_hint': 'Open the app on your display and enter this server URL:', + 'onboarding.step.pair.title': 'Step 2: Pair Your Display', + 'onboarding.step.pair.intro': 'Enter the 6-digit code shown on your display.', + 'onboarding.step.pair.name_placeholder': 'Display name (e.g., Lobby TV)', + 'onboarding.step.upload.title': 'Step 3: Upload Content', + 'onboarding.step.upload.intro': 'Upload a video or image to display.', + 'onboarding.step.upload.click_to_select': 'Click to select a file', + 'onboarding.step.upload.formats': 'MP4, WebM, JPEG, PNG, GIF', + 'onboarding.step.upload.uploading': 'Uploading...', + 'onboarding.step.done.title': "You're All Set!", + 'onboarding.step.done.intro': 'Your display is paired and content is playing!', + 'onboarding.step.done.whats_next': "What's next?", + 'onboarding.step.done.next_content': 'Add more content in the Content Library', + 'onboarding.step.done.next_layouts': 'Create multi-zone layouts in Layouts', + 'onboarding.step.done.next_schedule': 'Set up a schedule in the Schedule calendar', + 'onboarding.step.done.next_widgets': 'Add live widgets (clock, weather, ticker) in Widgets', + 'onboarding.step.done.next_kiosk': 'Create interactive screens in Kiosk', + 'onboarding.step.done.next_designer': 'Design custom content in the Designer', + 'onboarding.toast.invalid_code': 'Enter a valid 6-digit code', + 'onboarding.toast.pairing': 'Pairing...', + 'onboarding.toast.pair_failed': 'Pairing failed', + 'onboarding.toast.pair_failed_with_error': 'Pairing failed: {error}', + 'onboarding.toast.paired': 'Display paired!', + 'onboarding.toast.uploaded_assigning': 'Uploaded! Assigning to display...', + 'onboarding.toast.content_assigned': 'Content uploaded and assigned!', + 'onboarding.toast.upload_failed': 'Upload failed', + 'onboarding.toast.error_with_error': 'Error: {error}', + + // Admin (platform admin panel) + 'admin.title': 'Platform Admin', + 'admin.subtitle': 'Superadmin controls - only you can see this', + 'admin.access_denied': 'Access Denied', + 'admin.access_denied_desc': 'Platform admin access required.', + 'admin.all_users': 'All Users', + 'admin.plans': 'Subscription Plans', + 'admin.system': 'System', + 'admin.col.user': 'User', + 'admin.col.auth': 'Auth', + 'admin.col.last_login': 'Last Login', + 'admin.col.role': 'Role', + 'admin.col.plan': 'Plan', + 'admin.col.actions': 'Actions', + 'admin.col.devices': 'Devices', + 'admin.col.storage': 'Storage', + 'admin.col.monthly': 'Monthly', + 'admin.col.yearly': 'Yearly', + 'admin.role.user': 'User', + 'admin.role.admin': 'Admin', + 'admin.role.superadmin': 'Superadmin', + 'admin.remove': 'Remove', + 'admin.owner': 'Owner', + 'admin.confirm': 'Confirm?', + 'admin.total_users': '{n} total users', + 'admin.unlimited': 'Unlimited', + 'admin.free': 'Free', + 'admin.version': 'Version', + 'admin.frontend_hash': 'Frontend Hash', + 'admin.download_db_backup': 'Download DB Backup', + 'admin.server_status': 'Server Status', + 'admin.toast.role_updated': 'Role updated', + 'admin.toast.plan_updated': 'Plan updated', + 'admin.toast.user_removed': 'User removed', }; diff --git a/frontend/js/i18n/es.js b/frontend/js/i18n/es.js index dd98800..8139577 100644 --- a/frontend/js/i18n/es.js +++ b/frontend/js/i18n/es.js @@ -686,4 +686,85 @@ export default { 'playlist.add_btn': 'Agregar', 'playlist.adding': 'Agregando...', 'playlist.added': 'Agregado', + + // Onboarding + 'onboarding.back': 'Atrás', + 'onboarding.next': 'Siguiente', + 'onboarding.skip': 'Omitir asistente', + 'onboarding.go_to_dashboard': 'Ir al panel', + 'onboarding.pair_display': 'Vincular pantalla', + 'onboarding.step.welcome.title': '¡Bienvenido a ScreenTinker!', + 'onboarding.step.welcome.intro': 'Vamos a configurarlo todo en menos de 5 minutos.', + 'onboarding.step.welcome.guide_through': 'Este asistente te guiará a través de:', + 'onboarding.step.welcome.bullet_download': 'Descargar la app del reproductor', + 'onboarding.step.welcome.bullet_pair': 'Vincular tu primera pantalla', + 'onboarding.step.welcome.bullet_upload': 'Subir y asignar contenido', + 'onboarding.step.player.title': 'Paso 1: Obtén la app del reproductor', + 'onboarding.step.player.intro': 'Instala el reproductor en tu dispositivo de pantalla.', + 'onboarding.step.player.android_label': 'APK Android', + 'onboarding.step.player.android_desc': 'Cajas TV, tabletas, Fire TV', + 'onboarding.step.player.web_label': 'Reproductor web', + 'onboarding.step.player.web_desc': 'Cualquier navegador, Pi, ChromeOS', + 'onboarding.step.player.url_hint': 'Abre la app en tu pantalla e ingresa esta URL del servidor:', + 'onboarding.step.pair.title': 'Paso 2: Vincula tu pantalla', + 'onboarding.step.pair.intro': 'Ingresa el código de 6 dígitos mostrado en tu pantalla.', + 'onboarding.step.pair.name_placeholder': 'Nombre (p. ej., TV Vestíbulo)', + 'onboarding.step.upload.title': 'Paso 3: Sube contenido', + 'onboarding.step.upload.intro': 'Sube un video o imagen para mostrar.', + 'onboarding.step.upload.click_to_select': 'Haz clic para seleccionar un archivo', + 'onboarding.step.upload.formats': 'MP4, WebM, JPEG, PNG, GIF', + 'onboarding.step.upload.uploading': 'Subiendo...', + 'onboarding.step.done.title': '¡Todo listo!', + 'onboarding.step.done.intro': '¡Tu pantalla está vinculada y el contenido se está reproduciendo!', + 'onboarding.step.done.whats_next': '¿Qué sigue?', + 'onboarding.step.done.next_content': 'Agrega más contenido en la Biblioteca de contenido', + 'onboarding.step.done.next_layouts': 'Crea diseños multizona en Diseños', + 'onboarding.step.done.next_schedule': 'Configura un horario en el calendario de Horario', + 'onboarding.step.done.next_widgets': 'Agrega widgets en vivo (reloj, clima, ticker) en Widgets', + 'onboarding.step.done.next_kiosk': 'Crea pantallas interactivas en Kiosco', + 'onboarding.step.done.next_designer': 'Diseña contenido personalizado en el Diseñador', + 'onboarding.toast.invalid_code': 'Ingresa un código válido de 6 dígitos', + 'onboarding.toast.pairing': 'Vinculando...', + 'onboarding.toast.pair_failed': 'Falló la vinculación', + 'onboarding.toast.pair_failed_with_error': 'Falló la vinculación: {error}', + 'onboarding.toast.paired': '¡Pantalla vinculada!', + 'onboarding.toast.uploaded_assigning': '¡Subido! Asignando a la pantalla...', + 'onboarding.toast.content_assigned': '¡Contenido subido y asignado!', + 'onboarding.toast.upload_failed': 'Falló la subida', + 'onboarding.toast.error_with_error': 'Error: {error}', + + // Admin + 'admin.title': 'Administración de plataforma', + 'admin.subtitle': 'Controles de superadmin - solo tú puedes ver esto', + 'admin.access_denied': 'Acceso denegado', + 'admin.access_denied_desc': 'Se requiere acceso de administrador de plataforma.', + 'admin.all_users': 'Todos los usuarios', + 'admin.plans': 'Planes de suscripción', + 'admin.system': 'Sistema', + 'admin.col.user': 'Usuario', + 'admin.col.auth': 'Auth', + 'admin.col.last_login': 'Último inicio', + 'admin.col.role': 'Rol', + 'admin.col.plan': 'Plan', + 'admin.col.actions': 'Acciones', + 'admin.col.devices': 'Dispositivos', + 'admin.col.storage': 'Almacenamiento', + 'admin.col.monthly': 'Mensual', + 'admin.col.yearly': 'Anual', + 'admin.role.user': 'Usuario', + 'admin.role.admin': 'Admin', + 'admin.role.superadmin': 'Superadmin', + 'admin.remove': 'Eliminar', + 'admin.owner': 'Propietario', + 'admin.confirm': '¿Confirmar?', + 'admin.total_users': '{n} usuarios totales', + 'admin.unlimited': 'Ilimitado', + 'admin.free': 'Gratis', + 'admin.version': 'Versión', + 'admin.frontend_hash': 'Hash del frontend', + 'admin.download_db_backup': 'Descargar respaldo de BD', + 'admin.server_status': 'Estado del servidor', + 'admin.toast.role_updated': 'Rol actualizado', + 'admin.toast.plan_updated': 'Plan actualizado', + 'admin.toast.user_removed': 'Usuario eliminado', }; diff --git a/frontend/js/i18n/fr.js b/frontend/js/i18n/fr.js index c0175ab..4a51975 100644 --- a/frontend/js/i18n/fr.js +++ b/frontend/js/i18n/fr.js @@ -687,4 +687,85 @@ export default { 'playlist.add_btn': 'Ajouter', 'playlist.adding': 'Ajout...', 'playlist.added': 'Ajouté', + + // Onboarding + 'onboarding.back': 'Retour', + 'onboarding.next': 'Suivant', + 'onboarding.skip': 'Ignorer l\'assistant', + 'onboarding.go_to_dashboard': 'Aller au tableau de bord', + 'onboarding.pair_display': 'Apparier l\'écran', + 'onboarding.step.welcome.title': 'Bienvenue sur ScreenTinker !', + 'onboarding.step.welcome.intro': 'Configurons tout en moins de 5 minutes.', + 'onboarding.step.welcome.guide_through': 'Cet assistant vous guidera à travers :', + 'onboarding.step.welcome.bullet_download': 'Télécharger l\'app du lecteur', + 'onboarding.step.welcome.bullet_pair': 'Apparier votre premier écran', + 'onboarding.step.welcome.bullet_upload': 'Téléverser et attribuer du contenu', + 'onboarding.step.player.title': 'Étape 1 : Obtenez l\'app du lecteur', + 'onboarding.step.player.intro': 'Installez le lecteur sur votre appareil d\'affichage.', + 'onboarding.step.player.android_label': 'APK Android', + 'onboarding.step.player.android_desc': 'Boîtiers TV, tablettes, Fire TV', + 'onboarding.step.player.web_label': 'Lecteur web', + 'onboarding.step.player.web_desc': 'Tout navigateur, Pi, ChromeOS', + 'onboarding.step.player.url_hint': 'Ouvrez l\'app sur votre écran et saisissez cette URL du serveur :', + 'onboarding.step.pair.title': 'Étape 2 : Appariez votre écran', + 'onboarding.step.pair.intro': 'Saisissez le code à 6 chiffres affiché sur votre écran.', + 'onboarding.step.pair.name_placeholder': 'Nom (ex. TV du hall)', + 'onboarding.step.upload.title': 'Étape 3 : Téléversez du contenu', + 'onboarding.step.upload.intro': 'Téléversez une vidéo ou une image à afficher.', + 'onboarding.step.upload.click_to_select': 'Cliquez pour sélectionner un fichier', + 'onboarding.step.upload.formats': 'MP4, WebM, JPEG, PNG, GIF', + 'onboarding.step.upload.uploading': 'Téléversement...', + 'onboarding.step.done.title': 'Tout est prêt !', + 'onboarding.step.done.intro': 'Votre écran est apparié et le contenu est en lecture !', + 'onboarding.step.done.whats_next': 'Et après ?', + 'onboarding.step.done.next_content': 'Ajoutez plus de contenu dans la Bibliothèque', + 'onboarding.step.done.next_layouts': 'Créez des mises en page multi-zones dans Mises en page', + 'onboarding.step.done.next_schedule': 'Configurez un calendrier dans Calendrier', + 'onboarding.step.done.next_widgets': 'Ajoutez des widgets en direct (horloge, météo, ticker) dans Widgets', + 'onboarding.step.done.next_kiosk': 'Créez des écrans interactifs dans Kiosque', + 'onboarding.step.done.next_designer': 'Concevez du contenu personnalisé dans le Concepteur', + 'onboarding.toast.invalid_code': 'Saisissez un code valide à 6 chiffres', + 'onboarding.toast.pairing': 'Appairage...', + 'onboarding.toast.pair_failed': 'Échec de l\'appairage', + 'onboarding.toast.pair_failed_with_error': 'Échec de l\'appairage : {error}', + 'onboarding.toast.paired': 'Écran apparié !', + 'onboarding.toast.uploaded_assigning': 'Téléversé ! Attribution à l\'écran...', + 'onboarding.toast.content_assigned': 'Contenu téléversé et attribué !', + 'onboarding.toast.upload_failed': 'Échec du téléversement', + 'onboarding.toast.error_with_error': 'Erreur : {error}', + + // Admin + 'admin.title': 'Administration de la plateforme', + 'admin.subtitle': 'Contrôles superadmin - vous seul pouvez voir ceci', + 'admin.access_denied': 'Accès refusé', + 'admin.access_denied_desc': 'Accès administrateur plateforme requis.', + 'admin.all_users': 'Tous les utilisateurs', + 'admin.plans': 'Plans d\'abonnement', + 'admin.system': 'Système', + 'admin.col.user': 'Utilisateur', + 'admin.col.auth': 'Auth', + 'admin.col.last_login': 'Dernière connexion', + 'admin.col.role': 'Rôle', + 'admin.col.plan': 'Plan', + 'admin.col.actions': 'Actions', + 'admin.col.devices': 'Appareils', + 'admin.col.storage': 'Stockage', + 'admin.col.monthly': 'Mensuel', + 'admin.col.yearly': 'Annuel', + 'admin.role.user': 'Utilisateur', + 'admin.role.admin': 'Admin', + 'admin.role.superadmin': 'Superadmin', + 'admin.remove': 'Retirer', + 'admin.owner': 'Propriétaire', + 'admin.confirm': 'Confirmer ?', + 'admin.total_users': '{n} utilisateurs au total', + 'admin.unlimited': 'Illimité', + 'admin.free': 'Gratuit', + 'admin.version': 'Version', + 'admin.frontend_hash': 'Hash du frontend', + 'admin.download_db_backup': 'Télécharger la sauvegarde BDD', + 'admin.server_status': 'État du serveur', + 'admin.toast.role_updated': 'Rôle mis à jour', + 'admin.toast.plan_updated': 'Plan mis à jour', + 'admin.toast.user_removed': 'Utilisateur retiré', }; diff --git a/frontend/js/i18n/pt.js b/frontend/js/i18n/pt.js index b80cbd6..4be0a66 100644 --- a/frontend/js/i18n/pt.js +++ b/frontend/js/i18n/pt.js @@ -687,4 +687,85 @@ export default { 'playlist.add_btn': 'Adicionar', 'playlist.adding': 'Adicionando...', 'playlist.added': 'Adicionado', + + // Onboarding + 'onboarding.back': 'Voltar', + 'onboarding.next': 'Próximo', + 'onboarding.skip': 'Pular assistente', + 'onboarding.go_to_dashboard': 'Ir para o painel', + 'onboarding.pair_display': 'Parear tela', + 'onboarding.step.welcome.title': 'Bem-vindo ao ScreenTinker!', + 'onboarding.step.welcome.intro': 'Vamos configurar tudo em menos de 5 minutos.', + 'onboarding.step.welcome.guide_through': 'Este assistente irá guiá-lo através de:', + 'onboarding.step.welcome.bullet_download': 'Baixar o app do player', + 'onboarding.step.welcome.bullet_pair': 'Parear sua primeira tela', + 'onboarding.step.welcome.bullet_upload': 'Enviar e atribuir conteúdo', + 'onboarding.step.player.title': 'Passo 1: Obtenha o app do player', + 'onboarding.step.player.intro': 'Instale o player no seu dispositivo de exibição.', + 'onboarding.step.player.android_label': 'APK Android', + 'onboarding.step.player.android_desc': 'TV boxes, tablets, Fire TV', + 'onboarding.step.player.web_label': 'Player web', + 'onboarding.step.player.web_desc': 'Qualquer navegador, Pi, ChromeOS', + 'onboarding.step.player.url_hint': 'Abra o app na sua tela e digite esta URL do servidor:', + 'onboarding.step.pair.title': 'Passo 2: Pareie sua tela', + 'onboarding.step.pair.intro': 'Digite o código de 6 dígitos exibido na sua tela.', + 'onboarding.step.pair.name_placeholder': 'Nome (ex. TV do lobby)', + 'onboarding.step.upload.title': 'Passo 3: Envie conteúdo', + 'onboarding.step.upload.intro': 'Envie um vídeo ou imagem para exibir.', + 'onboarding.step.upload.click_to_select': 'Clique para selecionar um arquivo', + 'onboarding.step.upload.formats': 'MP4, WebM, JPEG, PNG, GIF', + 'onboarding.step.upload.uploading': 'Enviando...', + 'onboarding.step.done.title': 'Tudo pronto!', + 'onboarding.step.done.intro': 'Sua tela está pareada e o conteúdo está sendo exibido!', + 'onboarding.step.done.whats_next': 'O que vem a seguir?', + 'onboarding.step.done.next_content': 'Adicione mais conteúdo na Biblioteca de conteúdo', + 'onboarding.step.done.next_layouts': 'Crie layouts multi-zona em Layouts', + 'onboarding.step.done.next_schedule': 'Configure uma agenda no calendário Agenda', + 'onboarding.step.done.next_widgets': 'Adicione widgets ao vivo (relógio, clima, ticker) em Widgets', + 'onboarding.step.done.next_kiosk': 'Crie telas interativas em Quiosque', + 'onboarding.step.done.next_designer': 'Crie conteúdo personalizado no Designer', + 'onboarding.toast.invalid_code': 'Digite um código válido de 6 dígitos', + 'onboarding.toast.pairing': 'Pareando...', + 'onboarding.toast.pair_failed': 'Falha no pareamento', + 'onboarding.toast.pair_failed_with_error': 'Falha no pareamento: {error}', + 'onboarding.toast.paired': 'Tela pareada!', + 'onboarding.toast.uploaded_assigning': 'Enviado! Atribuindo à tela...', + 'onboarding.toast.content_assigned': 'Conteúdo enviado e atribuído!', + 'onboarding.toast.upload_failed': 'Falha no envio', + 'onboarding.toast.error_with_error': 'Erro: {error}', + + // Admin + 'admin.title': 'Administração da plataforma', + 'admin.subtitle': 'Controles de superadmin - apenas você pode ver isso', + 'admin.access_denied': 'Acesso negado', + 'admin.access_denied_desc': 'Acesso de admin da plataforma necessário.', + 'admin.all_users': 'Todos os usuários', + 'admin.plans': 'Planos de assinatura', + 'admin.system': 'Sistema', + 'admin.col.user': 'Usuário', + 'admin.col.auth': 'Auth', + 'admin.col.last_login': 'Último login', + 'admin.col.role': 'Função', + 'admin.col.plan': 'Plano', + 'admin.col.actions': 'Ações', + 'admin.col.devices': 'Dispositivos', + 'admin.col.storage': 'Armazenamento', + 'admin.col.monthly': 'Mensal', + 'admin.col.yearly': 'Anual', + 'admin.role.user': 'Usuário', + 'admin.role.admin': 'Admin', + 'admin.role.superadmin': 'Superadmin', + 'admin.remove': 'Remover', + 'admin.owner': 'Proprietário', + 'admin.confirm': 'Confirmar?', + 'admin.total_users': '{n} usuários no total', + 'admin.unlimited': 'Ilimitado', + 'admin.free': 'Grátis', + 'admin.version': 'Versão', + 'admin.frontend_hash': 'Hash do frontend', + 'admin.download_db_backup': 'Baixar backup do BD', + 'admin.server_status': 'Status do servidor', + 'admin.toast.role_updated': 'Função atualizada', + 'admin.toast.plan_updated': 'Plano atualizado', + 'admin.toast.user_removed': 'Usuário removido', }; diff --git a/frontend/js/views/admin.js b/frontend/js/views/admin.js index ca04f6e..cb6d5b3 100644 --- a/frontend/js/views/admin.js +++ b/frontend/js/views/admin.js @@ -1,6 +1,7 @@ import { api } from '../api.js'; import { showToast } from '../components/toast.js'; import { esc } from '../utils.js'; +import { t } from '../i18n.js'; const headers = () => ({ Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' }); const API = (url, opts = {}) => fetch('/api' + url, { headers: headers(), ...opts }).then(r => r.json()); @@ -8,31 +9,28 @@ const API = (url, opts = {}) => fetch('/api' + url, { headers: headers(), ...opt export async function render(container) { const user = JSON.parse(localStorage.getItem('user') || '{}'); if (user.role !== 'superadmin') { - container.innerHTML = '

Access Denied

Platform admin access required.

'; + container.innerHTML = `

${t('admin.access_denied')}

${t('admin.access_denied_desc')}

`; return; } container.innerHTML = ` -
-

All Users

-

Loading...

+

${t('admin.all_users')}

+

${t('common.loading')}

-
-

Subscription Plans

-

Loading...

+

${t('admin.plans')}

+

${t('common.loading')}

-
-

System

-

Loading...

+

${t('admin.system')}

+

${t('common.loading')}

`; @@ -51,24 +49,24 @@ async function loadUsers() {
- - - - - - + + + + + + ${users.map(u => ` - + `).join('')}
UserAuthLast LoginRolePlanActions${t('admin.col.user')}${t('admin.col.auth')}${t('admin.col.last_login')}${t('admin.col.role')}${t('admin.col.plan')}${t('admin.col.actions')}
${u.name || u.email}
${u.email}
${u.auth_provider}${u.last_login ? new Date(u.last_login * 1000).toLocaleString() : 'Never'}${u.last_login ? new Date(u.last_login * 1000).toLocaleString() : t('common.never')} @@ -77,47 +75,44 @@ async function loadUsers() { - ${u.role !== 'superadmin' ? `` : 'Owner'} + ${u.role !== 'superadmin' ? `` : `${t('admin.owner')}`}
-

${users.length} total users

+

${t('admin.total_users', { n: users.length })}

`; - // Role change el.querySelectorAll('[data-role-user]').forEach(select => { select.onchange = async () => { try { await API(`/auth/users/${select.dataset.roleUser}/role`, { method: 'PUT', body: JSON.stringify({ role: select.value }) }); - showToast('Role updated', 'success'); + showToast(t('admin.toast.role_updated'), 'success'); } catch (err) { showToast(err.message, 'error'); loadUsers(); } }; }); - // Plan change el.querySelectorAll('[data-plan-user]').forEach(select => { select.onchange = async () => { try { await API('/subscription/assign', { method: 'POST', body: JSON.stringify({ user_id: select.dataset.planUser, plan_id: select.value }) }); - showToast('Plan updated', 'success'); + showToast(t('admin.toast.plan_updated'), 'success'); } catch (err) { showToast(err.message, 'error'); loadUsers(); } }; }); - // Delete user el.querySelectorAll('[data-delete-user]').forEach(btn => { let confirming = false; btn.onclick = async () => { if (confirming) { - try { await api.deleteUser(btn.dataset.deleteUser); showToast('User removed', 'success'); loadUsers(); } + try { await api.deleteUser(btn.dataset.deleteUser); showToast(t('admin.toast.user_removed'), 'success'); loadUsers(); } catch (err) { showToast(err.message, 'error'); } return; } - confirming = true; btn.textContent = 'Confirm?'; btn.style.background = 'var(--danger)'; btn.style.color = 'white'; - setTimeout(() => { confirming = false; btn.textContent = 'Remove'; btn.style.background = ''; btn.style.color = ''; }, 3000); + confirming = true; btn.textContent = t('admin.confirm'); btn.style.background = 'var(--danger)'; btn.style.color = 'white'; + setTimeout(() => { confirming = false; btn.textContent = t('admin.remove'); btn.style.background = ''; btn.style.color = ''; }, 3000); }; }); } catch (err) { el.innerHTML = `

${esc(err.message)}

`; } @@ -131,19 +126,19 @@ async function loadPlans() {
- - - - - + + + + + ${plans.map(p => ` - - - + + + `).join('')} @@ -161,12 +156,12 @@ async function loadSystem() { const token = localStorage.getItem('token'); el.innerHTML = `
-
Version
${version.version}
-
Frontend Hash
${version.hash}
+
${t('admin.version')}
${version.version}
+
${t('admin.frontend_hash')}
${version.hash}
- Download DB Backup - Server Status + ${t('admin.download_db_backup')} + ${t('admin.server_status')}
`; } catch (err) { el.innerHTML = `

${esc(err.message)}

`; } diff --git a/frontend/js/views/onboarding.js b/frontend/js/views/onboarding.js index 4474ed7..485dfe2 100644 --- a/frontend/js/views/onboarding.js +++ b/frontend/js/views/onboarding.js @@ -1,96 +1,101 @@ import { showToast } from '../components/toast.js'; +import { t } from '../i18n.js'; -const STEPS = [ - { - title: 'Welcome to ScreenTinker!', - icon: '👋', - content: `

Let's get you set up in under 5 minutes.

-

This wizard will guide you through:

- `, - action: null - }, - { - title: 'Step 1: Get the Player App', - icon: '📥', - content: `

Install the player on your display device.

-
- -
🤖
-
Android APK
-
TV boxes, tablets, Fire TV
-
- -
🌐
-
Web Player
-
Any browser, Pi, ChromeOS
-
-
-

Open the app on your display and enter this server URL:

- ${window.location.origin}`, - action: null - }, - { - title: 'Step 2: Pair Your Display', - icon: '🔗', - content: `

Enter the 6-digit code shown on your display.

-
- -
-
- -
-

`, - action: 'pair' - }, - { - title: 'Step 3: Upload Content', - icon: '📤', - content: `

Upload a video or image to display.

-
-
📁
-

Click to select a file

-

MP4, WebM, JPEG, PNG, GIF

- -
-
- ${isFirst ? '
' : ``} + ${isFirst ? '
' : ``}
- ${!isLast ? `` : ''} - + ${!isLast ? `` : ''} +
`; - // Bind buttons document.getElementById('prevBtn')?.addEventListener('click', () => { currentStep--; renderStep(); }); document.getElementById('skipBtn')?.addEventListener('click', () => { localStorage.setItem('rd_onboarded', 'true'); @@ -132,7 +136,6 @@ export function render(container) { }); document.getElementById('nextBtn')?.addEventListener('click', handleNext); - // Step-specific setup if (step.action === 'upload') { const area = document.getElementById('onboardUploadArea'); const input = document.getElementById('onboardFileInput'); @@ -142,6 +145,7 @@ export function render(container) { } async function handleNext() { + const STEPS = getSteps(); const step = STEPS[currentStep]; if (step.action === 'pair') { @@ -150,12 +154,12 @@ export function render(container) { const status = document.getElementById('onboardPairStatus'); if (!code || code.length !== 6) { - if (status) status.textContent = 'Enter a valid 6-digit code'; + if (status) status.textContent = t('onboarding.toast.invalid_code'); return; } try { - if (status) status.textContent = 'Pairing...'; + if (status) status.textContent = t('onboarding.toast.pairing'); const token = localStorage.getItem('token'); const res = await fetch('/api/provision/pair', { method: 'POST', @@ -163,13 +167,13 @@ export function render(container) { body: JSON.stringify({ pairing_code: code, name: name || undefined }) }); const data = await res.json(); - if (!res.ok) { if (status) status.textContent = data.error || 'Pairing failed'; return; } + if (!res.ok) { if (status) status.textContent = data.error || t('onboarding.toast.pair_failed'); return; } pairedDeviceId = data.id; - showToast('Display paired!', 'success'); + showToast(t('onboarding.toast.paired'), 'success'); currentStep++; renderStep(); } catch (err) { - if (status) status.textContent = 'Pairing failed: ' + err.message; + if (status) status.textContent = t('onboarding.toast.pair_failed_with_error', { error: err.message }); } return; } @@ -208,9 +212,8 @@ export function render(container) { xhr.onload = async () => { if (xhr.status >= 200 && xhr.status < 300) { const content = JSON.parse(xhr.responseText); - if (text) text.textContent = 'Uploaded! Assigning to display...'; + if (text) text.textContent = t('onboarding.toast.uploaded_assigning'); - // Auto-assign to paired device if (pairedDeviceId) { try { await fetch(`/api/assignments/device/${pairedDeviceId}`, { @@ -221,17 +224,17 @@ export function render(container) { } catch {} } - showToast('Content uploaded and assigned!', 'success'); + showToast(t('onboarding.toast.content_assigned'), 'success'); currentStep++; renderStep(); } else { - if (text) text.textContent = 'Upload failed'; + if (text) text.textContent = t('onboarding.toast.upload_failed'); } }; - xhr.onerror = () => { if (text) text.textContent = 'Upload failed'; }; + xhr.onerror = () => { if (text) text.textContent = t('onboarding.toast.upload_failed'); }; xhr.send(formData); } catch (err) { - if (text) text.textContent = 'Error: ' + err.message; + if (text) text.textContent = t('onboarding.toast.error_with_error', { error: err.message }); } }
PlanDevicesStorageMonthlyYearly${t('admin.col.plan')}${t('admin.col.devices')}${t('admin.col.storage')}${t('admin.col.monthly')}${t('admin.col.yearly')}
${p.display_name}${p.max_devices === -1 ? 'Unlimited' : p.max_devices}${p.max_storage_mb === -1 ? 'Unlimited' : p.max_storage_mb >= 1024 ? (p.max_storage_mb/1024)+'GB' : p.max_storage_mb+'MB'}${p.price_monthly > 0 ? '$'+p.price_monthly : 'Free'}${p.max_devices === -1 ? t('admin.unlimited') : p.max_devices}${p.max_storage_mb === -1 ? t('admin.unlimited') : p.max_storage_mb >= 1024 ? (p.max_storage_mb/1024)+'GB' : p.max_storage_mb+'MB'}${p.price_monthly > 0 ? '$'+p.price_monthly : t('admin.free')} ${p.price_yearly > 0 ? '$'+p.price_yearly : '-'}