# PetalAir OS — Setup

Bangla/Banglish e likha, karon apni oivabei chaisilen. Command gulo obviously English.

---

## 1. Ki ki lagbe

| Jinis | Version | Keno |
|---|---|---|
| PHP | 8.2+ | Laravel 12 er minimum |
| Composer | 2.x | PHP dependency |
| PostgreSQL *ba* MySQL | 14+ / 5.7+ | Duitatei chole |
| Node.js | 20+ | **Optional** — sudhu design change korle |

PHP extension: `pdo_pgsql`, `mbstring`, `gd` (image resize), `zip`, `intl`, `dom` (PDF).

Ubuntu hole:

```bash
sudo apt install php8.2-{cli,fpm,pgsql,mbstring,gd,zip,intl,dom,curl,xml}
```

> **Kon database?** Duitatei chole — `app/Support/Sql.php` driver dekhe nije adjust kore
> ney. Choice thakle Postgres nen (JSON handling ar concurrency bhalo). **cPanel shared
> hosting e MySQL** — `docs/CPANEL.md` dekhen.

---

## 2. Install

```bash
cd petalair-os

composer install

cp .env.example .env
php artisan key:generate
```

### Database banan

```bash
sudo -u postgres createdb petalair
sudo -u postgres createuser petalair --pwprompt
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE petalair TO petalair;"
```

Ekhon `.env` e credentials boshan:

```env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=petalair
DB_USERNAME=petalair
DB_PASSWORD=apnar_password
```

### Migrate + seed

```bash
php artisan migrate --seed
php artisan storage:link      # product image gulo eta chara dekhabe na
```

`--seed` diye jekhane jabe: 10 ta industry, 4 ta diffuser, 6 ta oil (full note pyramid soho),
5 ta plan, 16 ta FAQ, 3 ta blog post, ar **demo data** — 42 ta order, 7 ta subscription,
8 ta lead. Eta icche kore rakha, karon fanka dashboard dekhe bojha jay na dashboard ta kaj kore ki na.

Demo data na chaile:

```bash
php artisan migrate
php artisan db:seed --class=SettingsSeeder
php artisan db:seed --class=IndustrySeeder
php artisan db:seed --class=CategorySeeder
php artisan db:seed --class=ProductSeeder
php artisan db:seed --class=PlanSeeder
php artisan db:seed --class=FaqSeeder
php artisan db:seed --class=UserSeeder
```

### Chalu korun

```bash
php artisan serve
```

CSS/JS already build kora (`public/css/app.css`) — `npm` lagbe na. Sudhu design
change korle `npm install && npm run build`.

`http://localhost:8000` — site.
`http://localhost:8000/admin` — admin.

**Login:**

| | Email | Password |
|---|---|---|
| Admin | `admin@petalair.com` | `password` |
| Manager | `manager@petalair.com` | `password` |
| Customer | `farhana@example.com` | `password` |

> ⚠️ Live e jawar age **first kaj**: ei password gulo change korun. `UserSeeder.php` e
> hardcode kora ache — local er jonno thik ache, server e na.

---

## 3. Client er business info

`.env` er niche `# --- The business ---` section:

```env
PETALAIR_COMPANY_NAME=PetalAir
PETALAIR_LEGAL_NAME="PetalAir Scent Solutions Ltd."
PETALAIR_EMAIL=hello@petalair.com
PETALAIR_PHONE="+880 1XXX-XXXXXX"
PETALAIR_WHATSAPP=8801XXXXXXXXX
PETALAIR_ADDRESS="Gulshan 1, Dhaka 1212"
PETALAIR_BIN=                          # VAT BIN — invoice e print hobe

PETALAIR_SHIPPING_FLAT=120
PETALAIR_SHIPPING_FREE_OVER=5000

PETALAIR_VAT_RATE=0                    # 0 dile VAT line puropuri hide hoye jabe
PETALAIR_INVOICE_DUE_DAYS=7

PETALAIR_PAY_BKASH="01XXX-XXXXXX"      # unpaid invoice PDF e print hobe
PETALAIR_PAY_NAGAD="01XXX-XXXXXX"
```

Ei gulo `.env` e karon deploy chara change kora jay. Ar je jinis gulo client
nijei change korte chabe (hero headline, tagline) — segula `settings` table e,
`Setting::get('site.hero_headline')` diye pora hoy.

---

## 4. Email

Development e default `MAIL_MAILER=log` — email pathabe na, `storage/logs/laravel.log`
e likhe rakhbe. Test korte chaile [Mailpit](https://mailpit.axllent.org/) use korun:

```env
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
```

Production e SMTP details boshan. Invoice email e PDF attach hoye jay — `app/Mail/InvoiceMail.php`.

---

## 5. Queue ar Scheduler — ei duita miss korle system ta kaj korbe na

Eta shobcheye important part, tai alada kore likhchi.

### Queue worker

Email `queue()` diye pathano hoy, tai worker na cholle **kono email jabe na**.
Kono error dekhabe na — chup kore queue e boshe thakbe.

```bash
php artisan queue:work --tries=3
```

Server e supervisor diye:

```ini
[program:petalair-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/petalair/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/petalair/storage/logs/worker.log
stopwaitsecs=3600
```

### Scheduler

Subscription auto-billing eta diye chole. **Cron na dile keu bill pabe na.**

```bash
crontab -e
```

```cron
* * * * * cd /var/www/petalair && php artisan schedule:run >> /dev/null 2>&1
```

Ki cholbe (`routes/console.php`):

- `petalair:bill-subscriptions` — roj raat 2:00 ta. Jader `next_billing_at` aaj ba
  age, tader invoice banay ar email kore.
- `petalair:mark-overdue` — roj 2:30 ta. Due date pass howa invoice gulo overdue mark kore.

Live e chalanor age test korun — **kichu change korbe na**:

```bash
php artisan petalair:bill-subscriptions --dry-run
```

---

## 6. Production deploy

```bash
composer install --no-dev --optimize-autoloader
npm ci && npm run build

php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan storage:link
```

`.env` e:

```env
APP_ENV=production
APP_DEBUG=false          # eta true thakle stack trace public hoye jabe
APP_URL=https://petalair.com
```

Nginx root **`/public`** e point korte hobe, project folder e na. Eta bhul korle
`.env` file browser theke download kora jabe.

```nginx
server {
    listen 443 ssl http2;
    server_name petalair.com;
    root /var/www/petalair/public;

    index index.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* { deny all; }
}
```

Permission:

```bash
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
```

### Go-live checklist

- [ ] `admin@petalair.com` ar `manager@petalair.com` er password change
- [ ] Demo data delete (ba fresh DB te demo seeder chara migrate)
- [ ] `APP_DEBUG=false`
- [ ] SMTP configure + ekta test invoice email pathiye dekha
- [ ] Cron boshano — `php artisan schedule:list` diye verify
- [ ] Queue worker cholche — supervisor status check
- [ ] `PETALAIR_PAY_BKASH` / `NAGAD` / bank details thik ache (invoice e print hoy)
- [ ] SSL on, `APP_URL` https
- [ ] DB backup schedule kora

---

## 7. Business logic — je jinis gulo dev er jana dorkar

Ei gula intentional decision, bug na. Change korar age poren.

**Subscription pending theke start hoy, active theke na.**
Customer subscribe korle status `pending` hoy. Technician install kore admin panel
theke "Mark as installed" chaple `active` hoy — **tokhon theke MRR count start hoy,
tokhon theke billing hoy**. Karon customer install er jonno ek soptaho wait korle
oi soptaho tar taka newa uchit na.

**Cancel korle sathe sathe bondho hoy na.**
Je period er taka already newa hoise, oi period ses porjonto chalu thake. `SubscriptionService::cancel()`
`ends_at` set kore, `status` cancelled kore, kintu diffuser cholte thake.

**Plan er price change korle purono customer er upor effect pore na.**
`subscriptions.price` alada column — sign up er somoy er price ta lock hoye jay.
Admin panel e plan edit korle sudhu notun subscription oi price pabe.

**Product delete korle purono order noshto hoy na.**
`order_items` e name, sku, price er nijer copy (snapshot) thake. Product soft-delete
hoy. Tai 6 mash ager invoice ekhono thik dekhabe.

**Order number gap chara hoy.**
`Sequence::next()` settings table e row lock kore — `ORD-2026-00042` er por 43,
kokhono 44 e jump korbe na. Audit er jonno eta lage.

**Invoice polymorphic.**
Ekta invoice Order er hote pare, abar Subscription er o hote pare — `invoiceable` morph.
Tai `$invoice->invoiceable` check kore dekhte hoy eta ki.

---

## 8. Kothay ki ache

```
app/
  Enums/           13 ta enum — status, role, scent family. label() ar tone() ache.
  Models/          19 ta model
  Services/        Business logic ekhane. Controller e na.
    CartService          session cart (dam DB theke re-read kore, session theke na)
    OrderService         placeFromCart() — order+stock+invoice ek transaction e
    SubscriptionService  subscribe/activate/pause/resume/cancel/renew
    InvoiceService       order ba subscription theke invoice
    ReportService        shob KPI, chart, MRR, churn
  Traits/          HandlesImageUpload, Auditable
  Helpers/
    helpers.php      setting(), uploadUrl(), money(), clearSiteCache()
  Support/
    Money.php        format — float use korben na, decimal
    Sequence.php     gap-chara document number
    Sql.php          MySQL/Postgres er difference ekhane isolated
  Http/Controllers/
    Web/       public site
    Shop/      cart, checkout, subscribe, order tracking
    Account/   customer login er por
    Admin/     admin panel
    Auth/      login, register, password reset

resources/
  css/app.css        design system — token, plume, note-bar shob ekhane
  views/
    web/       public page
    shop/      cart, checkout
    account/   customer area
    admin/     admin panel
    pdf/       invoice PDF (dompdf — Tailwind kaj kore na, table+inline CSS)
    mail/      email template

docs/DESIGN.md       design system er explanation — notun page banale poren
```

---

## 9. Common problem

**CSS load hocche na** → `public/css/app.css` file ta ache to? Na thakle `npm install && npm run build`.

**Product image dekhacche na** → `php artisan storage:link` koresen?

**Email jacche na** → queue worker cholche? `php artisan queue:work` diye dekhen.
`failed_jobs` table check korun.

**Subscription bill hocche na** → cron ache? `php artisan schedule:list` diye dekhen.
Tarpor `php artisan petalair:bill-subscriptions --dry-run`.

**cPanel e deploy korchen?** → `docs/CPANEL.md` — shared hosting e queue, cron ar
asset build alada bhabe korte hoy.

**403 admin e** → user er `role` `customer` na to? Ar `is_active` true ache?

**Font load hocche na** → Google Fonts theke ase. Internet nai emon server hole
self-host korte hobe, layout file e link ache.


## Menus and pages

Both live under **Content** in the admin sidebar.

### Menus

`admin/menus` — separate lists for the header and the footer.

- **Add item** to create a top-level entry, **+ Sub** on a row to hang a dropdown
  item under it. Two levels only; a third would be unreachable on a phone.
- Each item points at one of four things: a built-in site page, a custom page you
  wrote, a product category, or any URL.
- The site-page list is a fixed whitelist (`MenuItem::linkableRoutes()`). That is
  on purpose — the menu renders inside the layout, so one bad route name would
  500 every page on the site, not just one.
- **Fill dropdown with product categories** on the Shop item makes its dropdown
  read live categories. Add a category in the catalogue and it appears in the
  menu on its own.
- Menus are cached for six hours. Saving a menu, page or category clears it via
  `clearSiteCache()`. If a change ever seems to do nothing, that is the first
  thing to check.

If no menu rows exist at all the header falls back to a three-link stub, so a
fresh database never renders an empty site.

### Pages

`admin/pages` — free-form pages (terms, delivery, careers) written in TinyMCE,
loaded from a CDN.

Each page lives at `/{slug}`. That route is registered **last** in `routes/web.php`
because it matches any single-segment path; anything registered after it would be
unreachable. `PageRequest` also refuses to save a page on a reserved slug
(`cart`, `login`, `products`…) so the two guards meet in the middle.

Page bodies are printed unescaped — that is what makes the editor's formatting
survive. Only back-office roles can reach the editor. Never route customer input
into that column.
