# 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)
