AkurAI Build
Menu

Public repository / 9846363ea7fea56c5079985945447933709e6726

AkurAI-Notes / site/frontend/notes-app.js

Latest change 9846363ea7fea56c5079985945447933709e6726 - fix: restore Notes app SSR routes by Claude Code

(function () {
  "use strict";

  function esc(value) {
    return String(value ?? "").replace(/[&<>"]/g, function (m) {
      return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[m];
    });
  }

  async function jsonFetch(url) {
    const res = await fetch(url, {
      credentials: "same-origin",
      headers: { accept: "application/json" },
    });
    const text = await res.text();
    let data = null;
    if (text) {
      try {
        data = JSON.parse(text);
      } catch (err) {
        throw new Error("The server returned invalid JSON");
      }
    }
    if (data && data.error === "unauthorized") {
      window.location.href = "/auth/login";
      throw new Error("Redirecting to sign in");
    }
    if (!res.ok) {
      throw new Error((data && data.error) || `HTTP ${res.status}`);
    }
    return data;
  }

  function renderNoteCard(note) {
    const title = note.title || "Untitled";
    const body = note.snippet || note.body || "";
    return (
      '<article class="note-card">' +
      "<h3>" + esc(title) + "</h3>" +
      '<p class="note-preview">' + esc(body).slice(0, 360) + "</p>" +
      "</article>"
    );
  }

  function renderEmpty(target, message) {
    target.innerHTML = '<p class="empty-state">' + esc(message) + "</p>";
  }

  async function initNotesPage() {
    const notebooksEl = document.getElementById("notebook-list");
    const content = document.getElementById("notes-content");
    if (!notebooksEl || !content) return;

    try {
      const me = await jsonFetch("/api/me");
      if (!me || !me.authenticated) {
        window.location.href = "/auth/login";
        return;
      }

      const [notebooks, notes] = await Promise.all([
        jsonFetch("/api/notebooks"),
        jsonFetch("/api/notes"),
      ]);

      const notebookList = Array.isArray(notebooks) ? notebooks : [];
      const noteList = Array.isArray(notes) ? notes : [];

      notebooksEl.innerHTML = notebookList.length
        ? notebookList
            .map(function (nb) {
              return '<li class="notebook-item">' + esc(nb.name || "Untitled") + "</li>";
            })
            .join("")
        : '<li class="notebook-item muted">No notebooks</li>';

      if (!noteList.length) {
        renderEmpty(content, "No notes found");
        return;
      }
      content.innerHTML = '<div class="note-grid">' + noteList.map(renderNoteCard).join("") + "</div>";
    } catch (err) {
      if (String(err.message).includes("Redirecting")) return;
      renderEmpty(content, "Could not load notes: " + err.message);
    }
  }

  async function runSearch(query) {
    const resultsEl = document.getElementById("notes-search-results");
    const metaEl = document.getElementById("notes-search-meta");
    if (!resultsEl || !metaEl) return;
    const q = query.trim();
    if (!q) {
      metaEl.textContent = "";
      renderEmpty(resultsEl, "Type a search query");
      return;
    }
    metaEl.textContent = "Searching";
    try {
      const data = await jsonFetch("/api/search?q=" + encodeURIComponent(q));
      const results = Array.isArray(data && data.results) ? data.results : [];
      metaEl.textContent = results.length + " result" + (results.length === 1 ? "" : "s") + " ยท " + (data.mode || "search");
      if (!results.length) {
        renderEmpty(resultsEl, "No matching notes");
        return;
      }
      resultsEl.innerHTML = results.map(renderNoteCard).join("");
    } catch (err) {
      if (String(err.message).includes("Redirecting")) return;
      metaEl.textContent = "";
      renderEmpty(resultsEl, "Search failed: " + err.message);
    }
  }

  function initSearchPage() {
    const form = document.getElementById("notes-search-form");
    const input = document.getElementById("notes-search-input");
    const params = new URLSearchParams(window.location.search);
    if (!form || !input) return;
    input.value = params.get("q") || "";
    form.addEventListener("submit", function (event) {
      event.preventDefault();
      const q = input.value.trim();
      const next = q ? "/app/search?q=" + encodeURIComponent(q) : "/app/search";
      window.history.replaceState(null, "", next);
      runSearch(q);
    });
    runSearch(input.value);
  }

  async function initSettingsPage() {
    const email = document.getElementById("settings-email");
    if (!email) return;
    try {
      const me = await jsonFetch("/api/me");
      if (!me || !me.authenticated) {
        window.location.href = "/auth/login";
        return;
      }
      email.textContent = me.email || me.sub || "Authenticated";
    } catch (err) {
      if (String(err.message).includes("Redirecting")) return;
      email.textContent = "Session unavailable";
    }
  }

  window.NotesApp = { initNotesPage, initSearchPage, initSettingsPage };
})();