rdev/examples/dashboard-app/README.md
jordan 62460bf098 feat: complete template upgrade - chassis framework, UI library, auth, app-nextjs, OpenAPI, and cookbook
Weeks 1-7 of the template upgrade plan:
- pkg/api: typed HTTPError with sentinels, Wrap/WrapMiddleware, Bind, health probes, OpenAPI schema/param builders
- skeleton/packages: ui (design tokens, components), layout (DashboardShell), auth (AuthProvider, ProtectedRoute), api-client
- skeleton/pkg: httperror, app/handler, app/bind, app/health, auth (JWT/API key middleware)
- components/app-nextjs: Next.js 14 App Router template with dashboard, server actions, auth
- cookbooks/feature-development.md with test and validation scripts
- Handler tests for components, project management, and woodpecker webhook
- 3 rounds of code review fixes applied

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 00:46:51 -07:00

259 lines
8.0 KiB
Markdown

# Dashboard App Example
This example demonstrates a full-stack composable monorepo project using rdev's enhanced templates with:
- **Chassis patterns**: Wrap, Bind, HTTPError, Health probes
- **Design system**: packages/ui, packages/layout
- **Authentication**: pkg/auth, packages/auth
- **OpenAPI**: Auto-generated TypeScript client
- **Next.js 14**: App Router with server actions
## Creating This Example
```bash
# 1. Create the project
curl -X POST "$RDEV_API_URL/project" \
-H "X-API-Key: $RDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "dashboard-app",
"description": "Example dashboard application"
}'
# 2. Add the API service
curl -X POST "$RDEV_API_URL/projects/dashboard-app/components" \
-H "X-API-Key: $RDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "service",
"name": "api"
}'
# 3. Add the Next.js dashboard
curl -X POST "$RDEV_API_URL/projects/dashboard-app/components" \
-H "X-API-Key: $RDEV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "app-nextjs",
"name": "dashboard"
}'
```
## Project Structure
After creation, the project looks like:
```
dashboard-app/
├── CLAUDE.md # AI routing with guides
├── README.md # Project docs
├── go.work # Go workspace
├── pnpm-workspace.yaml # pnpm monorepo config
├── .woodpecker.yml # CI pipeline
├── pkg/ # Shared Go packages
│ ├── app/ # Wrap, Bind, Health
│ │ ├── app.go # NewApp, Run
│ │ ├── handler.go # Wrap pattern
│ │ ├── bind.go # BindAndValidate
│ │ └── health.go # Health probes
│ ├── httperror/ # Typed HTTP errors
│ │ └── error.go # Sentinels, factory functions
│ ├── httpresponse/ # JSON envelope
│ ├── httpvalidation/ # Validator wrapper
│ ├── middleware/ # HTTP middleware
│ └── auth/ # JWT + API key auth
│ ├── auth.go # Interface, types
│ ├── context.go # GetUser, SetUser
│ ├── jwt.go # JWTValidator
│ ├── apikey.go # APIKeyValidator
│ └── middleware.go # Auth middleware
├── packages/ # Shared TypeScript packages
│ ├── ui/ # Design system
│ │ ├── src/styles/tokens.css # CSS custom properties
│ │ ├── src/utils/cn.ts # clsx + tailwind-merge
│ │ └── src/components/ # Button, Card, Input, etc.
│ ├── layout/ # Shell components
│ │ ├── src/DashboardShell.tsx # Main layout
│ │ ├── src/Sidebar.tsx # Navigation
│ │ └── src/Header.tsx # Top bar
│ ├── auth/ # React auth
│ │ ├── src/AuthProvider.tsx # Context provider
│ │ ├── src/useAuth.ts # Hook
│ │ └── src/ProtectedRoute.tsx # Route guard
│ ├── logger/ # Structured logging
│ └── api-client/ # Generated client
│ └── src/schema.d.ts # TypeScript types
├── services/ # Backend services
│ └── api/
│ ├── cmd/server/main.go # Entry point
│ ├── internal/
│ │ ├── api/routes.go # Chi routes with Wrap
│ │ ├── config/config.go # Configuration
│ │ └── domain/ # Business models
│ ├── migrations/ # SQL migrations
│ ├── Dockerfile # Multi-stage Go build
│ └── component.yaml # Port, dependencies
└── apps/ # Frontend applications
└── dashboard/
├── src/app/
│ ├── layout.tsx # Root layout
│ ├── globals.css # Design tokens import
│ ├── (dashboard)/ # Protected route group
│ │ ├── layout.tsx # DashboardShell
│ │ ├── page.tsx # Home
│ │ └── settings/page.tsx
│ └── login/page.tsx # Public login
├── src/components/
│ └── providers.tsx # AuthProvider wrapper
├── src/actions/ # Server actions
├── Dockerfile # Next.js standalone
└── component.yaml
```
## Development Workflow
### Local Development
```bash
# Start infrastructure (Postgres, Redis)
docker-compose up -d
# Install dependencies
./scripts/install.sh
# Start all services
./scripts/dev.sh
```
### Building a Feature
1. **Create feature branch**
```bash
git checkout -b feature/user-profile
```
2. **Add API endpoint** using Wrap + Bind:
```go
func (h *Handler) GetMe() http.HandlerFunc {
return app.Wrap(func(w http.ResponseWriter, r *http.Request) error {
user, ok := auth.GetUser(r.Context())
if !ok {
return httperror.Unauthorized("not authenticated")
}
profile, err := h.svc.GetByID(r.Context(), user.ID)
if err != nil {
return httperror.WrapError(err, "failed to get profile")
}
httpresponse.JSON(w, http.StatusOK, profile)
return nil
})
}
```
3. **Regenerate TypeScript client**:
```bash
./scripts/generate-client.sh
```
4. **Add frontend page** using design system:
```tsx
import { Card, CardHeader, CardTitle } from '@dashboard-app/ui';
import { useAuth } from '@dashboard-app/auth';
export default function ProfilePage() {
const { user } = useAuth();
return (
<Card>
<CardHeader>
<CardTitle>Profile: {user?.name}</CardTitle>
</CardHeader>
</Card>
);
}
```
5. **Run tests and quality checks**:
```bash
./scripts/quality.sh
```
6. **Commit and push**:
```bash
git add -A
git commit -m "feat: add user profile"
git push -u origin feature/user-profile
```
## Key Patterns
### Backend: Wrap Pattern
```go
// Error-returning handlers become http.HandlerFunc
app.Wrap(func(w http.ResponseWriter, r *http.Request) error {
// Return errors - they become proper HTTP responses
if err := doThing(); err != nil {
return httperror.Internal("something went wrong")
}
httpresponse.JSON(w, http.StatusOK, data)
return nil
})
```
### Backend: Bind Pattern
```go
// Decode + validate in one call
var req CreateItemRequest
if err := app.BindAndValidate(r, &req); err != nil {
return err // Validation errors become 400 with details
}
```
### Frontend: Auth Hook
```tsx
const { user, isLoading, login, logout } = useAuth();
if (isLoading) return <Spinner />;
if (!user) return <LoginRedirect />;
```
### Frontend: Design System
```tsx
import { Button, Card, Input, Badge } from '@dashboard-app/ui';
import { DashboardShell, Sidebar } from '@dashboard-app/layout';
```
## Deployment
CI/CD is automated via Woodpecker:
1. Push triggers build
2. Kaniko builds Docker images
3. Images pushed to registry
4. kubectl deploys to K8s
Access the deployed app:
```bash
open https://<slug>.threesix.ai
```
## Cleanup
```bash
curl -X DELETE "$RDEV_API_URL/project/dashboard-app" \
-H "X-API-Key: $RDEV_API_KEY"
```
## Related Documentation
- [Feature Development Cookbook](../../cookbooks/feature-development.md)
- [Composable App Cookbook](../../cookbooks/composable-app.md)
- [API Framework Guide](../../.claude/guides/packages/api-framework.md)