# Florial consumer install — užduotis Claude'ui

**Kontekstas:**
- Esi aplanke `/Applications/XAMPP/xamppfiles/htdocs/ofisai` (macOS + XAMPP).
- Kuriame **atskirą consumer instaliaciją** „Florial" produkto (gėlių prenumeratos ofisams)
  **+ pilną CMS admin aplinką** (news, pages, users, roles).
  Visas kodas ateina iš privataus **Satis** Composer repo — nieko rankomis nekopijuojam.
- **NEREIKIA:** `va-themes/cleverphant` ir jokių `va-seglo/*` paketų — jų NEinstaliuok.
- **Virtual host jau sukurtas:** `ofisai.test` → `public/`.
- **DB pavadinimas: `ofisai`** (jei dar nesukurta — sukurk `ofisai` DB, utf8mb4; user `root`, tuščias slaptažodis lokaliai).
- Node/npm **nereikia** — temos assets ateina pre-built.

---

## Žingsniai

### 1. Host skeleton (jei aplankas tuščias)

```bash
composer create-project "laravel/laravel:^12.0" .
```

> ⚠️ **Versija PRIVALO būti pinned.** Be `:^12.0` Composer paima naujausią skeleton'ą
> (šiandien `laravel/laravel` 13.x), o visi `va-*` paketai require'ina
> `laravel/framework ^12.0` → 3 žingsnis krenta su „conflicts with your root
> composer.json require (^13.x)" ir NEĮDIEGIA nieko. Rasta per
> PackageStructure/task-25 consumer simuliaciją.

### 2. composer.json — pridėk root lygyje

```jsonc
"minimum-stability": "dev",
"prefer-stable": true,
"repositories": [
    { "type": "composer", "url": "https://satis.viartas.com/" }
],
```

### 3. Require paketus

Paketai beveik nesusiję priklausomybėmis — todėl vardijam viską ko reikia.
Išimtys, kurios traukiasi automatiškai: `va-modules/news` → atsitemps `va-modules/news-categories`;
`va-themes/viartas-default` → atsitemps `va-core/themes`; `va-florial/*` tarpusavyje.

```bash
composer require \
  va-core/core va-core/auth va-core/users va-core/translations \
  va-modules/pages va-modules/news va-modules/menus va-modules/custom-links va-modules/faq \
  va-themes/viartas-default \
  va-florial/core va-florial/subscriptions va-florial/companies \
  va-florial/employees va-florial/packages va-florial/orders \
  va-florial/procurement va-florial/invoicing va-florial/payments \
  va-florial/sales va-florial/notifications \
  va-themes/florial
```

**Kas ką duoda:**
- `va-core/core` — bazinė infra + **admin aplinka** (`/admin`) + dashboard
- `va-core/auth` — login / logout / password reset (**būtinas** prisijungimui)
- `va-core/users` — **users CRUD + roles** (Spatie)
- `va-core/translations` — vertimų valdymo UI
- `va-modules/pages` — **CMS puslapiai** + hierarchija + public slug
- `va-modules/news` (+ `news-categories`) — **naujienos** + kategorijos
- `va-modules/menus` — meniu; `va-modules/custom-links` — nuorodų biblioteka (menus source); `va-modules/faq` — DUK
- **Ateina automatiškai per `va-core/core`** (nebereikia vardyti): `va-core/module-manager` (`module.enabled` middleware) + `va-cross/{media,seo,geo,microdata,cms-badges,logs}` (cross-enhancer trait'ai — Page/News/User modeliai juos naudoja besąlygiškai). Core juos require'ina, kaip senasis `viartas-core/core` darė.
- `va-themes/viartas-default` — **admin tema** (kad `/admin` UI atsivaizduotų)
- `va-florial/*` + `va-themes/florial` — Florial produktas + jo tema

**Neįtraukta sąmoningai:** `va-themes/cleverphant`, `va-seglo/*`, shop moduliai (`va-shop/*`).

Provideriai registruojasi per Laravel auto-discovery — `bootstrap/providers.php` **neliesk**.

### 3b. `routes/web.php` — ištuštink skeleton welcome route (P6)

`composer create-project` palieka `Route::get('/', fn () => view('welcome'))`, kuris **užstoja** CMS web home (`/` rodytų Laravel welcome, ne Florial landing'ą). Ištrink tą route (arba ištuštink `routes/web.php` — CMS route'us registruoja paketai per `config('cms.environments')`).

### 4. .env

```env
APP_URL=http://ofisai.test
DB_DATABASE=ofisai
DB_USERNAME=root
DB_PASSWORD=

# User modelis gyvena va-core/users pakete (nėra App\Models\User):
AUTH_MODEL="ViartasCore\\Users\\Models\\User"

# Numatytoji kalba turi sutapti su locales lentelės default'u (lt):
APP_LOCALE=lt
APP_FALLBACK_LOCALE=lt

# Viešas nav + footer ateina iš /admin/menus (`web.main` / `web.footer`).
# BE ŠITO meniu redagavimas admin'e nieko nekeičia ekrane — tema render'ina
# blade fallback'ą (task-106). Platformos default'as yra `false`, nes jis
# bendras visoms temoms; Florial diegime jį reikia įjungti:
MENUS_DB_DRIVEN=true
```

### 5. Bootstrap

```bash
# Ištrink Laravel skeleton migracijas — paketai jas tiekia patys (users/cache/jobs);
# kitaip `migrate` lūžta „SQLSTATE[42S01]: Table 'cache' already exists":
rm -f database/migrations/0001_01_01_*_create_users_table.php \
      database/migrations/0001_01_01_*_create_cache_table.php \
      database/migrations/0001_01_01_*_create_jobs_table.php

# XAMPP/macOS: storage/ + bootstrap/cache rašomi Apache (daemon) vartotojui —
# kitaip login meta 500 su klaidinančiu „tempnam(): file created in temp directory":
chmod -R a+rwX storage bootstrap/cache

php artisan key:generate
php artisan storage:link
php artisan migrate

# BAZĖ: locales + admin@viartas.lt + roles + permissions + temų DB eilutės + route vertimai.
# CmsBootSeeder dabar class_exists-guarded → saugu Florial subsete (praleidžia neįdiegtus shop/module seederius).
php artisan db:seed --class="ViartasCore\Core\Database\Seeders\CmsBootSeeder" --force

# Florial produkto baseline (Florial roles + settings + holidays + content):
php artisan florial:install

# FRONTEND assets (Node/npm NEREIKIA):
php artisan themes:publish-assets                          # temų assets (florial + viartas-default) → public/vendor/*
php artisan vendor:publish --tag=package-assets --force    # visų paketų admin assets → public/vendor/*
php artisan optimize:clear                                 # švieži config/route/view cache (Blade direktyvos + assets)
```

> ℹ️ `CmsBootSeeder` sukuria `admin@viartas.lt` (per `UsersSeeder`), locales, temų eilutes — visą bazę. `florial:install` prideda Florial-specifinį turinį.
>
> ℹ️ `themes:publish-assets` dabar veikia vendor-install'e (temos aptinkamos per Composer `InstalledVersions`, ne `packages/` scan). Middleware alias'ai (`locale/admin/permission`) ir temų keliai resolve'inasi savaime — host `bootstrap/app.php` wiring **nereikia**.
>
> 🧹 **Švarus single-theme state (`--fresh`):** švariame Florial consumer'e (sava DB, tik Florial paketas) svetimo turinio nėra → papildomo valymo NEreikia. Bet jei DB dalinasi su kitomis temomis (dev/staging, kur buvo įdiegta Cleverphant/Seglo/Protera), jų CMS puslapiai/naujienos/DUK gali persidengti. Tada:
> ```bash
> php artisan florial:seed-content --fresh --dry-run   # peržiūra — ką ištrintų (nieko nekeičia)
> php artisan florial:seed-content --fresh --force     # išvalo svetimą CMS turinį + perseed'ina Florial
> ```
> Florial savo turinį laiko `florial_*` lentelėse (ne CMS Pages), tad `--fresh` tik pašalina kitų temų eilutes (bendras `ViartasCore\Core\Support\ForeignCmsContentWiper`). Destruktyvu — `--dry-run` parodo prieš tai. **NENAUDOK update'ui** — tik first-install / eksplicitiniam reset'ui.

---

## Prisijungimas + aplinkos

**Super admin (sukuria `CmsBootSeeder`):** `admin@viartas.lt` / **`password`** — turi `super-admin` rolę, mato viską.

| Aplinka | URL | Kam |
|---|---|---|
| **Landing** | `http://ofisai.test/` | Vieša Florial svetainė (florial web tema) |
| **Admin CMS** | `http://ofisai.test/admin` | pages/news/users/roles/temos (super-admin) |
| **Savitarna** | `http://ofisai.test/savitarna` | Klientų (įmonių/prenumeratorių) kabinetas |
| **Pardavimai** | `http://ofisai.test/pardavimai` | Pardavimų atstovų kabinetas |
| **Operacijos** | `http://ofisai.test/operacijos` | Ops komanda (gamyba/pristatymas) |

> Admin slug'ai (lt) versti: `/admin/puslapiai`, `/admin/naujienos`, `/admin/vartotojai`, `/admin/vartotojai/roles`, `/admin/temos`. `/admin/pages` duoda 404 (teisinga — EN variantas yra `/admin/en/pages`).
>
> **Prisijungimas:** bazinis user'is — **`admin@viartas.lt` / `password`** (`super-admin`). Super-admin **bypass'ina visus `*.access` middleware** (`AbstractEnvAccessMiddleware::isSuperAdmin()`), tad su juo pasieki **VISAS** aplinkas: admin + savitarna + pardavimai + operacijos. Bazinis `florial:install` (be `--with-content`) atskirų demo kabinetų user'ių NEseeda — bet `--with-content` DABAR seed'ina demo tenant'ą (žr. žemiau): `imone@florial.lt` (company_admin), `pardavejas@florial.lt` (sales_rep), `florial-subscriber@viartas.lt` (subscriber). Ne-local aplinkose demo slaptažodžiai atsitiktiniai (paskyros neprisijungsite kol admin nenustatys) — local/testing'e `password`.
>
> **Testuoti konkrečią rolę** (pvz. tik savitarna klientas): sukurk user'į ir priskrik rolę:
> ```bash
> php artisan tinker
> >>> $u = ViartasCore\Users\Models\User::create(['name'=>'Test','email'=>'client@test.lt','password'=>bcrypt('password')]);
> >>> $u->assignRole('company_admin');   # savitarna. Kiti: subscriber (savitarna), sales_rep (pardavimai), florial_ops (operacijos)
> ```
> Guest svečias ties bet kuria aplinka → redirect į tos aplinkos login (P2, savaime).

## Verifikacija (švarus install, be jokių rankinių DB/host hack'ų)
- `/` → Florial landing (200), NE Laravel welcome.
- `/admin` (guest) → redirect į login (ne 500).
- Login → `/admin`, `/admin/puslapiai`, `/admin/naujienos`, `/admin/vartotojai`, `/admin/vartotojai/roles` (200).
- `/savitarna`, `/pardavimai`, `/operacijos` (200).
- `SELECT environment,slug,is_default FROM themes WHERE is_default=1` → web→florial, admin→viartas-default, kabinetai→florial (jokios protera/seglo).

> ℹ️ **Landing paveiksliukai (hero / case-study) — placeholder'iai švariame install'e yra NORMALU, ne bug'as.**
> Florial tema **neturi** statinių content paveiksliukų (nei `public/img/`, nei `<img>` nuorodų į temą) — hero (`Package::imageUrl()`, media collection `image`) ir case-study (`Testimonial::avatarUrl()`, collection `avatar`) ateina iš **spatie media library**, įkelti **per admin** (`/admin` → Florial paketai / Testimonials). Kol niekas neįkėlė, blade'ai rodo spalvotą placeholder dėžutę (`@else` fallback). Tai **NE** ta pati asset-publish beda kaip Cleverphant (florial'iui `themes:publish-assets` niekada neturėjo ko publish'inti kaip `img/` — jo fonts/CSS yra `public/build/assets/`, publish'inasi teisingai). Nuotraukos atsiras įkėlus jas per admin.

## Jei kažkas lūžta

- `composer require` „package not found" → Satis neturi visos priklausomybių grandinės
  (ne tik `va-florial/*`, bet ir `va-core/*`, `va-modules/*`). Pranešk kuris paketas trūksta — reikės jį sudėti į Satis.
- Stability klaida → patikrink ar `minimum-stability: dev` įrašyta.
- Migrate/route klaidos → paleisk `php artisan optimize:clear` ir bandyk vėl.

## Pabaigus

Trumpai atsiskaityk: kokie paketai įsidiegė, ar login veikia, ir ar visos 3 aplinkos atsidaro.

---

## Forge / production deploy

Florial (consumer app) Forge deploy — **paprastesnis nei monorepo** (**be `npm build`**: assets ateina pre-built per `vendor:publish`). Forge → site → **Deploy Script**:

```bash
cd $FORGE_SITE_PATH
git pull origin $FORGE_SITE_BRANCH
$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader

$FORGE_PHP artisan storage:link
$FORGE_PHP artisan migrate --force

# FIRST install — TIK vieną kartą (marker shared storage'e išlieka tarp release'ų).
# Marker'is dengia TIK tikrą bootstrap'ą (CMS bazė: locales + admin@viartas.lt +
# roles + temų eilutės) — Florial sync'as žemiau bėga KIEKVIENĄ deploy:
if [ ! -f storage/app/.viartas-installed ]; then
    $FORGE_PHP artisan vendor:publish --tag=cms-database-seeder --force
    $FORGE_PHP artisan db:seed --class="ViartasCore\Core\Database\Seeders\CmsBootSeeder" --force
    mkdir -p storage/app && touch storage/app/.viartas-installed
fi

# Florial sync — KIEKVIENĄ deploy (idempotentinis: roles + permissions + settings +
# holidays + temų default'ai). Būtent jis atneša naujus permission'us/seeder'ius iš
# dev serverio užduočių; be jo consumer'is tyliai lieka pasenęs:
$FORGE_PHP artisan florial:upgrade --force

# Bazinės global validation/auth lang + modelių aptikimas (va-core/translations 1.1.0).
# --force saugu: per-projekto vertimai yra DB (language_lines), ne failuose.
$FORGE_PHP artisan vendor:publish --tag=lang --force
$FORGE_PHP artisan translations:sync-models

# Assets (pre-built — jokio npm):
$FORGE_PHP artisan themes:publish-assets
$FORGE_PHP artisan vendor:publish --tag=package-assets --force
$FORGE_PHP artisan optimize

( flock -w 10 9 || exit 1; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
```

> **Po pirmo deploy — nieko rankomis.** First-install blokas pasileidžia automatiškai, marker'is (`storage/app/.viartas-installed`) laiko jį nuo vėlesnių deploy'ų; `florial:upgrade` bėga visada ir yra saugus kartoti. `.env`: `APP_URL`, DB, `AUTH_MODEL`, `APP_LOCALE=lt`.

---

## Atnaujinimas (update) — po dev serverio užduočių

Consumer'is atsinaujina **viena idempotentine komanda**. Deploy skriptas tai daro
automatiškai; rankomis (pvz. lokaliame `ofisai`) — tas pats:

```bash
composer update
php artisan migrate --force
php artisan florial:upgrade --force
php artisan vendor:publish --tag=lang --force        # bazinės validation/auth/passwords/pagination (lt+en) → global lang/
php artisan translations:sync-models                 # modelių aptikimas /admin/translations/modeliai
php artisan themes:publish-assets
php artisan vendor:publish --tag=package-assets --force
php artisan optimize
```

> ℹ️ **Nuo task-107 `deploy:sync` (`composer sync` / `composer sync:prod`) PATS paleidžia
> `florial:upgrade --force`** — tame pačiame chain'e, kur jau seniai stovi `seglo:install`,
> iškart po jo (t. y. PO `db:seed` ir PRIEŠ `themes:publish-assets`). Žingsnis
> presence-gated: host'as be `va-florial/*` jį praleidžia (`skip (Florial not installed)`),
> deploy nenutrūksta. Praktinė pasekmė: **host'e, kuris naudoja `composer sync`, atskiro
> `php artisan florial:upgrade` nebereikia** — žemiau esantis rankinis sąrašas lieka
> galioti consumer'iams (ofisai), kur deploy skriptas kviečia komandas po vieną.
>
> ⚠️ `--with-content` per `deploy:sync` **NIEKADA neperduodamas** (struktūra kas deploy,
> turinys niekada) — viešo turinio seed'as lieka sąmoningas vienkartinis veiksmas.
>
> ⚠️ **`web` numatytosios temos apsauga (task-107):** jei `web` aplinkos default'as jau
> priklauso KITAI **įdiegtai** temai (multi-produkto host'e — pvz. Cleverphant), Florial
> jo **nebeperima** (`web` already belongs to another INSTALLED theme). Vieno produkto
> consumer'yje (ofisai) niekas nesikeičia: kitos web temos ten neįdiegtos, tad
> `themes:sync-defaults` ir taip parenka vienintelę tinkamą — Florial. Kabinetų
> (`savitarna`/`pardavimai`/`operacijos`) default'ai perimami besąlygiškai — tos aplinkos
> egzistuoja tik dėl Florial.

`florial:upgrade` paleidžia tą pačią struktūrinę grandinę kaip `florial:install`
(baseline seeder → `cms:sync-permissions` → temų eilutės + per-aplinkos default'ai),
tik pavadinimu, kuris sako, kad jį galima leisti kaskart. Idempotentiškas: pakartotinis
run'as nieko nedublikuoja (RE-ASSERT'ina seed'intus default'us).

**Viešas turinys + demo tenant — atskirai:** `--with-content` numatytai IŠJUNGTAS (jis
perrašytų per admin redaguotą katalogo copy/kainas). Kai reikia užpildyti viešas
sekcijas — `php artisan florial:upgrade --with-content --force` (arba
`florial:seed-content --force`). **Nuo QA #1 `--with-content` DABAR seed'ina ir demo
tenant'ą** (įmonė + company_admin + darbuotojai + sales_rep + owner login'ai + B2C
subscriber), kad savitarna/pardavimai kabinetai būtų pasiekiami iškart po install'o.
Visi demo įrašai idempotentiški (`updateOrCreate`) — ištrink rankomis, kai atsiranda
realūs duomenys. Tik viešas turinys be demo: `florial:seed-content --force` (be `--demo`);
tik demo įrašai: `florial:seed-content --demo --force`. Meniu (**`web.main` IR `web.footer`**)
taip pat perseed'inamas į Florial landing'us + numetamas shop `web.brands` (QA #8) — DB nav
įsijungia tik kai `.env` `MENUS_DB_DRIVEN=true`, kitaip lieka hardcoded (per-locale) nav.

> ⚠️ **Meniu seed'as skaito route'ų lentelę.** `/`, `/duk` ir `/kontaktai` registruojami
> TIK kai Florial yra numatytoji `web` tema, tad seed'ą leisk jau perjungtoje temoje
> (consumer'yje ji vienintelė; shared repo — `php artisan florial:activate` pirma).
> Kitaip meniu bus teisingas, bet trumpesnis (trūkstami punktai praleidžiami, o ne
> nukreipiami į 404). Perseed'inti bet kada saugu — jis idempotentiškas.

> 📋 **Išimtys** — žingsniai, kurių ta komanda NEabsorbuoja (naujas `.env`, Satis
> rebuild, vienkartinis turinio seed'as) — [`CHANGELOG.md`](CHANGELOG.md).
