From 84b49bc186eb2c1cfa19b7f07021e933613cf852 Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Fri, 17 Apr 2026 23:20:21 +0200 Subject: [PATCH] feat(docs): update NEXT_STEPS, README, and TEKNISK_BESKRIVNING with new features and improvements --- NEXT_STEPS.md | 32 +++----- README.md | 40 +++++++--- TEKNISK_BESKRIVNING.md | 176 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 200 insertions(+), 48 deletions(-) diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index b44cbc9e..ee1eff84 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -14,8 +14,11 @@ | Snabbimport (URL/PDF/bild/ICA) | ✅ Klart | | Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart | | Matplanering (veckovy, inköpslista) | ✅ Klart | +| Matplan — portionsjustering per dag | ✅ Klart | +| Matplan — inventariejämförelse | ✅ Klart | | Baslager (lista, lägg till, ta bort) | ✅ Klart | | Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart | +| Admin: Bulk-kategorisering | ✅ Klart | | Receptredigering (frontend UX) | ✅ Klart | | Receptbilder (upload URL) | ✅ Klart | | Autentisering (JWT, Auth.js v5, User-modell) | ✅ Klart | @@ -24,8 +27,7 @@ | Taggning av produkter | ✅ Klart | | Näringsvärden på produkter | ✅ Klart (schema + API) | | Kategoritilldelning i admin-UI | ✅ Klart | -| Portionsjustering | ❌ Saknas | -| Matplan — inventariejämförelse | ❌ Saknas | +| Kategori-seed (supplement, idempotent) | ✅ Klart | | Seed produktdata med kategoritilldelning | ❌ Saknas (002-seed-products.sql.disabled) | | Användarspecifika produkter (UserProduct) | ⚠️ Schema klart, UI basic | | Användarroller (user / admin) | ❌ Saknas | @@ -37,27 +39,17 @@ ### 1. Seed produktdata med kategoritilldelning `db/init/002-seed-products.sql` är inaktiverad (`.disabled`) tills den uppdateras med rätt `categoryId` för varje produkt. Utan detta är produktdatabasen tom vid fresh install. -- Gå igenom de ~190 produkterna och tilldela rätt kategori-ID från tabellen `Category` +- Gå igenom de ~190 produkterna och tilldela rätt kategori-ID från tabellen `Category` (nu inklusive supplement-kategorier) - Aktivera filen igen genom att ta bort `.disabled`-suffixet -- Alternativt: bygg ett admin-verktyg för bulk-kategorisering +- Kontrollera att varje produkts `categoryId` matchar mot det ID som genereras i databasen (auto-increment — kör ett SELECT för att verifiera) -### 2. Portionsjustering av recept -Recept lagras utan portionsangivelse. Lägg till ett `servings`-fält och låt användaren justera antal portioner i receptvyn — ingrediensmängderna räknas om proportionellt (t.ex. 4 → 6 pers: × 1,5). -- **Databas:** `servings Int?` på `Recipe` i Prisma + migration -- **Backend:** `servings` exponeras i `RecipeDto`, sätts vid create/update -- **Frontend (`app/recipes/[id]/`):** räknare (+ / −) bredvid ingredienslistan, beräkning i klientkomponent utan extra API-anrop -- **Receptskapande (`write/`):** lägg till grundportioner-fält -- **Matplan (`app/matplan/`):** inköpslistan justeras efter önskat portionsantal per dag +### 2. Matplan — djupare inventariejämförelse i frontend +Backend-endpointen `GET /api/meal-plan/inventory-compare?from=...&to=...` returnerar ingrediensstatus per dag. Funktionen saknar dock en frontend-vy som tydligt visar "vad behöver jag handla — och vad har jag redan hemma?" aggregerat för hela veckan. +- Visa inköpslistan med tydliga statusindikatorer: ✅ Finns hemma / ⚠️ Delvis / ❌ Saknas +- Möjlig placering: ny flik i matplanen eller sidopanel i veckovy +- Kräver: aggregering av `inventory-compare`-svaret per ingrediens över hela veckan -### 3. Matplanering — jämförelse mot inventariet -Veckovy och inköpslista fungerar. Nästa steg är att visa vilka ingredienser på inköpslistan som redan finns hemma och i vilken mängd — liknande receptvyns inventory-preview. Implementeras via `GET /api/recipes/:id/inventory-preview` per recept, aggregerat på veckonivå. - -### 4. Bulk-kategorisering av produkter i admin -Admin-UI:t tillåter idag att sätta kategori per produkt. För att effektivt kategorisera hundratals produkter behövs: -- Filtervy för okategoriserade produkter -- Möjlighet att sätta kategori på flera produkter samtidigt (bulk-select) - -### 5. Användarroller och användarhantering i admin +### 3. Användarroller och användarhantering i admin Idag har alla inloggade användare samma behörighetsnivå. Behövs: - **Databas:** Lägg till `role` (enum `user` | `admin`) på `User`-modellen i Prisma + migration - **Backend:** Rollbaserad guard (`@Roles('admin')`) — skyddar admin-endpoints; vanliga användare nekas med 403 diff --git a/README.md b/README.md index 921d4514..85572751 100644 --- a/README.md +++ b/README.md @@ -14,30 +14,46 @@ En fullstack-applikation för hantering av hemmavaror och recept. Håll koll på - **Filtrera och sortera** — efter plats (kyl, frys, skafferi), bäst före-datum, och namn (A–Ö) - **Konsumera varor** — registrera förbrukad mängd med eventuell kommentar - **Konsumtionshistorik** — spåra vad som använts när och i vilken mängd -- **Utförlig information** — stöd för varumärke, lagringsnot, tillkomsttid, mera detaljer +- **Utförlig information** — stöd för varumärke, lagringsnot, tillkomsttid och mer ### Recept -- **Skapa och redigera recept** — med ingredienser, kvantiteter, enheter och instruktioner (Markdown-stöd) -- **Receptjämförelse mot inventorie** — se direkt vilka ingredienser du har hemma, vad som saknas och enhetskonflikt +- **Skapa och redigera recept** — med namn, beskrivning, portionsantal, ingredienser (kvantitet och enhet) och instruktioner i Markdown-format +- **Portionsjustering** — ange antal portioner vid skapandet; matplanen räknar automatiskt om ingrediensmängder om du lagar fler eller färre portioner +- **Receptjämförelse mot inventorie** — se direkt vilka ingredienser du har hemma, vad som saknas och om enheter är inkompatibla - **Importera recept från Markdown** — klistra in ett recept i enkelt format, låt systemet matcha ingredienser, granska och spara med ett klick - **Importera recept från PDF, bild eller länk** — stöd för PDF-textutvinning, OCR av bilder och webbimport via snabbimport -- **Intelligenta matchningar** — Levenshtein-baserad likhetsbedömning hittar rätt produkt även på osäker stavning +- **Intelligenta matchningar** — Levenshtein-baserad likhetsbedömning hittar rätt produkt även vid osäker stavning - **Enhetskonvertering** — automatisk konvertering mellan viktenheter (g/kg), volymenheter (ml/dl) och portionsenheter (tsk/msk) +### Matplanering +- **Veckovy** — planera veckans måltider dag för dag med ett enkelt receptval +- **Portionsjustering per dag** — välj hur många portioner du vill laga för varje dag; avviker du från receptets grundportioner visas en återställningsknapp +- **Inköpslista** — genereras automatiskt utifrån veckans planerade recept; ingrediensmängder skalas proportionellt om portioner justerats +- **Inventariejämförelse** — jämför inköpslistans ingredienser mot vad du faktiskt har hemma (aggregerat per vecka) + +### Kvittoimport +- **Fotografera eller ladda upp kvitto** — JPEG, PNG, WebP, HEIC och PDF stöds (max 15 MB) +- **AI-tolkning via Mistral** — Mistral AI extraherar varunamn och mängder direkt från kvittobilden +- **Alias-matchning** — kvittots produktnamn matchas mot kända alias (t.ex. "ICA Kvarg Jordg" → "Kvarg") och mot befintliga produkter +- **Granska och lägg till** — se tolkningsresultatet, justera kvantitet och enhet, och lägg till direkt i inventariet + ### Baslager - **Ständigt lager** — markera produkter du alltid räknar med att ha hemma -- **Grupperat per kategori** — produkterna i baslagret visas grupperade -- **Lägg till och ta bort** — välj från produktlistan via dropdown, ta bort med ett klick +- **Grupperat per kategori** — produkterna i baslagret visas grupperade under kategorirubrik +- **Lägg till och ta bort** — välj från produktlistan via sökbar dropdown, ta bort med ett klick ### Admin: Produkter -- **Redigera produkter** — uppdatera visningsnamn (name), canonical name, kategori (hierarkisk dropdown) och varumärke inline direkt i listan +- **Redigera produkter** — uppdatera visningsnamn, canonical name, kategori (hierarkisk dropdown) och varumärke inline direkt i listan - **Kategoritilldelning** — välj kategori ur ett 3-nivåträd (huvudkategori → underkategori → typ) som laddas dynamiskt från API:et -- **Ta bort produkter** — soft-delete enskilda produkter +- **Bulk-kategorisering** — filtrera fram okategoriserade produkter, markera flera (eller "välj alla synliga") och sätt kategori på alla markerade på en gång — effektivt för att kategorisera många produkter i ett svep - **Hitta dubbletter** — identifiera produkter med samma normaliserade namn -- **Slå ihop produkter** — merge två produktposter, flytta alla inventarieföremål till målprodukten (källan soft-deleteras) -- **Förhandsvisning** — granska vad som kommer att hända innan merge genomförs -- **Återställ produkter** — restore tidigare raderade produkter -- **Återställ all produktdata** — rensningsknapp som raderar alla produkter, inventorie, taggar och kvitto-alias (behåller användare och kategorier) +- **Slå ihop produkter** — merge av två produktposter: alla inventarieföremål och receptreferenser flyttas till målprodukten, källan soft-deleteras +- **Förhandsvisning** — granska vad som händer (inventarieräkningar, utfall) innan merge genomförs +- **Ta bort och återställ** — soft-delete enskilda produkter, återställ med ett klick +- **Återställ all produktdata** — rensningsknapp som raderar alla produkter, inventarie, taggar och kvitto-alias (behåller användare och kategorier) + +### Användarprofil +- **Redigera profilinformation** — uppdatera förnamn, efternamn och e-postadress under "Min profil" --- diff --git a/TEKNISK_BESKRIVNING.md b/TEKNISK_BESKRIVNING.md index 797ba01f..6203db22 100644 --- a/TEKNISK_BESKRIVNING.md +++ b/TEKNISK_BESKRIVNING.md @@ -82,18 +82,19 @@ docker exec recipe-db mariadb -uroot -p"LÖSENORD" recipe_app -e "SHOW TABLES;" | **Lägg till recept** | `app/recipes/create/page.tsx` | Server component med Navigation | | | `app/recipes/create/CreateRecipeClient.tsx` | Klientkomponent: snabbimport + metodval | | **Skriv in recept** | `app/recipes/write/page.tsx` | Server component med Navigation | -| | `app/recipes/write/WriteRecipePage.tsx` | Markdown-baserat receptskapande (3-steg) | +| | `app/recipes/write/WriteRecipePage.tsx` | Markdown-baserat receptskapande (3-steg): Markdown-inmatning → ingrediensgranskning (produktval + portionsantal) → spara | | **Importera från fil** | `app/recipes/import/page.tsx` | Startpunkt för fil/länk-import | | | `app/recipes/import/ImportFilePage.tsx` | Fil-/länk-import (PDF, URL, etc) | -| **Import (flikar)** | `app/import/page.tsx` | Server component med Navigation + flikvy | +| **Matplan** | `app/matplan/page.tsx` | Matplanering (server component) | +| | `app/matplan/MealPlanClient.tsx` | Veckovy, receptval per dag, portionsjustering, inköpslista, inventariejämförelse | +| **Kvittoimport** | `app/import/page.tsx` | Server component med Navigation + flikvy | | | `app/import/ImportTabsClient.tsx` | Klientkomponent: kvitto/recept-flikar | -| **Recipe detail** | `app/recipes/[id]/` | Enskilt recept (detaljer, redigering) | | **Admin: Produkter** | `app/admin/products/page.tsx` | Produktadmin-panel | -| | `AdminProductList.tsx` | Lista produkter, sök, sortera | +| | `AdminProductList.tsx` | Lista produkter, sök, sortera, filter okategoriserade, bulk-select + bulk-kategorisering | | | `EditProductForm.tsx` | Inline redigering: name, canonicalName, kategori (hierarkisk dropdown), brand, taggar | | | `ResetProductsButton.tsx` | Knapp för att rensa all produktdata | | | `MergePreviewForm.tsx` | Förhandsgranska merge | -| | `actions.ts` | Server actions: updateProduct, deleteProduct, resetAllProducts | +| | `actions.ts` | Server actions: updateProduct, deleteProduct, resetAllProducts, bulkSetCategory | | **Baslager** | `app/baslager/page.tsx` | Visa och hantera baslager (server component) | | | `AddToPantryForm.tsx` | Lägg till produkt i baslager (dropdown) | | | `PantryList.tsx` | Visa baslager grupperat per kategori | @@ -116,6 +117,9 @@ Alla proxy-routes läser auth-token via `auth()` (Auth.js v5) och vidarebefordra | `/api/recipe-preview-proxy` | GET | Receptförhandsvisning | | `/api/admin/merge-preview-proxy` | GET | Produktmerge-preview | | `/api/receipt-import-proxy` | POST | Kvittoimport via Mistral AI | +| `/api/meal-plan-proxy` | GET, POST, DELETE | Matplanering (veckovy, upsert, ta bort) | +| `/api/meal-plan-shopping-proxy` | GET | Inköpslista för datumintervall | +| `/api/meal-plan-compare-proxy` | GET | Inventariejämförelse för datumintervall | | `/api/user-products` | GET, POST, DELETE | Användarspecifika produkter | ### Autentisering (Auth.js v5) @@ -203,6 +207,21 @@ backend/src/ │ ├── base.parser.ts # Abstract RecipeParser class │ ├── ica.parser.ts # ICA.se-specifik parser (JSON-LD) │ └── generic.parser.ts # Fallback-parser (HTML + JSON-LD) +├── meal-plan/ +│ ├── meal-plan.controller.ts # GET/POST/DELETE + shopping-list + inventory-compare +│ ├── meal-plan.service.ts # Upsert, shoppingList (portionsskalad), inventoryCompare +│ ├── meal-plan.module.ts +│ └── dto/ +│ └── create-meal-plan-entry.dto.ts # { date, recipeId, servings? } +├── receipt-import/ +│ ├── receipt-import.controller.ts # POST /api/receipt-import (multipart) +│ ├── receipt-import.service.ts # Mistral AI-anrop, bildtolkning +│ └── dto/ +│ └── parsed-receipt-item.dto.ts +├── receipt-alias/ +│ ├── receipt-alias.controller.ts # CRUD /api/receipt-alias +│ ├── receipt-alias.service.ts +│ └── dto/ └── recipes/ ├── recipes.controller.ts # Recept endpoints ├── recipes.service.ts # Recept + Markdown-parsing @@ -282,6 +301,21 @@ backend/src/ - **Soft delete & restore:** - `remove()` - Soft-delete produkt (isActive = false) - `restore()` - Återställ borttagen produkt +- **Bulk-uppdatering:** `bulkUpdate(ids, data)` — Uppdatera ett godtyckligt antal produkter i ett enda DB-anrop (`updateMany`). Används primärt för bulk-kategorisering i admin-UI. Body: `{ ids: number[], categoryId?: number | null }` + +**Matplan-API:** +- **`upsert(dto)`** — Skapar eller uppdaterar en `MealPlanEntry` för ett givet datum (unik per dag). Sparar `recipeId` och valfritt `servings`. +- **`findByRange(from, to)`** — Hämtar alla planerade dagar i ett datumintervall, inkl. receptinfo. +- **`shoppingList(from, to)`** — Aggregerar ingrediensmängder för alla planerade recept i intervallet. + - Om `entry.servings` och `recipe.servings` är satta beräknas en skala: `scale = entry.servings / recipe.servings` + - Ingrediensmängder multipliceras med skalan innan aggregering + - Returnerar lista av `{ productName, quantity, unit }` +- **`inventoryCompare(from, to)`** — Kör samma aggregering som `shoppingList` men jämför sedan varje ingrediens mot aktuellt inventarielager. Returnerar status per ingrediens: `räcker | saknas | enhetskonflikt`. + +**Kvittoimport-API:** +- **`parseReceipt(file)`** — Tar emot en bildel eller PDF (max 15 MB), skickar den till Mistral AI för tolkning och returnerar en lista av kandidatprodukter med namn, kvantitet och enhet. +- Alias-matchning: före returneringen slås varje rånamn upp mot `ReceiptAlias`-tabellen och mot `Product.normalizedName`. Träffar kopplas automatiskt till rätt produkt-ID. +- Stödda MIME-typer: `image/jpeg`, `image/png`, `image/webp`, `image/heic`, `image/heif`, `application/pdf` --- @@ -333,6 +367,8 @@ POST /api/products/merge Slå ihop två produkter PATCH /api/products/:id/canonical-name Uppdatera canonical name POST /api/products/backfill-canonical Backfill canonical names (admin) POST /api/products/reset-all Rensa all produktdata (admin) +POST /api/products/bulk-update Uppdatera flera produkter (t.ex. sätt kategori) + Body: { ids: number[], categoryId?: number | null } ``` ### Kategori-endpoints @@ -355,6 +391,30 @@ POST /api/pantry Lägg till produkt i baslagret DELETE /api/pantry/:id Ta bort produkt från baslagret ``` +### 🗓️ Matplan-endpoints +``` +GET /api/meal-plan?from=YYYY-MM-DD&to=YYYY-MM-DD Lista planerade recept för datumintervall +POST /api/meal-plan Skapa eller uppdatera post (upsert per datum) + Body: { date, recipeId, servings? } +DELETE /api/meal-plan/:date Ta bort recept för ett specifikt datum + +GET /api/meal-plan/shopping-list?from=...&to=... Generera inköpslista för veckan + Skalad proportionellt efter portionsjustering +GET /api/meal-plan/inventory-compare?from=...&to=... Jämför inköpslista mot inventarie + Returnerar status per ingrediens: räcker | saknas | enhetskonflikt +``` + +### 🧾 Kvitto-endpoints +``` +POST /api/receipt-import Tolka kvittobild (JPEG, PNG, WebP, HEIC, PDF) + Multipart-form med "file"; max 15 MB + Returnerar lista av { name, quantity, unit, productId?, confidence } + +GET /api/receipt-alias Lista alla kvitto-alias +POST /api/receipt-alias Skapa nytt alias (receiptName → productId) +DELETE /api/receipt-alias/:id Ta bort alias +``` + --- ## Datamodell (Prisma ORM) @@ -475,14 +535,19 @@ model Recipe { id Int @id @default(autoincrement()) name String # Receptnamn description String? # Receptbeskrivning + servings Int? # Antal portioner receptet är dimensionerat för + imageUrl String? # URL till receptbild (valfritt) instructions String? @db.Text # Tillagningsinstruktioner (kan vara långt, stöder Markdown) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - ingredients RecipeIngredient[] + ingredients RecipeIngredient[] + mealPlanEntries MealPlanEntry[] } ``` +`servings` är grundportionsantalet — matplanen använder det för att skala ingrediensmängder om användaren anger ett avvikande portionsantal per dag. + ### RecipeIngredient ```prisma model RecipeIngredient { @@ -512,6 +577,38 @@ model PantryItem { } ``` +### MealPlanEntry +```prisma +model MealPlanEntry { + id Int @id @default(autoincrement()) + date DateTime # Datum för planerad måltid (en per dag) + recipeId Int # Foreign key till Recipe + servings Int? # Justerat portionsantal för den dagen (null = använd receptets grundvärde) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + recipe Recipe @relation(fields: [recipeId], references: [id]) + + @@unique([date]) # Bara ett recept per dag +} +``` + +**Portionsskalning:** Om `servings` är satt och skiljer sig från `recipe.servings` beräknar `shoppingList()` och `inventoryCompare()` en skala: `scale = entry.servings / recipe.servings`. Alla ingrediensmängder multipliceras med denna faktor. + +### ReceiptAlias +```prisma +model ReceiptAlias { + id Int @id @default(autoincrement()) + receiptName String @unique # Namn som kvittosystemet returnerar (råtext) + productId Int # FK till matchad Product + createdAt DateTime @default(now()) + + product Product @relation(...) +} +``` + +Kvitto-alias lagrar mappningar från kvittots råtext till produkt-ID. När Mistral AI returnerar t.ex. "ICA Kvarg Jordg" slås det upp mot alias-tabellen. Om träff hoppas manuell matchning över. + --- ## Receptimport och receptskaping — Detaljerad arkitektur @@ -754,6 +851,52 @@ Top 5: Max 5 förslag per ingrediens --- +## Matplanering och portionsjustering — Detaljerad arkitektur + +### Syfte +Matplaneringsfunktionen låter användaren planera veckans måltider dag för dag och generera en inköpslista automatiskt. Portionsjusteringen gör det möjligt att anpassa mängden per dag utan att ändra receptet — t.ex. laga en dubbel sats en dag. + +### Dataflöde + +``` +Användaren väljer recept + portionsantal för ett datum + → POST /api/meal-plan { date, recipeId, servings } + → MealPlanEntry upserteras (unik per datum) + +Veckovy hämtar alla poster i intervallet + → GET /api/meal-plan?from=...&to=... + +Inköpslista genereras + → GET /api/meal-plan/shopping-list?from=...&to=... + → Varje ingredient × scale (entry.servings / recipe.servings, eller 1 om ej satt) + → Aggregerat per produkt + enhet + +Inventariejämförelse + → GET /api/meal-plan/inventory-compare?from=...&to=... + → Samma aggregering, sedan jämförs mot aktuell inventarie + → Status: räcker | saknas | enhetskonflikt +``` + +### Frontend: MealPlanClient + +- Veckovy renderar en kolumn per dag med aktuellt recept +- Om receptet har `servings` satt visas ett portionsinmatningsfält direkt i dagsvyn +- Avviker inmatat portionsantal från receptets grundvärde visas en återställningsknapp (↩ N portioner) +- `handleServingsChange()` POSTar direkt till backend och uppdaterar lokal state utan sidomladdning + +### Portionsskalning i backend + +```typescript +const scale = recipeServings && entryServings + ? entryServings / recipeServings + : 1; + +// Exempel: recept för 4, vill laga 6 → scale = 1.5 +// 200 g pasta → 300 g pasta i inköpslistan +``` + +--- + ## Enhetskonvertering (backendsida) ### Stödda enhetstyper @@ -882,18 +1025,19 @@ Konfigureras via `.env` eller `docker compose up`: ## Säkerhet & Utbyggbarhet -- **Ingen auth i grundutförande** (kan enkelt byggas på) -- **Validering:** Alla DTO:er valideras med class-validator -- **Felhantering:** GlobalExceptionFilter med svenska meddelanden -- **CORS:** Proxies hanteras via Next.js API routes +- **Autentisering:** JWT-baserad, 7 dagars token. Auth.js v5 (Credentials provider) i frontend. Alla backend-routes skyddas av `JwtAuthGuard` — öppna endpoints markeras med `@Public()`. +- **Middleware:** `middleware.ts` skyddar alla Next.js-routes utom `/login`, `/register` och `/api/auth`. Oinloggade användare omdirigeras automatiskt. +- **Validering:** Alla DTO:er valideras med `class-validator`. Inkommande fält i server actions bör kompletteringsvalideras (se teknisk skuld E). +- **Felhantering:** `GlobalExceptionFilter` fångar alla oupphanterade fel och returnerar svenska felmeddelanden. +- **CORS:** API-anrop proxias via Next.js API routes — klientkod når aldrig backenden direkt. +- **Filuppladdning:** Multer med `memoryStorage` och MIME-typvalidering; max 15 MB för kvittoimport. ### Möjliga utbyggnader -- Authentication (JWT, OAuth) -- Multi-user support -- Shoppinglistor -- Recept-delning -- Nutrition facts -- Allergi-tracking +- Användarroller (user / admin) — rollbaserad guard, skyddade admin-routes +- Delade recept / recept-export +- Push-notifieringar för utgångna varor +- Nutrition-baserat receptförslag +- Allergi-tracking per användare ---