# Hands-On tutorial — sample artifacts

These files are the companion materials for
[`../hands-on.md`](../hands-on.md). They're meant to be **copied into a
working directory** outside this docs repo (or used as a reference while
you scaffold your own).

Layout:

```
hands-on-files/
├── compose/                                    ← docker-compose stack
│   ├── docker-compose.yml
│   ├── .env.example
│   ├── bootstrap-databases.sh                  ← creates handson_mib DB
│   ├── generate-certs.sh                       ← self-signed cert + keychain trust
│   ├── pull-images.sh                          ← ECR + GHCR image pulls
│   ├── seed-oauth-clients.sh                   ← API_CLIENTS rows (mibapi/mibfrontend/mibbackend)
│   ├── seed-permissions.sh                     ← AUTH_PERMISSIONS + AUTH_USER_GROUP
│   ├── start-frontend-with-local-ca.sh         ← BFF entrypoint (trusts local CA)
│   ├── start-react.sh                          ← React-shell entrypoint (seeds localStorage)
│   ├── nginx/
│   │   └── nginx.conf                          ← outer gateway routing
│   └── plugins/                                ← drop custom DLLs here (mounted to BFF /app/Custom)
├── migrator-config/                            ← MibMigrator -configPath dir (canonical MIB migrations)
│   ├── run-canonical-migrations.sh             ← runs the seven canonical migrations in order
│   └── handson-mib/
│       ├── MibDatabaseConfig.mibconfig
│       └── MibConfigurableObjectConfig.mibconfig
├── migrations/                                 ← knex migrations (custom tables + UI rows). All
│   │                                             three profiles target the same handson_mib DB.
│   ├── knexfile.js                             ← three envs: mib / cms / dashboard
│   ├── package.json
│   ├── _helpers/
│   │   └── meta.js                             ← MEDIA_TYPES / ADM_FIELDS / ADM_RELATEDS helpers
│   ├── mib/                                    ← custom tables + ADM rows
│   │   ├── 001_create_handson_movies.js
│   │   └── 002_create_handson_assets.js
│   ├── cms/                                    ← UI rows: pages / templates / menu
│   │   └── 001_create_handson_ui_pages.js
│   └── dashboard/                              ← Part 3 — register custom backend component.
│       └── 001_register_movie_dashboard.js     ← Run AFTER the DLL is built and dropped.
├── backend/                                    ← custom C# overlay project
│   ├── Acme.Mib.HandsOn.csproj
│   ├── MovieDashboardComponent.cs              ← the IRenderableComponentV2 class
│   └── Controllers/
│       ├── MoviesController.cs                 ← /api/v2/handson/movies/{reindex,{id}/reindex}
│       ├── AssetsController.cs                 ← /api/v2/handson/assets/{reindex,{id}/probe}
│       └── TestController.cs                   ← /api/v2/handson/test/ping (smoke-test)
└── frontend/                                   ← federated React remote
    ├── Dockerfile                              ← multi-stage: vite build + nginx runtime
    ├── nginx.conf                              ← serves /dist with permissive CORS
    ├── index.html                              ← stub — vite-plugin-federation needs an entry
    ├── package.json
    ├── tsconfig.json
    ├── vite.config.ts
    └── src/
        ├── main.tsx                            ← federation entry: components / decorators / hooks
        ├── components/
        │   ├── MovieDashboard/
        │   │   └── index.tsx                   ← the acme_movie_dashboard widget
        │   ├── ProbeAssetAction.tsx            ← related-list itemActions decorator (wired)
        │   ├── RefreshAllAssetsAction.tsx      ← reference source (closed C# schema today)
        │   ├── RefreshAllMoviesAction.tsx      ← reference source (list.headerActions not wired)
        │   └── RefreshOneMovieAction.tsx       ← reference source (list.itemActions not wired)
        └── hooks/
            ├── useAssetEvents.ts               ← cross-component event bus
            └── useTitleAutoSlug.ts             ← `useFormChange` export — auto-slug demo
```

Each piece is referenced from the tutorial steps in
[`../hands-on.md`](../hands-on.md):

| Tutorial step | File(s) |
|---|---|
| Step 1 — pull MIB images | `compose/pull-images.sh` |
| Step 2 — docker-compose | `compose/docker-compose.yml`, `compose/.env.example`, `compose/nginx/nginx.conf`, `compose/start-frontend-with-local-ca.sh`, `compose/start-react.sh` |
| Step 3 — certs + SQL bootstrap | `compose/generate-certs.sh`, `compose/bootstrap-databases.sh` |
| Steps 4–6 — canonical MIB migrations | `migrator-config/run-canonical-migrations.sh`, `migrator-config/handson-mib/` |
| Step 7 — seed OAuth clients | `compose/seed-oauth-clients.sh` |
| Step 9 — custom tables (movies + assets) | `migrations/_helpers/meta.js`, `migrations/mib/001_*.js`, `migrations/mib/002_*.js`, `migrations/cms/001_*.js` |
| Step 9.5 — seed permissions | `compose/seed-permissions.sh` |
| Steps 11–13 — custom backend | `backend/Acme.Mib.HandsOn.csproj`, `backend/MovieDashboardComponent.cs` |
| Step 14 — register backend component | `migrations/dashboard/001_register_movie_dashboard.js` |
| Steps 15–19 — federated React remote | `frontend/*` |
| Decorator-backed REST endpoints | `backend/Controllers/MoviesController.cs`, `backend/Controllers/AssetsController.cs` |

> **Single-database setup.** Earlier drafts split UI rows into a
> separate `handson_cms` database; the verified-working stack uses one
> database (`handson_mib`) for everything. All three knex envs
> (`mib` / `cms` / `dashboard`) point at it; they exist only so the
> three phases of migration use independent `_knex_migrations` ledgers.
> See the `live-run note` in `../hands-on.md` Part 2 for the reasoning.

For the canonical, much larger example that follows the same pattern
see [`mini-agiletv`](https://github.com/agilecontent/mini-agiletv).
