Dashboard
A feature-rich dashboard demonstrating CRUD, live stats, and search.
Full Code
"""kokage-ui sample: Dashboard application."""
import enum
import random
from typing import Literal
from fastapi import FastAPI, Request
from pydantic import BaseModel, Field
from kokage_ui import (
A, Alert, AutoRefresh, Card, DaisyButton, DaisyTable,
Div, Form, H1, H2, Hero, InMemoryStorage, KokageUI,
ModelDetail, ModelForm, ModelTable, NavBar, P, Page,
SearchFilter, Span, Stat, Stats,
)
app = FastAPI()
ui = KokageUI(app)
class Role(enum.Enum):
ADMIN = "admin"
EDITOR = "editor"
VIEWER = "viewer"
class User(BaseModel):
id: str = ""
name: str = Field(min_length=1, max_length=100)
email: str
age: int = Field(ge=0, le=150, default=30)
role: Role = Role.VIEWER
is_active: bool = True
bio: str = Field(default="", max_length=500)
INITIAL_USERS = [
User(id="1", name="Tanaka Taro", email="tanaka@example.com",
age=35, role=Role.ADMIN, is_active=True, bio="System administrator"),
User(id="2", name="Suzuki Hanako", email="suzuki@example.com",
age=28, role=Role.EDITOR, is_active=True),
# ... more users
]
USERS_DICT = [
{"name": u.name, "email": u.email, "role": u.role.value.title()}
for u in INITIAL_USERS
]
storage = InMemoryStorage(User, initial=INITIAL_USERS)
def _navbar():
return NavBar(
start=A("kokage Dashboard", cls="btn btn-ghost text-xl", href="/"),
end=Div(
A("Home", cls="btn btn-ghost", href="/"),
A("Users", cls="btn btn-ghost", href="/users"),
A("New User", cls="btn btn-ghost", href="/users/new"),
),
)
def _page_wrapper(content, title):
return Page(
_navbar(), content,
title=f"{title} - kokage Dashboard", theme="light",
)
# CRUD for User model
ui.crud(
"/users", model=User, storage=storage,
title="Users", form_exclude=["bio"],
page_wrapper=_page_wrapper,
)
@ui.page("/")
def index():
return Page(
_navbar(),
Div(
Hero(
H1("kokage-ui Demo", cls="text-5xl font-bold"),
P("FastAPI + htmx + DaisyUI", cls="py-4 text-lg"),
DaisyButton("Get Started", color="primary", size="lg"),
min_height="50vh",
),
H2("Live Stats (auto-refresh every 3s)",
cls="text-2xl font-bold mt-8 mb-4 px-4"),
AutoRefresh(
Span("Loading...", cls="loading loading-spinner"),
url="/api/stats", interval=3,
),
H2("User Search", cls="text-2xl font-bold mt-8 mb-4 px-4"),
Div(
SearchFilter(
url="/api/users/search", target="#user-table",
placeholder="Search users...",
),
cls="px-4 mb-4",
),
Div(id="user-table"),
cls="container mx-auto",
),
title="kokage Dashboard", theme="light",
)
@ui.fragment("/api/stats")
def live_stats(request: Request):
return Stats(
Stat(title="Active Users", value=str(random.randint(80, 150))),
Stat(title="Requests/s", value=f"{random.randint(100, 500):,}"),
Stat(title="CPU Usage", value=f"{random.randint(20, 80)}%"),
Stat(title="Memory", value=f"{random.randint(40, 90)}%"),
)
@ui.fragment("/api/users/search")
def search_users(request: Request, q: str = ""):
filtered = (
[u for u in USERS_DICT if q.lower() in u["name"].lower()]
if q else USERS_DICT
)
return DaisyTable(
headers=["Name", "Email", "Role"],
rows=[[u["name"], u["email"], u["role"]] for u in filtered],
zebra=True,
)
Run
Open http://localhost:8000.
Key Patterns
Hero Section
The landing page uses Hero for a prominent banner:
Hero(
H1("kokage-ui Demo", cls="text-5xl font-bold"),
P("FastAPI + htmx + DaisyUI", cls="py-4 text-lg"),
DaisyButton("Get Started", color="primary", size="lg"),
min_height="50vh",
)
Live Auto-Refreshing Stats
AutoRefresh polls /api/stats every 3 seconds, and the @ui.fragment endpoint returns a Stats component with random values:
Live Search
SearchFilter sends debounced keyup events to /api/users/search, which filters and returns a DaisyTable:
CRUD with Custom page_wrapper
Instead of using Layout, this example defines a custom _page_wrapper function to wrap CRUD pages with a navbar:
def _page_wrapper(content, title):
return Page(_navbar(), content, title=f"{title} - kokage Dashboard")
ui.crud("/users", model=User, storage=storage, page_wrapper=_page_wrapper)
Enum Fields
The Role enum automatically renders as a <select> dropdown in forms, with options derived from enum members.