Files
st-pierre/public/app.js
T
2026-06-08 08:54:01 -04:00

157 lines
5.0 KiB
JavaScript

import m from "https://esm.sh/mithril@2.3.8";
const API = "/api/v1/flashcards";
const Card = {
view: function (v) {
const c = v.attrs.card;
const maxNote = (c.note || "").length > 120
? c.note.slice(0, 120) + "…"
: c.note;
return m(
".cyber-card.cyber-card--interactive.cyber-neon-border.cyber-mb-md",
{
onclick: () => m.route.set("/flashcard/" + c.id),
style: "cursor:pointer",
},
[
c.tags?.length
? m(
".cyber-card__header",
c.tags.map((t) => m("span.cyber-badge.cyber-badge--pulse.cyber-mr-xs", t)),
)
: null,
m(".cyber-card__body", [
m(".cyber-text-muted.cyber-font-mono", `#${c.id} v${c.version}`),
m("p.cyber-text-primary.cyber-mt-sm", maxNote || m("em.cyber-text-muted", "no content")),
]),
],
);
},
};
const PAGE_SIZE = 20;
const Pager = {
view: function (v) {
const { page, total } = v.attrs;
if (total <= 1) return null;
return m(".cyber-flex.cyber-justify-center.cyber-items-center.cyber-gap-sm.cyber-my-md", [
m(
"button.cyber-btn.cyber-btn--sm" + (page <= 1 ? ".cyber-btn--ghost" : ""),
{ disabled: page <= 1, onclick: () => v.attrs.onpage(page - 1) },
"\u2190",
),
m("span.cyber-font-mono.cyber-text-muted", `${page} / ${total}`),
m(
"button.cyber-btn.cyber-btn--sm" + (page >= total ? ".cyber-btn--ghost" : ""),
{ disabled: page >= total, onclick: () => v.attrs.onpage(page + 1) },
"\u2192",
),
]);
},
};
const List = {
oninit: async function (v) {
v.state.cards = [];
v.state.loading = true;
const res = await fetch(API);
const data = await res.json();
v.state.cards = data.flashcards || [];
v.state.loading = false;
v.state.page = 1;
m.redraw();
},
view: function (v) {
if (v.state.loading) {
return m(".cyber-center-xy", [
m(".cyber-spinner.cyber-spinner--lg"),
m("p.cyber-text-muted.cyber-mt-md", "loading…"),
]);
}
const totalPages = Math.max(1, Math.ceil(v.state.cards.length / PAGE_SIZE));
const page = Math.min(v.state.page, totalPages);
const slice = v.state.cards.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
return m(".cyber-p-lg", { style: "max-width:720px;margin:0 auto" }, [
m(Pager, {
page,
total: totalPages,
onpage: (n) => { v.state.page = n; },
}),
m(".cyber-scanlines", slice.map((c) => m(Card, { card: c }))),
m(Pager, {
page,
total: totalPages,
onpage: (n) => { v.state.page = n; },
}),
]);
},
};
const Detail = {
oninit: async function (v) {
v.state.entry = null;
v.state.loading = true;
try {
const res = await fetch(`${API}/${v.attrs.id}`);
if (res.ok) v.state.entry = await res.json();
} catch {}
v.state.loading = false;
m.redraw();
},
view: function (v) {
if (v.state.loading) {
return m(".cyber-center-xy", m(".cyber-spinner.cyber-spinner--lg"));
}
const e = v.state.entry;
if (!e) {
return m(".cyber-p-lg", { style: "max-width:720px;margin:0 auto" }, [
m("button.cyber-btn.cyber-btn--ghost", { onclick: () => m.route.set("/") }, "\u2B05 Back"),
m("p.cyber-text-magenta", "Flashcard not found."),
]);
}
return m(".cyber-p-lg", { style: "max-width:720px;margin:0 auto" }, [
m("button.cyber-btn.cyber-btn--ghost.cyber-mb-md", { onclick: () => m.route.set("/") }, "\u2B05 Back"),
m(".cyber-card.cyber-card--holo", [
m(".cyber-card__header", [
m("span.cyber-font-mono.cyber-text-cyan", `#${e.id} v${e.version}`),
]),
m(".cyber-card__body", [
m(".cyber-field", [m(".cyber-field__label", "Note"), m("p", e.note)]),
e.host
? m(".cyber-field.cyber-mt-sm", [m(".cyber-field__label", "Host"), m("p", e.host)])
: null,
e.user
? m(".cyber-field.cyber-mt-sm", [m(".cyber-field__label", "User"), m("p", e.user)])
: null,
e.context
? m(".cyber-field.cyber-mt-sm", [m(".cyber-field__label", "Context"), m("p", e.context)])
: null,
e.repos?.length
? m(".cyber-field.cyber-mt-sm", [
m(".cyber-field__label", "Repos"),
m("div", e.repos.map((r) => m("span.cyber-badge.cyber-mr-xs", r))),
])
: null,
e.tags?.length
? m(".cyber-field.cyber-mt-sm", [
m(".cyber-field__label", "Tags"),
m("div", e.tags.map((t) => m("span.cyber-badge.cyber-mr-xs", t))),
])
: null,
m(".cyber-field.cyber-mt-sm", [m(".cyber-field__label", "Created"), m("p", e.created)]),
e.delete_timestamp
? m(".cyber-field.cyber-mt-sm", [m(".cyber-field__label", "Deleted"), m("p", e.delete_timestamp)])
: null,
]),
]),
]);
},
};
m.route(document.getElementById("app"), "/", {
"/": List,
"/flashcard/:id": Detail,
});