From fd3cbb5d31d29c06ea71a905c1cd29b697fdcfb3 Mon Sep 17 00:00:00 2001 From: tempus2016 Date: Tue, 20 Oct 2026 09:44:58 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat(panel):=20Help=20&=20feedback=20page?= =?UTF-8?q?=20=E2=80=94=20report=20a=20bug=20or=20suggest=20a=20feature=20?= =?UTF-8?q?from=20Home=20Assistant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new System → Help & feedback page in the admin panel builds a pre-filled GitHub issue from the repo's own issue templates and opens it for the parent to check and submit. Nothing is sent from Home Assistant. - Bug and feature forms mirroring bug_report.yml / feature_request.yml, required fields enforced, live character counts, draft kept in the browser - Setup details filled in automatically via a new admin-only taskmate/feedback/info command: TaskMate and HA versions, installation type, HACS default/custom/manual, Lovelace mode, family counts (counts only), card design, health-check codes - Detected drop-down answers can be corrected; GitHub can't pre-select drop-downs, so they also travel as text in Additional context - Recent TaskMate warnings/errors from HA's system log, with names, phone and person ids, signed-URL signatures, tokens and e-mail addresses redacted; oldest entries dropped first to stay under GitHub's link length limit - Similar-issue search on GitHub as the title is typed - Diagnostics download, Copy as Markdown (works on plain http), search link - Warns when the browser is running an older cached panel than the installed integration, and points to the health check when it found problems - Still works when the integration can't answer, using the browser's versions - Bug template gains a "HACS (default)" installation option - Translated into all locales; docs page + nav entry --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + custom_components/taskmate/feedback.py | 128 +++ custom_components/taskmate/websocket.py | 15 + .../taskmate/www/locales/da.json | 141 +++ .../taskmate/www/locales/de.json | 141 +++ .../taskmate/www/locales/en-GB.json | 141 +++ .../taskmate/www/locales/en.json | 141 +++ .../taskmate/www/locales/fr.json | 141 +++ .../taskmate/www/locales/nb.json | 141 +++ .../taskmate/www/locales/nl.json | 141 +++ .../taskmate/www/locales/nn.json | 141 +++ .../taskmate/www/locales/pl.json | 141 +++ .../taskmate/www/locales/pt-BR.json | 141 +++ .../taskmate/www/locales/pt.json | 141 +++ .../taskmate/www/taskmate-panel.js | 883 ++++++++++++++++++ mkdocs.yml | 1 + tests/data/locale_english_baseline.json | 72 ++ .../frontend/taskmate-panel-feedback.test.cjs | 445 +++++++++ tests/test_feedback_info.py | 196 ++++ tests/test_feedback_template_sync.py | 95 ++ wiki/Admin-Panel.md | 2 +- wiki/Help-and-Feedback.md | 120 +++ wiki/Repairs-and-Diagnostics.md | 2 +- wiki/Troubleshooting.md | 4 +- wiki/images/help-feedback-bug.png | Bin 0 -> 92384 bytes wiki/images/help-feedback-details.png | Bin 0 -> 110487 bytes wiki/images/help-feedback-phone.png | Bin 0 -> 52109 bytes 27 files changed, 3512 insertions(+), 3 deletions(-) create mode 100644 custom_components/taskmate/feedback.py create mode 100644 tests/frontend/taskmate-panel-feedback.test.cjs create mode 100644 tests/test_feedback_info.py create mode 100644 tests/test_feedback_template_sync.py create mode 100644 wiki/Help-and-Feedback.md create mode 100644 wiki/images/help-feedback-bug.png create mode 100644 wiki/images/help-feedback-details.png create mode 100644 wiki/images/help-feedback-phone.png diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 050af5ed..2a6e1497 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -51,6 +51,7 @@ body: attributes: label: TaskMate Installation Method options: + - HACS (default) - HACS (custom repository) - Manual (copied files) validations: diff --git a/custom_components/taskmate/feedback.py b/custom_components/taskmate/feedback.py new file mode 100644 index 00000000..1794319d --- /dev/null +++ b/custom_components/taskmate/feedback.py @@ -0,0 +1,128 @@ +"""What the admin panel's Help & feedback page fills into a GitHub issue. + +The page builds a pre-filled ``issues/new`` link; nothing is sent from Home +Assistant. This gathers the environment half of a bug report — the bit people +get wrong or leave out by hand: versions, how HA and TaskMate are installed, +the Lovelace resource mode, rough sizes and the health check's issue codes. + +It never says who the family are: counts only, no names, and the health +check contributes its issue *codes*, not its messages (those name chores). +""" + +from __future__ import annotations + +import logging +from typing import Any + +import homeassistant.const as ha_const +from homeassistant.core import HomeAssistant +from homeassistant.loader import async_get_integration + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +REPO_FULL_NAME = "tempus2016/taskmate" + +# HA's system_info installation_type → the bug template's dropdown options. +_INSTALL_TYPES = { + "Home Assistant OS": "haos", + "Home Assistant Supervised": "supervised", + "Home Assistant Container": "container", + "Unsupported Third Party Container": "container", + "Home Assistant Core": "core", +} + + +async def _installation_type(hass: HomeAssistant) -> tuple[str, str]: + """(template key, HA's own wording, e.g. "Home Assistant OS").""" + try: + from homeassistant.helpers.system_info import async_get_system_info + + info = await async_get_system_info(hass) + except Exception: # noqa: BLE001 — a report without it is still a report + _LOGGER.debug("TaskMate feedback: system info unavailable", exc_info=True) + return "unknown", "" + raw = str(info.get("installation_type") or "") + return _INSTALL_TYPES.get(raw, "unknown"), raw + + +def _install_method(hass: HomeAssistant) -> str: + """How TaskMate itself was installed: hacs_default, hacs_custom, manual or unknown. + + Reads HACS's in-memory repository list. That is HACS's internal API, so + any surprise in its shape degrades to "unknown" rather than failing. + """ + if "hacs" not in hass.config.components: + return "manual" + try: + hacs = hass.data.get("hacs") + repo = hacs.repositories.get_by_full_name(REPO_FULL_NAME) + if repo is None: + return "manual" + if not getattr(repo.data, "installed", True): + return "manual" + return "hacs_default" if hacs.repositories.is_default(str(repo.data.id)) else "hacs_custom" + except Exception: # noqa: BLE001 + _LOGGER.debug("TaskMate feedback: could not read HACS repositories", exc_info=True) + return "unknown" + + +def _lovelace_mode(hass: HomeAssistant) -> str: + """storage, yaml or unknown — where Lovelace resources are managed. + + HA keeps this as a dataclass since 2024.12 (``mode``, later renamed + ``resource_mode``) and as a plain dict before it. + """ + data = hass.data.get("lovelace") + if data is None: + return "unknown" + if isinstance(data, dict): + mode = data.get("resource_mode") or data.get("mode") + else: + mode = getattr(data, "resource_mode", None) or getattr(data, "mode", None) + mode = str(mode or "").lower() + return mode if mode in ("storage", "yaml") else "unknown" + + +def _entry_id(hass: HomeAssistant, coordinator) -> str: + entry = getattr(coordinator, "config_entry", None) + if entry is not None and getattr(entry, "entry_id", None): + return str(entry.entry_id) + for key, value in hass.data.get(DOMAIN, {}).items(): + if value is coordinator: + return str(key) + return "" + + +async def async_feedback_info(hass: HomeAssistant, coordinator) -> dict[str, Any]: + """The environment summary for a bug report or feature request.""" + integration = await async_get_integration(hass, DOMAIN) + install_type, install_type_raw = await _installation_type(hass) + storage = coordinator.storage + children = storage.get_children() + try: + health = coordinator.health_report() + health_codes = sorted({str(i.get("code", "")) for i in health.get("issues", []) if i.get("code")}) + except Exception: # noqa: BLE001 + _LOGGER.debug("TaskMate feedback: health report failed", exc_info=True) + health_codes = [] + settings = storage.get_settings() or {} + return { + "integration_version": str(integration.version or ""), + "ha_version": str(getattr(ha_const, "__version__", "")), + "installation_type": install_type, + "installation_type_raw": install_type_raw, + "install_method": _install_method(hass), + "lovelace_mode": _lovelace_mode(hass), + "language": str(getattr(hass.config, "language", "") or ""), + "counts": { + "children": sum(1 for c in children if not getattr(c, "is_adult", False)), + "adults": sum(1 for c in children if getattr(c, "is_adult", False)), + "chores": len(storage.get_chores()), + "rewards": len(storage.get_rewards()), + }, + "card_design": str(settings.get("card_design") or "classic"), + "health_codes": health_codes, + "entry_id": _entry_id(hass, coordinator), + } diff --git a/custom_components/taskmate/websocket.py b/custom_components/taskmate/websocket.py index da12254c..97159bb9 100644 --- a/custom_components/taskmate/websocket.py +++ b/custom_components/taskmate/websocket.py @@ -127,6 +127,8 @@ WS_REPORT_HEALTH: Final = "taskmate/reports/health" WS_REPORT_QUALITY: Final = "taskmate/reports/quality" WS_REPORT_MOODS: Final = "taskmate/reports/moods" +# Help & feedback page: the environment half of a GitHub issue. +WS_FEEDBACK_INFO: Final = "taskmate/feedback/info" WS_SCHEDULED_LIST: Final = "taskmate/scheduled/list" WS_SCHEDULED_ADD: Final = "taskmate/scheduled/add" WS_SCHEDULED_REMOVE: Final = "taskmate/scheduled/remove" @@ -269,6 +271,7 @@ WS_REPORT_HEALTH, WS_REPORT_QUALITY, WS_REPORT_MOODS, + WS_FEEDBACK_INFO, WS_SETUP_CATALOGUE, } @@ -1150,6 +1153,17 @@ async def _ws_report_health(hass, connection, msg, coordinator): connection.send_result(msg["id"], coordinator.health_report()) +@websocket_api.websocket_command({vol.Required("type"): WS_FEEDBACK_INFO}) +@websocket_api.async_response +@_admin_only +async def _ws_feedback_info(hass, connection, msg, coordinator): + # Imported here: feedback.py needs homeassistant.loader, which nothing + # else on this module's import path does. + from .feedback import async_feedback_info + + connection.send_result(msg["id"], await async_feedback_info(hass, coordinator)) + + # --------------------------------------------------------------------------- # Scheduled config changes (#675) # --------------------------------------------------------------------------- @@ -3290,6 +3304,7 @@ async def _ws_config_import(hass, connection, msg, coordinator): _ws_report_friction, _ws_report_projection, _ws_report_health, + _ws_feedback_info, _ws_report_quality, _ws_report_moods, _ws_templates_export, diff --git a/custom_components/taskmate/www/locales/da.json b/custom_components/taskmate/www/locales/da.json index 93092d60..e71e96de 100644 --- a/custom_components/taskmate/www/locales/da.json +++ b/custom_components/taskmate/www/locales/da.json @@ -1560,6 +1560,147 @@ "panel.tab_penalties": "Straffe", "panel.tab_rewards": "Belønninger", "panel.tab_settings": "Indstillinger", + "panel.tab_help": "Hjælp og feedback", + "panel.fb_intro": "Har du fundet et problem eller fået en idé? Beskriv det her, så åbner TaskMate en GitHub-issue, hvor dine versioner og din opsætning allerede er udfyldt. Intet sendes, før du trykker Submit på GitHub.", + "panel.fb_type_label": "Hvad vil du gøre?", + "panel.fb_type_bug": "Rapportér en fejl", + "panel.fb_type_bug_hint": "Noget virker ikke, som det skal", + "panel.fb_type_feature": "Foreslå en funktion", + "panel.fb_type_feature_hint": "En idé, der kan gøre TaskMate bedre", + "panel.fb_title": "Titel", + "panel.fb_bug_heading": "Hvad gik galt?", + "panel.fb_bug_title_placeholder": "f.eks. Børnekortet viser 0 point efter nulstillingen ved midnat", + "panel.fb_bug_description": "Hvad skete der?", + "panel.fb_bug_description_placeholder": "Beskriv, hvad du så, og hvor", + "panel.fb_bug_expected": "Hvad forventede du, der skulle ske?", + "panel.fb_bug_steps": "Trin til at genskabe fejlen", + "panel.fb_bug_steps_placeholder": "1. Åbn…\n2. Tryk på…\n3. Se…", + "panel.fb_bug_area": "Hvilken del af TaskMate?", + "panel.fb_bug_repro": "Sker det hver gang?", + "panel.fb_bug_console": "Fejl i browserkonsollen", + "panel.fb_bug_console_hint": "Ved problemer med kort: tryk F12, åbn fanen Console, genskab problemet, og indsæt eventuelle røde fejl.", + "panel.fb_bug_config": "Kortets YAML", + "panel.fb_bug_config_hint": "Ved problemer med kort: indsæt kortets YAML fra dashboard-editoren.", + "panel.fb_more_details": "Flere detaljer (valgfrit)", + "panel.fb_additional": "Andet?", + "panel.fb_additional_hint": "Skærmbilleder kan trækkes ind på issuen på GitHub.", + "panel.fb_feat_heading": "Hvad er din idé?", + "panel.fb_feat_title_placeholder": "f.eks. Lad børn bytte pligter med hinanden", + "panel.fb_feat_summary": "Resumé", + "panel.fb_feat_summary_placeholder": "Beskriv funktionen med en sætning eller to", + "panel.fb_feat_problem": "Hvilket problem vil den løse?", + "panel.fb_feat_problem_hint": "Gode idéer starter med et reelt problem i familiens hverdag.", + "panel.fb_feat_solution": "Hvordan vil du gerne have, at den virker?", + "panel.fb_feat_area": "Hvilket område?", + "panel.fb_feat_priority": "Hvor vigtig er den for dig?", + "panel.fb_feat_alternatives": "Løsninger eller alternativer, du har prøvet", + "panel.fb_area_child_card": "Børnekort", + "panel.fb_area_rewards_card": "Belønningskort", + "panel.fb_area_approvals_card": "Godkendelseskort", + "panel.fb_area_points_card": "Pointkort", + "panel.fb_area_reorder_card": "Sorteringskort", + "panel.fb_area_parent_card": "Forældre-dashboard-kort", + "panel.fb_area_overview_card": "Oversigtskort", + "panel.fb_area_activity_card": "Aktivitetskort", + "panel.fb_area_streak_card": "Streak-kort", + "panel.fb_area_weekly_card": "Ugekort", + "panel.fb_area_graph_card": "Pointgrafkort", + "panel.fb_area_progress_card": "Belønningsfremskridtskort", + "panel.fb_area_leaderboard_card": "Ranglistekort", + "panel.fb_area_setup": "Opsætning af integrationen", + "panel.fb_area_chores": "Håndtering af pligter", + "panel.fb_area_rewards": "Håndtering af belønninger", + "panel.fb_area_children": "Håndtering af børn", + "panel.fb_area_points": "Point og pointgivning", + "panel.fb_area_streaks": "Streaks og milepæle", + "panel.fb_area_reward_approval": "Godkendelse af belønninger", + "panel.fb_area_chore_approval": "Godkendelse af pligter", + "panel.fb_area_services": "Tjenester og automatiseringer", + "panel.fb_area_sensors": "Sensorer og entitetsattributter", + "panel.fb_area_resources": "Kort indlæses ikke", + "panel.fb_area_bonus": "Bonuspoint", + "panel.fb_area_settings": "Indstillinger", + "panel.fb_area_notifications": "Notifikationer", + "panel.fb_area_install": "Installation / HACS", + "panel.fb_area_new_card": "Et nyt kort", + "panel.fb_area_other": "Noget andet", + "panel.fb_repro_always": "Ja, hver gang", + "panel.fb_repro_sometimes": "Nogle gange", + "panel.fb_repro_once": "Det skete kun én gang", + "panel.fb_priority_nice": "Rart at have", + "panel.fb_priority_important": "Vigtig — et hul i vores daglige brug", + "panel.fb_priority_critical": "Kritisk — det forhindrer os i at bruge TaskMate ordentligt", + "panel.fb_details_heading": "Medtages automatisk", + "panel.fb_details_intro": "Disse oplysninger hjælper med hurtigt at finde årsagen til et problem. De indeholder aldrig navne, point eller noget personligt.", + "panel.fb_reload": "Opdater", + "panel.fb_loading": "Indsamler oplysninger om din opsætning…", + "panel.fb_info_failed": "Kunne ikke læse oplysningerne om din opsætning: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate-version", + "panel.fb_ha_version": "Home Assistant-version", + "panel.fb_family": "Familie", + "panel.fb_family_value": "{children} børn, {adults} voksne · {chores} pligter · {rewards} belønninger", + "panel.fb_browser": "Browser", + "panel.fb_health": "Tilstandstjek", + "panel.fb_health_ok": "Ingen problemer", + "panel.fb_children_count": "Børn", + "panel.fb_install_type": "Home Assistant-installation", + "panel.fb_install_method": "TaskMate installeret via", + "panel.fb_lovelace_mode": "Dashboard-tilstand (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (brugerdefineret repository)", + "panel.fb_method_manual": "Manuelt kopierede filer", + "panel.fb_lovelace_storage": "Storage (redigeret i brugerfladen)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Ved ikke", + "panel.fb_pick_one": "Vælg…", + "panel.fb_dropdowns_hint": "Registreret for dig — ret dem, hvis de er forkerte. GitHub kan ikke forudvælge sine rullelister, så vælg de samme svar der; de kopieres også ind i issue-teksten.", + "panel.fb_include_details": "Medtag disse oplysninger", + "panel.fb_logs_heading": "TaskMate-logposter", + "panel.fb_logs_found": "{count} nylige advarsler eller fejl fra TaskMate. Navne er erstattet med pladsholdere.", + "panel.fb_logs_none": "Ingen nylige advarsler eller fejl fra TaskMate i loggen.", + "panel.fb_logs_unavailable": "Home Assistant-loggen kunne ikke læses.", + "panel.fb_logs_include": "Føj disse til issuen", + "panel.fb_logs_preview": "Vis, hvad der medtages", + "panel.fb_copy_logs": "Kopiér logposter", + "panel.fb_copied_logs": "Logposter kopieret", + "panel.fb_logs_debug_hint": "Home Assistant gemmer kun advarsler og fejl her. Hvis du vil have hele historien, så slå fejlfindingslogning til (Indstillinger → Enheder og tjenester → TaskMate → Aktivér fejlfindingslogning), genskab problemet, og download så loggen fra Indstillinger → System → Logfiler, og træk den ind på issuen.", + "panel.fb_logs_trimmed": "Linket har plads til {used} logposter — de {dropped} ældste er udeladt. Brug Kopiér som Markdown for at få dem alle med.", + "panel.fb_diag_heading": "Diagnosticeringsfil", + "panel.fb_diag_hint": "En fil, der beskriver din TaskMate-opsætning, med navne og personlige oplysninger fjernet. Træk den ind på issuen på GitHub.", + "panel.fb_diag_download": "Download", + "panel.fb_diag_started": "Download af diagnosticering er startet", + "panel.fb_diag_failed": "Kunne ikke downloade diagnosticeringsfilen", + "panel.fb_open_github": "Åbn på GitHub", + "panel.fb_copy_markdown": "Kopiér som Markdown", + "panel.fb_copied_markdown": "Rapporten er kopieret — indsæt den i en ny GitHub-issue", + "panel.fb_copy_failed": "Kunne ikke kopiere til udklipsholderen", + "panel.fb_search_github": "Søg i eksisterende issues", + "panel.fb_ready": "Klar — tjek den på GitHub, og tryk så på Submit new issue.", + "panel.fb_still_needed": "Mangler stadig: {fields}", + "panel.fb_too_long": "{count} tegn for lang til et link.", + "panel.fb_too_long_plain": "For lang til et link — gør den kortere, eller brug Kopiér som Markdown.", + "panel.fb_too_long_hint": "Gør det længste felt kortere, eller brug Kopiér som Markdown, og indsæt rapporten i en ny issue på GitHub.", + "panel.fb_account_hint": "Du skal bruge en gratis GitHub-konto. Din tekst åbnes i en ny fane, så du kan tjekke den, før noget bliver offentliggjort.", + "panel.fb_similar_title": "Lignende issues på GitHub", + "panel.fb_similar_hint": "Hvis en af dem er dit problem, så skriv hellere en kommentar eller giv en 👍 der.", + "panel.fb_issue_open": "Åben", + "panel.fb_issue_closed": "Lukket", + "panel.fb_stale": "Denne side er fra TaskMate {panel}, men {installed} er installeret. Din browser viser en gammel kopi — opdater, før du rapporterer et problem med et kort.", + "panel.fb_refresh_now": "Opdater nu", + "panel.fb_health_found": "Tilstandstjekket fandt {count} ting, der skal rettes. De kan forklare problemet.", + "panel.fb_health_open": "Åbn tilstandstjek", + "panel.fb_opened": "GitHub er åbnet i en ny fane. Når du har indsendt issuen, kan du rydde formularen.", + "panel.fb_clear": "Ryd formular", + "panel.fb_clear_confirm": "Ryd alt, hvad du har skrevet?", + "panel.fb_other_help": "Anden hjælp", + "panel.fb_link_troubleshooting": "Fejlfindingsguide", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Spørg fællesskabet", "panel.tab_templates": "Skabeloner", "panel.tab_today": "I dag", "panel.tab_unknown": "Ukendt fane", diff --git a/custom_components/taskmate/www/locales/de.json b/custom_components/taskmate/www/locales/de.json index 5288d587..c35cbb52 100644 --- a/custom_components/taskmate/www/locales/de.json +++ b/custom_components/taskmate/www/locales/de.json @@ -1496,6 +1496,147 @@ "panel.tab_penalties": "Strafen", "panel.tab_rewards": "Belohnungen", "panel.tab_settings": "Einstellungen", + "panel.tab_help": "Hilfe und Feedback", + "panel.fb_intro": "Ein Problem gefunden oder eine Idee? Beschreib es hier, und TaskMate öffnet ein GitHub-Issue, in dem deine Versionen und deine Einrichtung schon ausgefüllt sind. Es wird nichts gesendet, bevor du auf GitHub auf Submit drückst.", + "panel.fb_type_label": "Was möchtest du tun?", + "panel.fb_type_bug": "Fehler melden", + "panel.fb_type_bug_hint": "Etwas funktioniert nicht so, wie es soll", + "panel.fb_type_feature": "Funktion vorschlagen", + "panel.fb_type_feature_hint": "Eine Idee, um TaskMate besser zu machen", + "panel.fb_title": "Titel", + "panel.fb_bug_heading": "Was ist schiefgelaufen?", + "panel.fb_bug_title_placeholder": "z.B. Kinderkarte zeigt nach dem Zurücksetzen um Mitternacht 0 Punkte", + "panel.fb_bug_description": "Was ist passiert?", + "panel.fb_bug_description_placeholder": "Beschreib, was du gesehen hast und wo", + "panel.fb_bug_expected": "Was hast du erwartet?", + "panel.fb_bug_steps": "Schritte zum Nachstellen", + "panel.fb_bug_steps_placeholder": "1. Öffne…\n2. Tippe auf…\n3. Sieh…", + "panel.fb_bug_area": "Welcher Teil von TaskMate?", + "panel.fb_bug_repro": "Passiert es jedes Mal?", + "panel.fb_bug_console": "Fehler in der Browser-Konsole", + "panel.fb_bug_console_hint": "Bei Problemen mit Karten: Drück F12, öffne den Tab Console, stell das Problem nach und füge alle roten Fehler ein.", + "panel.fb_bug_config": "Karten-YAML", + "panel.fb_bug_config_hint": "Bei Problemen mit Karten: Füge das YAML der Karte aus dem Dashboard-Editor ein.", + "panel.fb_more_details": "Weitere Details (optional)", + "panel.fb_additional": "Sonst noch etwas?", + "panel.fb_additional_hint": "Screenshots kannst du auf GitHub direkt in das Issue ziehen.", + "panel.fb_feat_heading": "Was ist deine Idee?", + "panel.fb_feat_title_placeholder": "z.B. Kinder können Aufgaben untereinander tauschen", + "panel.fb_feat_summary": "Zusammenfassung", + "panel.fb_feat_summary_placeholder": "Beschreib die Funktion in ein, zwei Sätzen", + "panel.fb_feat_problem": "Welches Problem würde sie lösen?", + "panel.fb_feat_problem_hint": "Gute Ideen beginnen mit einem echten Problem im Familienalltag.", + "panel.fb_feat_solution": "Wie soll sie funktionieren?", + "panel.fb_feat_area": "Welcher Bereich?", + "panel.fb_feat_priority": "Wie wichtig ist sie dir?", + "panel.fb_feat_alternatives": "Umgehungen oder Alternativen, die du ausprobiert hast", + "panel.fb_area_child_card": "Kinderkarte", + "panel.fb_area_rewards_card": "Belohnungskarte", + "panel.fb_area_approvals_card": "Genehmigungskarte", + "panel.fb_area_points_card": "Punktekarte", + "panel.fb_area_reorder_card": "Sortierkarte", + "panel.fb_area_parent_card": "Eltern-Dashboard-Karte", + "panel.fb_area_overview_card": "Übersichtskarte", + "panel.fb_area_activity_card": "Aktivitätskarte", + "panel.fb_area_streak_card": "Serienkarte", + "panel.fb_area_weekly_card": "Wochenkarte", + "panel.fb_area_graph_card": "Punktediagramm-Karte", + "panel.fb_area_progress_card": "Belohnungsfortschritt-Karte", + "panel.fb_area_leaderboard_card": "Bestenlisten-Karte", + "panel.fb_area_setup": "Einrichtung der Integration", + "panel.fb_area_chores": "Aufgaben verwalten", + "panel.fb_area_rewards": "Belohnungen verwalten", + "panel.fb_area_children": "Kinder verwalten", + "panel.fb_area_points": "Punkte und Wertung", + "panel.fb_area_streaks": "Serien und Meilensteine", + "panel.fb_area_reward_approval": "Belohnungen genehmigen", + "panel.fb_area_chore_approval": "Aufgaben genehmigen", + "panel.fb_area_services": "Dienste und Automatisierungen", + "panel.fb_area_sensors": "Sensoren und Entitätsattribute", + "panel.fb_area_resources": "Karten werden nicht geladen", + "panel.fb_area_bonus": "Bonuspunkte", + "panel.fb_area_settings": "Einstellungen", + "panel.fb_area_notifications": "Benachrichtigungen", + "panel.fb_area_install": "Installation / HACS", + "panel.fb_area_new_card": "Eine neue Karte", + "panel.fb_area_other": "Etwas anderes", + "panel.fb_repro_always": "Ja, jedes Mal", + "panel.fb_repro_sometimes": "Manchmal", + "panel.fb_repro_once": "Es ist nur einmal passiert", + "panel.fb_priority_nice": "Wäre schön", + "panel.fb_priority_important": "Wichtig — eine Lücke in unserem Alltag", + "panel.fb_priority_critical": "Kritisch — wir können TaskMate so nicht richtig nutzen", + "panel.fb_details_heading": "Automatisch enthalten", + "panel.fb_details_intro": "Diese Angaben helfen, einem Problem schnell auf den Grund zu gehen. Sie enthalten nie Namen, Punkte oder andere persönliche Daten.", + "panel.fb_reload": "Aktualisieren", + "panel.fb_loading": "Angaben zu deiner Einrichtung werden gesammelt…", + "panel.fb_info_failed": "Angaben zu deiner Einrichtung konnten nicht gelesen werden: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate-Version", + "panel.fb_ha_version": "Home Assistant-Version", + "panel.fb_family": "Familie", + "panel.fb_family_value": "{children} Kinder, {adults} Erwachsene · {chores} Aufgaben · {rewards} Belohnungen", + "panel.fb_browser": "Browser", + "panel.fb_health": "Zustandsprüfung", + "panel.fb_health_ok": "Keine Probleme", + "panel.fb_children_count": "Kinder", + "panel.fb_install_type": "Home Assistant-Installation", + "panel.fb_install_method": "TaskMate installiert über", + "panel.fb_lovelace_mode": "Dashboard-Modus (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (benutzerdefiniertes Repository)", + "panel.fb_method_manual": "Dateien manuell kopiert", + "panel.fb_lovelace_storage": "Storage (in der Oberfläche bearbeitet)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Weiß nicht", + "panel.fb_pick_one": "Auswählen…", + "panel.fb_dropdowns_hint": "Für dich erkannt — ändere sie, falls sie falsch sind. GitHub kann seine Auswahllisten nicht vorbelegen, also wähl dort dieselben Antworten; sie werden außerdem in den Issue-Text kopiert.", + "panel.fb_include_details": "Diese Angaben einfügen", + "panel.fb_logs_heading": "TaskMate-Protokolleinträge", + "panel.fb_logs_found": "{count} aktuelle Warnungen oder Fehler von TaskMate. Namen sind durch Platzhalter ersetzt.", + "panel.fb_logs_none": "Keine aktuellen TaskMate-Warnungen oder -Fehler im Protokoll.", + "panel.fb_logs_unavailable": "Das Home Assistant-Protokoll konnte nicht gelesen werden.", + "panel.fb_logs_include": "Zum Issue hinzufügen", + "panel.fb_logs_preview": "Anzeigen, was eingefügt wird", + "panel.fb_copy_logs": "Protokolleinträge kopieren", + "panel.fb_copied_logs": "Protokolleinträge kopiert", + "panel.fb_logs_debug_hint": "Home Assistant speichert hier nur Warnungen und Fehler. Für das ganze Bild schalte die Debug-Protokollierung ein (Einstellungen → Geräte und Dienste → TaskMate → Debug-Protokollierung aktivieren), stell das Problem nach, lade dann das Protokoll unter Einstellungen → System → Protokolle herunter und zieh es in das Issue.", + "panel.fb_logs_trimmed": "Der Link hat Platz für {used} Protokolleinträge — die {dropped} ältesten wurden weggelassen. Nutze Als Markdown kopieren, um alle einzufügen.", + "panel.fb_diag_heading": "Diagnosedatei", + "panel.fb_diag_hint": "Eine Datei, die deine TaskMate-Einrichtung beschreibt, ohne Namen und persönliche Daten. Zieh sie auf GitHub in das Issue.", + "panel.fb_diag_download": "Herunterladen", + "panel.fb_diag_started": "Download der Diagnosedaten gestartet", + "panel.fb_diag_failed": "Die Diagnosedatei konnte nicht heruntergeladen werden", + "panel.fb_open_github": "Auf GitHub öffnen", + "panel.fb_copy_markdown": "Als Markdown kopieren", + "panel.fb_copied_markdown": "Bericht kopiert — füge ihn in ein neues GitHub-Issue ein", + "panel.fb_copy_failed": "Kopieren in die Zwischenablage fehlgeschlagen", + "panel.fb_search_github": "Vorhandene Issues durchsuchen", + "panel.fb_ready": "Fertig — prüf es auf GitHub und drück dann auf Submit new issue.", + "panel.fb_still_needed": "Fehlt noch: {fields}", + "panel.fb_too_long": "Um {count} Zeichen zu lang für einen Link.", + "panel.fb_too_long_plain": "Zu lang für einen Link — kürze den Text oder nutze Als Markdown kopieren.", + "panel.fb_too_long_hint": "Kürze das längste Feld oder nutze Als Markdown kopieren und füge den Bericht auf GitHub in ein neues Issue ein.", + "panel.fb_account_hint": "Du brauchst ein kostenloses GitHub-Konto. Dein Text öffnet sich in einem neuen Tab, damit du ihn prüfen kannst, bevor etwas veröffentlicht wird.", + "panel.fb_similar_title": "Ähnliche Issues auf GitHub", + "panel.fb_similar_hint": "Wenn eins davon dein Problem ist, schreib lieber dort einen Kommentar oder gib ein 👍.", + "panel.fb_issue_open": "Offen", + "panel.fb_issue_closed": "Geschlossen", + "panel.fb_stale": "Diese Seite stammt von TaskMate {panel}, installiert ist aber {installed}. Dein Browser zeigt eine alte Kopie — aktualisiere, bevor du ein Problem mit einer Karte meldest.", + "panel.fb_refresh_now": "Jetzt aktualisieren", + "panel.fb_health_found": "Die Zustandsprüfung hat {count} Punkt(e) zum Beheben gefunden. Sie könnten das Problem erklären.", + "panel.fb_health_open": "Zustandsprüfung öffnen", + "panel.fb_opened": "GitHub wurde in einem neuen Tab geöffnet. Sobald du das Issue abgeschickt hast, kannst du dieses Formular leeren.", + "panel.fb_clear": "Formular leeren", + "panel.fb_clear_confirm": "Alles löschen, was du eingegeben hast?", + "panel.fb_other_help": "Weitere Hilfe", + "panel.fb_link_troubleshooting": "Anleitung zur Fehlerbehebung", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Die Community fragen", "panel.tab_templates": "Vorlagen", "panel.tab_today": "Heute", "panel.tab_unknown": "Unbekannter Tab", diff --git a/custom_components/taskmate/www/locales/en-GB.json b/custom_components/taskmate/www/locales/en-GB.json index aa29f05a..f89ec74b 100644 --- a/custom_components/taskmate/www/locales/en-GB.json +++ b/custom_components/taskmate/www/locales/en-GB.json @@ -1560,6 +1560,147 @@ "panel.tab_penalties": "Penalties", "panel.tab_rewards": "Rewards", "panel.tab_settings": "Settings", + "panel.tab_help": "Help & feedback", + "panel.fb_intro": "Found a problem or have an idea? Describe it here and TaskMate opens a GitHub issue with your versions and setup already filled in. Nothing is sent until you press Submit on GitHub.", + "panel.fb_type_label": "What would you like to do?", + "panel.fb_type_bug": "Report a bug", + "panel.fb_type_bug_hint": "Something isn't working as it should", + "panel.fb_type_feature": "Suggest a feature", + "panel.fb_type_feature_hint": "An idea to make TaskMate better", + "panel.fb_title": "Title", + "panel.fb_bug_heading": "What went wrong?", + "panel.fb_bug_title_placeholder": "e.g. Child card shows 0 points after the midnight reset", + "panel.fb_bug_description": "What happened?", + "panel.fb_bug_description_placeholder": "Describe what you saw, and where", + "panel.fb_bug_expected": "What did you expect to happen?", + "panel.fb_bug_steps": "Steps to reproduce", + "panel.fb_bug_steps_placeholder": "1. Open…\n2. Tap…\n3. See…", + "panel.fb_bug_area": "Which part of TaskMate?", + "panel.fb_bug_repro": "Does it happen every time?", + "panel.fb_bug_console": "Browser console errors", + "panel.fb_bug_console_hint": "For card problems: press F12, open the Console tab, reproduce the problem and paste any red errors.", + "panel.fb_bug_config": "Card YAML", + "panel.fb_bug_config_hint": "For card problems: paste the card's YAML from the dashboard editor.", + "panel.fb_more_details": "More details (optional)", + "panel.fb_additional": "Anything else?", + "panel.fb_additional_hint": "Screenshots can be dragged onto the issue on GitHub.", + "panel.fb_feat_heading": "What's your idea?", + "panel.fb_feat_title_placeholder": "e.g. Let children swap chores with each other", + "panel.fb_feat_summary": "Summary", + "panel.fb_feat_summary_placeholder": "Describe the feature in a sentence or two", + "panel.fb_feat_problem": "What problem would it solve?", + "panel.fb_feat_problem_hint": "Good ideas start with a real problem in your family's routine.", + "panel.fb_feat_solution": "How would you like it to work?", + "panel.fb_feat_area": "Which area?", + "panel.fb_feat_priority": "How important is it to you?", + "panel.fb_feat_alternatives": "Workarounds or alternatives you've tried", + "panel.fb_area_child_card": "Child card", + "panel.fb_area_rewards_card": "Rewards card", + "panel.fb_area_approvals_card": "Approvals card", + "panel.fb_area_points_card": "Points card", + "panel.fb_area_reorder_card": "Reorder card", + "panel.fb_area_parent_card": "Parent dashboard card", + "panel.fb_area_overview_card": "Overview card", + "panel.fb_area_activity_card": "Activity card", + "panel.fb_area_streak_card": "Streak card", + "panel.fb_area_weekly_card": "Weekly card", + "panel.fb_area_graph_card": "Points graph card", + "panel.fb_area_progress_card": "Reward progress card", + "panel.fb_area_leaderboard_card": "Leaderboard card", + "panel.fb_area_setup": "Setting up the integration", + "panel.fb_area_chores": "Managing chores", + "panel.fb_area_rewards": "Managing rewards", + "panel.fb_area_children": "Managing children", + "panel.fb_area_points": "Points and scoring", + "panel.fb_area_streaks": "Streaks and milestones", + "panel.fb_area_reward_approval": "Approving rewards", + "panel.fb_area_chore_approval": "Approving chores", + "panel.fb_area_services": "Services and automations", + "panel.fb_area_sensors": "Sensors and entity attributes", + "panel.fb_area_resources": "Cards not loading", + "panel.fb_area_bonus": "Bonus points", + "panel.fb_area_settings": "Settings", + "panel.fb_area_notifications": "Notifications", + "panel.fb_area_install": "Installing / HACS", + "panel.fb_area_new_card": "A new card", + "panel.fb_area_other": "Something else", + "panel.fb_repro_always": "Yes, every time", + "panel.fb_repro_sometimes": "Sometimes", + "panel.fb_repro_once": "It only happened once", + "panel.fb_priority_nice": "Nice to have", + "panel.fb_priority_important": "Important — a gap in our daily use", + "panel.fb_priority_critical": "Critical — it stops us using TaskMate properly", + "panel.fb_details_heading": "Included automatically", + "panel.fb_details_intro": "These details help get to the bottom of a problem quickly. They never include names, points or anything personal.", + "panel.fb_reload": "Refresh", + "panel.fb_loading": "Gathering your setup details…", + "panel.fb_info_failed": "Couldn't read your setup details: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate version", + "panel.fb_ha_version": "Home Assistant version", + "panel.fb_family": "Family", + "panel.fb_family_value": "{children} children, {adults} adults · {chores} chores · {rewards} rewards", + "panel.fb_browser": "Browser", + "panel.fb_health": "Health check", + "panel.fb_health_ok": "No issues", + "panel.fb_children_count": "Children", + "panel.fb_install_type": "Home Assistant installation", + "panel.fb_install_method": "TaskMate installed via", + "panel.fb_lovelace_mode": "Dashboard (Lovelace) mode", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (custom repository)", + "panel.fb_method_manual": "Manually copied files", + "panel.fb_lovelace_storage": "Storage (edited in the UI)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Not sure", + "panel.fb_pick_one": "Choose…", + "panel.fb_dropdowns_hint": "Detected for you — change them if they're wrong. GitHub can't pre-select its drop-down lists, so pick the same answers there; they're also copied into the issue text.", + "panel.fb_include_details": "Include these details", + "panel.fb_logs_heading": "TaskMate log entries", + "panel.fb_logs_found": "{count} recent warnings or errors from TaskMate. Names are replaced with placeholders.", + "panel.fb_logs_none": "No recent TaskMate warnings or errors in the log.", + "panel.fb_logs_unavailable": "The Home Assistant log couldn't be read.", + "panel.fb_logs_include": "Add these to the issue", + "panel.fb_logs_preview": "Show what will be included", + "panel.fb_copy_logs": "Copy log entries", + "panel.fb_copied_logs": "Log entries copied", + "panel.fb_logs_debug_hint": "Home Assistant only keeps warnings and errors here. For the full story, turn on debug logging (Settings → Devices & services → TaskMate → Enable debug logging), reproduce the problem, then download the log from Settings → System → Logs and drag it onto the issue.", + "panel.fb_logs_trimmed": "The link has room for {used} log entries — the {dropped} oldest were left out. Use Copy as Markdown to include them all.", + "panel.fb_diag_heading": "Diagnostics file", + "panel.fb_diag_hint": "A file describing your TaskMate setup, with names and personal details removed. Drag it onto the issue on GitHub.", + "panel.fb_diag_download": "Download", + "panel.fb_diag_started": "Diagnostics download started", + "panel.fb_diag_failed": "Couldn't download the diagnostics file", + "panel.fb_open_github": "Open on GitHub", + "panel.fb_copy_markdown": "Copy as Markdown", + "panel.fb_copied_markdown": "Report copied — paste it into a new GitHub issue", + "panel.fb_copy_failed": "Couldn't copy to the clipboard", + "panel.fb_search_github": "Search existing issues", + "panel.fb_ready": "Ready — check it on GitHub, then press Submit new issue.", + "panel.fb_still_needed": "Still needed: {fields}", + "panel.fb_too_long": "Too long for a link by {count} characters.", + "panel.fb_too_long_plain": "Too long for a link — shorten it, or use Copy as Markdown.", + "panel.fb_too_long_hint": "Shorten the longest box, or use Copy as Markdown and paste the report into a new issue on GitHub.", + "panel.fb_account_hint": "You'll need a free GitHub account. Your text opens in a new tab for you to check before anything is posted.", + "panel.fb_similar_title": "Similar issues already on GitHub", + "panel.fb_similar_hint": "If one of these is your problem, add a comment or a 👍 there instead.", + "panel.fb_issue_open": "Open", + "panel.fb_issue_closed": "Closed", + "panel.fb_stale": "This page is from TaskMate {panel}, but {installed} is installed. Your browser is showing an old copy — refresh before reporting a problem with a card.", + "panel.fb_refresh_now": "Refresh now", + "panel.fb_health_found": "The health check found {count} thing(s) to fix. They may explain the problem.", + "panel.fb_health_open": "Open health check", + "panel.fb_opened": "GitHub opened in a new tab. Once you've submitted the issue you can clear this form.", + "panel.fb_clear": "Clear form", + "panel.fb_clear_confirm": "Clear everything you've typed?", + "panel.fb_other_help": "Other help", + "panel.fb_link_troubleshooting": "Troubleshooting guide", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Ask the community", "panel.tab_templates": "Templates", "panel.tab_today": "Today", "panel.tab_unknown": "Unknown tab", diff --git a/custom_components/taskmate/www/locales/en.json b/custom_components/taskmate/www/locales/en.json index aa29f05a..f89ec74b 100644 --- a/custom_components/taskmate/www/locales/en.json +++ b/custom_components/taskmate/www/locales/en.json @@ -1560,6 +1560,147 @@ "panel.tab_penalties": "Penalties", "panel.tab_rewards": "Rewards", "panel.tab_settings": "Settings", + "panel.tab_help": "Help & feedback", + "panel.fb_intro": "Found a problem or have an idea? Describe it here and TaskMate opens a GitHub issue with your versions and setup already filled in. Nothing is sent until you press Submit on GitHub.", + "panel.fb_type_label": "What would you like to do?", + "panel.fb_type_bug": "Report a bug", + "panel.fb_type_bug_hint": "Something isn't working as it should", + "panel.fb_type_feature": "Suggest a feature", + "panel.fb_type_feature_hint": "An idea to make TaskMate better", + "panel.fb_title": "Title", + "panel.fb_bug_heading": "What went wrong?", + "panel.fb_bug_title_placeholder": "e.g. Child card shows 0 points after the midnight reset", + "panel.fb_bug_description": "What happened?", + "panel.fb_bug_description_placeholder": "Describe what you saw, and where", + "panel.fb_bug_expected": "What did you expect to happen?", + "panel.fb_bug_steps": "Steps to reproduce", + "panel.fb_bug_steps_placeholder": "1. Open…\n2. Tap…\n3. See…", + "panel.fb_bug_area": "Which part of TaskMate?", + "panel.fb_bug_repro": "Does it happen every time?", + "panel.fb_bug_console": "Browser console errors", + "panel.fb_bug_console_hint": "For card problems: press F12, open the Console tab, reproduce the problem and paste any red errors.", + "panel.fb_bug_config": "Card YAML", + "panel.fb_bug_config_hint": "For card problems: paste the card's YAML from the dashboard editor.", + "panel.fb_more_details": "More details (optional)", + "panel.fb_additional": "Anything else?", + "panel.fb_additional_hint": "Screenshots can be dragged onto the issue on GitHub.", + "panel.fb_feat_heading": "What's your idea?", + "panel.fb_feat_title_placeholder": "e.g. Let children swap chores with each other", + "panel.fb_feat_summary": "Summary", + "panel.fb_feat_summary_placeholder": "Describe the feature in a sentence or two", + "panel.fb_feat_problem": "What problem would it solve?", + "panel.fb_feat_problem_hint": "Good ideas start with a real problem in your family's routine.", + "panel.fb_feat_solution": "How would you like it to work?", + "panel.fb_feat_area": "Which area?", + "panel.fb_feat_priority": "How important is it to you?", + "panel.fb_feat_alternatives": "Workarounds or alternatives you've tried", + "panel.fb_area_child_card": "Child card", + "panel.fb_area_rewards_card": "Rewards card", + "panel.fb_area_approvals_card": "Approvals card", + "panel.fb_area_points_card": "Points card", + "panel.fb_area_reorder_card": "Reorder card", + "panel.fb_area_parent_card": "Parent dashboard card", + "panel.fb_area_overview_card": "Overview card", + "panel.fb_area_activity_card": "Activity card", + "panel.fb_area_streak_card": "Streak card", + "panel.fb_area_weekly_card": "Weekly card", + "panel.fb_area_graph_card": "Points graph card", + "panel.fb_area_progress_card": "Reward progress card", + "panel.fb_area_leaderboard_card": "Leaderboard card", + "panel.fb_area_setup": "Setting up the integration", + "panel.fb_area_chores": "Managing chores", + "panel.fb_area_rewards": "Managing rewards", + "panel.fb_area_children": "Managing children", + "panel.fb_area_points": "Points and scoring", + "panel.fb_area_streaks": "Streaks and milestones", + "panel.fb_area_reward_approval": "Approving rewards", + "panel.fb_area_chore_approval": "Approving chores", + "panel.fb_area_services": "Services and automations", + "panel.fb_area_sensors": "Sensors and entity attributes", + "panel.fb_area_resources": "Cards not loading", + "panel.fb_area_bonus": "Bonus points", + "panel.fb_area_settings": "Settings", + "panel.fb_area_notifications": "Notifications", + "panel.fb_area_install": "Installing / HACS", + "panel.fb_area_new_card": "A new card", + "panel.fb_area_other": "Something else", + "panel.fb_repro_always": "Yes, every time", + "panel.fb_repro_sometimes": "Sometimes", + "panel.fb_repro_once": "It only happened once", + "panel.fb_priority_nice": "Nice to have", + "panel.fb_priority_important": "Important — a gap in our daily use", + "panel.fb_priority_critical": "Critical — it stops us using TaskMate properly", + "panel.fb_details_heading": "Included automatically", + "panel.fb_details_intro": "These details help get to the bottom of a problem quickly. They never include names, points or anything personal.", + "panel.fb_reload": "Refresh", + "panel.fb_loading": "Gathering your setup details…", + "panel.fb_info_failed": "Couldn't read your setup details: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate version", + "panel.fb_ha_version": "Home Assistant version", + "panel.fb_family": "Family", + "panel.fb_family_value": "{children} children, {adults} adults · {chores} chores · {rewards} rewards", + "panel.fb_browser": "Browser", + "panel.fb_health": "Health check", + "panel.fb_health_ok": "No issues", + "panel.fb_children_count": "Children", + "panel.fb_install_type": "Home Assistant installation", + "panel.fb_install_method": "TaskMate installed via", + "panel.fb_lovelace_mode": "Dashboard (Lovelace) mode", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (custom repository)", + "panel.fb_method_manual": "Manually copied files", + "panel.fb_lovelace_storage": "Storage (edited in the UI)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Not sure", + "panel.fb_pick_one": "Choose…", + "panel.fb_dropdowns_hint": "Detected for you — change them if they're wrong. GitHub can't pre-select its drop-down lists, so pick the same answers there; they're also copied into the issue text.", + "panel.fb_include_details": "Include these details", + "panel.fb_logs_heading": "TaskMate log entries", + "panel.fb_logs_found": "{count} recent warnings or errors from TaskMate. Names are replaced with placeholders.", + "panel.fb_logs_none": "No recent TaskMate warnings or errors in the log.", + "panel.fb_logs_unavailable": "The Home Assistant log couldn't be read.", + "panel.fb_logs_include": "Add these to the issue", + "panel.fb_logs_preview": "Show what will be included", + "panel.fb_copy_logs": "Copy log entries", + "panel.fb_copied_logs": "Log entries copied", + "panel.fb_logs_debug_hint": "Home Assistant only keeps warnings and errors here. For the full story, turn on debug logging (Settings → Devices & services → TaskMate → Enable debug logging), reproduce the problem, then download the log from Settings → System → Logs and drag it onto the issue.", + "panel.fb_logs_trimmed": "The link has room for {used} log entries — the {dropped} oldest were left out. Use Copy as Markdown to include them all.", + "panel.fb_diag_heading": "Diagnostics file", + "panel.fb_diag_hint": "A file describing your TaskMate setup, with names and personal details removed. Drag it onto the issue on GitHub.", + "panel.fb_diag_download": "Download", + "panel.fb_diag_started": "Diagnostics download started", + "panel.fb_diag_failed": "Couldn't download the diagnostics file", + "panel.fb_open_github": "Open on GitHub", + "panel.fb_copy_markdown": "Copy as Markdown", + "panel.fb_copied_markdown": "Report copied — paste it into a new GitHub issue", + "panel.fb_copy_failed": "Couldn't copy to the clipboard", + "panel.fb_search_github": "Search existing issues", + "panel.fb_ready": "Ready — check it on GitHub, then press Submit new issue.", + "panel.fb_still_needed": "Still needed: {fields}", + "panel.fb_too_long": "Too long for a link by {count} characters.", + "panel.fb_too_long_plain": "Too long for a link — shorten it, or use Copy as Markdown.", + "panel.fb_too_long_hint": "Shorten the longest box, or use Copy as Markdown and paste the report into a new issue on GitHub.", + "panel.fb_account_hint": "You'll need a free GitHub account. Your text opens in a new tab for you to check before anything is posted.", + "panel.fb_similar_title": "Similar issues already on GitHub", + "panel.fb_similar_hint": "If one of these is your problem, add a comment or a 👍 there instead.", + "panel.fb_issue_open": "Open", + "panel.fb_issue_closed": "Closed", + "panel.fb_stale": "This page is from TaskMate {panel}, but {installed} is installed. Your browser is showing an old copy — refresh before reporting a problem with a card.", + "panel.fb_refresh_now": "Refresh now", + "panel.fb_health_found": "The health check found {count} thing(s) to fix. They may explain the problem.", + "panel.fb_health_open": "Open health check", + "panel.fb_opened": "GitHub opened in a new tab. Once you've submitted the issue you can clear this form.", + "panel.fb_clear": "Clear form", + "panel.fb_clear_confirm": "Clear everything you've typed?", + "panel.fb_other_help": "Other help", + "panel.fb_link_troubleshooting": "Troubleshooting guide", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Ask the community", "panel.tab_templates": "Templates", "panel.tab_today": "Today", "panel.tab_unknown": "Unknown tab", diff --git a/custom_components/taskmate/www/locales/fr.json b/custom_components/taskmate/www/locales/fr.json index 1df22e91..45df8923 100644 --- a/custom_components/taskmate/www/locales/fr.json +++ b/custom_components/taskmate/www/locales/fr.json @@ -1496,6 +1496,147 @@ "panel.tab_penalties": "Pénalités", "panel.tab_rewards": "Récompenses", "panel.tab_settings": "Paramètres", + "panel.tab_help": "Aide et commentaires", + "panel.fb_intro": "Vous avez trouvé un problème ou eu une idée ? Décrivez-le ici et TaskMate ouvre une issue GitHub avec vos versions et votre configuration déjà remplies. Rien n'est envoyé avant que vous appuyiez sur Submit sur GitHub.", + "panel.fb_type_label": "Que souhaitez-vous faire ?", + "panel.fb_type_bug": "Signaler un bug", + "panel.fb_type_bug_hint": "Quelque chose ne fonctionne pas comme prévu", + "panel.fb_type_feature": "Proposer une fonctionnalité", + "panel.fb_type_feature_hint": "Une idée pour améliorer TaskMate", + "panel.fb_title": "Titre", + "panel.fb_bug_heading": "Qu'est-ce qui n'a pas marché ?", + "panel.fb_bug_title_placeholder": "ex. La carte enfant affiche 0 point après la réinitialisation de minuit", + "panel.fb_bug_description": "Que s'est-il passé ?", + "panel.fb_bug_description_placeholder": "Décrivez ce que vous avez vu, et où", + "panel.fb_bug_expected": "À quoi vous attendiez-vous ?", + "panel.fb_bug_steps": "Étapes pour reproduire", + "panel.fb_bug_steps_placeholder": "1. Ouvrir…\n2. Appuyer sur…\n3. Constater…", + "panel.fb_bug_area": "Quelle partie de TaskMate ?", + "panel.fb_bug_repro": "Cela arrive-t-il à chaque fois ?", + "panel.fb_bug_console": "Erreurs de la console du navigateur", + "panel.fb_bug_console_hint": "Pour un problème de carte : appuyez sur F12, ouvrez l'onglet Console, reproduisez le problème et collez les erreurs en rouge.", + "panel.fb_bug_config": "YAML de la carte", + "panel.fb_bug_config_hint": "Pour un problème de carte : collez le YAML de la carte depuis l'éditeur du tableau de bord.", + "panel.fb_more_details": "Plus de détails (optionnel)", + "panel.fb_additional": "Autre chose ?", + "panel.fb_additional_hint": "Vous pouvez glisser des captures d'écran dans l'issue sur GitHub.", + "panel.fb_feat_heading": "Quelle est votre idée ?", + "panel.fb_feat_title_placeholder": "ex. Permettre aux enfants d'échanger leurs tâches", + "panel.fb_feat_summary": "Résumé", + "panel.fb_feat_summary_placeholder": "Décrivez la fonctionnalité en une ou deux phrases", + "panel.fb_feat_problem": "Quel problème résoudrait-elle ?", + "panel.fb_feat_problem_hint": "Les bonnes idées partent d'un vrai problème dans le quotidien de la famille.", + "panel.fb_feat_solution": "Comment aimeriez-vous qu'elle fonctionne ?", + "panel.fb_feat_area": "Quel domaine ?", + "panel.fb_feat_priority": "Quelle importance a-t-elle pour vous ?", + "panel.fb_feat_alternatives": "Solutions de contournement ou alternatives essayées", + "panel.fb_area_child_card": "Carte enfant", + "panel.fb_area_rewards_card": "Carte des récompenses", + "panel.fb_area_approvals_card": "Carte des approbations", + "panel.fb_area_points_card": "Carte des points", + "panel.fb_area_reorder_card": "Carte de réorganisation", + "panel.fb_area_parent_card": "Carte tableau de bord parent", + "panel.fb_area_overview_card": "Carte vue d'ensemble", + "panel.fb_area_activity_card": "Carte d'activité", + "panel.fb_area_streak_card": "Carte des séries", + "panel.fb_area_weekly_card": "Carte hebdomadaire", + "panel.fb_area_graph_card": "Carte graphique des points", + "panel.fb_area_progress_card": "Carte de progression des récompenses", + "panel.fb_area_leaderboard_card": "Carte classement", + "panel.fb_area_setup": "Configuration de l'intégration", + "panel.fb_area_chores": "Gestion des tâches", + "panel.fb_area_rewards": "Gestion des récompenses", + "panel.fb_area_children": "Gestion des enfants", + "panel.fb_area_points": "Points et calcul des scores", + "panel.fb_area_streaks": "Séries et paliers", + "panel.fb_area_reward_approval": "Approbation des récompenses", + "panel.fb_area_chore_approval": "Approbation des tâches", + "panel.fb_area_services": "Services et automatisations", + "panel.fb_area_sensors": "Capteurs et attributs d'entité", + "panel.fb_area_resources": "Les cartes ne se chargent pas", + "panel.fb_area_bonus": "Points bonus", + "panel.fb_area_settings": "Paramètres", + "panel.fb_area_notifications": "Notifications", + "panel.fb_area_install": "Installation / HACS", + "panel.fb_area_new_card": "Une nouvelle carte", + "panel.fb_area_other": "Autre chose", + "panel.fb_repro_always": "Oui, à chaque fois", + "panel.fb_repro_sometimes": "Parfois", + "panel.fb_repro_once": "Une seule fois", + "panel.fb_priority_nice": "Ce serait bien", + "panel.fb_priority_important": "Important — un manque dans notre usage quotidien", + "panel.fb_priority_critical": "Critique — cela nous empêche d'utiliser TaskMate correctement", + "panel.fb_details_heading": "Inclus automatiquement", + "panel.fb_details_intro": "Ces informations aident à trouver rapidement l'origine d'un problème. Elles ne contiennent jamais de noms, de points ni rien de personnel.", + "panel.fb_reload": "Actualiser", + "panel.fb_loading": "Collecte des informations sur votre configuration…", + "panel.fb_info_failed": "Impossible de lire les informations sur votre configuration : {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "Version de TaskMate", + "panel.fb_ha_version": "Version de Home Assistant", + "panel.fb_family": "Famille", + "panel.fb_family_value": "{children} enfants, {adults} adultes · {chores} tâches · {rewards} récompenses", + "panel.fb_browser": "Navigateur", + "panel.fb_health": "Bilan de santé", + "panel.fb_health_ok": "Aucun problème", + "panel.fb_children_count": "Enfants", + "panel.fb_install_type": "Installation de Home Assistant", + "panel.fb_install_method": "TaskMate installé via", + "panel.fb_lovelace_mode": "Mode du tableau de bord (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Conteneur (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (dépôt personnalisé)", + "panel.fb_method_manual": "Fichiers copiés manuellement", + "panel.fb_lovelace_storage": "Stockage (modifié dans l'interface)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Je ne sais pas", + "panel.fb_pick_one": "Choisir…", + "panel.fb_dropdowns_hint": "Détectés pour vous — corrigez-les s'ils sont faux. GitHub ne peut pas présélectionner ses listes déroulantes, choisissez donc les mêmes réponses là-bas ; elles sont aussi copiées dans le texte de l'issue.", + "panel.fb_include_details": "Inclure ces informations", + "panel.fb_logs_heading": "Entrées du journal TaskMate", + "panel.fb_logs_found": "{count} avertissements ou erreurs récents de TaskMate. Les noms sont remplacés par des substituts.", + "panel.fb_logs_none": "Aucun avertissement ni aucune erreur récente de TaskMate dans le journal.", + "panel.fb_logs_unavailable": "Impossible de lire le journal de Home Assistant.", + "panel.fb_logs_include": "Ajouter à l'issue", + "panel.fb_logs_preview": "Voir ce qui sera inclus", + "panel.fb_copy_logs": "Copier les entrées du journal", + "panel.fb_copied_logs": "Entrées du journal copiées", + "panel.fb_logs_debug_hint": "Home Assistant ne conserve ici que les avertissements et les erreurs. Pour avoir tout le détail, activez la journalisation de débogage (Paramètres → Appareils et services → TaskMate → Activer la journalisation de débogage), reproduisez le problème, puis téléchargez le journal depuis Paramètres → Système → Journaux et glissez-le dans l'issue.", + "panel.fb_logs_trimmed": "Le lien a de la place pour {used} entrées du journal — les {dropped} plus anciennes ont été omises. Utilisez Copier en Markdown pour toutes les inclure.", + "panel.fb_diag_heading": "Fichier de diagnostic", + "panel.fb_diag_hint": "Un fichier décrivant votre configuration TaskMate, sans les noms ni les informations personnelles. Glissez-le dans l'issue sur GitHub.", + "panel.fb_diag_download": "Télécharger", + "panel.fb_diag_started": "Téléchargement du diagnostic lancé", + "panel.fb_diag_failed": "Impossible de télécharger le fichier de diagnostic", + "panel.fb_open_github": "Ouvrir sur GitHub", + "panel.fb_copy_markdown": "Copier en Markdown", + "panel.fb_copied_markdown": "Rapport copié — collez-le dans une nouvelle issue GitHub", + "panel.fb_copy_failed": "Impossible de copier dans le presse-papiers", + "panel.fb_search_github": "Rechercher les issues existantes", + "panel.fb_ready": "Prêt — vérifiez sur GitHub, puis appuyez sur Submit new issue.", + "panel.fb_still_needed": "Il manque encore : {fields}", + "panel.fb_too_long": "Trop long de {count} caractères pour un lien.", + "panel.fb_too_long_plain": "Trop long pour un lien — raccourcissez-le ou utilisez Copier en Markdown.", + "panel.fb_too_long_hint": "Raccourcissez le champ le plus long, ou utilisez Copier en Markdown et collez le rapport dans une nouvelle issue sur GitHub.", + "panel.fb_account_hint": "Il vous faut un compte GitHub gratuit. Votre texte s'ouvre dans un nouvel onglet pour que vous le vérifiiez avant toute publication.", + "panel.fb_similar_title": "Issues similaires déjà sur GitHub", + "panel.fb_similar_hint": "Si l'une d'elles correspond à votre problème, ajoutez plutôt un commentaire ou un 👍 dessus.", + "panel.fb_issue_open": "Ouverte", + "panel.fb_issue_closed": "Fermée", + "panel.fb_stale": "Cette page provient de TaskMate {panel}, mais c'est {installed} qui est installé. Votre navigateur affiche une ancienne copie — actualisez avant de signaler un problème de carte.", + "panel.fb_refresh_now": "Actualiser maintenant", + "panel.fb_health_found": "Le bilan de santé a trouvé {count} point(s) à corriger. Ils peuvent expliquer le problème.", + "panel.fb_health_open": "Ouvrir le bilan de santé", + "panel.fb_opened": "GitHub s'est ouvert dans un nouvel onglet. Une fois l'issue envoyée, vous pouvez effacer ce formulaire.", + "panel.fb_clear": "Effacer le formulaire", + "panel.fb_clear_confirm": "Effacer tout ce que vous avez saisi ?", + "panel.fb_other_help": "Autres ressources", + "panel.fb_link_troubleshooting": "Guide de dépannage", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Demander à la communauté", "panel.tab_templates": "Modèles", "panel.tab_today": "Aujourd'hui", "panel.tab_unknown": "Onglet inconnu", diff --git a/custom_components/taskmate/www/locales/nb.json b/custom_components/taskmate/www/locales/nb.json index acb20d43..13e5c6dd 100644 --- a/custom_components/taskmate/www/locales/nb.json +++ b/custom_components/taskmate/www/locales/nb.json @@ -1496,6 +1496,147 @@ "panel.tab_penalties": "Straffer", "panel.tab_rewards": "Belønninger", "panel.tab_settings": "Innstillinger", + "panel.tab_help": "Hjelp og tilbakemelding", + "panel.fb_intro": "Har du funnet et problem eller fått en idé? Beskriv det her, så åpner TaskMate en GitHub-sak med versjoner og oppsett allerede fylt ut. Ingenting sendes før du trykker Submit på GitHub.", + "panel.fb_type_label": "Hva vil du gjøre?", + "panel.fb_type_bug": "Rapporter en feil", + "panel.fb_type_bug_hint": "Noe fungerer ikke som det skal", + "panel.fb_type_feature": "Foreslå en funksjon", + "panel.fb_type_feature_hint": "En idé som kan gjøre TaskMate bedre", + "panel.fb_title": "Tittel", + "panel.fb_bug_heading": "Hva gikk galt?", + "panel.fb_bug_title_placeholder": "f.eks. Barnekortet viser 0 poeng etter tilbakestillingen ved midnatt", + "panel.fb_bug_description": "Hva skjedde?", + "panel.fb_bug_description_placeholder": "Beskriv hva du så, og hvor", + "panel.fb_bug_expected": "Hva forventet du skulle skje?", + "panel.fb_bug_steps": "Slik gjenskaper du feilen", + "panel.fb_bug_steps_placeholder": "1. Åpne…\n2. Trykk på…\n3. Se…", + "panel.fb_bug_area": "Hvilken del av TaskMate?", + "panel.fb_bug_repro": "Skjer det hver gang?", + "panel.fb_bug_console": "Feil i nettleserkonsollen", + "panel.fb_bug_console_hint": "For problemer med kort: trykk F12, åpne fanen Console, gjenskap problemet og lim inn eventuelle røde feil.", + "panel.fb_bug_config": "Kort-YAML", + "panel.fb_bug_config_hint": "For problemer med kort: lim inn kortets YAML fra dashbordredigeringen.", + "panel.fb_more_details": "Flere detaljer (valgfritt)", + "panel.fb_additional": "Noe annet?", + "panel.fb_additional_hint": "Skjermbilder kan dras inn i saken på GitHub.", + "panel.fb_feat_heading": "Hva er ideen din?", + "panel.fb_feat_title_placeholder": "f.eks. La barn bytte oppgaver med hverandre", + "panel.fb_feat_summary": "Sammendrag", + "panel.fb_feat_summary_placeholder": "Beskriv funksjonen med en setning eller to", + "panel.fb_feat_problem": "Hvilket problem ville det løse?", + "panel.fb_feat_problem_hint": "Gode ideer starter med et ekte problem i familiens hverdag.", + "panel.fb_feat_solution": "Hvordan vil du at det skal fungere?", + "panel.fb_feat_area": "Hvilket område?", + "panel.fb_feat_priority": "Hvor viktig er det for deg?", + "panel.fb_feat_alternatives": "Løsninger eller alternativer du har prøvd", + "panel.fb_area_child_card": "Barnekort", + "panel.fb_area_rewards_card": "Belønningskort", + "panel.fb_area_approvals_card": "Godkjenningskort", + "panel.fb_area_points_card": "Poengkort", + "panel.fb_area_reorder_card": "Omorganiseringskort", + "panel.fb_area_parent_card": "Foreldredashbord-kort", + "panel.fb_area_overview_card": "Oversiktskort", + "panel.fb_area_activity_card": "Aktivitetskort", + "panel.fb_area_streak_card": "Rekkekort", + "panel.fb_area_weekly_card": "Ukeskort", + "panel.fb_area_graph_card": "Poenggrafkort", + "panel.fb_area_progress_card": "Belønningsfremgangskort", + "panel.fb_area_leaderboard_card": "Ledertavlekort", + "panel.fb_area_setup": "Oppsett av integrasjonen", + "panel.fb_area_chores": "Administrere oppgaver", + "panel.fb_area_rewards": "Administrere belønninger", + "panel.fb_area_children": "Administrere barn", + "panel.fb_area_points": "Poeng og poenggiving", + "panel.fb_area_streaks": "Rekker og milepæler", + "panel.fb_area_reward_approval": "Godkjenne belønninger", + "panel.fb_area_chore_approval": "Godkjenne oppgaver", + "panel.fb_area_services": "Tjenester og automasjoner", + "panel.fb_area_sensors": "Sensorer og entitetsattributter", + "panel.fb_area_resources": "Kort lastes ikke", + "panel.fb_area_bonus": "Bonuspoeng", + "panel.fb_area_settings": "Innstillinger", + "panel.fb_area_notifications": "Varsler", + "panel.fb_area_install": "Installasjon / HACS", + "panel.fb_area_new_card": "Et nytt kort", + "panel.fb_area_other": "Noe annet", + "panel.fb_repro_always": "Ja, hver gang", + "panel.fb_repro_sometimes": "Noen ganger", + "panel.fb_repro_once": "Det skjedde bare én gang", + "panel.fb_priority_nice": "Kjekt å ha", + "panel.fb_priority_important": "Viktig — et hull i hverdagsbruken vår", + "panel.fb_priority_critical": "Kritisk — det hindrer oss i å bruke TaskMate ordentlig", + "panel.fb_details_heading": "Tas med automatisk", + "panel.fb_details_intro": "Disse detaljene hjelper med å finne årsaken til et problem raskt. De inneholder aldri navn, poeng eller noe personlig.", + "panel.fb_reload": "Oppdater", + "panel.fb_loading": "Henter oppsettdetaljene dine…", + "panel.fb_info_failed": "Kunne ikke lese oppsettdetaljene dine: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate-versjon", + "panel.fb_ha_version": "Home Assistant-versjon", + "panel.fb_family": "Familie", + "panel.fb_family_value": "{children} barn, {adults} voksne · {chores} oppgaver · {rewards} belønninger", + "panel.fb_browser": "Nettleser", + "panel.fb_health": "Tilstandssjekk", + "panel.fb_health_ok": "Ingen problemer", + "panel.fb_children_count": "Barn", + "panel.fb_install_type": "Home Assistant-installasjon", + "panel.fb_install_method": "TaskMate installert via", + "panel.fb_lovelace_mode": "Dashbordmodus (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (egendefinert repository)", + "panel.fb_method_manual": "Manuelt kopierte filer", + "panel.fb_lovelace_storage": "Lagring (redigert i grensesnittet)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Usikker", + "panel.fb_pick_one": "Velg…", + "panel.fb_dropdowns_hint": "Oppdaget for deg — endre dem hvis de er feil. GitHub kan ikke forhåndsvelge nedtrekkslistene sine, så velg de samme svarene der; de kopieres også inn i saksteksten.", + "panel.fb_include_details": "Ta med disse detaljene", + "panel.fb_logs_heading": "TaskMate-loggoppføringer", + "panel.fb_logs_found": "{count} nylige advarsler eller feil fra TaskMate. Navn er byttet ut med plassholdere.", + "panel.fb_logs_none": "Ingen nylige advarsler eller feil fra TaskMate i loggen.", + "panel.fb_logs_unavailable": "Home Assistant-loggen kunne ikke leses.", + "panel.fb_logs_include": "Legg disse til i saken", + "panel.fb_logs_preview": "Vis hva som tas med", + "panel.fb_copy_logs": "Kopier loggoppføringer", + "panel.fb_copied_logs": "Loggoppføringer kopiert", + "panel.fb_logs_debug_hint": "Home Assistant tar bare vare på advarsler og feil her. For hele bildet, slå på feilsøkingslogging (Innstillinger → Enheter og tjenester → TaskMate → Aktiver feilsøkingslogging), gjenskap problemet, last deretter ned loggen fra Innstillinger → System → Logger og dra den inn i saken.", + "panel.fb_logs_trimmed": "Lenken har plass til {used} loggoppføringer — de {dropped} eldste ble utelatt. Bruk Kopier som Markdown for å ta med alle.", + "panel.fb_diag_heading": "Diagnosefil", + "panel.fb_diag_hint": "En fil som beskriver TaskMate-oppsettet ditt, med navn og personlige detaljer fjernet. Dra den inn i saken på GitHub.", + "panel.fb_diag_download": "Last ned", + "panel.fb_diag_started": "Nedlasting av diagnosefil startet", + "panel.fb_diag_failed": "Kunne ikke laste ned diagnosefilen", + "panel.fb_open_github": "Åpne på GitHub", + "panel.fb_copy_markdown": "Kopier som Markdown", + "panel.fb_copied_markdown": "Rapport kopiert — lim den inn i en ny GitHub-sak", + "panel.fb_copy_failed": "Kunne ikke kopiere til utklippstavlen", + "panel.fb_search_github": "Søk i eksisterende saker", + "panel.fb_ready": "Klart — sjekk den på GitHub, og trykk deretter Submit new issue.", + "panel.fb_still_needed": "Mangler fortsatt: {fields}", + "panel.fb_too_long": "For lang for en lenke med {count} tegn.", + "panel.fb_too_long_plain": "For lang for en lenke — kort den ned, eller bruk Kopier som Markdown.", + "panel.fb_too_long_hint": "Kort ned den lengste boksen, eller bruk Kopier som Markdown og lim rapporten inn i en ny sak på GitHub.", + "panel.fb_account_hint": "Du trenger en gratis GitHub-konto. Teksten åpnes i en ny fane slik at du kan sjekke den før noe publiseres.", + "panel.fb_similar_title": "Lignende saker som allerede finnes på GitHub", + "panel.fb_similar_hint": "Hvis en av disse er ditt problem, legg heller til en kommentar eller en 👍 der.", + "panel.fb_issue_open": "Åpen", + "panel.fb_issue_closed": "Lukket", + "panel.fb_stale": "Denne siden er fra TaskMate {panel}, men {installed} er installert. Nettleseren viser en gammel kopi — oppdater før du rapporterer et problem med et kort.", + "panel.fb_refresh_now": "Oppdater nå", + "panel.fb_health_found": "Tilstandssjekken fant {count} ting å fikse. De kan forklare problemet.", + "panel.fb_health_open": "Åpne tilstandssjekk", + "panel.fb_opened": "GitHub ble åpnet i en ny fane. Når du har sendt inn saken, kan du tømme dette skjemaet.", + "panel.fb_clear": "Tøm skjema", + "panel.fb_clear_confirm": "Tømme alt du har skrevet?", + "panel.fb_other_help": "Annen hjelp", + "panel.fb_link_troubleshooting": "Feilsøkingsveiledning", + "panel.fb_link_faq": "Vanlige spørsmål", + "panel.fb_link_discussions": "Spør fellesskapet", "panel.tab_templates": "Maler", "panel.tab_today": "I dag", "panel.tab_unknown": "Ukjent fane", diff --git a/custom_components/taskmate/www/locales/nl.json b/custom_components/taskmate/www/locales/nl.json index 481e40ac..60b7431f 100644 --- a/custom_components/taskmate/www/locales/nl.json +++ b/custom_components/taskmate/www/locales/nl.json @@ -1560,6 +1560,147 @@ "panel.tab_penalties": "Puntenaftrek", "panel.tab_rewards": "Beloningen", "panel.tab_settings": "Instellingen", + "panel.tab_help": "Hulp en feedback", + "panel.fb_intro": "Een probleem gevonden of een idee? Beschrijf het hier en TaskMate opent een GitHub-issue waarin je versies en installatie al zijn ingevuld. Er wordt niets verstuurd tot je op GitHub op Submit drukt.", + "panel.fb_type_label": "Wat wil je doen?", + "panel.fb_type_bug": "Een fout melden", + "panel.fb_type_bug_hint": "Iets werkt niet zoals het hoort", + "panel.fb_type_feature": "Een functie voorstellen", + "panel.fb_type_feature_hint": "Een idee om TaskMate beter te maken", + "panel.fb_title": "Titel", + "panel.fb_bug_heading": "Wat ging er mis?", + "panel.fb_bug_title_placeholder": "bijv. Kindkaart toont 0 punten na de reset om middernacht", + "panel.fb_bug_description": "Wat gebeurde er?", + "panel.fb_bug_description_placeholder": "Beschrijf wat je zag, en waar", + "panel.fb_bug_expected": "Wat had je verwacht?", + "panel.fb_bug_steps": "Stappen om het te herhalen", + "panel.fb_bug_steps_placeholder": "1. Open…\n2. Tik op…\n3. Zie…", + "panel.fb_bug_area": "Welk deel van TaskMate?", + "panel.fb_bug_repro": "Gebeurt het elke keer?", + "panel.fb_bug_console": "Fouten in de browserconsole", + "panel.fb_bug_console_hint": "Bij problemen met een kaart: druk op F12, open het tabblad Console, herhaal het probleem en plak eventuele rode fouten.", + "panel.fb_bug_config": "YAML van de kaart", + "panel.fb_bug_config_hint": "Bij problemen met een kaart: plak de YAML van de kaart uit de dashboard-editor.", + "panel.fb_more_details": "Meer details (optioneel)", + "panel.fb_additional": "Nog iets?", + "panel.fb_additional_hint": "Schermafbeeldingen kun je op GitHub in het issue slepen.", + "panel.fb_feat_heading": "Wat is je idee?", + "panel.fb_feat_title_placeholder": "bijv. Kinderen taken met elkaar laten ruilen", + "panel.fb_feat_summary": "Samenvatting", + "panel.fb_feat_summary_placeholder": "Beschrijf de functie in een paar zinnen", + "panel.fb_feat_problem": "Welk probleem lost het op?", + "panel.fb_feat_problem_hint": "Goede ideeën beginnen bij een echt probleem in de dagelijkse gang van zaken in je gezin.", + "panel.fb_feat_solution": "Hoe zou je willen dat het werkt?", + "panel.fb_feat_area": "Welk onderdeel?", + "panel.fb_feat_priority": "Hoe belangrijk is het voor je?", + "panel.fb_feat_alternatives": "Omwegen of alternatieven die je hebt geprobeerd", + "panel.fb_area_child_card": "Kindkaart", + "panel.fb_area_rewards_card": "Beloningenkaart", + "panel.fb_area_approvals_card": "Goedkeuringskaart", + "panel.fb_area_points_card": "Puntenkaart", + "panel.fb_area_reorder_card": "Volgordekaart", + "panel.fb_area_parent_card": "Ouderbord-kaart", + "panel.fb_area_overview_card": "Overzichtskaart", + "panel.fb_area_activity_card": "Activiteitenkaart", + "panel.fb_area_streak_card": "Reekskaart", + "panel.fb_area_weekly_card": "Weekkaart", + "panel.fb_area_graph_card": "Puntengrafiek-kaart", + "panel.fb_area_progress_card": "Beloningsvoortgang-kaart", + "panel.fb_area_leaderboard_card": "Ranglijstkaart", + "panel.fb_area_setup": "De integratie instellen", + "panel.fb_area_chores": "Taken beheren", + "panel.fb_area_rewards": "Beloningen beheren", + "panel.fb_area_children": "Kinderen beheren", + "panel.fb_area_points": "Punten en scores", + "panel.fb_area_streaks": "Reeksen en mijlpalen", + "panel.fb_area_reward_approval": "Beloningen goedkeuren", + "panel.fb_area_chore_approval": "Taken goedkeuren", + "panel.fb_area_services": "Services en automatiseringen", + "panel.fb_area_sensors": "Sensoren en entiteitsattributen", + "panel.fb_area_resources": "Kaarten laden niet", + "panel.fb_area_bonus": "Bonuspunten", + "panel.fb_area_settings": "Instellingen", + "panel.fb_area_notifications": "Meldingen", + "panel.fb_area_install": "Installeren / HACS", + "panel.fb_area_new_card": "Een nieuwe kaart", + "panel.fb_area_other": "Iets anders", + "panel.fb_repro_always": "Ja, elke keer", + "panel.fb_repro_sometimes": "Soms", + "panel.fb_repro_once": "Het is maar één keer gebeurd", + "panel.fb_priority_nice": "Leuk om te hebben", + "panel.fb_priority_important": "Belangrijk — we missen het in ons dagelijks gebruik", + "panel.fb_priority_critical": "Kritiek — we kunnen TaskMate hierdoor niet goed gebruiken", + "panel.fb_details_heading": "Automatisch toegevoegd", + "panel.fb_details_intro": "Deze details helpen om een probleem snel uit te zoeken. Ze bevatten nooit namen, punten of iets persoonlijks.", + "panel.fb_reload": "Vernieuwen", + "panel.fb_loading": "Je installatiegegevens verzamelen…", + "panel.fb_info_failed": "Kon je installatiegegevens niet lezen: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate-versie", + "panel.fb_ha_version": "Home Assistant-versie", + "panel.fb_family": "Gezin", + "panel.fb_family_value": "{children} kinderen, {adults} volwassenen · {chores} taken · {rewards} beloningen", + "panel.fb_browser": "Browser", + "panel.fb_health": "Systeemcontrole", + "panel.fb_health_ok": "Geen problemen", + "panel.fb_children_count": "Kinderen", + "panel.fb_install_type": "Home Assistant-installatie", + "panel.fb_install_method": "TaskMate geïnstalleerd via", + "panel.fb_lovelace_mode": "Dashboardmodus (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (aangepaste repository)", + "panel.fb_method_manual": "Handmatig gekopieerde bestanden", + "panel.fb_lovelace_storage": "Opslag (bewerkt in de interface)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Weet ik niet", + "panel.fb_pick_one": "Kies…", + "panel.fb_dropdowns_hint": "Voor je herkend — pas ze aan als ze niet kloppen. GitHub kan de keuzelijsten niet vooraf invullen, dus kies daar dezelfde antwoorden; ze worden ook in de tekst van het issue gezet.", + "panel.fb_include_details": "Deze details meesturen", + "panel.fb_logs_heading": "TaskMate-logregels", + "panel.fb_logs_found": "{count} recente waarschuwingen of fouten van TaskMate. Namen zijn vervangen door plaatshouders.", + "panel.fb_logs_none": "Geen recente waarschuwingen of fouten van TaskMate in het logboek.", + "panel.fb_logs_unavailable": "Het logboek van Home Assistant kon niet worden gelezen.", + "panel.fb_logs_include": "Toevoegen aan het issue", + "panel.fb_logs_preview": "Laat zien wat er wordt meegestuurd", + "panel.fb_copy_logs": "Logregels kopiëren", + "panel.fb_copied_logs": "Logregels gekopieerd", + "panel.fb_logs_debug_hint": "Home Assistant bewaart hier alleen waarschuwingen en fouten. Voor het hele verhaal zet je debuglogging aan (Instellingen → Apparaten en diensten → TaskMate → Debuglogging inschakelen), herhaal je het probleem en download je daarna het logboek via Instellingen → Systeem → Logboeken om het in het issue te slepen.", + "panel.fb_logs_trimmed": "De link heeft ruimte voor {used} logregels — de {dropped} oudste zijn weggelaten. Gebruik Kopiëren als Markdown om ze allemaal mee te sturen.", + "panel.fb_diag_heading": "Diagnosebestand", + "panel.fb_diag_hint": "Een bestand dat je TaskMate-installatie beschrijft, zonder namen en persoonlijke gegevens. Sleep het op GitHub in het issue.", + "panel.fb_diag_download": "Downloaden", + "panel.fb_diag_started": "Downloaden van diagnosebestand gestart", + "panel.fb_diag_failed": "Kon het diagnosebestand niet downloaden", + "panel.fb_open_github": "Openen op GitHub", + "panel.fb_copy_markdown": "Kopiëren als Markdown", + "panel.fb_copied_markdown": "Melding gekopieerd — plak het in een nieuw GitHub-issue", + "panel.fb_copy_failed": "Kopiëren naar het klembord is mislukt", + "panel.fb_search_github": "Bestaande issues doorzoeken", + "panel.fb_ready": "Klaar — controleer het op GitHub en druk dan op Submit new issue.", + "panel.fb_still_needed": "Nog nodig: {fields}", + "panel.fb_too_long": "{count} tekens te lang voor een link.", + "panel.fb_too_long_plain": "Te lang voor een link — maak het korter of gebruik Kopiëren als Markdown.", + "panel.fb_too_long_hint": "Maak het langste vak korter, of gebruik Kopiëren als Markdown en plak de melding in een nieuw issue op GitHub.", + "panel.fb_account_hint": "Je hebt een gratis GitHub-account nodig. Je tekst opent in een nieuw tabblad, zodat je hem kunt controleren voordat er iets wordt geplaatst.", + "panel.fb_similar_title": "Vergelijkbare issues op GitHub", + "panel.fb_similar_hint": "Staat jouw probleem hiertussen? Plaats daar dan liever een reactie of een 👍.", + "panel.fb_issue_open": "Open", + "panel.fb_issue_closed": "Gesloten", + "panel.fb_stale": "Deze pagina is van TaskMate {panel}, maar {installed} is geïnstalleerd. Je browser toont een oude versie — vernieuw voordat je een probleem met een kaart meldt.", + "panel.fb_refresh_now": "Nu vernieuwen", + "panel.fb_health_found": "De systeemcontrole vond {count} ding(en) om op te lossen. Die kunnen het probleem verklaren.", + "panel.fb_health_open": "Systeemcontrole openen", + "panel.fb_opened": "GitHub is geopend in een nieuw tabblad. Als je het issue hebt ingediend, kun je dit formulier leegmaken.", + "panel.fb_clear": "Formulier leegmaken", + "panel.fb_clear_confirm": "Alles wat je hebt getypt wissen?", + "panel.fb_other_help": "Andere hulp", + "panel.fb_link_troubleshooting": "Handleiding probleemoplossing", + "panel.fb_link_faq": "Veelgestelde vragen", + "panel.fb_link_discussions": "Vraag het de community", "panel.tab_templates": "Sjablonen", "panel.tab_today": "Vandaag", "panel.tab_unknown": "Onbekend tabblad", diff --git a/custom_components/taskmate/www/locales/nn.json b/custom_components/taskmate/www/locales/nn.json index 3e6ea46c..c1b852fb 100644 --- a/custom_components/taskmate/www/locales/nn.json +++ b/custom_components/taskmate/www/locales/nn.json @@ -1496,6 +1496,147 @@ "panel.tab_penalties": "Straffar", "panel.tab_rewards": "Premiar", "panel.tab_settings": "Innstillingar", + "panel.tab_help": "Hjelp og tilbakemelding", + "panel.fb_intro": "Har du funne eit problem eller fått ein idé? Skildra det her, så opnar TaskMate ei GitHub-sak med versjonar og oppsett alt fylte ut. Ingenting vert sendt før du trykkjer Submit på GitHub.", + "panel.fb_type_label": "Kva vil du gjere?", + "panel.fb_type_bug": "Rapporter ein feil", + "panel.fb_type_bug_hint": "Noko fungerer ikkje som det skal", + "panel.fb_type_feature": "Føreslå ein funksjon", + "panel.fb_type_feature_hint": "Ein idé som kan gjere TaskMate betre", + "panel.fb_title": "Tittel", + "panel.fb_bug_heading": "Kva gjekk gale?", + "panel.fb_bug_title_placeholder": "t.d. Bornekortet viser 0 poeng etter nullstillinga ved midnatt", + "panel.fb_bug_description": "Kva skjedde?", + "panel.fb_bug_description_placeholder": "Skildra kva du såg, og kvar", + "panel.fb_bug_expected": "Kva venta du skulle skje?", + "panel.fb_bug_steps": "Slik får du fram feilen", + "panel.fb_bug_steps_placeholder": "1. Opna…\n2. Trykk på…\n3. Sjå…", + "panel.fb_bug_area": "Kva del av TaskMate?", + "panel.fb_bug_repro": "Skjer det kvar gong?", + "panel.fb_bug_console": "Feil i nettlesarkonsollen", + "panel.fb_bug_console_hint": "For problem med kort: trykk F12, opna fana Console, få fram problemet att og lim inn eventuelle raude feil.", + "panel.fb_bug_config": "Kort-YAML", + "panel.fb_bug_config_hint": "For problem med kort: lim inn YAML-en til kortet frå dashbordredigeringa.", + "panel.fb_more_details": "Fleire detaljar (valfritt)", + "panel.fb_additional": "Noko anna?", + "panel.fb_additional_hint": "Skjermbilete kan dragast inn i saka på GitHub.", + "panel.fb_feat_heading": "Kva er ideen din?", + "panel.fb_feat_title_placeholder": "t.d. La born byte oppgåver med kvarandre", + "panel.fb_feat_summary": "Samandrag", + "panel.fb_feat_summary_placeholder": "Skildra funksjonen med ei setning eller to", + "panel.fb_feat_problem": "Kva problem ville det løyse?", + "panel.fb_feat_problem_hint": "Gode idear byrjar med eit verkeleg problem i kvardagen til familien.", + "panel.fb_feat_solution": "Korleis vil du at det skal fungere?", + "panel.fb_feat_area": "Kva område?", + "panel.fb_feat_priority": "Kor viktig er det for deg?", + "panel.fb_feat_alternatives": "Løysingar eller alternativ du har prøvd", + "panel.fb_area_child_card": "Bornekort", + "panel.fb_area_rewards_card": "Premiekort", + "panel.fb_area_approvals_card": "Godkjenningskort", + "panel.fb_area_points_card": "Poengkort", + "panel.fb_area_reorder_card": "Omorganiseringskort", + "panel.fb_area_parent_card": "Foreldredashbord-kort", + "panel.fb_area_overview_card": "Oversiktskort", + "panel.fb_area_activity_card": "Aktivitetskort", + "panel.fb_area_streak_card": "Rekkjekort", + "panel.fb_area_weekly_card": "Vekekort", + "panel.fb_area_graph_card": "Poenggrafkort", + "panel.fb_area_progress_card": "Premiefremgangskort", + "panel.fb_area_leaderboard_card": "Leiartavlekort", + "panel.fb_area_setup": "Oppsett av integrasjonen", + "panel.fb_area_chores": "Handtere oppgåver", + "panel.fb_area_rewards": "Handtere premiar", + "panel.fb_area_children": "Handtere born", + "panel.fb_area_points": "Poeng og poenggjeving", + "panel.fb_area_streaks": "Rekkjer og milepælar", + "panel.fb_area_reward_approval": "Godkjenne premiar", + "panel.fb_area_chore_approval": "Godkjenne oppgåver", + "panel.fb_area_services": "Tenester og automasjonar", + "panel.fb_area_sensors": "Sensorar og entitetsattributt", + "panel.fb_area_resources": "Kort vert ikkje lasta", + "panel.fb_area_bonus": "Bonuspoeng", + "panel.fb_area_settings": "Innstillingar", + "panel.fb_area_notifications": "Varsel", + "panel.fb_area_install": "Installasjon / HACS", + "panel.fb_area_new_card": "Eit nytt kort", + "panel.fb_area_other": "Noko anna", + "panel.fb_repro_always": "Ja, kvar gong", + "panel.fb_repro_sometimes": "Av og til", + "panel.fb_repro_once": "Det skjedde berre éin gong", + "panel.fb_priority_nice": "Kjekt å ha", + "panel.fb_priority_important": "Viktig — eit hol i kvardagsbruken vår", + "panel.fb_priority_critical": "Kritisk — det hindrar oss i å bruke TaskMate skikkeleg", + "panel.fb_details_heading": "Vert teke med automatisk", + "panel.fb_details_intro": "Desse detaljane hjelper med å finne årsaka til eit problem raskt. Dei inneheld aldri namn, poeng eller noko personleg.", + "panel.fb_reload": "Oppdater", + "panel.fb_loading": "Hentar oppsettdetaljane dine…", + "panel.fb_info_failed": "Kunne ikkje lese oppsettdetaljane dine: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "TaskMate-versjon", + "panel.fb_ha_version": "Home Assistant-versjon", + "panel.fb_family": "Familie", + "panel.fb_family_value": "{children} born, {adults} vaksne · {chores} oppgåver · {rewards} premiar", + "panel.fb_browser": "Nettlesar", + "panel.fb_health": "Tilstandssjekk", + "panel.fb_health_ok": "Ingen problem", + "panel.fb_children_count": "Born", + "panel.fb_install_type": "Home Assistant-installasjon", + "panel.fb_install_method": "TaskMate installert via", + "panel.fb_lovelace_mode": "Dashbordmodus (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Container (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (eigendefinert repository)", + "panel.fb_method_manual": "Manuelt kopierte filer", + "panel.fb_lovelace_storage": "Lagring (redigert i grensesnittet)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Usikker", + "panel.fb_pick_one": "Vel…", + "panel.fb_dropdowns_hint": "Oppdaga for deg — endra dei viss dei er feil. GitHub kan ikkje førehandsvelje nedtrekkslistene sine, så vel dei same svara der; dei vert òg kopierte inn i saksteksten.", + "panel.fb_include_details": "Ta med desse detaljane", + "panel.fb_logs_heading": "TaskMate-loggoppføringar", + "panel.fb_logs_found": "{count} nylege åtvaringar eller feil frå TaskMate. Namn er bytte ut med plasshaldarar.", + "panel.fb_logs_none": "Ingen nylege åtvaringar eller feil frå TaskMate i loggen.", + "panel.fb_logs_unavailable": "Home Assistant-loggen kunne ikkje lesast.", + "panel.fb_logs_include": "Legg desse til i saka", + "panel.fb_logs_preview": "Vis kva som vert teke med", + "panel.fb_copy_logs": "Kopier loggoppføringar", + "panel.fb_copied_logs": "Loggoppføringar kopierte", + "panel.fb_logs_debug_hint": "Home Assistant tek berre vare på åtvaringar og feil her. For heile biletet, slå på feilsøkingslogging (Innstillingar → Einingar og tenester → TaskMate → Slå på feilsøkingslogging), få fram problemet att, last så ned loggen frå Innstillingar → System → Loggar og dra han inn i saka.", + "panel.fb_logs_trimmed": "Lenkja har plass til {used} loggoppføringar — dei {dropped} eldste vart utelatne. Bruk Kopier som Markdown for å ta med alle.", + "panel.fb_diag_heading": "Diagnosefil", + "panel.fb_diag_hint": "Ei fil som skildrar TaskMate-oppsettet ditt, med namn og personlege detaljar fjerna. Dra ho inn i saka på GitHub.", + "panel.fb_diag_download": "Last ned", + "panel.fb_diag_started": "Nedlasting av diagnosefil starta", + "panel.fb_diag_failed": "Kunne ikkje laste ned diagnosefila", + "panel.fb_open_github": "Opna på GitHub", + "panel.fb_copy_markdown": "Kopier som Markdown", + "panel.fb_copied_markdown": "Rapport kopiert — lim han inn i ei ny GitHub-sak", + "panel.fb_copy_failed": "Kunne ikkje kopiere til utklippstavla", + "panel.fb_search_github": "Søk i eksisterande saker", + "panel.fb_ready": "Klart — sjekk ho på GitHub, og trykk så Submit new issue.", + "panel.fb_still_needed": "Manglar framleis: {fields}", + "panel.fb_too_long": "For lang for ei lenkje med {count} teikn.", + "panel.fb_too_long_plain": "For lang for ei lenkje — kort ho ned, eller bruk Kopier som Markdown.", + "panel.fb_too_long_hint": "Kort ned den lengste boksen, eller bruk Kopier som Markdown og lim rapporten inn i ei ny sak på GitHub.", + "panel.fb_account_hint": "Du treng ein gratis GitHub-konto. Teksten vert opna i ei ny fane, så du kan sjekke han før noko vert publisert.", + "panel.fb_similar_title": "Liknande saker som alt finst på GitHub", + "panel.fb_similar_hint": "Viss ei av desse er problemet ditt, legg heller til ein kommentar eller ein 👍 der.", + "panel.fb_issue_open": "Open", + "panel.fb_issue_closed": "Lukka", + "panel.fb_stale": "Denne sida er frå TaskMate {panel}, men {installed} er installert. Nettlesaren viser ein gammal kopi — oppdater før du rapporterer eit problem med eit kort.", + "panel.fb_refresh_now": "Oppdater no", + "panel.fb_health_found": "Tilstandssjekken fann {count} ting å fikse. Dei kan forklare problemet.", + "panel.fb_health_open": "Opna tilstandssjekk", + "panel.fb_opened": "GitHub vart opna i ei ny fane. Når du har sendt inn saka, kan du tømme dette skjemaet.", + "panel.fb_clear": "Tøm skjema", + "panel.fb_clear_confirm": "Tømme alt du har skrive?", + "panel.fb_other_help": "Anna hjelp", + "panel.fb_link_troubleshooting": "Feilsøkingsrettleiing", + "panel.fb_link_faq": "Vanlege spørsmål", + "panel.fb_link_discussions": "Spør fellesskapet", "panel.tab_templates": "Malar", "panel.tab_today": "I dag", "panel.tab_unknown": "Ukjend fane", diff --git a/custom_components/taskmate/www/locales/pl.json b/custom_components/taskmate/www/locales/pl.json index 723f144a..d21d6d1d 100644 --- a/custom_components/taskmate/www/locales/pl.json +++ b/custom_components/taskmate/www/locales/pl.json @@ -1560,6 +1560,147 @@ "panel.tab_penalties": "Kary", "panel.tab_rewards": "Nagrody", "panel.tab_settings": "Ustawienia", + "panel.tab_help": "Pomoc i opinie", + "panel.fb_intro": "Masz problem albo pomysł? Opisz go tutaj, a TaskMate otworzy zgłoszenie na GitHubie z już uzupełnionymi wersjami i konfiguracją. Nic nie zostanie wysłane, dopóki nie klikniesz Submit na GitHubie.", + "panel.fb_type_label": "Co chcesz zrobić?", + "panel.fb_type_bug": "Zgłoś błąd", + "panel.fb_type_bug_hint": "Coś nie działa tak, jak powinno", + "panel.fb_type_feature": "Zaproponuj funkcję", + "panel.fb_type_feature_hint": "Pomysł na ulepszenie TaskMate", + "panel.fb_title": "Tytuł", + "panel.fb_bug_heading": "Co poszło nie tak?", + "panel.fb_bug_title_placeholder": "np. Karta dziecka pokazuje 0 punktów po resecie o północy", + "panel.fb_bug_description": "Co się stało?", + "panel.fb_bug_description_placeholder": "Opisz, co widzisz i gdzie", + "panel.fb_bug_expected": "Co powinno się wydarzyć?", + "panel.fb_bug_steps": "Kroki do odtworzenia", + "panel.fb_bug_steps_placeholder": "1. Otwórz…\n2. Dotknij…\n3. Zobacz…", + "panel.fb_bug_area": "Której części TaskMate to dotyczy?", + "panel.fb_bug_repro": "Czy dzieje się to za każdym razem?", + "panel.fb_bug_console": "Błędy z konsoli przeglądarki", + "panel.fb_bug_console_hint": "Przy problemach z kartami: naciśnij F12, otwórz kartę Console, odtwórz problem i wklej wszystkie czerwone błędy.", + "panel.fb_bug_config": "YAML karty", + "panel.fb_bug_config_hint": "Przy problemach z kartami: wklej YAML karty z edytora pulpitu.", + "panel.fb_more_details": "Więcej szczegółów (opcjonalnie)", + "panel.fb_additional": "Coś jeszcze?", + "panel.fb_additional_hint": "Zrzuty ekranu można przeciągnąć na zgłoszenie na GitHubie.", + "panel.fb_feat_heading": "Jaki masz pomysł?", + "panel.fb_feat_title_placeholder": "np. Pozwól dzieciom zamieniać się obowiązkami", + "panel.fb_feat_summary": "Podsumowanie", + "panel.fb_feat_summary_placeholder": "Opisz funkcję w jednym lub dwóch zdaniach", + "panel.fb_feat_problem": "Jaki problem by rozwiązała?", + "panel.fb_feat_problem_hint": "Dobre pomysły zaczynają się od prawdziwego problemu w rodzinnej codzienności.", + "panel.fb_feat_solution": "Jak miałoby to działać?", + "panel.fb_feat_area": "Jakiego obszaru dotyczy?", + "panel.fb_feat_priority": "Jak ważne jest to dla Ciebie?", + "panel.fb_feat_alternatives": "Obejścia lub alternatywy, które już próbowano", + "panel.fb_area_child_card": "Karta dziecka", + "panel.fb_area_rewards_card": "Karta nagród", + "panel.fb_area_approvals_card": "Karta zatwierdzeń", + "panel.fb_area_points_card": "Karta punktów", + "panel.fb_area_reorder_card": "Karta kolejności obowiązków", + "panel.fb_area_parent_card": "Karta pulpitu rodzica", + "panel.fb_area_overview_card": "Karta przeglądu", + "panel.fb_area_activity_card": "Karta aktywności", + "panel.fb_area_streak_card": "Karta serii", + "panel.fb_area_weekly_card": "Karta tygodnia", + "panel.fb_area_graph_card": "Karta wykresu punktów", + "panel.fb_area_progress_card": "Karta postępu nagrody", + "panel.fb_area_leaderboard_card": "Karta rankingu", + "panel.fb_area_setup": "Konfiguracja integracji", + "panel.fb_area_chores": "Zarządzanie obowiązkami", + "panel.fb_area_rewards": "Zarządzanie nagrodami", + "panel.fb_area_children": "Zarządzanie dziećmi", + "panel.fb_area_points": "Punkty i punktacja", + "panel.fb_area_streaks": "Serie i kamienie milowe", + "panel.fb_area_reward_approval": "Zatwierdzanie nagród", + "panel.fb_area_chore_approval": "Zatwierdzanie obowiązków", + "panel.fb_area_services": "Usługi i automatyzacje", + "panel.fb_area_sensors": "Sensory i atrybuty encji", + "panel.fb_area_resources": "Karty się nie ładują", + "panel.fb_area_bonus": "Punkty premiowe", + "panel.fb_area_settings": "Ustawienia", + "panel.fb_area_notifications": "Powiadomienia", + "panel.fb_area_install": "Instalacja / HACS", + "panel.fb_area_new_card": "Nowa karta", + "panel.fb_area_other": "Coś innego", + "panel.fb_repro_always": "Tak, za każdym razem", + "panel.fb_repro_sometimes": "Czasami", + "panel.fb_repro_once": "Zdarzyło się tylko raz", + "panel.fb_priority_nice": "Miło by było", + "panel.fb_priority_important": "Ważne — brakuje tego na co dzień", + "panel.fb_priority_critical": "Krytyczne — utrudnia normalne korzystanie z TaskMate", + "panel.fb_details_heading": "Dołączane automatycznie", + "panel.fb_details_intro": "Te informacje pomagają szybko dotrzeć do źródła problemu. Nigdy nie zawierają imion, punktów ani danych osobowych.", + "panel.fb_reload": "Odśwież", + "panel.fb_loading": "Zbieranie informacji o konfiguracji…", + "panel.fb_info_failed": "Nie udało się odczytać informacji o konfiguracji: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "Wersja TaskMate", + "panel.fb_ha_version": "Wersja Home Assistant", + "panel.fb_family": "Rodzina", + "panel.fb_family_value": "Dzieci: {children}, dorośli: {adults} · obowiązki: {chores} · nagrody: {rewards}", + "panel.fb_browser": "Przeglądarka", + "panel.fb_health": "Stan", + "panel.fb_health_ok": "Brak problemów", + "panel.fb_children_count": "Dzieci", + "panel.fb_install_type": "Instalacja Home Assistant", + "panel.fb_install_method": "Sposób instalacji TaskMate", + "panel.fb_lovelace_mode": "Tryb pulpitu (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Kontener (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (własne repozytorium)", + "panel.fb_method_manual": "Ręcznie skopiowane pliki", + "panel.fb_lovelace_storage": "Storage (edycja w interfejsie)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Nie wiem", + "panel.fb_pick_one": "Wybierz…", + "panel.fb_dropdowns_hint": "Wykryte automatycznie — popraw, jeśli są błędne. GitHub nie potrafi wstępnie zaznaczyć swoich list rozwijanych, więc wybierz tam te same odpowiedzi; są też skopiowane do treści zgłoszenia.", + "panel.fb_include_details": "Dołącz te informacje", + "panel.fb_logs_heading": "Wpisy dziennika TaskMate", + "panel.fb_logs_found": "Ostatnie ostrzeżenia lub błędy TaskMate: {count}. Imiona zastąpiono symbolami zastępczymi.", + "panel.fb_logs_none": "Brak ostatnich ostrzeżeń ani błędów TaskMate w dzienniku.", + "panel.fb_logs_unavailable": "Nie udało się odczytać dziennika Home Assistant.", + "panel.fb_logs_include": "Dodaj je do zgłoszenia", + "panel.fb_logs_preview": "Pokaż, co zostanie dołączone", + "panel.fb_copy_logs": "Kopiuj wpisy dziennika", + "panel.fb_copied_logs": "Skopiowano wpisy dziennika", + "panel.fb_logs_debug_hint": "Home Assistant przechowuje tu tylko ostrzeżenia i błędy. Aby uzyskać pełny obraz, włącz debugowanie (Ustawienia → Urządzenia oraz usługi → TaskMate → Włącz rejestrowanie debugowania), odtwórz problem, a następnie pobierz dziennik z Ustawienia → System → Logi i przeciągnij go na zgłoszenie.", + "panel.fb_logs_trimmed": "Link mieści wpisy dziennika: {used} — pominięto najstarsze: {dropped}. Użyj Kopiuj jako Markdown, aby dołączyć wszystkie.", + "panel.fb_diag_heading": "Plik diagnostyczny", + "panel.fb_diag_hint": "Plik opisujący konfigurację TaskMate, bez imion i danych osobowych. Przeciągnij go na zgłoszenie na GitHubie.", + "panel.fb_diag_download": "Pobierz", + "panel.fb_diag_started": "Rozpoczęto pobieranie pliku diagnostycznego", + "panel.fb_diag_failed": "Nie udało się pobrać pliku diagnostycznego", + "panel.fb_open_github": "Otwórz na GitHubie", + "panel.fb_copy_markdown": "Kopiuj jako Markdown", + "panel.fb_copied_markdown": "Zgłoszenie skopiowane — wklej je do nowego zgłoszenia na GitHubie", + "panel.fb_copy_failed": "Nie udało się skopiować do schowka", + "panel.fb_search_github": "Szukaj istniejących zgłoszeń", + "panel.fb_ready": "Gotowe — sprawdź wszystko na GitHubie, a potem kliknij Submit new issue.", + "panel.fb_still_needed": "Jeszcze potrzebne: {fields}", + "panel.fb_too_long": "Za długie na link (nadmiar znaków: {count}).", + "panel.fb_too_long_plain": "Za długie na link — skróć tekst albo użyj Kopiuj jako Markdown.", + "panel.fb_too_long_hint": "Skróć najdłuższe pole albo użyj Kopiuj jako Markdown i wklej raport do nowego zgłoszenia na GitHubie.", + "panel.fb_account_hint": "Potrzebne jest darmowe konto GitHub. Tekst otworzy się w nowej karcie, aby można go było sprawdzić przed publikacją.", + "panel.fb_similar_title": "Podobne zgłoszenia na GitHubie", + "panel.fb_similar_hint": "Jeśli któreś z nich dotyczy Twojego problemu, dodaj tam komentarz albo 👍.", + "panel.fb_issue_open": "Otwarte", + "panel.fb_issue_closed": "Zamknięte", + "panel.fb_stale": "Ta strona pochodzi z TaskMate {panel}, ale zainstalowana jest wersja {installed}. Przeglądarka pokazuje starą kopię — odśwież ją przed zgłoszeniem problemu z kartą.", + "panel.fb_refresh_now": "Odśwież teraz", + "panel.fb_health_found": "Sprawdzenie stanu wykryło sprawy do naprawy: {count}. Mogą one wyjaśniać problem.", + "panel.fb_health_open": "Otwórz stan", + "panel.fb_opened": "GitHub otworzył się w nowej karcie. Po wysłaniu zgłoszenia możesz wyczyścić ten formularz.", + "panel.fb_clear": "Wyczyść formularz", + "panel.fb_clear_confirm": "Wyczyścić wszystko, co zostało wpisane?", + "panel.fb_other_help": "Inna pomoc", + "panel.fb_link_troubleshooting": "Rozwiązywanie problemów", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Zapytaj społeczność", "panel.tab_templates": "Szablony", "panel.tab_today": "Dziś", "panel.tab_unknown": "Nieznana zakładka", diff --git a/custom_components/taskmate/www/locales/pt-BR.json b/custom_components/taskmate/www/locales/pt-BR.json index f83485ac..d3105aea 100644 --- a/custom_components/taskmate/www/locales/pt-BR.json +++ b/custom_components/taskmate/www/locales/pt-BR.json @@ -1496,6 +1496,147 @@ "panel.tab_penalties": "Penalidades", "panel.tab_rewards": "Recompensas", "panel.tab_settings": "Configurações", + "panel.tab_help": "Ajuda e feedback", + "panel.fb_intro": "Encontrou um problema ou tem uma ideia? Descreva aqui e o TaskMate abre uma issue no GitHub com suas versões e configuração já preenchidas. Nada é enviado até você clicar em Submit no GitHub.", + "panel.fb_type_label": "O que você gostaria de fazer?", + "panel.fb_type_bug": "Relatar um bug", + "panel.fb_type_bug_hint": "Algo não está funcionando como deveria", + "panel.fb_type_feature": "Sugerir um recurso", + "panel.fb_type_feature_hint": "Uma ideia para deixar o TaskMate melhor", + "panel.fb_title": "Título", + "panel.fb_bug_heading": "O que deu errado?", + "panel.fb_bug_title_placeholder": "ex: O card da criança mostra 0 pontos depois do reinício da meia-noite", + "panel.fb_bug_description": "O que aconteceu?", + "panel.fb_bug_description_placeholder": "Descreva o que você viu e onde", + "panel.fb_bug_expected": "O que você esperava que acontecesse?", + "panel.fb_bug_steps": "Passos para reproduzir", + "panel.fb_bug_steps_placeholder": "1. Abra…\n2. Toque em…\n3. Veja…", + "panel.fb_bug_area": "Qual parte do TaskMate?", + "panel.fb_bug_repro": "Acontece toda vez?", + "panel.fb_bug_console": "Erros do console do navegador", + "panel.fb_bug_console_hint": "Para problemas em cards: pressione F12, abra a aba Console, reproduza o problema e cole os erros em vermelho.", + "panel.fb_bug_config": "YAML do card", + "panel.fb_bug_config_hint": "Para problemas em cards: cole o YAML do card copiado do editor do painel.", + "panel.fb_more_details": "Mais detalhes (opcional)", + "panel.fb_additional": "Mais alguma coisa?", + "panel.fb_additional_hint": "Você pode arrastar capturas de tela para a issue no GitHub.", + "panel.fb_feat_heading": "Qual é a sua ideia?", + "panel.fb_feat_title_placeholder": "ex: Deixar as crianças trocarem tarefas entre si", + "panel.fb_feat_summary": "Resumo", + "panel.fb_feat_summary_placeholder": "Descreva o recurso em uma ou duas frases", + "panel.fb_feat_problem": "Que problema ele resolveria?", + "panel.fb_feat_problem_hint": "Boas ideias começam com um problema real na rotina da família.", + "panel.fb_feat_solution": "Como você gostaria que funcionasse?", + "panel.fb_feat_area": "Qual área?", + "panel.fb_feat_priority": "Qual a importância para você?", + "panel.fb_feat_alternatives": "Soluções alternativas que você já tentou", + "panel.fb_area_child_card": "Card da criança", + "panel.fb_area_rewards_card": "Card de recompensas", + "panel.fb_area_approvals_card": "Card de aprovações", + "panel.fb_area_points_card": "Card de pontos", + "panel.fb_area_reorder_card": "Card de reordenação", + "panel.fb_area_parent_card": "Card do painel dos pais", + "panel.fb_area_overview_card": "Card de visão geral", + "panel.fb_area_activity_card": "Card de atividade", + "panel.fb_area_streak_card": "Card de sequências", + "panel.fb_area_weekly_card": "Card semanal", + "panel.fb_area_graph_card": "Card do gráfico de pontos", + "panel.fb_area_progress_card": "Card de progresso da recompensa", + "panel.fb_area_leaderboard_card": "Card de classificação", + "panel.fb_area_setup": "Configurar a integração", + "panel.fb_area_chores": "Gerenciar tarefas", + "panel.fb_area_rewards": "Gerenciar recompensas", + "panel.fb_area_children": "Gerenciar crianças", + "panel.fb_area_points": "Pontos e pontuação", + "panel.fb_area_streaks": "Sequências e marcos", + "panel.fb_area_reward_approval": "Aprovar recompensas", + "panel.fb_area_chore_approval": "Aprovar tarefas", + "panel.fb_area_services": "Serviços e automações", + "panel.fb_area_sensors": "Sensores e atributos de entidades", + "panel.fb_area_resources": "Cards não carregam", + "panel.fb_area_bonus": "Pontos de bônus", + "panel.fb_area_settings": "Configurações", + "panel.fb_area_notifications": "Notificações", + "panel.fb_area_install": "Instalação / HACS", + "panel.fb_area_new_card": "Um card novo", + "panel.fb_area_other": "Outra coisa", + "panel.fb_repro_always": "Sim, toda vez", + "panel.fb_repro_sometimes": "Às vezes", + "panel.fb_repro_once": "Só aconteceu uma vez", + "panel.fb_priority_nice": "Seria legal ter", + "panel.fb_priority_important": "Importante — faz falta no nosso dia a dia", + "panel.fb_priority_critical": "Crítico — nos impede de usar o TaskMate direito", + "panel.fb_details_heading": "Incluído automaticamente", + "panel.fb_details_intro": "Esses detalhes ajudam a chegar rápido à causa de um problema. Eles nunca incluem nomes, pontos nem nada pessoal.", + "panel.fb_reload": "Atualizar", + "panel.fb_loading": "Coletando os detalhes da sua configuração…", + "panel.fb_info_failed": "Não foi possível ler os detalhes da configuração: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "Versão do TaskMate", + "panel.fb_ha_version": "Versão do Home Assistant", + "panel.fb_family": "Família", + "panel.fb_family_value": "Crianças: {children}, adultos: {adults} · tarefas: {chores} · recompensas: {rewards}", + "panel.fb_browser": "Navegador", + "panel.fb_health": "Saúde", + "panel.fb_health_ok": "Nenhum problema", + "panel.fb_children_count": "Crianças", + "panel.fb_install_type": "Instalação do Home Assistant", + "panel.fb_install_method": "TaskMate instalado via", + "panel.fb_lovelace_mode": "Modo do painel (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Contêiner (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (repositório personalizado)", + "panel.fb_method_manual": "Arquivos copiados manualmente", + "panel.fb_lovelace_storage": "Storage (editado pela interface)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Não tenho certeza", + "panel.fb_pick_one": "Escolha…", + "panel.fb_dropdowns_hint": "Detectado para você — altere se estiver errado. O GitHub não consegue pré-selecionar as listas suspensas dele, então escolha as mesmas respostas lá; elas também são copiadas para o texto da issue.", + "panel.fb_include_details": "Incluir estes detalhes", + "panel.fb_logs_heading": "Entradas de log do TaskMate", + "panel.fb_logs_found": "Avisos ou erros recentes do TaskMate: {count}. Os nomes são substituídos por marcadores.", + "panel.fb_logs_none": "Nenhum aviso ou erro recente do TaskMate no log.", + "panel.fb_logs_unavailable": "Não foi possível ler o log do Home Assistant.", + "panel.fb_logs_include": "Adicionar à issue", + "panel.fb_logs_preview": "Mostrar o que será incluído", + "panel.fb_copy_logs": "Copiar entradas de log", + "panel.fb_copied_logs": "Entradas de log copiadas", + "panel.fb_logs_debug_hint": "O Home Assistant só guarda avisos e erros aqui. Para ver tudo, ative o log de depuração (Configurações → Dispositivos e serviços → TaskMate → Ativar log de depuração), reproduza o problema e depois baixe o log em Configurações → Sistema → Logs e arraste-o para a issue.", + "panel.fb_logs_trimmed": "O link só comporta {used} entradas de log — as {dropped} mais antigas ficaram de fora. Use Copiar como Markdown para incluir todas.", + "panel.fb_diag_heading": "Arquivo de diagnóstico", + "panel.fb_diag_hint": "Um arquivo que descreve sua configuração do TaskMate, sem nomes nem dados pessoais. Arraste-o para a issue no GitHub.", + "panel.fb_diag_download": "Baixar", + "panel.fb_diag_started": "Download do diagnóstico iniciado", + "panel.fb_diag_failed": "Não foi possível baixar o arquivo de diagnóstico", + "panel.fb_open_github": "Abrir no GitHub", + "panel.fb_copy_markdown": "Copiar como Markdown", + "panel.fb_copied_markdown": "Relatório copiado — cole em uma nova issue no GitHub", + "panel.fb_copy_failed": "Não foi possível copiar para a área de transferência", + "panel.fb_search_github": "Buscar issues existentes", + "panel.fb_ready": "Pronto — confira no GitHub e depois clique em Submit new issue.", + "panel.fb_still_needed": "Ainda falta: {fields}", + "panel.fb_too_long": "Longo demais para um link ({count} caracteres a mais).", + "panel.fb_too_long_plain": "Longo demais para um link — encurte ou use Copiar como Markdown.", + "panel.fb_too_long_hint": "Encurte o campo mais longo ou use Copiar como Markdown e cole o relatório em uma nova issue no GitHub.", + "panel.fb_account_hint": "Você vai precisar de uma conta gratuita no GitHub. Seu texto abre em uma nova aba para você conferir antes de qualquer publicação.", + "panel.fb_similar_title": "Issues parecidas já no GitHub", + "panel.fb_similar_hint": "Se uma delas for o seu problema, deixe um comentário ou um 👍 lá.", + "panel.fb_issue_open": "Aberta", + "panel.fb_issue_closed": "Fechada", + "panel.fb_stale": "Esta página é do TaskMate {panel}, mas a versão instalada é {installed}. Seu navegador está mostrando uma cópia antiga — atualize antes de relatar um problema em um card.", + "panel.fb_refresh_now": "Atualizar agora", + "panel.fb_health_found": "A verificação de saúde encontrou {count} coisa(s) para corrigir. Elas podem explicar o problema.", + "panel.fb_health_open": "Abrir verificação de saúde", + "panel.fb_opened": "O GitHub abriu em uma nova aba. Depois de enviar a issue, você pode limpar este formulário.", + "panel.fb_clear": "Limpar formulário", + "panel.fb_clear_confirm": "Limpar tudo o que você digitou?", + "panel.fb_other_help": "Outras ajudas", + "panel.fb_link_troubleshooting": "Guia de solução de problemas", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Pergunte à comunidade", "panel.tab_templates": "Modelos", "panel.tab_today": "Hoje", "panel.tab_unknown": "Aba desconhecida", diff --git a/custom_components/taskmate/www/locales/pt.json b/custom_components/taskmate/www/locales/pt.json index c37619dd..ce1c3f3e 100644 --- a/custom_components/taskmate/www/locales/pt.json +++ b/custom_components/taskmate/www/locales/pt.json @@ -1501,6 +1501,147 @@ "panel.tab_penalties": "Penalizações", "panel.tab_rewards": "Recompensas", "panel.tab_settings": "Definições", + "panel.tab_help": "Ajuda e sugestões", + "panel.fb_intro": "Encontrou um problema ou tem uma ideia? Descreva-o aqui e o TaskMate abre um issue no GitHub com as suas versões e configuração já preenchidas. Nada é enviado até carregar em Submit no GitHub.", + "panel.fb_type_label": "O que pretende fazer?", + "panel.fb_type_bug": "Reportar um erro", + "panel.fb_type_bug_hint": "Algo não está a funcionar como devia", + "panel.fb_type_feature": "Sugerir uma funcionalidade", + "panel.fb_type_feature_hint": "Uma ideia para melhorar o TaskMate", + "panel.fb_title": "Título", + "panel.fb_bug_heading": "O que correu mal?", + "panel.fb_bug_title_placeholder": "p. ex. O cartão da criança mostra 0 pontos após o reinício da meia-noite", + "panel.fb_bug_description": "O que aconteceu?", + "panel.fb_bug_description_placeholder": "Descreva o que viu e onde", + "panel.fb_bug_expected": "O que esperava que acontecesse?", + "panel.fb_bug_steps": "Passos para reproduzir", + "panel.fb_bug_steps_placeholder": "1. Abra…\n2. Toque em…\n3. Veja…", + "panel.fb_bug_area": "Que parte do TaskMate?", + "panel.fb_bug_repro": "Acontece sempre?", + "panel.fb_bug_console": "Erros da consola do navegador", + "panel.fb_bug_console_hint": "Para problemas com cartões: prima F12, abra o separador Console, reproduza o problema e cole os erros a vermelho.", + "panel.fb_bug_config": "YAML do cartão", + "panel.fb_bug_config_hint": "Para problemas com cartões: cole o YAML do cartão a partir do editor do painel.", + "panel.fb_more_details": "Mais detalhes (opcional)", + "panel.fb_additional": "Mais alguma coisa?", + "panel.fb_additional_hint": "Pode arrastar capturas de ecrã para o issue no GitHub.", + "panel.fb_feat_heading": "Qual é a sua ideia?", + "panel.fb_feat_title_placeholder": "p. ex. Permitir que as crianças troquem tarefas entre si", + "panel.fb_feat_summary": "Resumo", + "panel.fb_feat_summary_placeholder": "Descreva a funcionalidade numa ou duas frases", + "panel.fb_feat_problem": "Que problema resolveria?", + "panel.fb_feat_problem_hint": "As boas ideias começam com um problema real na rotina da família.", + "panel.fb_feat_solution": "Como gostaria que funcionasse?", + "panel.fb_feat_area": "Que área?", + "panel.fb_feat_priority": "Qual a importância para si?", + "panel.fb_feat_alternatives": "Soluções alternativas que já experimentou", + "panel.fb_area_child_card": "Cartão da criança", + "panel.fb_area_rewards_card": "Cartão de recompensas", + "panel.fb_area_approvals_card": "Cartão de aprovações", + "panel.fb_area_points_card": "Cartão de pontos", + "panel.fb_area_reorder_card": "Cartão de reordenação", + "panel.fb_area_parent_card": "Cartão do painel dos pais", + "panel.fb_area_overview_card": "Cartão de visão geral", + "panel.fb_area_activity_card": "Cartão de atividade", + "panel.fb_area_streak_card": "Cartão de sequências", + "panel.fb_area_weekly_card": "Cartão semanal", + "panel.fb_area_graph_card": "Cartão do gráfico de pontos", + "panel.fb_area_progress_card": "Cartão de progresso da recompensa", + "panel.fb_area_leaderboard_card": "Cartão de classificação", + "panel.fb_area_setup": "Configurar a integração", + "panel.fb_area_chores": "Gerir tarefas", + "panel.fb_area_rewards": "Gerir recompensas", + "panel.fb_area_children": "Gerir crianças", + "panel.fb_area_points": "Pontos e pontuação", + "panel.fb_area_streaks": "Sequências e marcos", + "panel.fb_area_reward_approval": "Aprovar recompensas", + "panel.fb_area_chore_approval": "Aprovar tarefas", + "panel.fb_area_services": "Serviços e automações", + "panel.fb_area_sensors": "Sensores e atributos de entidades", + "panel.fb_area_resources": "Cartões não carregam", + "panel.fb_area_bonus": "Pontos de bónus", + "panel.fb_area_settings": "Definições", + "panel.fb_area_notifications": "Notificações", + "panel.fb_area_install": "Instalação / HACS", + "panel.fb_area_new_card": "Um cartão novo", + "panel.fb_area_other": "Outra coisa", + "panel.fb_repro_always": "Sim, sempre", + "panel.fb_repro_sometimes": "Às vezes", + "panel.fb_repro_once": "Só aconteceu uma vez", + "panel.fb_priority_nice": "Seria bom ter", + "panel.fb_priority_important": "Importante — falta no nosso dia a dia", + "panel.fb_priority_critical": "Crítico — impede-nos de usar bem o TaskMate", + "panel.fb_details_heading": "Incluído automaticamente", + "panel.fb_details_intro": "Estes detalhes ajudam a chegar rapidamente à origem de um problema. Nunca incluem nomes, pontos nem nada pessoal.", + "panel.fb_reload": "Atualizar", + "panel.fb_loading": "A recolher os detalhes da sua configuração…", + "panel.fb_info_failed": "Não foi possível ler os detalhes da configuração: {error}", + "panel.fb_auto": "auto", + "panel.fb_taskmate_version": "Versão do TaskMate", + "panel.fb_ha_version": "Versão do Home Assistant", + "panel.fb_family": "Família", + "panel.fb_family_value": "Crianças: {children}, adultos: {adults} · tarefas: {chores} · recompensas: {rewards}", + "panel.fb_browser": "Navegador", + "panel.fb_health": "Estado", + "panel.fb_health_ok": "Sem problemas", + "panel.fb_children_count": "Crianças", + "panel.fb_install_type": "Instalação do Home Assistant", + "panel.fb_install_method": "TaskMate instalado via", + "panel.fb_lovelace_mode": "Modo do painel (Lovelace)", + "panel.fb_install_haos": "Home Assistant OS", + "panel.fb_install_supervised": "Supervised", + "panel.fb_install_container": "Contentor (Docker)", + "panel.fb_install_core": "Core", + "panel.fb_method_hacs_default": "HACS", + "panel.fb_method_hacs_custom": "HACS (repositório personalizado)", + "panel.fb_method_manual": "Ficheiros copiados manualmente", + "panel.fb_lovelace_storage": "Storage (editado na interface)", + "panel.fb_lovelace_yaml": "YAML", + "panel.fb_not_sure": "Não sei", + "panel.fb_pick_one": "Escolha…", + "panel.fb_dropdowns_hint": "Detetado automaticamente — altere se estiver errado. O GitHub não consegue pré-selecionar as suas listas pendentes, por isso escolha lá as mesmas respostas; também são copiadas para o texto do issue.", + "panel.fb_include_details": "Incluir estes detalhes", + "panel.fb_logs_heading": "Entradas de registo do TaskMate", + "panel.fb_logs_found": "Avisos ou erros recentes do TaskMate: {count}. Os nomes são substituídos por marcadores.", + "panel.fb_logs_none": "Sem avisos ou erros recentes do TaskMate no registo.", + "panel.fb_logs_unavailable": "Não foi possível ler o registo do Home Assistant.", + "panel.fb_logs_include": "Adicionar ao issue", + "panel.fb_logs_preview": "Mostrar o que será incluído", + "panel.fb_copy_logs": "Copiar entradas de registo", + "panel.fb_copied_logs": "Entradas de registo copiadas", + "panel.fb_logs_debug_hint": "O Home Assistant só guarda aqui avisos e erros. Para a história completa, ative o registo de depuração (Definições → Dispositivos e serviços → TaskMate → Ativar registo de depuração), reproduza o problema e depois transfira o registo em Definições → Sistema → Registos e arraste-o para o issue.", + "panel.fb_logs_trimmed": "A ligação só tem espaço para {used} entradas de registo — as {dropped} mais antigas ficaram de fora. Use Copiar como Markdown para incluir todas.", + "panel.fb_diag_heading": "Ficheiro de diagnóstico", + "panel.fb_diag_hint": "Um ficheiro que descreve a sua configuração do TaskMate, sem nomes nem dados pessoais. Arraste-o para o issue no GitHub.", + "panel.fb_diag_download": "Transferir", + "panel.fb_diag_started": "Transferência do diagnóstico iniciada", + "panel.fb_diag_failed": "Não foi possível transferir o ficheiro de diagnóstico", + "panel.fb_open_github": "Abrir no GitHub", + "panel.fb_copy_markdown": "Copiar como Markdown", + "panel.fb_copied_markdown": "Relatório copiado — cole-o num novo issue no GitHub", + "panel.fb_copy_failed": "Não foi possível copiar para a área de transferência", + "panel.fb_search_github": "Pesquisar issues existentes", + "panel.fb_ready": "Pronto — verifique no GitHub e depois carregue em Submit new issue.", + "panel.fb_still_needed": "Ainda falta: {fields}", + "panel.fb_too_long": "Demasiado longo para uma ligação ({count} caracteres a mais).", + "panel.fb_too_long_plain": "Demasiado longo para uma ligação — encurte-o ou use Copiar como Markdown.", + "panel.fb_too_long_hint": "Encurte o campo mais longo ou use Copiar como Markdown e cole o relatório num novo issue no GitHub.", + "panel.fb_account_hint": "Precisa de uma conta gratuita do GitHub. O seu texto abre num novo separador para o verificar antes de ser publicado.", + "panel.fb_similar_title": "Issues semelhantes já no GitHub", + "panel.fb_similar_hint": "Se um destes for o seu problema, deixe antes um comentário ou um 👍 aí.", + "panel.fb_issue_open": "Aberto", + "panel.fb_issue_closed": "Fechado", + "panel.fb_stale": "Esta página é do TaskMate {panel}, mas está instalada a versão {installed}. O navegador está a mostrar uma cópia antiga — atualize antes de reportar um problema com um cartão.", + "panel.fb_refresh_now": "Atualizar agora", + "panel.fb_health_found": "A verificação de estado encontrou {count} coisa(s) a corrigir. Podem explicar o problema.", + "panel.fb_health_open": "Abrir verificação de estado", + "panel.fb_opened": "O GitHub abriu num novo separador. Depois de submeter o issue, pode limpar este formulário.", + "panel.fb_clear": "Limpar formulário", + "panel.fb_clear_confirm": "Limpar tudo o que escreveu?", + "panel.fb_other_help": "Outra ajuda", + "panel.fb_link_troubleshooting": "Guia de resolução de problemas", + "panel.fb_link_faq": "FAQ", + "panel.fb_link_discussions": "Perguntar à comunidade", "panel.tab_templates": "Modelos", "panel.tab_today": "Hoje", "panel.tab_unknown": "Separador desconhecido", diff --git a/custom_components/taskmate/www/taskmate-panel.js b/custom_components/taskmate/www/taskmate-panel.js index 2c4f5631..0037cdc9 100644 --- a/custom_components/taskmate/www/taskmate-panel.js +++ b/custom_components/taskmate/www/taskmate-panel.js @@ -198,6 +198,160 @@ const DASH_PATH_RE = /^[a-z0-9]+(-[a-z0-9]+)+$/; // Mood check-in faces, by the mood id the backend stores. const MOOD_FACES = { great: "😄", good: "🙂", ok: "😐", sad: "😢", upset: "😠" }; +// Help & feedback page: builds a pre-filled GitHub "new issue" link. Nothing is +// sent from Home Assistant — the parent reviews the text on GitHub and submits +// it there. Option values are the issue templates' own English option text +// (.github/ISSUE_TEMPLATE), which is what GitHub matches; `lk` is the label the +// panel shows. Field ids in the URL are the templates' field ids. +const FEEDBACK_REPO = "https://github.com/tempus2016/taskmate"; +const FEEDBACK_API = "https://api.github.com/search/issues"; +// GitHub answers 414 somewhere past 8 KB; stay clear of it. +const FEEDBACK_URL_MAX = 7800; +const FEEDBACK_DRAFT_KEY = "taskmate-feedback-draft"; +const FEEDBACK_LOG_MAX = 20; +const FEEDBACK_TEXT_MAX = 6000; + +const FEEDBACK_BUG_AREAS = [ + ["child_card", "Child Card"], ["rewards_card", "Rewards Card"], ["approvals_card", "Approvals Card"], + ["points_card", "Points Card"], ["reorder_card", "Reorder Card"], ["parent_card", "Parent Dashboard Card"], + ["overview_card", "Overview Card"], ["activity_card", "Activity Card"], ["streak_card", "Streak Card"], + ["weekly_card", "Weekly Card"], ["graph_card", "Points Graph Card"], ["progress_card", "Reward Progress Card"], + ["leaderboard_card", "Leaderboard Card"], ["setup", "Integration setup / config flow"], + ["chores", "Chore management"], ["rewards", "Reward management"], ["children", "Children management"], + ["points", "Points / scoring"], ["streaks", "Streaks / milestones"], ["reward_approval", "Reward approval flow"], + ["chore_approval", "Chore approval flow"], ["services", "Services (complete_chore, add_points, etc.)"], + ["sensors", "Sensors / entity attributes"], ["resources", "Lovelace resources (cards not loading)"], + ["bonus", "Bonus points (weekend / streak / perfect week)"], ["other", "Other"], +].map(([k, v]) => ({ v, lk: `panel.fb_area_${k}` })); + +const FEEDBACK_FEATURE_AREAS = [ + ["child_card", "Child Card"], ["rewards_card", "Rewards Card"], ["approvals_card", "Approvals Card"], + ["points_card", "Points Card"], ["reorder_card", "Reorder Card"], ["parent_card", "Parent Dashboard Card"], + ["overview_card", "Overview Card"], ["activity_card", "Activity Card"], ["streak_card", "Streak Card"], + ["weekly_card", "Weekly Card"], ["graph_card", "Points Graph Card"], ["progress_card", "Reward Progress Card"], + ["leaderboard_card", "Leaderboard Card"], ["chores", "Chore management (config flow)"], + ["rewards", "Reward management (config flow)"], ["children", "Children management (config flow)"], + ["settings", "Settings / configuration"], ["bonus", "Bonus points system"], ["streaks", "Streaks / milestones"], + ["notifications", "Notifications / alerts"], ["services", "Services / automations"], + ["sensors", "Sensors / entity attributes"], ["install", "Installation / HACS"], + ["new_card", "New card (not listed above)"], ["other", "Other / not sure"], +].map(([k, v]) => ({ v, lk: `panel.fb_area_${k}` })); + +const FEEDBACK_REPRO = [ + { id: "always", v: "Always — happens every time", lk: "panel.fb_repro_always" }, + { id: "sometimes", v: "Sometimes — happens intermittently", lk: "panel.fb_repro_sometimes" }, + { id: "once", v: "Once — only happened one time", lk: "panel.fb_repro_once" }, +]; +const FEEDBACK_PRIORITY = [ + { id: "nice", v: "Nice to have — would improve the experience", lk: "panel.fb_priority_nice" }, + { id: "important", v: "Important — a gap that affects my daily use", lk: "panel.fb_priority_important" }, + { id: "critical", v: "Critical — a blocker for me using TaskMate properly", lk: "panel.fb_priority_critical" }, +]; +// Keys match feedback.py's installation_type / install_method / lovelace_mode. +const FEEDBACK_INSTALL_TYPES = [ + { id: "haos", v: "Home Assistant OS (HAOS)", lk: "panel.fb_install_haos" }, + { id: "supervised", v: "Home Assistant Supervised", lk: "panel.fb_install_supervised" }, + { id: "container", v: "Home Assistant Container (Docker)", lk: "panel.fb_install_container" }, + { id: "core", v: "Home Assistant Core", lk: "panel.fb_install_core" }, + { id: "unknown", v: "Not sure", lk: "panel.fb_not_sure" }, +]; +const FEEDBACK_INSTALL_METHODS = [ + { id: "hacs_default", v: "HACS (default)", lk: "panel.fb_method_hacs_default" }, + { id: "hacs_custom", v: "HACS (custom repository)", lk: "panel.fb_method_hacs_custom" }, + { id: "manual", v: "Manual (copied files)", lk: "panel.fb_method_manual" }, +]; +const FEEDBACK_LOVELACE_MODES = [ + { id: "storage", v: "Storage (default — managed via UI)", lk: "panel.fb_lovelace_storage" }, + { id: "yaml", v: "YAML (configuration.yaml)", lk: "panel.fb_lovelace_yaml" }, + { id: "unknown", v: "Not sure", lk: "panel.fb_not_sure" }, +]; + +function feedbackOption(list, id) { + return list.find(o => o.id === id) || list[list.length - 1]; +} + +/** "Chrome 141 on Windows", "Home Assistant app on iOS 18.1" — never the raw user agent. */ +function feedbackBrowser(ua) { + ua = String(ua || ""); + let m; + let os = ""; + if ((m = /(?:iPhone|iPad|CPU) OS (\d+)[_.](\d+)/.exec(ua)) || (m = /\biOS (\d+)\.(\d+)/.exec(ua))) os = `iOS ${m[1]}.${m[2]}`; + else if ((m = /Android (\d+(?:\.\d+)?)/.exec(ua))) os = `Android ${m[1]}`; + else if (/Mac OS X|Macintosh/.test(ua)) os = "macOS"; + else if (/Windows/.test(ua)) os = "Windows"; + else if (/CrOS/.test(ua)) os = "ChromeOS"; + else if (/Linux/.test(ua)) os = "Linux"; + let name; + if (/Home ?Assistant\//.test(ua) || /io\.robbie\.HomeAssistant/.test(ua)) name = "Home Assistant app"; + else if ((m = /Edg\/(\d+)/.exec(ua))) name = `Edge ${m[1]}`; + else if ((m = /(?:Firefox|FxiOS)\/(\d+)/.exec(ua))) name = `Firefox ${m[1]}`; + else if ((m = /(?:Chrome|CriOS)\/(\d+)/.exec(ua))) name = `Chrome ${m[1]}`; + else if ((m = /Version\/(\d+(?:\.\d+)?).*Safari/.exec(ua))) name = `Safari ${m[1]}`; + else name = "Unknown browser"; + return os ? `${name} on ${os}` : name; +} + +/** + * Take people's names and secrets out of log text before it can leave the + * house. `names` are swapped for stable placeholders ("[name 1]"); signed-URL + * signatures, bearer tokens and e-mail addresses are masked. + */ +function feedbackRedact(text, names) { + // Secrets and addresses first: a name inside an address ("john@…") would + // otherwise be swapped out and leave the rest of the address unmatched. + let out = String(text || "") + .replace(/(authSig|token|access_token|api_key|password)=([^&\s"']+)/gi, "$1=[hidden]") + .replace(/\bBearer\s+[\w.-]+/g, "Bearer [hidden]") + .replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, "[email]") + // Phone and person ids are usually named after someone ("mobile_app_johns_iphone"). + .replace(/\b(mobile_app|person|device_tracker)([._])[\w-]+/gi, "$1$2[device]"); + const uniq = [...new Set((names || []).map(n => String(n || "").trim()).filter(n => n.length >= 2))] + .sort((a, b) => b.length - a.length); + uniq.forEach((name, i) => { + const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + // No lookbehind: iOS 15 WebKit still runs this panel and has none. + out = out.replace(new RegExp(`(^|[^\\p{L}\\p{N}])${esc}(?![\\p{L}\\p{N}])`, "giu"), `$1[name ${i + 1}]`); + }); + return out; +} + +/** + * HA's system_log records → TaskMate's own entries as text lines, oldest + * first, at most `max` of them. A traceback keeps its last lines — the ones + * that say what broke. + */ +function feedbackLogEntries(records, names, max = FEEDBACK_LOG_MAX) { + const ours = (records || []).filter(r => { + const name = String(r && r.name || ""); + const src = Array.isArray(r && r.source) ? String(r.source[0] || "") : ""; + return name.startsWith("custom_components.taskmate") || src.includes("custom_components/taskmate"); + }); + ours.sort((a, b) => (Number(b.timestamp) || 0) - (Number(a.timestamp) || 0)); + return ours.slice(0, max).reverse().map(r => { + let when = ""; + try { when = new Date(Number(r.timestamp) * 1000).toISOString().replace("T", " ").slice(0, 19); } catch (_) {} + const msg = (Array.isArray(r.message) ? r.message : [r.message]).filter(Boolean).join(" | "); + const count = Number(r.count) > 1 ? ` (×${Number(r.count)})` : ""; + let line = `${when} ${String(r.level || "").toUpperCase()} [${r.name || ""}] ${msg}${count}`.trim(); + if (r.exception) { + const tb = String(r.exception).trim().split("\n"); + line += "\n" + (tb.length > 12 ? [" …", ...tb.slice(-12)] : tb).join("\n"); + } + return feedbackRedact(line, names); + }); +} + +/** The issues/new link for the given template and field values (empty fields left out). */ +function feedbackIssueUrl(template, title, fields) { + const p = new URLSearchParams(); + p.set("template", template); + p.set("title", title); + for (const [k, v] of Object.entries(fields || {})) { + if (v != null && String(v).trim() !== "") p.set(k, String(v)); + } + return `${FEEDBACK_REPO}/issues/new?${p}`; +} + class TaskMatePanel extends HTMLElement { constructor() { super(); @@ -582,6 +736,7 @@ class TaskMatePanel extends HTMLElement { if (this._palette && t && t.closest(".tm-pal-item")) this._palette = null; if (!t) { if (closedNewMenu) this._render(); return; } const act = t.dataset.act; + if (act.startsWith("fb-")) { this._fbClick(t, e); return; } // Setup wizard (#980) actions all start "wz-". if (act.startsWith("wz-")) { this._wizardAct(t, e); return; } // Ready-made dashboards (#1053) actions all start "dash-". @@ -1056,6 +1211,7 @@ class TaskMatePanel extends HTMLElement { _onInput(e) { const t = e.target; if (!t.dataset) return; + if (t.dataset.fbField) { this._fbInput(t); return; } if (t.dataset.wzIn) { this._wizardInput(t); return; } if (t.dataset.palette === "q" && this._palette) { this._palette.q = t.value || ""; @@ -1172,6 +1328,7 @@ class TaskMatePanel extends HTMLElement { _onChange(e) { const t = e.target; if (!t.dataset) return; + if (t.dataset.fbField) { this._fbInput(t); return; } if (t.dataset.act === "bulk-toggle-row") { const id = t.dataset.id; if (t.checked) this._bulkSel.add(id); else this._bulkSel.delete(id); @@ -3706,6 +3863,9 @@ class TaskMatePanel extends HTMLElement { this._dialog._openAdvanced = openSet; } + // A state push can redraw the Help & feedback page mid-sentence; put the + // caret back where it was. + const fbFocus = this._fbFocusSnapshot(); let anyChanged = false; let paletteChanged = false; for (const [name, html] of Object.entries(zones)) { @@ -3721,6 +3881,7 @@ class TaskMatePanel extends HTMLElement { } // A rebuilt command search has a fresh input: give it the focus back. if (paletteChanged && this._palette) this._focusPalette(); + if (fbFocus) this._fbFocusRestore(fbFocus); if (anyChanged) { this._bindHaPickers(); @@ -3826,6 +3987,7 @@ class TaskMatePanel extends HTMLElement { { key: "system", head: this._t("panel.nav_system"), items: [ { id: "notifications", label: this._t("panel.tab_notifications"), icon: "mdi:bell-outline" }, { id: "settings", label: this._t("panel.tab_settings"), icon: "mdi:cog-outline" }, + { id: "help", label: this._t("panel.tab_help"), icon: "mdi:lifebuoy" }, ]}, ].map(g => ({ ...g, @@ -4149,6 +4311,7 @@ class TaskMatePanel extends HTMLElement { case "notifications": return this._renderNotificationsTab(); case "audit": return this._renderAuditTab(); case "settings": return this._renderSettingsTab(); + case "help": return this._renderHelpTab(); case "setup": return this._wizardBody(); default: return `
${this._t("panel.tab_unknown")}
`; } @@ -9661,6 +9824,629 @@ class TaskMatePanel extends HTMLElement { `; } + // -- Help & feedback --------------------------------------------------- + /** + * Report a bug or suggest a feature without leaving Home Assistant. The page + * builds GitHub's pre-filled "new issue" link from the repo's own issue + * templates; nothing is sent from here. GitHub only pre-fills text fields, + * so the template's drop-down answers also travel as text in "Additional + * context", and the page tells the parent which ones to pick. + * + * Typing never re-renders the page: the draft lives in this._fb and + * localStorage, and _fbPaint() refreshes just the link, the counters and the + * similar-issue list. + */ + _fbDraft() { + if (this._fb) return this._fb; + const blank = { + type: "bug", + bug: { title: "", description: "", expected: "", steps: "", area: FEEDBACK_BUG_AREAS[0].v, + repro: "always", config: "", console: "", additional: "" }, + feature: { title: "", summary: "", problem: "", solution: "", alternatives: "", + area: FEEDBACK_FEATURE_AREAS[0].v, priority: "nice", additional: "" }, + env: { include: true, logs: true, install_type: "", install_method: "", lovelace_mode: "" }, + }; + let saved = null; + try { saved = JSON.parse(localStorage.getItem(FEEDBACK_DRAFT_KEY) || "null"); } catch (_) {} + this._fb = saved && typeof saved === "object" ? { + type: saved.type === "feature" ? "feature" : "bug", + bug: { ...blank.bug, ...(saved.bug || {}) }, + feature: { ...blank.feature, ...(saved.feature || {}) }, + env: { ...blank.env, ...(saved.env || {}) }, + } : blank; + return this._fb; + } + + _fbSave() { + clearTimeout(this._fbSaveTimer); + this._fbSaveTimer = setTimeout(() => { + try { localStorage.setItem(FEEDBACK_DRAFT_KEY, JSON.stringify(this._fb)); } catch (_) {} + }, 300); + } + + _fbHasText() { + const d = this._fbDraft(); + const free = { bug: ["title", "description", "expected", "steps", "console", "config", "additional"], + feature: ["title", "summary", "problem", "solution", "alternatives", "additional"] }; + return Object.entries(free).some(([type, keys]) => keys.some(k => String(d[type][k] || "").trim())); + } + + async _fbLoad() { + if (this._fbLoading) return; + this._fbLoading = true; + const [info, logs] = await Promise.all([ + this._callWS({ type: "taskmate/feedback/info" }), + this._callWS({ type: "system_log/list" }), + ]); + this._fbLoading = false; + this._fbInfo = info.ok ? info.res : { error: info.err }; + this._fbLogs = logs.ok ? feedbackLogEntries(logs.res, this._fbNames()) : null; + this._render(); + } + + /** Every name we know in the household — the log redaction list. */ + _fbNames() { + const s = this._state || {}; + const parents = (this._notifState && this._notifState.recipients && this._notifState.recipients.parents) || []; + const full = [...(s.children || []).map(c => c.name), ...parents.map(p => p.name), + this._hass && this._hass.user && this._hass.user.name].filter(Boolean); + // "John Mackinnon" also hides "John" and "Mackinnon" on their own. + const parts = full.flatMap(n => String(n).split(/[\s_.-]+/)).filter(w => w.length >= 3); + return [...full, ...parts]; + } + + /** + * The setup details. If the integration couldn't answer (TaskMate failing + * to load is exactly when a bug report matters most) the versions the + * browser already knows stand in. + */ + _fbInfoOrFallback() { + const info = this._fbInfo || {}; + if (!info.error) return info; + const ha = this._hass && this._hass.config && this._hass.config.version; + return { integration_version: PANEL_VERSION !== "?" ? PANEL_VERSION : "", ha_version: ha || "", counts: null, health_codes: [] }; + } + + /** The install answers, detected by the integration unless the parent changed them. */ + _fbEnv() { + const d = this._fbDraft(); + const info = this._fbInfo || {}; + return { + install_type: d.env.install_type || info.installation_type || "unknown", + // "unknown" means HACS couldn't be read: leave it for the parent to pick. + install_method: d.env.install_method || (info.install_method !== "unknown" && info.install_method) || "", + lovelace_mode: d.env.lovelace_mode || info.lovelace_mode || "unknown", + }; + } + + _fbBrowser() { + return feedbackBrowser(typeof navigator !== "undefined" ? navigator.userAgent : ""); + } + + /** The "Sent from the TaskMate panel" block — the template's drop-down answers as text. */ + _fbEnvLines() { + const info = this._fbInfoOrFallback(); + const env = this._fbEnv(); + const c = info.counts || {}; + const method = env.install_method ? feedbackOption(FEEDBACK_INSTALL_METHODS, env.install_method).v : "Not sure"; + const lines = [ + `- Installation type: ${feedbackOption(FEEDBACK_INSTALL_TYPES, env.install_type).v}${info.installation_type_raw ? ` (HA reports "${info.installation_type_raw}")` : ""}`, + `- TaskMate installed via: ${method}`, + `- Lovelace mode: ${feedbackOption(FEEDBACK_LOVELACE_MODES, env.lovelace_mode).v}`, + info.counts ? `- Family: ${c.children || 0} children, ${c.adults || 0} adults · ${c.chores || 0} chores · ${c.rewards || 0} rewards` + : `- Setup details: unavailable (${this._fbInfo && this._fbInfo.error})`, + `- Language: ${info.language || "?"} · Card design: ${info.card_design || "?"}`, + `- Health check: ${(info.health_codes || []).length ? info.health_codes.join(", ") : "no issues"}`, + `- Panel build: ${PANEL_VERSION} · ${this._fbBrowser()}`, + ]; + return lines; + } + + /** + * The link, plus what had to give to keep it under GitHub's length limit: + * the oldest log lines go first; if the parent's own text is still too + * long, the link is withheld and `over` says by how much. + */ + _fbBuild() { + const d = this._fbDraft(); + const info = this._fbInfoOrFallback(); + const env = this._fbEnv(); + const includeEnv = d.env.include !== false; + if (d.type === "feature") { + const f = d.feature; + const extra = [f.additional.trim(), includeEnv && info.integration_version + ? `_Sent from the TaskMate panel · TaskMate ${info.integration_version} · Home Assistant ${info.ha_version}_` : ""] + .filter(Boolean).join("\n\n"); + const fields = { + summary: f.summary, problem: f.problem, solution: f.solution, area: f.area, alternatives: f.alternatives, + priority: feedbackOption(FEEDBACK_PRIORITY, f.priority).v, + children_count: includeEnv && info.counts ? String(info.counts.children) : "", + use_case: extra, + }; + const url = feedbackIssueUrl("feature_request.yml", `[Feature]: ${f.title.trim()}`, fields); + return { url, over: Math.max(0, url.length - FEEDBACK_URL_MAX), logsUsed: 0, logsDropped: 0 }; + } + const b = d.bug; + const additional = [b.additional.trim(), includeEnv ? ["**Sent from the TaskMate panel**", ...this._fbEnvLines()].join("\n") : ""] + .filter(Boolean).join("\n\n"); + const fields = { + taskmate_version: includeEnv ? info.integration_version : "", + ha_version: includeEnv ? info.ha_version : "", + ha_install_type: includeEnv ? feedbackOption(FEEDBACK_INSTALL_TYPES, env.install_type).v : "", + install_method: includeEnv && env.install_method ? feedbackOption(FEEDBACK_INSTALL_METHODS, env.install_method).v : "", + lovelace_mode: includeEnv ? feedbackOption(FEEDBACK_LOVELACE_MODES, env.lovelace_mode).v : "", + description: b.description, expected: b.expected, steps: b.steps, + reproducible: feedbackOption(FEEDBACK_REPRO, b.repro).v, affected_area: b.area, + config: b.config, console_errors: b.console, additional, + }; + const title = `[Bug]: ${b.title.trim()}`; + const logs = d.env.logs !== false && this._fbLogs ? this._fbLogs : []; + // Drop the oldest log entries until the link fits. + for (let keep = logs.length; keep >= 0; keep--) { + const url = feedbackIssueUrl("bug_report.yml", title, { ...fields, logs: logs.slice(logs.length - keep).join("\n") }); + if (url.length <= FEEDBACK_URL_MAX || keep === 0) { + return { url, over: Math.max(0, url.length - FEEDBACK_URL_MAX), logsUsed: keep, logsDropped: logs.length - keep }; + } + } + return { url: "", over: 0, logsUsed: 0, logsDropped: 0 }; + } + + /** Required fields still empty, as their labels. */ + _fbMissing() { + const d = this._fbDraft(); + const need = d.type === "feature" + ? [["title", "panel.fb_title"], ["summary", "panel.fb_feat_summary"], ["problem", "panel.fb_feat_problem"], ["solution", "panel.fb_feat_solution"]] + : [["title", "panel.fb_title"], ["description", "panel.fb_bug_description"], ["expected", "panel.fb_bug_expected"], ["steps", "panel.fb_bug_steps"]]; + const form = d[d.type]; + return need.filter(([k]) => !String(form[k] || "").trim()).map(([, lk]) => this._t(lk)); + } + + /** Markdown of the whole report, for pasting when the link can't carry it. */ + _fbMarkdown() { + const d = this._fbDraft(); + const info = this._fbInfoOrFallback(); + const sec = (h, v, fence = "") => (String(v || "").trim() ? `### ${h}\n\n${fence ? "```" + fence + "\n" : ""}${String(v).trim()}${fence ? "\n```" : ""}\n` : ""); + if (d.type === "feature") { + const f = d.feature; + return [`## [Feature]: ${f.title.trim()}\n`, sec("Feature Summary", f.summary), sec("What problem does this solve?", f.problem), + sec("Proposed Solution", f.solution), sec("Which area does this relate to?", f.area), sec("Alternatives Considered", f.alternatives), + sec("How important is this to you?", feedbackOption(FEEDBACK_PRIORITY, f.priority).v), sec("Additional Context", f.additional), + d.env.include !== false && info.integration_version ? `_TaskMate ${info.integration_version} · Home Assistant ${info.ha_version}_\n` : "", + ].filter(Boolean).join("\n"); + } + const b = d.bug; + const env = d.env.include !== false + ? sec("Environment", [`- TaskMate: ${info.integration_version || "?"}`, `- Home Assistant: ${info.ha_version || "?"}`, ...this._fbEnvLines()].join("\n")) + : ""; + const logs = d.env.logs !== false && this._fbLogs && this._fbLogs.length ? sec("Home Assistant Logs", this._fbLogs.join("\n"), "text") : ""; + return [`## [Bug]: ${b.title.trim()}\n`, env, sec("What happened?", b.description), sec("What did you expect to happen?", b.expected), + sec("Steps to Reproduce", b.steps), sec("Is this reproducible?", feedbackOption(FEEDBACK_REPRO, b.repro).v), + sec("Affected Area", b.area), logs, sec("Browser Console Errors", b.console, "text"), + sec("Card YAML Configuration", b.config, "yaml"), sec("Additional Context", b.additional), + ].filter(Boolean).join("\n"); + } + + /** Clipboard with a fallback: navigator.clipboard needs https, and plenty of HA installs are plain http. */ + async _fbCopy(text, okMsg) { + try { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + this._showToast("ok", okMsg); + return; + } + } catch (_) { /* fall through to the legacy path */ } + const ta = document.createElement("textarea"); + ta.value = text; + ta.setAttribute("readonly", ""); + ta.style.cssText = "position:fixed;top:0;inset-inline-start:0;width:1px;height:1px;opacity:0"; + this.appendChild(ta); + ta.select(); + let ok = false; + try { ok = document.execCommand("copy"); } catch (_) {} + ta.remove(); + this._showToast(ok ? "ok" : "err", ok ? okMsg : this._t("panel.fb_copy_failed")); + } + + async _fbDownloadDiagnostics() { + const id = this._fbInfo && this._fbInfo.entry_id; + if (!id) { this._showToast("err", this._t("panel.fb_diag_failed")); return; } + const { ok, res, err } = await this._callWS({ type: "auth/sign_path", path: `/api/diagnostics/config_entry/${id}` }); + if (!ok || !res || !res.path) { this._showToast("err", this._t("panel.fb_diag_failed") + (err ? ` (${err})` : "")); return; } + const a = document.createElement("a"); + a.href = res.path; + a.download = "taskmate-diagnostics.json"; + this.appendChild(a); + a.click(); + a.remove(); + this._showToast("ok", this._t("panel.fb_diag_started")); + } + + /** Similar issues on GitHub, searched as the title is typed (debounced; quiet on failure). */ + /** The search the current title calls for, or "" when it is too short to search. */ + _fbSearchQuery() { + const d = this._fbDraft(); + const words = d[d.type].title.trim().split(/\s+/).filter(w => w.length >= 3); + return words.length < 2 ? "" : words.slice(0, 8).join(" "); + } + + _fbQueueSearch() { + clearTimeout(this._fbSearchTimer); + const q = this._fbSearchQuery(); + if (!q) { this._fbDupes = null; this._fbPaint(); return; } + if (this._fbDupes && this._fbDupes.q === q) return; + this._fbSearchTimer = setTimeout(() => this._fbSearch(q), 700); + } + + async _fbSearch(q) { + const url = `${FEEDBACK_API}?per_page=5&q=${encodeURIComponent(`${q} repo:tempus2016/taskmate is:issue`)}`; + try { + const resp = await fetch(url, { headers: { Accept: "application/vnd.github+json" } }); + if (!resp.ok) throw new Error(String(resp.status)); + const data = await resp.json(); + const items = (data.items || []).slice(0, 5).map(i => ({ + number: Number(i.number), title: String(i.title || ""), state: i.state === "closed" ? "closed" : "open", + url: /^https:\/\/github\.com\//.test(i.html_url || "") ? i.html_url : "", + })).filter(i => i.url); + if (this._fbSearchQuery() !== q) return; // the title changed while GitHub answered + this._fbDupes = { q, items }; + } catch (_) { + if (this._fbSearchQuery() !== q) return; + this._fbDupes = { q, items: [], failed: true }; + } + this._fbPaint(); + } + + _fbSearchUrl() { + const d = this._fbDraft(); + const q = d[d.type].title.trim(); + return `${FEEDBACK_REPO}/issues?q=${encodeURIComponent(`is:issue ${q}`.trim())}`; + } + + _fbDupesHtml() { + const dupes = this._fbDupes; + if (!dupes || dupes.failed || !dupes.items.length) return ""; + return ` +
+
${this._t("panel.fb_similar_title")}
+ ${dupes.items.map(i => ` + + ${this._t(i.state === "open" ? "panel.fb_issue_open" : "panel.fb_issue_closed")} + #${i.number} ${this._esc(i.title)} + `).join("")} + ${this._t("panel.fb_similar_hint")} +
`; + } + + _fbLinkHtml() { + const d = this._fbDraft(); + const missing = this._fbMissing(); + const built = this._fbBuild(); + const ready = !missing.length && !built.over && this._fbInfo; + const status = missing.length + ? `${this._t("panel.fb_still_needed", { fields: missing.map(m => this._esc(m)).join(", ") })}` + : built.over + ? `${this._t("panel.fb_too_long", { count: built.over })}` + : `${this._t("panel.fb_ready")}`; + const logNote = d.type === "bug" && built.logsDropped + ? `${this._t("panel.fb_logs_trimmed", { used: built.logsUsed, dropped: built.logsDropped })}` : ""; + return ` +
+ + ${this._t("panel.fb_open_github")} + + + ${this._t("panel.fb_search_github")} +
+ ${status} + ${logNote} + ${built.over ? `${this._t("panel.fb_too_long_hint")}` : ""} + ${this._t("panel.fb_account_hint")}`; + } + + /** Refresh the bits that depend on what's typed, without rebuilding the form. */ + _fbPaint() { + const link = this.querySelector('[data-fb-region="link"]'); + if (link) link.innerHTML = this._fbLinkHtml(); + const dupes = this.querySelector('[data-fb-region="dupes"]'); + if (dupes) dupes.innerHTML = this._fbDupesHtml(); + const d = this._fbDraft(); + this.querySelectorAll("[data-fb-count]").forEach(el => { + const v = String(d[d.type][el.dataset.fbCount] || ""); + el.textContent = this._ltrNums(`${v.length} / ${FEEDBACK_TEXT_MAX}`); + }); + } + + _fbFocusSnapshot() { + const root = this.getRootNode && this.getRootNode(); + const el = root && root.activeElement; + if (!el || !el.dataset || !el.dataset.fbField || !this.contains(el)) return null; + return { field: el.dataset.fbField, start: el.selectionStart, end: el.selectionEnd }; + } + + _fbFocusRestore(snap) { + const el = this.querySelector(`[data-fb-field="${snap.field}"]`); + if (!el || el === (this.getRootNode() || {}).activeElement) return; + el.focus(); + try { if (snap.start != null) el.setSelectionRange(snap.start, snap.end); } catch (_) {} + } + + _fbInput(t) { + const d = this._fbDraft(); + const key = t.dataset.fbField; + if (key.startsWith("env.")) { + const k = key.slice(4); + d.env[k] = t.type === "checkbox" ? t.checked : t.value; + } else { + d[d.type][key] = t.value; + if (key === "title") this._fbQueueSearch(); + } + this._fbSave(); + this._fbPaint(); + } + + _fbClick(t, e) { + const act = t.dataset.act; + const d = this._fbDraft(); + if (act === "fb-type") { + d.type = t.dataset.id === "feature" ? "feature" : "bug"; + this._fbDupes = null; + this._fbSave(); + this._render(); + this._fbQueueSearch(); + return; + } + if (act === "fb-open") { + if (t.getAttribute("aria-disabled") === "true") { + e.preventDefault(); + const missing = this._fbMissing(); + this._showToast("err", missing.length ? this._t("panel.fb_still_needed", { fields: missing.join(", ") }) + : !this._fbInfo ? this._t("panel.fb_loading") : this._t("panel.fb_too_long_plain")); + const first = [...this.querySelectorAll(".tm-fb-form [data-fb-required]")].find(el => !el.value.trim()); + if (first) first.focus(); + return; + } + // The browser follows the link itself (target=_blank); offer to clear afterwards. + this._fbOpened = true; + setTimeout(() => this._render(), 0); + return; + } + if (act === "fb-copy-md") { this._fbCopy(this._fbMarkdown(), this._t("panel.fb_copied_markdown")); return; } + if (act === "fb-copy-logs") { this._fbCopy((this._fbLogs || []).join("\n"), this._t("panel.fb_copied_logs")); return; } + if (act === "fb-diagnostics") { this._fbDownloadDiagnostics(); return; } + if (act === "fb-reload") { this._fbInfo = null; this._fbLogs = null; this._fbLoad(); this._render(); return; } + if (act === "fb-hard-refresh") { location.reload(); return; } + if (act === "fb-health") { this._activeTab = "insights"; this._insightView = "health"; this._render(); return; } + if (act === "fb-clear") { + if (this._fbHasText() && !this._fbOpened && !confirm(this._t("panel.fb_clear_confirm"))) return; + this._fb = null; + this._fbOpened = false; + this._fbDupes = null; + try { localStorage.removeItem(FEEDBACK_DRAFT_KEY); } catch (_) {} + this._fbDraft(); + this._render(); + } + } + + _fbText(key, labelKey, { required = false, rows = 4, placeholderKey = "", hintKey = "" } = {}) { + const d = this._fbDraft(); + const v = d[d.type][key] || ""; + return ` + `; + } + + _fbSelect(key, labelKey, options, current, { scope = "" } = {}) { + const field = scope ? `${scope}.${key}` : key; + return ` + `; + } + + _fbBugForm() { + const b = this._fbDraft().bug; + return ` +
+

${this._t("panel.fb_bug_heading")}

+ +
${this._fbDupesHtml()}
+ ${this._fbText("description", "panel.fb_bug_description", { required: true, rows: 5, placeholderKey: "panel.fb_bug_description_placeholder" })} +
+ ${this._fbText("expected", "panel.fb_bug_expected", { required: true, rows: 3 })} + ${this._fbText("steps", "panel.fb_bug_steps", { required: true, rows: 3, placeholderKey: "panel.fb_bug_steps_placeholder" })} +
+
+ ${this._fbSelect("area", "panel.fb_bug_area", FEEDBACK_BUG_AREAS, b.area)} + ${this._fbSelect("repro", "panel.fb_bug_repro", FEEDBACK_REPRO, b.repro)} +
+
+ ${this._t("panel.fb_more_details")} + ${this._fbText("console", "panel.fb_bug_console", { rows: 3, hintKey: "panel.fb_bug_console_hint" })} + ${this._fbText("config", "panel.fb_bug_config", { rows: 3, hintKey: "panel.fb_bug_config_hint" })} + ${this._fbText("additional", "panel.fb_additional", { rows: 3, hintKey: "panel.fb_additional_hint" })} +
+
`; + } + + _fbFeatureForm() { + const f = this._fbDraft().feature; + return ` +
+

${this._t("panel.fb_feat_heading")}

+ +
${this._fbDupesHtml()}
+ ${this._fbText("summary", "panel.fb_feat_summary", { required: true, rows: 3, placeholderKey: "panel.fb_feat_summary_placeholder" })} + ${this._fbText("problem", "panel.fb_feat_problem", { required: true, rows: 3, hintKey: "panel.fb_feat_problem_hint" })} + ${this._fbText("solution", "panel.fb_feat_solution", { required: true, rows: 4 })} +
+ ${this._fbSelect("area", "panel.fb_feat_area", FEEDBACK_FEATURE_AREAS, f.area)} + ${this._fbSelect("priority", "panel.fb_feat_priority", FEEDBACK_PRIORITY, f.priority)} +
+
+ ${this._t("panel.fb_more_details")} + ${this._fbText("alternatives", "panel.fb_feat_alternatives", { rows: 3 })} + ${this._fbText("additional", "panel.fb_additional", { rows: 3, hintKey: "panel.fb_additional_hint" })} +
+
`; + } + + _fbDetailsCard() { + const d = this._fbDraft(); + const info = this._fbInfo; + if (!info) return `
${this._t("panel.fb_loading")}
`; + if (info.error) return ` +
+

${this._t("panel.fb_info_failed", { error: this._esc(info.error) })}

+ +
`; + const env = this._fbEnv(); + const c = info.counts || {}; + const row = (label, value) => `
${label}
${value}
`; + const auto = `${this._t("panel.fb_auto")}`; + const logs = this._fbLogs; + return ` +
+
+

${this._t("panel.fb_details_heading")}

+ +
+

${this._t("panel.fb_details_intro")}

+
+ ${row(this._t("panel.fb_taskmate_version"), `${this._esc(info.integration_version)}${auto}`)} + ${row(this._t("panel.fb_ha_version"), `${this._esc(info.ha_version)}${auto}`)} + ${d.type === "bug" ? ` + ${row(this._t("panel.fb_family"), `${this._ltrNums(this._t("panel.fb_family_value", { children: c.children || 0, adults: c.adults || 0, chores: c.chores || 0, rewards: c.rewards || 0 }))}${auto}`)} + ${row(this._t("panel.fb_browser"), `${this._esc(this._fbBrowser())}${auto}`)} + ${row(this._t("panel.fb_health"), `${(info.health_codes || []).length ? this._esc(info.health_codes.join(", ")) : this._t("panel.fb_health_ok")}${auto}`)} + ` : row(this._t("panel.fb_children_count"), `${this._num(c.children)}${auto}`)} +
+ ${d.type === "bug" ? ` +
+ ${this._fbSelect("install_type", "panel.fb_install_type", FEEDBACK_INSTALL_TYPES, env.install_type, { scope: "env" })} + ${this._fbSelect("install_method", "panel.fb_install_method", + env.install_method ? FEEDBACK_INSTALL_METHODS : [{ id: "", lk: "panel.fb_pick_one" }, ...FEEDBACK_INSTALL_METHODS], + env.install_method, { scope: "env" })} + ${this._fbSelect("lovelace_mode", "panel.fb_lovelace_mode", FEEDBACK_LOVELACE_MODES, env.lovelace_mode, { scope: "env" })} +
+ ${this._t("panel.fb_dropdowns_hint")}` : ""} + + ${d.type === "bug" ? ` +
+
+ +
+ ${this._t("panel.fb_logs_heading")} + ${logs == null ? this._t("panel.fb_logs_unavailable") + : logs.length ? this._t("panel.fb_logs_found", { count: logs.length }) : this._t("panel.fb_logs_none")} +
+
+ ${logs && logs.length ? ` + +
+ ${this._t("panel.fb_logs_preview")} +
${this._esc(logs.join("\n"))}
+ +
` : ""} + ${this._t("panel.fb_logs_debug_hint")} +
+
+
+ ${this._t("panel.fb_diag_heading")} + ${this._t("panel.fb_diag_hint")} +
+ +
` : ""} +
`; + } + + _renderHelpTab() { + const d = this._fbDraft(); + if (!this._fbInfo && !this._fbLoading) this._fbLoad(); + const info = this._fbInfo || {}; + const stale = info.integration_version && PANEL_VERSION !== "?" && PANEL_VERSION !== info.integration_version; + const typeBtn = (id, icon, titleKey, subKey) => ` + `; + return ` +
+
+

${this._t("panel.tab_help")}

+

${this._t("panel.fb_intro")}

+
+ ${stale ? ` +
+ + ${this._t("panel.fb_stale", { panel: this._esc(PANEL_VERSION), installed: this._esc(info.integration_version) })} + +
` : ""} + ${d.type === "bug" && (info.health_codes || []).length ? ` +
+ + ${this._t("panel.fb_health_found", { count: info.health_codes.length })} + +
` : ""} + ${this._fbOpened ? ` +
+ + ${this._t("panel.fb_opened")} + +
` : ""} +
+ ${typeBtn("bug", "mdi:bug-outline", "panel.fb_type_bug", "panel.fb_type_bug_hint")} + ${typeBtn("feature", "mdi:lightbulb-on-outline", "panel.fb_type_feature", "panel.fb_type_feature_hint")} +
+ ${d.type === "feature" ? this._fbFeatureForm() : this._fbBugForm()} + ${this._fbDetailsCard()} +
+
${this._fbLinkHtml()}
+ ${this._fbHasText() && !this._fbOpened ? `` : ""} +
+ +
`; + } + // -- Settings tab ------------------------------------------------------ /** * Custom completion sounds (#856). @@ -14715,6 +15501,103 @@ class TaskMatePanel extends HTMLElement { font-size: 10px; font-weight: 700; line-height: 16px; text-align: center; } } + /* Help & feedback */ + .tm-fb { max-width: 920px; } + .tm-fb .tm-card { margin-bottom: 16px; } + .tm-fb .tm-card h2 { margin-top: 0; } + .tm-fb-intro h2 { margin: 0 0 4px; } + .tm-fb-intro { margin-bottom: 16px; } + .tm-fb-types { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; } + .tm-fb-type { + display: flex; gap: 12px; align-items: flex-start; text-align: start; + background: var(--tm-surface-1); color: var(--tm-text); + border: 2px solid var(--tm-border); border-radius: var(--tm-radius); + padding: 14px 16px; cursor: pointer; font: inherit; + } + .tm-fb-type:hover { border-color: var(--tm-border-strong); background: var(--tm-surface-hover); } + .tm-fb-type:focus-visible { outline: none; box-shadow: var(--tm-shadow-focus); } + .tm-fb-type strong { display: block; font-size: 15px; margin-bottom: 2px; } + .tm-fb-type small { color: var(--tm-text-muted); font-size: 12.5px; } + .tm-fb-type-ico { + flex: none; width: 40px; height: 40px; border-radius: 10px; + display: grid; place-items: center; + } + .tm-fb-type-bug .tm-fb-type-ico { background: var(--tm-danger-soft); color: var(--tm-danger); } + .tm-fb-type-feature .tm-fb-type-ico { background: var(--tm-positive-soft); color: var(--tm-positive); } + .tm-fb-type-bug.tm-fb-type-on { border-color: var(--tm-danger); } + .tm-fb-type-feature.tm-fb-type-on { border-color: var(--tm-positive); } + .tm-fb-req { color: var(--tm-danger); } + .tm-fb-field-foot { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; } + .tm-fb-count { color: var(--tm-text-faint); font-size: 11px; font-variant-numeric: tabular-nums; flex: none; margin-top: 4px; } + .tm-fb-more { margin: 4px 0 0; } + .tm-fb-more > summary { cursor: pointer; color: var(--tm-text-muted); font-size: 13px; margin-bottom: 10px; } + .tm-fb-dupes { + border: 1px solid var(--tm-warning-border); background: var(--tm-warning-soft); + border-radius: var(--tm-radius-sm); padding: 10px 12px; margin: -6px 0 14px; + } + .tm-fb-dupes-head { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: 13px; margin-bottom: 6px; } + .tm-fb-dupes-head ha-icon { --mdc-icon-size: 18px; } + .tm-fb-dupe { display: flex; align-items: center; gap: 8px; padding: 4px 0; color: var(--tm-text); text-decoration: none; font-size: 13px; } + .tm-fb-dupe:hover .tm-fb-dupe-title { text-decoration: underline; } + .tm-fb-dupe-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } + .tm-fb-diag { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 12px 0 14px; font-size: 13px; } + .tm-fb-diag dt { color: var(--tm-text-muted); } + .tm-fb-diag dd { margin: 0; min-width: 0; overflow-wrap: anywhere; } + .tm-fb-diag code { font-family: ui-monospace, "SF Mono", Menlo, monospace; } + .tm-fb-auto { + margin-inline-start: 8px; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; + color: var(--tm-accent-text); border: 1px solid var(--tm-accent-border); + border-radius: 999px; padding: 0 6px; vertical-align: 1px; + } + .tm-fb-env { grid-template-columns: repeat(3, 1fr); } + .tm-fb-env .tm-field { margin-bottom: 4px; } + .tm-fb-check { display: flex; gap: 8px; align-items: flex-start; margin: 12px 0 0; font-size: 13px; cursor: pointer; } + .tm-fb-check input { margin-top: 2px; } + .tm-fb-logs, .tm-fb-diagfile { + background: var(--tm-surface-2); border-radius: var(--tm-radius-sm); + padding: 12px 14px; margin-top: 14px; + } + .tm-fb-logs-head { display: flex; gap: 10px; align-items: flex-start; } + .tm-fb-logs-head ha-icon { color: var(--tm-text-muted); flex: none; } + .tm-fb-logs .tm-fb-more { margin-top: 10px; } + .tm-fb-pre { + white-space: pre-wrap; overflow-wrap: anywhere; max-height: 240px; overflow: auto; + background: var(--tm-surface-0); border: 1px solid var(--tm-border); border-radius: var(--tm-radius-sm); + padding: 10px; font: 11.5px/1.5 ui-monospace, "SF Mono", Menlo, monospace; color: var(--tm-text-muted); + margin: 0 0 8px; + } + .tm-fb-diagfile { display: flex; gap: 12px; align-items: center; justify-content: space-between; } + .tm-fb-diagfile .tm-btn { flex: none; } + .tm-fb-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; } + .tm-fb-actions a.tm-btn { text-decoration: none; } + .tm-fb-disabled { opacity: .45; cursor: not-allowed; } + .tm-fb-status { display: block; font-size: 13px; color: var(--tm-text-muted); margin-bottom: 4px; } + .tm-fb-status-ok { color: var(--tm-positive); } + .tm-fb-status-warn { color: var(--tm-warning); font-weight: 500; } + .tm-fb-send { position: relative; } + .tm-fb-clear { margin-top: 10px; } + .tm-fb-banner { + display: flex; gap: 10px; align-items: center; flex-wrap: wrap; + padding: 10px 14px; margin-bottom: 14px; border-radius: var(--tm-radius-sm); + background: var(--tm-accent-soft); border: 1px solid var(--tm-accent-border); font-size: 13px; + } + .tm-fb-banner > span { flex: 1; min-width: 200px; } + .tm-fb-banner-warn { background: var(--tm-warning-soft); border-color: var(--tm-warning-border); } + .tm-fb-banner-warn ha-icon { color: var(--tm-warning); } + .tm-fb-banner-ok { background: var(--tm-positive-soft); border-color: var(--tm-positive-border); } + .tm-fb-banner-ok ha-icon { color: var(--tm-positive); } + .tm-fb-links { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; } + .tm-fb-links a { + display: flex; gap: 8px; align-items: center; padding: 12px; + background: var(--tm-surface-2); border-radius: var(--tm-radius-sm); + color: var(--tm-text); text-decoration: none; font-size: 13px; + } + .tm-fb-links a:hover { background: var(--tm-surface-3); } + @media (max-width: 760px) { + .tm-fb-types, .tm-fb-env, .tm-fb-links, .tm-fb .tm-field-row { grid-template-columns: 1fr; } + .tm-fb-diagfile { flex-direction: column; align-items: stretch; } + .tm-fb-actions > * { flex: 1 1 100%; justify-content: center; } + } @media (max-width: 480px) { .tm-scope { max-width: 46vw; overflow-x: auto; } .tm-today-quick { grid-template-columns: 1fr; } diff --git a/mkdocs.yml b/mkdocs.yml index acba84f2..55463fd0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -164,4 +164,5 @@ nav: - "Automations": "Automations.md" - "Localization": "Localization.md" - "Troubleshooting": "Troubleshooting.md" + - "Help & Feedback": "Help-and-Feedback.md" - "FAQ": "FAQ.md" diff --git a/tests/data/locale_english_baseline.json b/tests/data/locale_english_baseline.json index 6a530453..b9183fac 100644 --- a/tests/data/locale_english_baseline.json +++ b/tests/data/locale_english_baseline.json @@ -40,6 +40,16 @@ "panel.dash_card_kiosk", "panel.dash_layout_label", "panel.entity_bonus", + "panel.fb_auto", + "panel.fb_browser", + "panel.fb_diag_download", + "panel.fb_install_container", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.health_count_badges", "panel.insp_bonus", "panel.nav_system", @@ -115,6 +125,15 @@ "panel.common_name", "panel.dash_card_kiosk", "panel.entity_bonus", + "panel.fb_auto", + "panel.fb_browser", + "panel.fb_install_container", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.group_policy_sticky_short", "panel.insights_view_fairness", "panel.insp_bonus", @@ -209,6 +228,14 @@ "panel.dash_title_parents", "panel.dash_view_points", "panel.entity_bonus", + "panel.fb_area_notifications", + "panel.fb_auto", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.health_count_badges", "panel.health_sev_info", "panel.insights_col_verdict", @@ -281,6 +308,13 @@ "panel.child_avatar_label", "panel.dash_card_kiosk", "panel.entity_bonus", + "panel.fb_auto", + "panel.fb_install_container", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.group_policy_sticky_short", "panel.insp_bonus", "panel.nav_system", @@ -346,6 +380,15 @@ "panel.child_stat_level", "panel.chore_weekly_target_pill", "panel.dash_card_kiosk", + "panel.fb_auto", + "panel.fb_browser", + "panel.fb_install_container", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_issue_open", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.insp_bonus", "panel.notif_escalation_minutes_suffix", "panel.pts_display", @@ -417,6 +460,14 @@ "panel.dash_card_kiosk", "panel.day_sun", "panel.entity_bonus", + "panel.fb_auto", + "panel.fb_install_container", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_issue_open", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.group_policy_sticky_short", "panel.insp_bonus", "panel.nav_system", @@ -467,6 +518,13 @@ "panel.bounty_col_status", "panel.bounty_completed_meta", "panel.dash_card_kiosk", + "panel.fb_auto", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.nav_system", "panel.notif_escalation_minutes_suffix", "panel.pts_display", @@ -512,6 +570,13 @@ "panel.child_avatar_label", "panel.chore_subtask_points_placeholder", "panel.dash_layout_label", + "panel.fb_auto", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.group_policy_spread_short", "panel.group_policy_sticky_short", "panel.notif_escalation_minutes_suffix", @@ -557,6 +622,13 @@ "panel.bounty_completed_meta", "panel.child_avatar_label", "panel.chore_subtask_points_placeholder", + "panel.fb_auto", + "panel.fb_install_core", + "panel.fb_install_haos", + "panel.fb_install_supervised", + "panel.fb_link_faq", + "panel.fb_lovelace_yaml", + "panel.fb_method_hacs_default", "panel.group_policy_sticky_short", "panel.notif_escalation_minutes_suffix", "panel.pts_display", diff --git a/tests/frontend/taskmate-panel-feedback.test.cjs b/tests/frontend/taskmate-panel-feedback.test.cjs new file mode 100644 index 00000000..888acc9d --- /dev/null +++ b/tests/frontend/taskmate-panel-feedback.test.cjs @@ -0,0 +1,445 @@ +// Admin panel → Help & feedback: a pre-filled GitHub "new issue" link. +// +// Pins the link GitHub receives (template, field ids, the template's own +// option text), that it stays under GitHub's length limit by shedding the +// oldest log lines first, that names and secrets never leave in the logs, +// and that text a parent typed is escaped when the page is drawn. + +const assert = require("node:assert/strict"); +const { test } = require("node:test"); + +const { loadCard, localize } = require("./harness.cjs"); + +function load(storage = {}, extra = {}) { + const store = { ...storage }; + const localStorage = { + getItem: (k) => (k in store ? store[k] : null), + setItem: (k, v) => { store[k] = String(v); }, + removeItem: (k) => { delete store[k]; }, + }; + const loaded = loadCard("taskmate-panel.js", { + sandbox: { ...extra, localStorage, navigator: { userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" } }, + transform: (src) => `${src}\nwindow.__fb = { feedbackRedact, feedbackLogEntries, feedbackIssueUrl, feedbackBrowser, FEEDBACK_URL_MAX, FEEDBACK_DRAFT_KEY };`, + }); + return { Panel: loaded.get("taskmate-panel"), fb: loaded.window.__fb, store }; +} + +const INFO = { + integration_version: "test", ha_version: "2026.10.2", + installation_type: "container", installation_type_raw: "Home Assistant Container", + install_method: "hacs_default", lovelace_mode: "storage", language: "en-GB", + counts: { children: 2, adults: 1, chores: 42, rewards: 9 }, card_design: "graphite", + health_codes: [], entry_id: "entry-1", +}; + +function panel(storage, info = INFO) { + const { Panel, fb, store } = load(storage); + const p = Object.create(Panel.prototype); + p._t = (key, params) => localize(key, params); + p._state = { children: [{ id: "k1", name: "Malia" }, { id: "k2", name: "Vaiha" }], settings: {} }; + p._fbInfo = info; + p._fbLogs = []; + p._render = () => {}; + return { p, fb, store }; +} + +function params(url) { + return new URL(url).searchParams; +} + +function fill(p, fields) { + const d = p._fbDraft(); + Object.assign(d[d.type], fields); +} + +const BUG = { title: "Points vanish", description: "Malia's points went to 0", expected: "They stay", steps: "1. Wait for midnight" }; + +// ── pure helpers ──────────────────────────────────────────────────────────── + +test("the issue link carries the template, the title and only non-empty fields", () => { + const { fb } = load(); + const url = fb.feedbackIssueUrl("bug_report.yml", "[Bug]: A & B", { description: "x y", steps: "", logs: null }); + assert.ok(url.startsWith("https://github.com/tempus2016/taskmate/issues/new?")); + const q = params(url); + assert.equal(q.get("template"), "bug_report.yml"); + assert.equal(q.get("title"), "[Bug]: A & B"); + assert.equal(q.get("description"), "x y"); + assert.equal(q.has("steps"), false); + assert.equal(q.has("logs"), false); +}); + +test("names are replaced with placeholders, whole words only, longest first", () => { + const { fb } = load(); + const out = fb.feedbackRedact("Anna and ann; Annabel. MALIA's chore — Maliable stays", ["Ann", "Anna", "Malia", "", "x"]); + assert.equal(out, "[name 2] and [name 3]; Annabel. [name 1]'s chore — Maliable stays"); +}); + +test("non-Latin names are matched as whole words too", () => { + const { fb } = load(); + assert.equal(fb.feedbackRedact("Zoë done, Zoëlle not", ["Zoë"]), "[name 1] done, Zoëlle not"); + assert.equal(fb.feedbackRedact("Łukasz: ok", ["łukasz"]), "[name 1]: ok"); +}); + +test("signed URLs, bearer tokens and e-mail addresses are masked", () => { + const { fb } = load(); + const out = fb.feedbackRedact("GET /api/taskmate/photo/a.jpg?authSig=abc.def&x=1 Bearer eyJ.abc-d mail john@example.co.uk password=hunter2", []); + assert.ok(!out.includes("abc.def") && out.includes("authSig=[hidden]&x=1")); + assert.ok(out.includes("Bearer [hidden]") && !out.includes("eyJ")); + assert.ok(out.includes("[email]") && !out.includes("john@")); + assert.ok(!out.includes("hunter2")); +}); + +test("an address holding a family name is still masked whole", () => { + // Found on ha-dev: the signed-in user's name was swapped out of their own + // e-mail first, which left "@example.com" behind. + const { fb } = load(); + const out = fb.feedbackRedact("contact john@example.com or John", ["John"]); + assert.equal(out, "contact [email] or [name 1]"); +}); + +test("log entries keep TaskMate's own records, newest N, printed oldest first", () => { + const { fb } = load(); + const records = [ + { name: "custom_components.taskmate.coordinator", level: "ERROR", message: ["Boom for Malia"], timestamp: 300, count: 3 }, + { name: "homeassistant.components.zha", level: "WARNING", message: ["not ours"], timestamp: 400 }, + { name: "homeassistant.helpers.entity", level: "WARNING", message: ["entity from us"], timestamp: 200, + source: ["custom_components/taskmate/sensor.py", 10] }, + { name: "custom_components.taskmate", level: "WARNING", message: ["oldest"], timestamp: 100 }, + ]; + const lines = fb.feedbackLogEntries(records, ["Malia"], 2); + assert.equal(lines.length, 2); + assert.ok(lines[0].includes("entity from us"), "older of the two newest comes first"); + assert.ok(lines[1].includes("ERROR [custom_components.taskmate.coordinator] Boom for [name 1] (×3)")); + assert.ok(!lines.join().includes("not ours") && !lines.join().includes("Malia")); +}); + +test("a long traceback keeps its last lines", () => { + const { fb } = load(); + const exception = Array.from({ length: 30 }, (_, i) => `line ${i}`).join("\n"); + const [entry] = fb.feedbackLogEntries([{ name: "custom_components.taskmate", level: "ERROR", message: ["x"], timestamp: 1, exception }], []); + assert.ok(entry.includes("line 29") && entry.includes("line 18") && !entry.includes("line 17\n")); + assert.ok(entry.includes(" …")); +}); + +test("the browser is summarised, never sent as the raw user agent", () => { + const { fb } = load(); + assert.equal(fb.feedbackBrowser("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/141.0.0.0 Safari/537.36"), "Chrome 141 on Windows"); + assert.equal(fb.feedbackBrowser("Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 Version/18.1 Mobile/15E148 Safari/604.1"), "Safari 18.1 on iOS 18.1"); + assert.equal(fb.feedbackBrowser("Home Assistant/2025.1 (io.robbie.HomeAssistant; build:1; iOS 17.5.1) Mobile"), "Home Assistant app on iOS 17.5"); + assert.equal(fb.feedbackBrowser("Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0"), "Firefox 131 on Linux"); + assert.equal(fb.feedbackBrowser("Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 Chrome/130.0 Mobile Safari/537.36 Edg/130.0"), "Edge 130 on Android 14"); + assert.equal(fb.feedbackBrowser(""), "Unknown browser"); +}); + +// ── the bug link ──────────────────────────────────────────────────────────── + +test("a bug link fills every text field the template has, with its own option text", () => { + const { p } = panel(); + fill(p, { ...BUG, area: "Child Card", repro: "sometimes", console: "TypeError", config: "type: custom:x" }); + p._fbLogs = ["2026-10-20 ERROR [custom_components.taskmate] boom"]; + const q = params(p._fbBuild().url); + assert.equal(q.get("template"), "bug_report.yml"); + assert.equal(q.get("title"), "[Bug]: Points vanish"); + assert.equal(q.get("taskmate_version"), "test"); + assert.equal(q.get("ha_version"), "2026.10.2"); + assert.equal(q.get("ha_install_type"), "Home Assistant Container (Docker)"); + assert.equal(q.get("install_method"), "HACS (default)"); + assert.equal(q.get("lovelace_mode"), "Storage (default — managed via UI)"); + assert.equal(q.get("reproducible"), "Sometimes — happens intermittently"); + assert.equal(q.get("affected_area"), "Child Card"); + assert.equal(q.get("description"), BUG.description); + assert.equal(q.get("expected"), BUG.expected); + assert.equal(q.get("steps"), BUG.steps); + assert.equal(q.get("console_errors"), "TypeError"); + assert.equal(q.get("config"), "type: custom:x"); + assert.equal(q.get("logs"), "2026-10-20 ERROR [custom_components.taskmate] boom"); + const additional = q.get("additional"); + assert.ok(additional.includes("**Sent from the TaskMate panel**")); + assert.ok(additional.includes("TaskMate installed via: HACS (default)")); + assert.ok(additional.includes("Family: 2 children, 1 adults · 42 chores · 9 rewards")); + assert.ok(additional.includes("Chrome 141 on Windows")); +}); + +test("unticking 'include these details' leaves the environment out", () => { + const { p } = panel(); + fill(p, BUG); + p._fbDraft().env.include = false; + const q = params(p._fbBuild().url); + for (const k of ["taskmate_version", "ha_version", "ha_install_type", "install_method", "lovelace_mode", "additional"]) { + assert.equal(q.has(k), false, k); + } +}); + +test("a parent's corrections to the detected answers win", () => { + const { p } = panel(); + fill(p, BUG); + Object.assign(p._fbDraft().env, { install_type: "haos", install_method: "manual", lovelace_mode: "yaml" }); + const q = params(p._fbBuild().url); + assert.equal(q.get("ha_install_type"), "Home Assistant OS (HAOS)"); + assert.equal(q.get("install_method"), "Manual (copied files)"); + assert.equal(q.get("lovelace_mode"), "YAML (configuration.yaml)"); +}); + +test("an install method HACS couldn't tell us is left for the parent to pick", () => { + const { p } = panel(undefined, { ...INFO, install_method: "unknown" }); + fill(p, BUG); + assert.equal(params(p._fbBuild().url).has("install_method"), false); + const html = p._fbDetailsCard(); + assert.ok(html.includes(localize("panel.fb_pick_one"))); +}); + +test("the oldest log lines are dropped to keep the link under GitHub's limit", () => { + const { p, fb } = panel(); + fill(p, BUG); + p._fbLogs = Array.from({ length: 20 }, (_, i) => `entry-${String(i).padStart(2, "0")} ${"x".repeat(400)}`); + const built = p._fbBuild(); + assert.ok(built.url.length <= fb.FEEDBACK_URL_MAX); + assert.ok(built.logsDropped > 0 && built.logsUsed > 0); + assert.equal(built.logsUsed + built.logsDropped, 20); + const logs = params(built.url).get("logs"); + assert.ok(logs.includes("entry-19"), "the newest entry survives"); + assert.ok(!logs.includes("entry-00"), "the oldest goes first"); + assert.equal(built.over, 0); + assert.ok(p._fbLinkHtml().includes(localize("panel.fb_logs_trimmed", { used: built.logsUsed, dropped: built.logsDropped }))); +}); + +test("text too long for any link withholds the link and says by how much", () => { + const { p, fb } = panel(); + fill(p, { ...BUG, description: "é".repeat(3000) }); + const built = p._fbBuild(); + assert.ok(built.over > 0); + assert.equal(built.url.length - fb.FEEDBACK_URL_MAX, built.over); + const html = p._fbLinkHtml(); + assert.ok(html.includes('aria-disabled="true"') && html.includes('href="#"')); + assert.ok(html.includes(localize("panel.fb_too_long", { count: built.over }))); +}); + +test("unticking the logs leaves them out", () => { + const { p } = panel(); + fill(p, BUG); + p._fbLogs = ["boom"]; + p._fbDraft().env.logs = false; + assert.equal(params(p._fbBuild().url).has("logs"), false); +}); + +// ── the feature link ──────────────────────────────────────────────────────── + +test("a feature link uses the feature template and its field ids", () => { + const { p } = panel(); + const d = p._fbDraft(); + d.type = "feature"; + fill(p, { title: "Swap chores", summary: "S", problem: "P", solution: "So", alternatives: "A", area: "Settings / configuration", priority: "critical" }); + const q = params(p._fbBuild().url); + assert.equal(q.get("template"), "feature_request.yml"); + assert.equal(q.get("title"), "[Feature]: Swap chores"); + assert.equal(q.get("summary"), "S"); + assert.equal(q.get("problem"), "P"); + assert.equal(q.get("solution"), "So"); + assert.equal(q.get("alternatives"), "A"); + assert.equal(q.get("area"), "Settings / configuration"); + assert.equal(q.get("priority"), "Critical — a blocker for me using TaskMate properly"); + assert.equal(q.get("children_count"), "2"); + assert.ok(q.get("use_case").includes("TaskMate test · Home Assistant 2026.10.2")); + assert.equal(q.has("description"), false); +}); + +// ── required fields and the button ────────────────────────────────────────── + +test("required fields are listed until filled, and the button stays off", () => { + const { p } = panel(); + assert.deepEqual([...p._fbMissing()], ["Title", "What happened?", "What did you expect to happen?", "Steps to reproduce"]); + assert.ok(p._fbLinkHtml().includes('aria-disabled="true"')); + fill(p, BUG); + assert.deepEqual([...p._fbMissing()], []); + const html = p._fbLinkHtml(); + assert.ok(html.includes('aria-disabled="false"')); + assert.ok(html.includes('href="https://github.com/tempus2016/taskmate/issues/new?template=bug_report.yml')); + p._fbDraft().type = "feature"; + assert.deepEqual([...p._fbMissing()], ["Title", "Summary", "What problem would it solve?", "How would you like it to work?"]); +}); + +test("the button stays off until the setup details have loaded", () => { + const { p } = panel(undefined, null); + fill(p, BUG); + assert.ok(p._fbLinkHtml().includes('aria-disabled="true"')); +}); + +// ── drawing the page ──────────────────────────────────────────────────────── + +test("the page draws every string translated and escapes what was typed", () => { + const { p, fb } = panel({}); + p._fbLoading = true; + fill(p, { ...BUG, title: '', description: "" }); + p._fbLogs = ["log"]; + const html = p._renderHelpTab(); + assert.ok(!/panel\.(fb_|tab_help)/.test(html), "no raw keys"); + assert.ok(!html.includes("