feat: started to make things work

This commit is contained in:
didier committed 2026-06-07 12:30:08 -04:00
1 parent 9e21732d00
commit de6480ba4e
8 files changed
+157 -106

No files matched your search

+86 -52
View File
@@ -1,77 +1,111 @@
import m from 'https://unpkg.com/mithril@2/mithril.esm.js';
import m from "https://esm.sh/mithril@2.3.8";
const API = '/api/v1/flashcards';
const API = "/api/v1/flashcards";
const Card = {
view: function (v) {
return m("li", v.attrs.card.id);
},
};
const List = {
oninit: async function (v) {
v.state.ids = [];
v.state.cards = [];
v.state.loading = true;
try {
const res = await fetch(API);
const data = await res.json();
v.state.ids = (data.flashcards || []).map(u => u.split('/').pop());
} catch { /* ignore */ }
const res = await fetch(API);
const data = await res.json();
v.state.cards = data.flashcards || [];
v.state.loading = false;
m.redraw();
},
view: function (v) {
if (v.state.loading) return m('.loading', m('i.nes-icon.nes-logo.is-small'));
return [
m('.header', [
m('h1', 'St\u2011Pierre Flashcards'),
m('span.nes-badge', m('span', String(v.state.ids.length))),
]),
v.state.ids.length === 0 && m('p', 'No flashcards yet.'),
v.state.ids.map(id => m('.card-preview.nes-container.is-rounded.with-title', {
onclick: () => m.route.set('/flashcard/' + id),
}, [
m('p.title', '#' + id),
m('p', 'Click to view details \u2192'),
])),
];
if (v.state.loading) {
return m("h1", "loading");
}
const cards = m(
".cards-list",
v.state.cards.map((c) => m(Card, { card: c })),
);
return m("ol", cards);
},
};
const Detail = {
oninit: async function (v) {
v.state.entry = null;
v.state.flashcard = null;
v.state.loading = true;
try {
const res = await fetch(API + '/' + v.attrs.id);
if (res.ok) v.state.entry = await res.json();
} catch { /* ignore */ }
v.state.flashcard = await findFlashCardByID(v.attrs.id);
v.state.loading = false;
m.redraw();
},
view: function (v) {
if (v.state.loading) return m('.loading', m('i.nes-icon.nes-logo.is-small'));
if (v.state.loading)
return m(".loading", m("i.nes-icon.nes-logo.is-small"));
const e = v.state.entry;
if (!e) return [
m('.back', m('button.nes-btn', { onclick: () => m.route.set('/') }, '\u2B05 Back')),
m('p', 'Flashcard not found.'),
];
if (!e)
return [
m(
".back",
m(
"button.nes-btn",
{ onclick: () => m.route.set("/") },
"\u2B05 Back",
),
),
m("p", "Flashcard not found."),
];
return [
m('.back', m('button.nes-btn.is-warning', { onclick: () => m.route.set('/') }, '\u2B05 Back')),
m('.detail-card.nes-container.is-rounded.with-title', [
m('p.title', '#' + e.public_id + ' v' + e.version),
m('.field', [ m('.field-label', 'Note'), m('p', e.note) ]),
e.host ? m('.field', [ m('.field-label', 'Host'), m('p', e.host) ]) : null,
e.user ? m('.field', [ m('.field-label', 'User'), m('p', e.user) ]) : null,
e.context ? m('.field', [ m('.field-label', 'Context'), m('p', e.context) ]) : null,
e.repos?.length ? m('.field', [
m('.field-label', 'Repos'),
m('div', e.repos.map(r => m('span.nes-badge', m('span', r)))),
]) : null,
e.tags?.length ? m('.field', [
m('.field-label', 'Tags'),
m('div', e.tags.map(t => m('span.nes-badge', m('span', t)))),
]) : null,
m('.field', [ m('.field-label', 'Created'), m('p', e.created) ]),
e.delete_timestamp ? m('.field', [ m('.field-label', 'Deleted'), m('p', e.delete_timestamp) ]) : null,
m(
".back",
m(
"button.nes-btn.is-warning",
{ onclick: () => m.route.set("/") },
"\u2B05 Back",
),
),
m(".detail-card.nes-container.is-rounded.with-title", [
m("p.title", "#" + e.public_id + " v" + e.version),
m(".field", [m(".field-label", "Note"), m("p", e.note)]),
e.host
? m(".field", [m(".field-label", "Host"), m("p", e.host)])
: null,
e.user
? m(".field", [m(".field-label", "User"), m("p", e.user)])
: null,
e.context
? m(".field", [m(".field-label", "Context"), m("p", e.context)])
: null,
e.repos?.length
? m(".field", [
m(".field-label", "Repos"),
m(
"div",
e.repos.map((r) => m("span.nes-badge", m("span", r))),
),
])
: null,
e.tags?.length
? m(".field", [
m(".field-label", "Tags"),
m(
"div",
e.tags.map((t) => m("span.nes-badge", m("span", t))),
),
])
: null,
m(".field", [m(".field-label", "Created"), m("p", e.created)]),
e.delete_timestamp
? m(".field", [
m(".field-label", "Deleted"),
m("p", e.delete_timestamp),
])
: null,
]),
];
},
};
m.route(document.getElementById('app'), '/', {
'/': List,
'/flashcard/:id': Detail,
m.route(document.getElementById("app"), "/", {
"/": List,
"/flashcard/:id": Detail,
});
+11 -29
View File
@@ -1,31 +1,13 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>St‑Pierre Flashcards</title>
<link rel="stylesheet" href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css">
<style>
html, body { background: #212529; min-height: 100vh; }
body { padding: 1rem; }
.flashcard-body { max-width: 800px; margin: 0 auto; }
.card-preview { cursor: pointer; margin-bottom: .75rem; }
.card-preview:hover { filter: brightness(1.2); }
.card-preview .note { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.tags-row { margin-top: .5rem; }
.meta-row { display: flex; flex-wrap: wrap; gap: .25rem .75rem; font-size: .85rem; margin-top: .25rem; }
.back { margin-bottom: 1.5rem; }
.detail-card .nes-badge { margin-right: .25rem; }
.detail-card .field { margin-bottom: 1rem; }
.detail-card .field-label { font-size: .75rem; opacity: .7; margin-bottom: .25rem; }
.loading { text-align: center; padding: 4rem 0; }
.header { margin-bottom: 2rem; display: flex; align-items: center; gap: .75rem; }
.nes-badge.is-icon span { position: static; }
.vote-summary { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #fff3; }
</style>
</head>
<body>
<div class="flashcard-body" id="app"></div>
<script type="module" src="/app.js"></script>
</body>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>St‑Pierre Flashcards</title>
</head>
<body>
<h1>coquelicot d'amour</h1>
<div class="flashcard-body" id="app"></div>
<script type="module" src="/app.js"></script>
</body>
</html>