feat(ui): redesign UI with Tailwind CSS, CodeMirror 6, and new layout
- Replace all hand-written CSS with Tailwind utility classes - Upgrade CodeMirror 5 to CodeMirror 6 (scoped @codemirror/* packages) - Redesign layout with responsive sidebar navigation using JSX/Mithril - Flatten admin routes (e.g. /admin/presets -> /presets) - Add "remember me" login option with 2-week persistent cookie - Add delete confirmation dialogs across admin pages - Upgrade esbuild from 0.21 to 0.27 and update browser targets - Add Inter and Roboto Mono variable fonts with subset script
This commit is contained in:
1 parent
a7256620e0
commit
4a191afdec
64 files changed
+4147
-2421
No files matched your search
@@ -2,7 +2,6 @@ import { ClosureComponent, Component } from "mithril";
|
||||
import { m } from "../components.ts";
|
||||
import * as store from "../store.ts";
|
||||
import * as notifications from "../notifications.ts";
|
||||
import { getIcon } from "../icons.ts";
|
||||
import { stringify } from "../../lib/common/yaml.ts";
|
||||
|
||||
const component: ClosureComponent = (): Component => {
|
||||
@@ -23,63 +22,111 @@ const component: ClosureComponent = (): Component => {
|
||||
"Detail",
|
||||
"Retries",
|
||||
"Timestamp",
|
||||
].map((l) => m("th", l));
|
||||
const thead = m("thead", m("tr", headers));
|
||||
"",
|
||||
].map((l, i) => {
|
||||
let padding: string;
|
||||
if (i === 0) padding = "pl-6 pr-3";
|
||||
else if (i === 6) padding = "pl-3";
|
||||
else padding = "px-3";
|
||||
return m(
|
||||
"th",
|
||||
{
|
||||
scope: "col",
|
||||
class:
|
||||
"py-3.5 text-left text-sm font-semibold text-stone-500 " +
|
||||
padding,
|
||||
},
|
||||
l,
|
||||
);
|
||||
});
|
||||
const thead = m("thead.bg-stone-50", m("tr", headers));
|
||||
|
||||
const rows = [];
|
||||
for (const f of faults.value) {
|
||||
rows.push([
|
||||
m("td", f["channel"]),
|
||||
m("td", f["code"]),
|
||||
m("td", m("long-text", { text: f["message"] })),
|
||||
m("td", m("long-text", { text: stringify(f["detail"]) })),
|
||||
|
||||
m("td", f["retries"]),
|
||||
m("td", new Date(f["timestamp"]).toLocaleString()),
|
||||
rows.push(
|
||||
m(
|
||||
"td",
|
||||
"tr",
|
||||
m(
|
||||
"button",
|
||||
{
|
||||
title: "Delete fault",
|
||||
onclick: (e) => {
|
||||
e.redraw = false;
|
||||
store
|
||||
.deleteResource("faults", f["_id"])
|
||||
.then(() => {
|
||||
notifications.push("success", "Fault deleted");
|
||||
store.setTimestamp(Date.now());
|
||||
m.redraw();
|
||||
})
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.setTimestamp(Date.now());
|
||||
});
|
||||
"td.whitespace-nowrap pl-6 pr-3 py-4 text-sm text-stone-900",
|
||||
f["channel"],
|
||||
),
|
||||
m(
|
||||
"td.whitespace-nowrap px-3 py-4 text-sm text-stone-900",
|
||||
f["code"],
|
||||
),
|
||||
m(
|
||||
"td.whitespace-nowrap px-3 py-4 text-sm text-stone-900",
|
||||
m("long-text", { text: f["message"], class: "max-w-xs" }),
|
||||
),
|
||||
m(
|
||||
"td.whitespace-nowrap px-3 py-4 text-sm text-stone-900",
|
||||
m("long-text", {
|
||||
text: stringify(f["detail"]),
|
||||
class: "max-w-xs",
|
||||
}),
|
||||
),
|
||||
|
||||
m(
|
||||
"td.whitespace-nowrap px-3 py-4 text-sm text-stone-900",
|
||||
f["retries"],
|
||||
),
|
||||
m(
|
||||
"td.whitespace-nowrap px-3 py-4 text-sm text-stone-900",
|
||||
new Date(f["timestamp"]).toLocaleString(),
|
||||
),
|
||||
m(
|
||||
"td.whitespace-nowrap pl-3 pr-6 py-4 text-sm text-stone-900",
|
||||
m(
|
||||
"button",
|
||||
{
|
||||
class: "text-cyan-700 hover:text-cyan-900 font-medium",
|
||||
title: "Delete fault",
|
||||
onclick: (e) => {
|
||||
e.redraw = false;
|
||||
store
|
||||
.deleteResource("faults", f["_id"])
|
||||
.then(() => {
|
||||
notifications.push("success", "Fault deleted");
|
||||
store.setTimestamp(Date.now());
|
||||
m.redraw();
|
||||
})
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
store.setTimestamp(Date.now());
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
getIcon("remove"),
|
||||
"Delete",
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
let tbody;
|
||||
if (rows.length) {
|
||||
tbody = m(
|
||||
"tbody",
|
||||
rows.map((r) => m("tr", r)),
|
||||
);
|
||||
} else {
|
||||
tbody = m(
|
||||
"tbody",
|
||||
m("tr.empty", m("td", { colspan: headers.length }, "No faults")),
|
||||
if (!rows.length) {
|
||||
rows.push(
|
||||
m(
|
||||
"tr",
|
||||
m(
|
||||
"td.bg-stripes text-sm font-medium text-center text-stone-500 p-4",
|
||||
{ colspan: headers.length },
|
||||
"No faults",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return m(
|
||||
"loading",
|
||||
{ queries: [faults] },
|
||||
m("table.table", thead, tbody),
|
||||
m(
|
||||
"div.shadow-sm overflow-hidden rounded-lg w-max",
|
||||
m(
|
||||
"table.divide-y divide-stone-200",
|
||||
thead,
|
||||
m("tbody.divide-y divide-stone-200 bg-white", rows),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user