AkurAI Build
Menu

Public repository / 6401b9edc6e7367bac167df29ad7cbad46e44b68

AkurAI-Platform / frontend/suite-tiles.js

Latest change b1ab07177739f3aa7251fa530ed2bd463402b5c5 - feat: complete managed workspace control plane by Ólafur Búi Ólafsson

// Suite tiles — catalog-driven product card rendering.
// Fetches catalog data from the payment API and fills tile grids.
// Category headers are server-rendered (full i18n); only product tiles are JS-generated.

const BUILD_BASE = "https://akurai-build.olibuijr.com/repos/";

const LABELS = {
  open: "Open",
  status: "Status",
  inDev: "In development",
  source: "Build",
};

function tileHtml(product) {
  const isDev = product.visibility === "dev";
  const hasUrl = product.url && !isDev;

  let actionHtml = "";
  if (isDev) {
    actionHtml = `<span class="tile-flag">${LABELS.inDev}</span>`;
  } else if (hasUrl) {
    const label =
      product.actionKey === "app.action.status"
        ? LABELS.status
        : LABELS.open;
    actionHtml = `<a class="tile-open" href="${product.url}">${label}<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a>`;
  } else {
    actionHtml = `<span class="tile-flag">${product.kind}</span>`;
  }

  let srcHtml = `<a class="tile-src" href="${BUILD_BASE}${product.buildRepo}">${LABELS.source}: ${product.buildRepo}</a>`;
  if (product.secondaryBuildRepo) {
    srcHtml += `<a class="tile-src" href="${BUILD_BASE}${product.secondaryBuildRepo}">${LABELS.source}: ${product.secondaryBuildRepo}</a>`;
  }
  const icon = product.id === "managed-workspace" ? "platform" : product.id;

  return `<article class="tile">
    <span class="tile-icon"><img src="/img/icons/${icon}.svg" alt="" width="48" height="48" loading="lazy"></span>
    <div class="tile-body">
      <div class="tile-top">
        <h4>${product.name}</h4>
        <span class="tile-kind">${product.kind}</span>
      </div>
      <p class="tile-prop">${product.description}</p>
    </div>
    <div class="tile-foot">
      ${actionHtml}
      ${srcHtml}
    </div>
  </article>`;
}

// Boot: fetch catalog, populate empty tile grids.
const suiteRoot = document.querySelector(".suite");
if (suiteRoot) {
  fetch("/api/payments/catalog", {
    headers: { accept: "application/json" },
  })
    .then((r) => {
      if (!r.ok) throw new Error("catalog_fetch_failed");
      return r.json();
    })
    .then((data) => {
      for (const product of data.products) {
        if (product.visibility === "hidden") continue;
        const grid = suiteRoot.querySelector(
          `.cat-grid[data-cat="${product.category}"]`
        );
        if (grid) {
          grid.insertAdjacentHTML("beforeend", tileHtml(product));
        }
      }
    })
    .catch(() => {
      // Keep existing content on failure (already server-rendered)
    });
}