From 06f91578da9bd351b12427dff0e0d7d84fad2ed4 Mon Sep 17 00:00:00 2001 From: tajniak81 <13187254+tajniak81@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:20:08 +0200 Subject: [PATCH] Integrations, sorted into the categories the panel already sorts them into MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API Server panel has grouped plugins by category since the plugin list was drawn — car manufacturers, EV chargers, notifications, each tab carrying its count. The Settings page had the same integrations in one flat stack, so the two screens described the same set of things in two different shapes. Now they agree: the same categories, the same counted tabs, the same accent border on the active one. The category ids come from the Category* constants both sides already read, so nothing here invents a third vocabulary. Only a category holding something gets a tab, which is the panel's rule too: two tabs today rather than six empty ones. Below one category the bar hides itself entirely — with nothing to switch between, a single tab is a label pretending to be a control. The active group falls back to the first rather than to a fixed id, because the three integration views load independently and a tab can be momentarily empty on first paint; falling back keeps the section populated instead of blanking it. The cards moved into a space-y-4 wrapper and lost their per-card mt-4, so the first card sits the same distance under the bar whichever tab is showing — previously Toyota was the only one that could ever be first. Co-Authored-By: Claude Opus 5 --- Web App/web/src/i18n/da.json | 1 + Web App/web/src/i18n/en.json | 1 + Web App/web/src/i18n/pl.json | 1 + Web App/web/src/views/Settings.vue | 56 ++++++++++++++++++++++++++++-- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Web App/web/src/i18n/da.json b/Web App/web/src/i18n/da.json index 83b6393..68d1f67 100644 --- a/Web App/web/src/i18n/da.json +++ b/Web App/web/src/i18n/da.json @@ -251,6 +251,7 @@ }, "integrations": { + "categories": { "vehicles": "Bilproducenter", "chargers": "EV-ladere" }, "title": "Integrationer", "subtitle": "Forbind eksterne tjenester til din konto.", "toyota": "Toyota Connected (MyToyota)", diff --git a/Web App/web/src/i18n/en.json b/Web App/web/src/i18n/en.json index 6aa24eb..b911664 100644 --- a/Web App/web/src/i18n/en.json +++ b/Web App/web/src/i18n/en.json @@ -250,6 +250,7 @@ }, "integrations": { + "categories": { "vehicles": "Car manufacturers", "chargers": "EV chargers" }, "title": "Integrations", "subtitle": "Connect external services to your account.", "toyota": "Toyota Connected (MyToyota)", diff --git a/Web App/web/src/i18n/pl.json b/Web App/web/src/i18n/pl.json index 36c36ff..ea4d6ba 100644 --- a/Web App/web/src/i18n/pl.json +++ b/Web App/web/src/i18n/pl.json @@ -255,6 +255,7 @@ }, "integrations": { + "categories": { "vehicles": "Producenci samochodów", "chargers": "Ładowarki EV" }, "title": "Integracje", "subtitle": "Połącz zewnętrzne usługi ze swoim kontem.", "toyota": "Toyota Connected (MyToyota)", diff --git a/Web App/web/src/views/Settings.vue b/Web App/web/src/views/Settings.vue index 0198511..8a52f60 100644 --- a/Web App/web/src/views/Settings.vue +++ b/Web App/web/src/views/Settings.vue @@ -424,6 +424,39 @@ async function testToyotaConnection() { } } +// --- Integrations: category tabs --- +// +// The API Server panel groups plugins into the same categories (see +// panel/src/components/PluginsCard.vue, and the Category* constants in +// internal/plugins/plugin.go that both read from), so grouping them the same way +// here means someone who has seen one list can read the other. Only a category +// that actually has an integration gets a tab — two tabs today, not six empty +// ones — and a category whose integrations are all still loading has none, which +// is why the active group falls back to the first rather than to a fixed id. +const INTEGRATION_CATEGORIES = ["vehicles", "chargers"]; + +const integrationsByCategory = computed(() => ({ + vehicles: [toyota.value && "toyota"].filter(Boolean), + chargers: [anker.value && "anker", greencell.value && "greencell"].filter(Boolean), +})); + +const integrationGroups = computed(() => + INTEGRATION_CATEGORIES.filter((c) => integrationsByCategory.value[c].length).map((c) => ({ + id: c, + label: t(`settings.integrations.categories.${c}`), + ids: integrationsByCategory.value[c], + })) +); + +const integrationTab = ref(""); +const activeIntegrationGroup = computed( + () => integrationGroups.value.find((g) => g.id === integrationTab.value) || integrationGroups.value[0] +); + +function showsIntegration(id) { + return !!activeIntegrationGroup.value?.ids.includes(id); +} + // --- Integrations: Anker Solix (V1 Smart EV Charger) --- // // Same superadmin → org admin → user cascade as Toyota above; the fields are the @@ -1211,7 +1244,23 @@ onBeforeUnmount(() => {

{{ t("settings.integrations.title") }}

{{ t("settings.integrations.subtitle") }}

-
+ + + +
+
-
+
-
+
+