# Plan App

## Scop
Transformarea fișierului [antrenament-sapt-2026-05-11.html](/Users/adu/Library/CloudStorage/GoogleDrive-adrian.ciutureanu@gmail.com/My%20Drive/Sport/programe-antrenament/antrenament-sapt-2026-05-11.html) într-o aplicație PHP cu:
- cod separat de date și asset-uri statice
- program încărcat din JSON servit de endpoint
- editare locală în timp real, cu persistență în `localStorage`
- salvare explicită prin buton `Save`
- suport pentru reguli diferite de greutăți pe aparat
- suport pentru mutarea / omiterea zilelor
- note pe fiecare set

## Stack recomandat
- backend: `Laravel 13`
- frontend: `Vue 3`
- build tool: `Vite`
- state management: `Pinia`
- swipe / set navigation: `Swiper`
- styling: CSS propriu, cu opțiunea de a adăuga utilitare ușoare ulterior

## De ce acest stack
- merge foarte bine atât pe mobile, cât și pe desktop
- e suficient de general ca să fie refolosit și la alte aplicații
- permite UI-uri foarte interactive fără a încărca inutil aplicația
- AI-ul poate genera și modifica fluent cod în acest ecosistem
- `Swiper` rezolvă elegant pattern-ul de navigare verticală tip TikTok
- `Laravel + Vue` oferă un echilibru bun între performanță, structură și flexibilitate

## Principii
- PHP servește HTML-ul aplicației și endpoint-urile JSON.
- JSON-ul de pe disk este sursa oficială.
- browserul lucrează pe un draft local până la `Save`.
- UI-ul păstrează lățimea fixă și dezactivează comportamentele de zoom accidental.
- logica de greutăți nu mai este hardcodată generic, ci per exercițiu / aparat.

## Structură propusă
```text
app/
  app/
    Http/Controllers/
    Services/
    Support/
  public/
  resources/
    js/
      app.js
      stores/
      components/
      composables/
      features/
        workout/
        schedule/
        weights/
    css/
      app.css
  routes/
    web.php
    api.php
  data/
    workouts/
      current.json
    history/
      saved-YYYY-MM-DD-HHMMSS.json
```

## Model de date
Un singur JSON trebuie să conțină:
- metadate program: săptămână, interval, fază, parametri
- definiția zilelor
- mapping flexibil zi logică -> zi calendaristică / stare `planned`, `done`, `skipped`
- exerciții
- configurația sistemului de greutăți pentru fiecare exercițiu
- serii planificate
- serii realizate
- note per set

### Structură minimă
```json
{
  "program": {
    "id": "antrenament-sapt-2026-05-11",
    "title": "Jurnal Antrenamente",
    "week_start": "2026-05-11"
  },
  "schedule": {
    "days": [
      { "day_no": 1, "label": "Ziua 1", "planned_weekday": "Luni", "actual_date": "2026-05-12", "status": "done" },
      { "day_no": 2, "label": "Ziua 2", "planned_weekday": "Marți", "actual_date": "2026-05-13", "status": "done" },
      { "day_no": 4, "label": "Ziua 4", "planned_weekday": "Joi", "actual_date": null, "status": "skipped" }
    ]
  },
  "days": [
    {
      "day_no": 1,
      "title": "Picioare",
      "note": "nota zilei",
      "exercises": [
        {
          "id": "z1e1",
          "name_ro": "Hack squat standard",
          "name_en": "Hack Squat",
          "guide_url": "https://...",
          "image": "assets/images/hack-squat_400px.jpeg",
          "setup": "text",
          "weight_system": {
            "type": "pin",
            "step": 5,
            "has_half_step": true,
            "custom_values": null
          },
          "planned_sets": [
            { "target_weight": 40, "rep_min": 12, "rep_max": 15 },
            { "target_weight": 45, "rep_min": 10, "rep_max": 12 }
          ],
          "performed_sets": [
            { "actual_weight": 40, "actual_reps": 12, "done": true, "notes": "ultima rep grea" },
            { "actual_weight": 45, "actual_reps": 10, "done": false, "notes": "" }
          ]
        }
      ]
    }
  }
}
```

## Tipuri de greutăți
Trebuie suportate explicit:
- `dumbbell`
- `plates`
- `pin`
- `pin_half_step`
- `clapets`
- `bodyweight`
- `custom_values`

### Reguli cerute
- Gantere:
  - `+` adaugă `2` dacă valoarea este `<= 8`
  - `+` adaugă `10` dacă valoarea este `> 8`
  - `-` scade `1` dacă valoarea este `<= 8`
  - `-` scade `2` dacă valoarea este `> 8`
  - lista de valori valide: `1..8`, apoi `10,12,14,...,28`, apoi `35`
- Discuri:
  - `+10`
  - `-2.5`
- Pin:
  - pas fix configurabil per exercițiu, de ex. `5kg`, `7kg`, `10kg`
- Pin cu intermediar:
  - suport pentru `base + half_step`
- Clapete:
  - valori discrete configurabile per exercițiu

## Regula de propagare a greutății
Când utilizatorul modifică greutatea unui set:
- se ia greutatea curentă modificată
- se calculează `next_step_value` după sistemul de greutăți al exercițiului
- pentru toate seturile următoare:
  - dacă greutatea lor este mai mică decât greutatea curentă, se setează la `next_step_value`
  - dacă greutatea lor este deja mai mare sau egală, rămân neschimbate

Observație:
- regula trebuie să funcționeze nu doar pentru primul set, ci pentru orice set modificat

## UI / UX

### 1. Layout
- ecran cu lățime fixă pentru zona principală
- viewport configurat să reducă riscul de zoom accidental
- butoane `+/-` suficient de mari și cu spațiere stabilă
- pe ecran se afișează un singur set la un moment dat
- navigarea între seturi se face vertical, tip TikTok:
  - `swipe up` = următorul set
  - `swipe down` = setul anterior
  - fallback desktop: rotiță / săgeți / butoane Next-Prev
- contextul minim vizibil pentru setul curent:
  - zi
  - exercițiu
  - index set `2/4`
  - greutate planificată / realizată
  - rep range
  - buton `Done`
  - buton `Note`
- trebuie să existe și un indicator de progres:
  - în exercițiul curent
  - în ziua curentă
- schimbarea între seturi trebuie să fie instantă și foarte stabilă, fără scroll accidental al întregii pagini

### 2. Tab-uri / zile
- zilele nu trebuie să mai fie strict legate de luni-marți-joi-vineri
- trebuie un mod de editare a programării:
  - schimb data reală a unei zile
  - mut ziua 1 de pe luni pe marți
  - marchez o zi ca `skipped`
  - eventual duplic sau reordonez mai târziu, dar nu e MVP

### 3. Note pe set
- fiecare set are un buton mic `Note`
- deschide un popover / modal mic / inline expander
- stochează textul în `performed_sets[i].notes`

### 4. Config aparat / greutăți
- la nivel de exercițiu trebuie un buton `Config`
- deschide modal cu:
  - tip greutate
  - pas
  - half-step da/nu
  - valori custom, câte una pe linie
- această configurație se salvează în JSON împreună cu exercițiul

## Stocare și salvare

### Draft local
- la încărcare:
  - app citește JSON-ul de la `GET /api/workout.php`
  - construiește un `working copy`
- orice modificare ulterioară:
  - actualizează state-ul din memorie
  - persistă imediat în `localStorage`
- cheia de draft trebuie să includă `program.id`

### Save explicit
- buton `Save`
- trimite întregul JSON curent la `POST /api/workout-save.php`
- serverul:
  - validează schema minimă
  - scrie `data/workouts/current.json`
  - opțional salvează și snapshot în `data/history/`
- după succes:
  - draftul local devine sincronizat cu serverul

## Endpoint-uri

### `GET /api/workout`
Returnează JSON-ul curent de pe disk.

### `POST /api/workout-save`
Primește JSON-ul complet și îl salvează.

### Opțional mai târziu
- `GET /api/workout-history`
- `POST /api/workout-reset-day`

## Reorganizare cod
Trebuie separat clar:
- datele din `PLAN` ies din HTML și intră în `data/workouts/current.json`
- CSS-ul iese în `public/assets/css/app.css`
- JS-ul iese în module separate
- `index.php` rămâne subțire și doar include shell-ul aplicației

## Plan de execuție

### Faza 1. Bootstrap PHP
- inițializez aplicația în `Laravel 13`
- creez structura de directoare pentru `Vue 3 + Vite`
- extrag CSS și JS din HTML-ul actual
- păstrez inițial aceeași funcționalitate vizuală, dar în shell-ul nou

### Faza 2. JSON server-backed
- mut `PLAN` în `data/workouts/current.json`
- adaug `GET /api/workout`
- refac inițializarea frontend din fetch, nu din constantă inline

### Faza 3. State nou
- introduc model separat:
  - `planned_sets`
  - `performed_sets`
  - `weight_system`
  - `schedule.days`
- refac `localStorage` ca draft complet de program, prin `Pinia`
- adaug buton `Save`

### Faza 4. Motor greutăți
- implementez `weights.js`
- reguli pentru:
  - gantere
  - discuri
  - pin
  - pin half-step
  - clapete / custom values
- implementez propagarea către seturile următoare

### Faza 5. Editare zile
- UI pentru modificarea zilei reale
- suport `done / planned / skipped`
- afișare corectă în tabs și bannere

### Faza 6. Note și config aparat
- buton `Note` pe fiecare set
- buton `Config` pe exercițiu
- modal pentru configurarea tipului de greutate și a pașilor

### Faza 7. Polish mobil
- lățime fixă
- anti-zoom accidental
- hit areas mai mari
- swipe vertical fluid între seturi
- blocarea scroll-ului clasic al paginii în modul de lucru pe seturi
- test rapid pe flow-ul real de sală

## MVP recomandat
Prima versiune utilă ar trebui să includă doar:
- shell Laravel + Vue
- JSON încărcat de la endpoint
- draft în `localStorage`
- buton `Save`
- reguli complete de greutăți
- propagare greutate către seturile următoare
- note per set
- viewer cu un singur set pe ecran și navigare swipe sus/jos

Poate rămâne pentru etapa 2:
- editor complet de zile
- config aparat avansat cu valori custom
- history / snapshots vizibile în UI

## Decizii deschise
- dacă `performed_sets` trebuie creat mereu 1:1 din `planned_sets` sau poate avea structură diferită
- dacă `actual_date` se alege liber sau doar din săptămâna curentă
- dacă `Save` suprascrie direct `current.json` sau cere și versionare
- dacă imagini / ghiduri rămân în JSON-ul principal sau se normalizează separat

## Ordinea recomandată de implementare
1. separare fișiere și bootstrap PHP
2. endpoint GET + încărcare din JSON
3. draft `localStorage` + `Save`
4. motor greutăți per exercițiu
5. note per set
6. editare zile
7. config aparat
