Dashboard
A web app with a sidebar, an overview of the week's numbers, a table of orders with a page for each, products, search, settings with a plan page, and a sign-in screen, made from the App kit.
The dashboard template is a small web app for running a shop: a sidebar of pages beside a top bar with a search, an overview of metric cards and a chart, a table of orders with filters and a page for each order, products by category, search results, a page with nothing in it yet, a feature to turn on, settings with a plan page under them, and a sign-in screen that the account menu's Sign out leads to. It has a page of every kind buttery add page writes for an app's shell. Use it for an admin, a back office or any app whose screens are pages of numbers, lists and settings.
Start from it
Make the app
npx create-buttery my-app --template dashboardcreate-butterymakesmy-appin the folder you're in and installs it with the package manager that ran it. The app takes the folder's name, which also replaces "buttery dashboard" in the sidebar.Inside the buttery repository,
pnpm new-app my-app --template dashboardcopiesapps/templates/dashboardtoapps/my-appinstead, with the next free port.Start it
cd my-app npm run devOpen the address it prints. The template is a web app;
--desktopadds the Tauri shell to it, as it does to any template.
What's inside
| File | What it does |
|---|---|
src/App.tsx | The shell: AppShell with a Sidebar, an AccountMenu and a TopBar with a CommandSearch and a New order button that opens a dialog, and the page the address names |
src/ | Two MetricCards for the week and a BarChart of sales each day |
src/pages/OrdersPage.tsx | A DataTable of orders, sorted by a column, with a status filter and a search in a FilterBar |
src/pages/OrderPage.tsx | A DetailPage for one order, its next step at the top of the aside, under breadcrumbs back to the orders |
src/ | A CatalogPage of products, one category at a time |
src/pages/SearchPage.tsx | SearchResults for what the top bar's search found, orders first |
src/ | An EmptyState in a page with no returns yet |
src/ | A FeatureIntro for subscriptions, before they're turned on |
src/ | A SettingsIndex of every setting, each opening its own page |
src/ | A SettingsPage with a form section that saves and a SettingRow that turns emails on and off |
src/pages/PlanPage.tsx | A PlanComparison of the two plans, monthly or yearly, with the shop's own marked |
src/pages/SignInPage.tsx | An AuthFlow on a screen of its own, outside the shell, which goes on to the overview once a code is accepted |
src/auth.ts | Stand-ins for your server's two calls, sending a code and checking it, which accept the demo code 123456; replace them with calls to your server |
src/NewOrderDialog.tsx | A FormDialog that adds an order once its three fields are filled in |
src/data.ts | Made-up orders, products and numbers, and the helpers that format them |
src/search.ts | What the top bar's search and the search page look through: the orders and the products |
src/status.ts | How each order status reads, and its badge's tone |
src/app.css | Makes the app as tall as the window |
src/router.tsx | The router every template has; see Routing below |
src/main.tsx | Follows links, then renders the app inside ButteryProvider |
src/index.ts | What another app imports to show this one, as the docs site does for the preview |
e2e/smoke.spec.ts | Renders every page with no console errors, and drives the sidebar, the filters, New order, and signing out and back in |
e2e/layout.spec.ts | Runs expectButtery on every page, with its menus and dialogs open |
It's made from these patterns: the app shell, an overview, a data table page, a detail page, a catalog page, search and results, settings pages, plans and authentication.
The overview screen
The app opens on src/, its main screen: the week's sales and orders in two MetricCards, then sales each day in a BarChart.
import type { ReactElement } from "react";
import { Grid } from "@butteryui/core";
import {
AppPage,
ArrowLink,
BarChart,
MetricCard,
PageHeader,
} from "@butteryui/react";
import { ORDER_COUNTS, SALES, WEEK, change, money, total } from "../data.ts";
import { useLink } from "../router.tsx";
/** The dashboard's front page: the week's numbers, then sales day by day. */
export function OverviewPage(): ReactElement {
const link = useLink();
const sales = total(SALES.now);
const orders = total(ORDER_COUNTS.now);
return (
<AppPage
header={
<PageHeader
title="Overview"
description="How the shop did over the last seven days."
/>
}
>
<AppPage.Section title="This week">
<Grid columns={{ base: 1, md: 2 }} gap={4}>
<MetricCard
title="Sales"
info="Every sale, before refunds and fees."
value={money(sales)}
delta={change(sales, total(SALES.before))}
labels={WEEK}
series={[
{ key: "now", label: "This week", values: SALES.now },
{
key: "before",
label: "Last week",
values: SALES.before,
comparison: true,
},
]}
formatValue={money}
updated="Updated just now"
href={link("/orders")}
/>
<MetricCard
title="Orders"
value={String(orders)}
delta={change(orders, total(ORDER_COUNTS.before))}
labels={WEEK}
series={[
{ key: "now", label: "This week", values: ORDER_COUNTS.now },
{
key: "before",
label: "Last week",
values: ORDER_COUNTS.before,
comparison: true,
},
]}
updated="Updated just now"
href={link("/orders")}
/>
</Grid>
</AppPage.Section>
<AppPage.Section
title="Sales each day"
actions={<ArrowLink href={link("/orders")}>See orders</ArrowLink>}
>
<BarChart
label="Sales each day this week"
summary={`${money(sales)} this week, against ${money(total(SALES.before))} the week before.`}
labels={WEEK}
labelsHeading="Day"
series={[
{ key: "now", label: "This week", values: SALES.now },
{
key: "before",
label: "Last week",
values: SALES.before,
comparison: true,
},
]}
formatValue={money}
/>
</AppPage.Section>
</AppPage>
);
}src/App.tsx shows it inside the AppShell, beside the sidebar and under the top bar, as it does every page. useLink comes from the template's router, and the numbers from src/data.ts.
Routing
Each template has a small router of its own in src/router.tsx, with no dependency, so its pages have real addresses, such as /orders and /settings. The app uses four of its exports:
| Export | What it does |
|---|---|
useRoute() | Reads the path and the query from the address, and follows it as it changes |
useLink() | Turns a path in the app into its address |
useNavigate() | Goes to a path in the app |
followLinks() | Makes a click on any link to the app a move within the page, so buttery's link components, which render a plain <a href>, work as they are; src/main.tsx calls it |
An order's page answers at its own path, such as /orders/1048, and the search page reads what to look for from the query, such as /search?q=tote.
An app served under a path of its own takes it as base, and every link and deep link keeps that start. The docs site shows this template at / with:
<App base="/templates/dashboard/preview" />A demo made from it
Halfmoon Roastery is this template with a coffee roaster's pages in place of the template's: orders, customers, wholesale, settings and a search across all of it.
Related
- App shell
The frame of a whole app, as an editor or a dashboard, and the page that goes in it.
- Overview
An app's home page, with headline metrics, a chart, what needs attention and guidance for a new account.
- Data table page
A page whose main block is a table, with filters, saved views and the table taking the rest of the height.
- Detail page
A page about one thing, with its main column and an aside holding its one primary action.
- Catalog page
A page of things to browse, with a filter column beside featured items and grids of cards.
- Search and results
The search in an app's top bar, and the page of every result it leads to.
- Settings pages
A settings page made of sections, each with a heading, one line about it and its one action.
- Authentication
A passwordless sign-in that's also the sign-up, with an email or phone number and then a one-time code, on one surface.