# Tillsynsappen — Arkitekturöversikt

## Stack
- **Next.js 14** (App Router, Server + Client Components)
- **MySQL** via `mysql2/promise` (cPanel, host: `anderspe_inspektorsappen`)
- **JWT-sessioner** via `jose` (cookie: `tillsyn_session`, 8h)
- **bcrypt** för lösenord och aktiveringskoder
- **nodemailer** för e-post (SMTP: `mail.cp02.polar55.com`, port 465, SSL)
- **Hosting**: cPanel, Node.js-app utan PM2, node env under `nodevenv/`

---

## Portaler & roller

| Portal | URL | Roller |
|--------|-----|--------|
| Handläggare | `/handlaggare` | `handlaggare` |
| Chef | `/chef` | `enhetschef`, `forvaltningschef`, `avdelningschef` |
| Admin | `/admin` | `superadmin` |

- `portalRole` i sessionen = vilken portal användaren loggade in via (`chef`, `admin`, `handlaggare`)
- `role` i sessionen = databasrollen (`enhetschef`, `superadmin`, osv.)
- Multi-enhet-roller (`forvaltningschef`, `avdelningschef`) har `enhetIds[]` i sessionen (via `portal_user_enheter`-tabellen)

---

## Databasschema (tabeller)

### `enheter`
Organisatoriska enheter (t.ex. Miljöenheten, Bygglovsenheten).
`id (VARCHAR 50 PK) | name | short | color | chef_name | chef_email`

### `portal_users`
Chefer och administratörer.
`id (UUID) | name | email | password_hash (nullable) | phone | signature | role (VARCHAR 30) | enhet_id (FK→enheter, nullable) | account_status (active/pending_activation) | last_login`

### `portal_user_enheter`
Koppling för multi-enhet-roller.
`portal_user_id (FK→portal_users) | enhet_id (FK→enheter)` — kompositnyckel

### `handlaggare`
Fälthandläggare.
`id (UUID) | name | signature (4 bokst.) | color | enhet_id (FK) | sektion | phone | email | status | work_location | account_status (active/pending_activation) | last_login | remote_limit_percent (default 49) | remote_tracking_opted_out`

### `handlaggare_auth`
`handlaggare_id (FK, unique) | pin_hash | password_hash`

### `activation_tokens`
Engångskoder för kontoaktivering (handläggare och portalanvändare).
`id | handlaggare_id (nullable FK) | portal_user_id (nullable FK) | token_hash | expires_at | used_at`

### `tillsyner`
Tillsynsärenden kopplade till en handläggare.
`id (UUID) | handlaggare_id (FK) | title | date (DATE) | time (VARCHAR 10) | tags (JSON array) | omrade_id (FK→tillsyn_omraden, nullable) | arendenummer | adress | anteckning (TEXT) | status (Planerad/Genomförd/Pågår/Inställd) | reminder | field_start | field_end | created_at | updated_at`

### `tillsyn_omraden`
Verksamhetsområden per enhet — nivå 1 i tvånivå-taggningen (t.ex. Miljöskydd, Livsmedel).
`id (UUID) | enhet_id (FK→enheter) | namn | sortering (INT) | aktiv (TINYINT) | created_at`

### `tillsyn_taggar`
Fria taggar per enhet/sektion — nivå 2 i tvånivå-taggningen, används för autocomplete.
`id (UUID) | namn | sektion (nullable) | enhet_id (nullable FK) | created_at`
Unik nyckel: `(namn, sektion, enhet_id)`

### `work_location_log`
Daglig logg av kontors- vs. distansarbete per handläggare.
`id (UUID) | handlaggare_id (FK) | date (DATE, unique per handläggare) | location (office/remote) | note | created_at | updated_at`

### Övriga
`delegations`, `alarm_log`, `settings` (key-value), `inspections`, `handlaggare_status`, `daily_logs`

---

## Migrationsfiler

Körs manuellt i phpMyAdmin vid schemaändringar (i ordning):

| Fil | Innehåll |
|-----|----------|
| `tillsyner_migration.sql` | Skapar `tillsyner`-tabellen |
| `activation_migration.sql` | `activation_tokens`, `account_status`-kolumner |
| `portal_user_activation_migration.sql` | `account_status` på `portal_users` |
| `team_status_migration.sql` | `share_scope` etc. på handläggare |
| `sektion_migration.sql` | `sektion`-kolumn på handläggare |
| `handlaggare_last_login_migration.sql` | `last_login` på handläggare |
| `handlaggare_status_migration.sql` | Statuskolumner på handläggare |
| `daily_logs_migration.sql` | `daily_logs`-tabellen |
| `remote_tracking_migration.sql` | `work_location_log`, `remote_limit_percent` |
| `taggning_migration.sql` | `tillsyn_omraden`, `tillsyn_taggar`, `omrade_id` på tillsyner |

---

## API-routes

### Auth
| Route | Beskrivning |
|-------|-------------|
| `POST /api/auth/login` | Loggar in alla rolltyper, returnerar `pending_activation` vid ej aktiverat konto |
| `GET /api/auth/session` | Returnerar aktuell session |
| `POST /api/auth/logout` | Rensar cookie |
| `POST /api/auth/change-password` | Byter lösenord |

### Handläggare
| Route | Beskrivning |
|-------|-------------|
| `GET/POST/PATCH/DELETE /api/handlaggare` | CRUD. GET filtrerar per roll (admin=alla, chef=sin enhet, handläggare=sig själv) |
| `POST /api/handlaggare/send-activation` | Genererar 6-siffrig kod, sparar hash i `activation_tokens`, skickar mejl |
| `POST /api/handlaggare/activate` | Validerar kod, sätter lösenord, loggar in direkt |
| `GET/POST /api/handlaggare/team-status` | Kollegornas status (filtreras av `share_scope`) |
| `GET/POST /api/handlaggare/status` | Incheckningsstatus för inloggad handläggare |
| `GET /api/handlaggare/statistik?period=week\|month\|year` | Tillsyns- och tidstatistik per handläggare |
| `GET/POST /api/handlaggare/daily-logs` | Dagliga incheckningstider |
| `GET/POST/PATCH/DELETE /api/handlaggare/work-log` | Distansarbetslogg per dag |
| `GET/POST/DELETE /api/handlaggare/work-log/holiday-override` | Röd dag → arbetsdag-overrides |

### Portal-användare
| Route | Beskrivning |
|-------|-------------|
| `GET/POST/PATCH/DELETE /api/portal-users` | CRUD. POST utan lösenord → `pending_activation`. Multi-enhet via `enhet_ids[]` |
| `POST /api/portal-users/send-activation` | Som handläggare-varianten men för portal_users |
| `POST /api/portal-users/activate` | Validerar kod, sätter lösenord, loggar in med rätt portalroll |

### Chef
| Route | Beskrivning |
|-------|-------------|
| `GET /api/chef/insights` | Tillsynspuls, handläggar­prestanda, distansstatistik, inaktiva, `perOmrade`-aggregering. Stöder multi-enhet via `enhetIds[]` |

### Tillsyner & taggning
| Route | Beskrivning |
|-------|-------------|
| `GET/POST/PATCH/DELETE /api/tillsyner` | Tillsynsärenden för inloggad handläggare. LEFT JOIN på `tillsyn_omraden` för `omrade_namn` |
| `GET/POST/DELETE /api/tillsyn-omraden` | Verksamhetsområden (nivå 1) per enhet. `?include_inactive=1` tillgängligt för chef/admin |
| `GET/POST/DELETE /api/tillsyn-taggar` | Taggbibliotek (nivå 2) per sektion. POST idempotent (returnerar befintlig vid dubblett) |

### Övriga
| Route | Beskrivning |
|-------|-------------|
| `GET/POST /api/enheter` | Enhetslista |
| `GET /api/admin/dashboard` | Adminöversikt |
| `GET/POST/PATCH/DELETE /api/delegationer` | Delegationer |
| `GET/POST /api/alarm` | Larm |
| `GET/PATCH /api/settings` | Inställningar (key-value) |

---

## Nyckelbibliotek (`lib/`)

| Fil | Innehåll |
|-----|----------|
| `auth.js` | `createToken`, `verifyToken`, `getSession`, `authenticateUser`, `CHEF_ROLES`, `MULTI_ENHET_ROLES` |
| `db.js` | `query`, `getOne`, `getMany` (mysql2-wrappers) |
| `email.js` | `sendActivationEmail` (handläggare), `sendPortalUserActivationEmail` (portal) |
| `migrate.js` | Skapar alla tabeller + seedar testdata. Körs med `node lib/migrate.js` |

---

## Tvånivå-taggning (tillsyner)

Tillsyner kan kategoriseras i två nivåer:

- **Nivå 1 — Verksamhetsområde** (`tillsyn_omraden`): obligatoriskt fält, administreras av admin per enhet, visas i chef-statistik. T.ex. *Miljöskydd*, *Livsmedel*.
- **Nivå 2 — Fria taggar** (`tillsyn_taggar`): valfria, autocomplete-baserade, hanteras av handläggare i inställningar. T.ex. *Restaurang*, *Klagomål*.

Chef-dashboardens statistikflik visar `perOmrade`-aggregering (planerade/genomförda/försenade/pågående per område).

---

## Distansspårning

Handläggare loggar kontors- vs. distansarbete per dag i `work_location_log`. Varje enhet har ett konfigurerat gränsvärde (`remote_limit_percent`, default 49 %). Chef-dashboardens insikts-API summerar distansandel per handläggare. Handläggare kan välja att stå utanför (`remote_tracking_opted_out`).

---

## Aktiveringsflöde (nytt konto)
1. Admin skapar användare utan lösenord → `account_status = pending_activation`
2. Admin klickar "Skicka kod" → 6-siffrig kod mejlas
3. Användaren loggar in → får `pending_activation`-svar → aktiveringsläge i UI
4. Användaren anger kod + väljer lösenord → konto aktiveras, loggas in direkt

Gäller både handläggare och portalanvändare (chef/admin).

---

## UI-design

- **Admin & chef**: desktop-first, delar designspråk (kort, tabbar, `card`-klassen)
- **Handläggare**: mobil PWA, bottom navigation, touch-optimerad
- Tailwind CSS med lokala utility-klasser (`card`, `btn-primary`, `input-field`, `badge`)

---

## Driftsättning

**Mac** (stå i `nextjs-app/`):
```bash
npm run build && git add -A && git commit -m "..." && git push origin main
```

**Server:**
```bash
source /home/anderspe/nodevenv/dev.anderspettersson.studio/inspektorsappen/nextjs-app/22/bin/activate && git fetch origin && git reset --hard origin/main && npm install
```
Sedan: **cPanel → Setup Node.js App → Restart**

Migrationsfiler körs manuellt i phpMyAdmin **innan** restart vid schemaändringar.
