import { ui } from "@rezi-ui/core";
import { createNodeApp } from "@rezi-ui/node";
type LoadingState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: string };
type User = { id: string; name: string; email: string };
type State = {
users: LoadingState<User[]>;
};
const app = createNodeApp<State>({
initialState: { users: { status: "idle" } },
});
function fetchUsers(): void {
app.update((s) => ({ ...s, users: { status: "loading" } }));
// Simulate API call
setTimeout(() => {
const success = Math.random() > 0.2; // 80% success rate
if (!success) {
app.update((s) => ({
...s,
users: { status: "error", error: "Failed to load users" },
}));
return;
}
app.update((s) => ({
...s,
users: {
status: "success",
data: [
{ id: "1", name: "Ada Lovelace", email: "[email protected]" },
{ id: "2", name: "Linus Torvalds", email: "[email protected]" },
{ id: "3", name: "Grace Hopper", email: "[email protected]" },
],
},
}));
}, 800);
}
function renderUsers(state: State) {
switch (state.users.status) {
case "idle":
return ui.button({
id: "load",
label: "Load Users",
intent: "primary",
onPress: fetchUsers,
});
case "loading":
return ui.column({ gap: 1 }, [
ui.spinner({ label: "Loading users..." }),
ui.skeleton(40),
ui.skeleton(40),
ui.skeleton(40),
]);
case "error":
return ui.empty("Error", {
description: state.users.error,
action: ui.button({
id: "retry",
label: "Retry",
intent: "primary",
onPress: fetchUsers,
}),
});
case "success":
return ui.column(
{ gap: 1 },
state.users.data.map((user) =>
ui.row({ key: user.id, gap: 2 }, [
ui.text(user.name, { flex: 1 }),
ui.text(user.email, { variant: "caption" }),
])
)
);
}
}
app.view((state) =>
ui.page({ p: 1 }, [
ui.panel("Users", [renderUsers(state)]),
])
);
app.keys({
"ctrl+c": () => app.stop(),
q: () => app.stop(),
r: () => fetchUsers(),
});
await app.start();