# Milestone 5: Developer Portal Frontend

**Status**: Decided
**Goal**: Web interface for developer account and app management.

## Decision

**htmx + Go Templates** for server-rendered UI from the single Go container:

```
Rendering:      Go html/template
Interactivity:  htmx (partial page updates)
Styling:        Tailwind CSS (compiled at build time)
Charts:         Chart.js (lightweight)
Icons:          Heroicons or Lucide
Build:          Embed static assets in Go binary
```

### Rationale

1. **Single container** - No separate Node.js server needed
2. **Server-rendered** - All HTML generated by Go templates
3. **htmx for interactivity** - AJAX without JavaScript framework
4. **Embedded assets** - CSS/JS bundled into Go binary via `embed`
5. **Simple deployment** - Just the Go binary, nothing else
6. **Low resource usage** - Perfect for Synology NAS

### Architecture

```
┌─────────────────────────────────────────┐
│  mosis-portal container                  │
│  ┌────────────────────────────────────┐ │
│  │ Go binary                           │ │
│  │  ├── Chi router                     │ │
│  │  ├── html/template rendering        │ │
│  │  ├── Embedded static assets         │ │
│  │  │   ├── tailwind.css (built)       │ │
│  │  │   ├── htmx.min.js                │ │
│  │  │   └── chart.min.js               │ │
│  │  └── SQLite database                │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
```

---

## Overview

The developer portal is the primary interface for developers to manage their accounts, create apps, submit versions, and view analytics.

---

## Pages Required

### Public Pages

| Page | URL | Purpose |
|------|-----|---------|
| Landing | `/` | Marketing, sign up CTA |
| Sign In | `/login` | OAuth + password login |
| Sign Up | `/register` | Create account |
| Docs | `/docs/*` | Documentation (separate site?) |

### Authenticated Pages

| Page | URL | Purpose |
|------|-----|---------|
| Dashboard | `/dashboard` | App list, quick stats |
| App Details | `/apps/:id` | Single app overview |
| App Settings | `/apps/:id/settings` | Edit app metadata |
| App Versions | `/apps/:id/versions` | Version history |
| Submit Version | `/apps/:id/versions/new` | Upload new version |
| App Analytics | `/apps/:id/analytics` | Telemetry dashboard |
| Create App | `/apps/new` | New app wizard |
| API Keys | `/settings/keys` | Manage API keys |
| Signing Keys | `/settings/signing` | Manage signing keys |
| Profile | `/settings/profile` | Account settings |

---

## Wireframes

### Dashboard

```
┌─────────────────────────────────────────────────────────────┐
│  [Logo]  Dashboard   Apps   Docs   Settings   [Avatar ▼]    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Welcome back, Developer!                                   │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ Total Apps  │  │  Downloads  │  │ Active Users│         │
│  │     12      │  │   45,230    │  │    1,234    │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
│  Your Apps                                    [+ New App]   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ [Icon] My Calculator    v1.2.0   Published   1.2K ↓  │  │
│  │ [Icon] Notes App        v2.0.1   Published   5.4K ↓  │  │
│  │ [Icon] Weather Widget   v0.9.0   In Review   ---     │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### App Details

```
┌─────────────────────────────────────────────────────────────┐
│  ← Back to Apps                                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  [Icon]  My Calculator                                      │
│          com.developer.calculator                           │
│          ● Published                                        │
│                                                             │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐       │
│  │ Overview │ │ Versions │ │Analytics │ │ Settings │       │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘       │
│                                                             │
│  Latest Version: 1.2.0                [Submit New Version]  │
│  Published: Jan 15, 2024                                    │
│  Downloads: 1,234                                           │
│                                                             │
│  ┌────────────────────────────────────────┐                │
│  │  Downloads over time                   │                │
│  │  [Chart: line graph]                   │                │
│  └────────────────────────────────────────┘                │
│                                                             │
│  Recent Crashes                           [View All →]      │
│  • attempt to index nil (v1.2.0) - 23 reports              │
│  • memory limit exceeded (v1.1.0) - 5 reports              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### Submit Version

```
┌─────────────────────────────────────────────────────────────┐
│  Submit New Version - My Calculator                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Step 2 of 3: Upload Package                                │
│  ○ Details  ● Upload  ○ Review                              │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                     │   │
│  │     ┌─────────┐                                     │   │
│  │     │  .mosis │   Drop your package here            │   │
│  │     │   📦    │   or click to browse                │   │
│  │     └─────────┘                                     │   │
│  │                                                     │   │
│  │     Max size: 50 MB                                 │   │
│  │                                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ✓ Package validated                                        │
│  ✓ Signature verified                                       │
│  ✓ Permissions: storage, network                            │
│                                                             │
│                              [Back]  [Continue to Review]   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## Tech Stack Options

### Option A: Next.js 14

```
Framework:  Next.js 14 (App Router)
UI:         shadcn/ui + Tailwind CSS
State:      React Query + Zustand
Forms:      React Hook Form + Zod
Charts:     Recharts or Tremor
Auth:       NextAuth.js
```

| Pros | Cons |
|------|------|
| SSR for SEO landing page | Complexity |
| Great developer experience | React knowledge required |
| Full-stack capability | Heavier bundle |
| Large ecosystem | |

### Option B: SvelteKit

```
Framework:  SvelteKit
UI:         Skeleton UI or custom
State:      Svelte stores
Forms:      Superforms
Charts:     Chart.js or LayerCake
Auth:       Lucia
```

| Pros | Cons |
|------|------|
| Fast, small bundles | Smaller ecosystem |
| Simple state management | Fewer UI libraries |
| Great DX | Less hiring pool |

### Option C: Astro + React Islands

```
Framework:  Astro
UI:         React components (islands)
State:      Nanostores
Forms:      React Hook Form
Charts:     Recharts
Auth:       Custom
```

| Pros | Cons |
|------|------|
| Ultra-fast static pages | More setup |
| Partial hydration | Newer approach |
| Use React where needed | Less documented patterns |

### Option D: htmx + Go Templates

```
Framework:  Go templates + htmx
UI:         Tailwind CSS
State:      Server-side
Forms:      Native HTML
Charts:     Chart.js
Auth:       Server sessions
```

| Pros | Cons |
|------|------|
| Simple, fast | Limited interactivity |
| No JS framework | Less polished UX |
| Server-rendered | Complex UI harder |

---

## UI Component Library

### Option A: shadcn/ui

```
Base:       Radix UI primitives
Styling:    Tailwind CSS
Approach:   Copy-paste components
```

| Pros | Cons |
|------|------|
| High quality | React only |
| Full control | Manual updates |
| Accessible | |

### Option B: Tailwind UI

```
Base:       Headless UI
Styling:    Tailwind CSS
Approach:   Copy-paste templates
```

| Pros | Cons |
|------|------|
| Beautiful designs | Paid ($299) |
| Production-ready | Templates, not components |

### Option C: Mantine

```
Base:       Custom components
Styling:    CSS-in-JS or CSS
Approach:   npm package
```

| Pros | Cons |
|------|------|
| Complete solution | Opinionated |
| Many components | Larger bundle |
| Good docs | |

---

## Key Features (htmx Implementation)

### File Upload with Progress

```html
<!-- htmx file upload with progress indicator -->
<form hx-post="/api/apps/{{.App.ID}}/versions"
      hx-encoding="multipart/form-data"
      hx-target="#upload-result"
      hx-indicator="#upload-spinner">

  <div class="dropzone" id="dropzone">
    <input type="file" name="package" accept=".mosis" required
           class="hidden" id="file-input">
    <label for="file-input" class="cursor-pointer">
      <svg><!-- upload icon --></svg>
      <p>Drop your .mosis package here or click to browse</p>
      <p class="text-sm text-gray-500">Max size: 50 MB</p>
    </label>
  </div>

  <div id="upload-spinner" class="htmx-indicator">
    Uploading...
  </div>

  <button type="submit" class="btn btn-primary">Upload</button>
</form>

<div id="upload-result"></div>
```

### Dynamic App List

```html
<!-- Server renders this, htmx updates it -->
<div id="app-list"
     hx-get="/partials/apps"
     hx-trigger="load, newApp from:body"
     hx-swap="innerHTML">
  {{range .Apps}}
  <div class="app-card">
    <img src="{{.IconURL}}" alt="{{.Name}}">
    <h3>{{.Name}}</h3>
    <span class="badge {{.StatusClass}}">{{.Status}}</span>
    <a href="/apps/{{.ID}}" hx-boost="true">View →</a>
  </div>
  {{end}}
</div>
```

### Form with Validation

```html
<!-- Create app form with server-side validation -->
<form hx-post="/apps" hx-target="#form-result" hx-swap="outerHTML">
  <div class="form-group">
    <label for="name">App Name</label>
    <input type="text" name="name" id="name" required
           hx-post="/api/validate/name"
           hx-trigger="blur"
           hx-target="next .error">
    <span class="error"></span>
  </div>

  <div class="form-group">
    <label for="package_id">Package ID</label>
    <input type="text" name="package_id" id="package_id"
           placeholder="com.yourname.appname" required
           hx-post="/api/validate/package-id"
           hx-trigger="blur"
           hx-target="next .error">
    <span class="error"></span>
  </div>

  <button type="submit" class="btn btn-primary">Create App</button>
</form>
```

### Analytics Chart

```html
<!-- Chart.js for analytics -->
<div class="chart-container">
  <canvas id="downloads-chart"></canvas>
</div>

<script>
  // Data injected from Go template
  const chartData = {{.ChartDataJSON}};

  new Chart(document.getElementById('downloads-chart'), {
    type: 'line',
    data: {
      labels: chartData.labels,
      datasets: [{
        label: 'Downloads',
        data: chartData.values,
        borderColor: '#6366f1',
        tension: 0.3
      }]
    }
  });
</script>
```

---

## Go Template Structure

### Base Layout

```go
// templates/layouts/base.html
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{.Title}} - Mosis Developer Portal</title>
  <link href="/static/tailwind.css" rel="stylesheet">
  <script src="/static/htmx.min.js"></script>
</head>
<body class="bg-gray-50" hx-boost="true">
  {{template "navbar" .}}
  <main class="container mx-auto py-8">
    {{template "content" .}}
  </main>
</body>
</html>
{{end}}
```

### Page Template

```go
// templates/pages/dashboard.html
{{define "content"}}
<h1 class="text-2xl font-bold mb-6">Dashboard</h1>

<div class="grid grid-cols-3 gap-6 mb-8">
  {{template "stat-card" dict "Label" "Total Apps" "Value" .Stats.TotalApps}}
  {{template "stat-card" dict "Label" "Downloads" "Value" .Stats.Downloads}}
  {{template "stat-card" dict "Label" "Active Users" "Value" .Stats.ActiveUsers}}
</div>

<div class="flex justify-between items-center mb-4">
  <h2 class="text-xl font-semibold">Your Apps</h2>
  <a href="/apps/new" class="btn btn-primary">+ New App</a>
</div>

<div id="app-list" hx-get="/partials/apps" hx-trigger="load">
  Loading...
</div>
{{end}}
```

---

## Authentication Flow

### Login Page (Go Template)

```html
{{define "content"}}
<div class="min-h-screen flex items-center justify-center">
  <div class="card w-96 bg-white shadow-lg rounded-lg p-6">
    <h1 class="text-2xl font-bold text-center mb-6">Sign in to Mosis</h1>

    <div class="space-y-4">
      <a href="/auth/github" class="btn btn-github w-full flex items-center justify-center gap-2">
        <svg><!-- GitHub icon --></svg>
        Continue with GitHub
      </a>

      <a href="/auth/google" class="btn btn-google w-full flex items-center justify-center gap-2">
        <svg><!-- Google icon --></svg>
        Continue with Google
      </a>

      <div class="divider">or</div>

      <form hx-post="/auth/login" hx-target="#login-error" class="space-y-4">
        <input type="email" name="email" placeholder="Email"
               class="input input-bordered w-full" required>
        <input type="password" name="password" placeholder="Password"
               class="input input-bordered w-full" required>
        <div id="login-error" class="text-red-500 text-sm"></div>
        <button type="submit" class="btn btn-primary w-full">Sign in</button>
      </form>
    </div>
  </div>
</div>
{{end}}
```

### Auth Middleware (Go)

```go
// middleware/auth.go
func RequireAuth(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        session, err := sessionStore.Get(r, "session")
        if err != nil || session.Values["developer_id"] == nil {
            http.Redirect(w, r, "/login", http.StatusSeeOther)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// Usage in router
r.Group(func(r chi.Router) {
    r.Use(RequireAuth)
    r.Get("/dashboard", handlers.Dashboard)
    r.Get("/apps", handlers.AppList)
    r.Get("/apps/{id}", handlers.AppDetail)
})
```

---

## Responsive Design

### Breakpoints

| Size | Width | Target |
|------|-------|--------|
| sm | 640px | Mobile landscape |
| md | 768px | Tablet |
| lg | 1024px | Desktop |
| xl | 1280px | Large desktop |

### Mobile Navigation

```tsx
// Hamburger menu for mobile
<Sheet>
  <SheetTrigger className="md:hidden">
    <MenuIcon />
  </SheetTrigger>
  <SheetContent side="left">
    <Navigation />
  </SheetContent>
</Sheet>
```

---

## Accessibility

### Requirements

- [ ] Keyboard navigation
- [ ] Screen reader support
- [ ] Color contrast (WCAG AA)
- [ ] Focus indicators
- [ ] Alt text for images
- [ ] Form labels
- [ ] Error announcements

### Testing

```bash
# Lighthouse audit
npx lighthouse http://localhost:3000 --view

# axe-core
npm install @axe-core/react
```

---

## Deliverables

- [x] Framework selection (htmx + Go Templates)
- [x] UI component library selection (Tailwind CSS + Chart.js)
- [ ] Design system (colors, typography)
- [x] Page wireframes (see above)
- [x] Authentication flow (server sessions + OAuth)
- [ ] Dashboard implementation
- [ ] App management pages
- [ ] Version submission flow
- [ ] Settings pages
- [ ] Responsive design
- [ ] Accessibility audit

---

## Open Questions

1. ~~Dark mode support?~~ → Defer to post-MVP (Tailwind makes it easy to add later)
2. ~~Internationalization (i18n)?~~ → English only for MVP
3. ~~Custom domain for docs vs integrated?~~ → Integrated into same Go server at /docs
4. Email notifications UI? → Consider for v1.1

---

## References

- [shadcn/ui](https://ui.shadcn.com/)
- [Next.js App Router](https://nextjs.org/docs/app)
- [Tailwind CSS](https://tailwindcss.com/)
- [React Query](https://tanstack.com/query/latest)
