diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js
index 12be78a..008954a 100644
--- a/custom_components/bahmcloud_store/panel/panel.js
+++ b/custom_components/bahmcloud_store/panel/panel.js
@@ -1,24 +1,472 @@
class BahmcloudStorePanel extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: "open" });
+
+ this._hass = null;
+ this._view = "store"; // store | manage | about
+ this._data = null;
+ this._loading = true;
+ this._error = null;
+
+ this._customAddUrl = "";
+ this._customAddName = "";
+ }
+
set hass(hass) {
- if (this._rendered) return;
- this._rendered = true;
+ this._hass = hass;
+ if (!this._rendered) {
+ this._rendered = true;
+ this._render();
+ this._load();
+ }
+ }
- const root = this.attachShadow({ mode: "open" });
- const iframe = document.createElement("iframe");
+ async _load() {
+ if (!this._hass) return;
- iframe.src = "/api/bahmcloud_store_static/index.html";
- iframe.style.width = "100%";
- iframe.style.height = "100%";
- iframe.style.border = "0";
- iframe.style.display = "block";
+ this._loading = true;
+ this._error = null;
+ this._update();
- const style = document.createElement("style");
- style.textContent = `
- :host { display: block; height: 100vh; }
+ try {
+ const data = await this._hass.callApi("get", "bcs");
+ this._data = data;
+ } catch (e) {
+ this._error = e?.message ? String(e.message) : String(e);
+ } finally {
+ this._loading = false;
+ this._update();
+ }
+ }
+
+ async _addCustomRepo() {
+ if (!this._hass) return;
+
+ const url = (this._customAddUrl || "").trim();
+ const name = (this._customAddName || "").trim() || null;
+
+ if (!url) {
+ this._error = "Please enter a repository URL.";
+ this._update();
+ return;
+ }
+
+ this._error = null;
+ this._update();
+
+ try {
+ await this._hass.callApi("post", "bcs", {
+ op: "add_custom_repo",
+ url,
+ name,
+ });
+ this._customAddUrl = "";
+ this._customAddName = "";
+ await this._load();
+ this._view = "manage";
+ this._update();
+ } catch (e) {
+ this._error = e?.message ? String(e.message) : String(e);
+ this._update();
+ }
+ }
+
+ async _removeCustomRepo(id) {
+ if (!this._hass) return;
+
+ try {
+ await this._hass.callApi("delete", `bcs/custom_repo?id=${encodeURIComponent(id)}`);
+ await this._load();
+ this._view = "manage";
+ this._update();
+ } catch (e) {
+ this._error = e?.message ? String(e.message) : String(e);
+ this._update();
+ }
+ }
+
+ _render() {
+ const root = this.shadowRoot;
+
+ root.innerHTML = `
+
+
+
+
+
+
+
Bahmcloud Store
+
BCS 0.2.0 — foundation build
+
+
+
+
+
+
+
+
+
+
Store
+
Manage repositories
+
Settings / About
+
+
+
+
+
`;
- root.appendChild(style);
- root.appendChild(iframe);
+ root.getElementById("refreshBtn").addEventListener("click", () => this._load());
+
+ for (const tab of root.querySelectorAll(".tab")) {
+ tab.addEventListener("click", () => {
+ this._view = tab.getAttribute("data-view");
+ this._update();
+ });
+ }
+ }
+
+ _update() {
+ const root = this.shadowRoot;
+ const content = root.getElementById("content");
+ const err = root.getElementById("error");
+
+ for (const tab of root.querySelectorAll(".tab")) {
+ tab.classList.toggle("active", tab.getAttribute("data-view") === this._view);
+ }
+
+ err.textContent = this._error ? `Error: ${this._error}` : "";
+
+ if (this._loading) {
+ content.innerHTML = `Loading…
`;
+ return;
+ }
+
+ if (!this._data) {
+ content.innerHTML = `No data.
`;
+ return;
+ }
+
+ if (this._view === "store") {
+ content.innerHTML = this._renderStore();
+ return;
+ }
+
+ if (this._view === "manage") {
+ content.innerHTML = this._renderManage();
+ this._wireManage();
+ return;
+ }
+
+ content.innerHTML = this._renderAbout();
+ }
+
+ _renderStore() {
+ const repos = Array.isArray(this._data.repos) ? this._data.repos : [];
+
+ const rows = repos.map((r) => {
+ const badge = r.source === "custom"
+ ? `Custom`
+ : `Index`;
+
+ const owner = r.owner ? `Owner: ${this._esc(r.owner)}` : "Owner: -";
+ const provider = r.provider ? `Provider: ${this._esc(r.provider)}` : "Provider: -";
+
+ return `
+
+
+
+
${this._esc(r.name)}
+
${this._esc(r.description || "Description will be loaded in a later version.")}
+
${owner} · ${provider}
+
+
+ ${badge}
+
+
+ `;
+ }).join("");
+
+ return `
+
+
Store
+
Index URL: ${this._esc(this._data.store_url || "-")}
+
Refresh seconds: ${this._esc(String(this._data.refresh_seconds || "-"))}
+
+
+ ${rows || `No repositories configured.
`}
+ `;
+ }
+
+ _renderManage() {
+ const repos = Array.isArray(this._data.repos) ? this._data.repos : [];
+ const custom = repos.filter((r) => r.source === "custom");
+
+ const list = custom.map((r) => {
+ return `
+
+
+
+
${this._esc(r.name)}
+
${this._esc(r.url)}
+
+
+
+
+
+
+ `;
+ }).join("");
+
+ return `
+
+
Manage repositories
+
Add public repositories from any git provider.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${list || `No custom repositories added yet.
`}
+ `;
+ }
+
+ _wireManage() {
+ const root = this.shadowRoot;
+
+ const addUrl = root.getElementById("addUrl");
+ const addName = root.getElementById("addName");
+ const addBtn = root.getElementById("addBtn");
+
+ if (addUrl) {
+ addUrl.addEventListener("input", (e) => {
+ this._customAddUrl = e.target.value;
+ });
+ }
+ if (addName) {
+ addName.addEventListener("input", (e) => {
+ this._customAddName = e.target.value;
+ });
+ }
+ if (addBtn) {
+ addBtn.addEventListener("click", () => this._addCustomRepo());
+ }
+
+ for (const btn of root.querySelectorAll("[data-remove]")) {
+ btn.addEventListener("click", () => {
+ const id = btn.getAttribute("data-remove");
+ if (id) this._removeCustomRepo(id);
+ });
+ }
+ }
+
+ _renderAbout() {
+ return `
+
+
Settings / About
+
Language: English (v1). i18n will be added later.
+
Theme: follows Home Assistant light/dark automatically.
+
Accent: Bahmcloud Blue.
+
BCS version: ${this._esc(this._data.version || "-")}
+
+
+
+
Roadmap
+
+ Next versions will add: repo metadata (bcs.yaml / hacs.*), README view, install/uninstall, update entities.
+
+
+ `;
+ }
+
+ _esc(s) {
+ return String(s ?? "")
+ .replaceAll("&", "&")
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll('"', """)
+ .replaceAll("'", "'");
}
}