# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Start all dev services (Laravel server + queue + log watcher + Vite)
composer dev

# Individual services
php artisan serve
php artisan queue:listen --tries=1
npm run dev

# Tests
php artisan test                          # run all tests
php artisan test --filter TestName        # run single test
./vendor/bin/pest                         # run via Pest directly

# Database
php artisan migrate
php artisan db:seed

# Filament
php artisan shield:generate --panel=app   # regenerate permissions for app panel
php artisan filament:upgrade              # upgrade Filament assets

# After editing Blade files
npm run build
```

## Architecture Overview

Laravel 11 + Filament v3 multi-tenant ERP for creative/tech studios. UI is in **Bahasa Indonesia**.

### Three Filament Panels

| Panel | Path | Tenancy | Purpose |
|-------|------|---------|---------|
| **App** | `/app` | Team-scoped | Main operational panel |
| **Admin** | `/admin` | Global | Super-admin (users, teams, categories) |
| **Mentor** | `/mentor` | Global | Cross-team KPI & finance dashboards |

Panel providers: `app/Providers/Filament/{App,Admin,Mentor}PanelProvider.php`

### Multi-Tenancy

- Tenant model: `Team`; pivot: `team_user`
- All App panel resources scope via `->where('team_id', filament()->getTenant()->id)` in `getEloquentQuery()`
- Custom pages scope via `filament()->getTenant()?->id`
- Users with role `Koordinator` can access all teams

### Auth & Permissions

- **Spatie Permission** + **FilamentShield** (`bezhansalleh/filament-shield`)
- Resource permissions auto-generated; custom pages gate via `hasPermissionTo('page_PageName')` or `hasRole('super_admin')`
- Admin panel restricted to `super_admin` role
- Regenerate after adding resources/pages: `php artisan shield:generate --panel=app`

## Key Patterns

### Standard Filament Resource
Form + table + `getEloquentQuery()` with tenant scope. Relation managers in `{Resource}/RelationManagers/`.

### Custom Livewire Report Pages
Several pages extend `Page` directly with custom Blade views (not `InteractsWithTable`):
- `app/Filament/Pages/BonusContribution.php`
- `app/Filament/Pages/TargetRealization.php`

Pattern:
- Public Livewire properties for filters (`$month`, `$year`, etc.)
- `getComputedData(): array` does all DB queries and returns a flat array consumed by the Blade view
- Blade uses `@php $d = $this->getComputedData() @endphp` then renders custom HTML tables
- Use `wire:model.live` for selects, `wire:model.live.debounce.600ms` for number inputs
- `updatedMonth()` / `updatedYear()` hooks reload saved state from DB

### Batch-Entry Create Page (ProjectEarningResource)
`app/Filament/Resources/ProjectEarningResource/Pages/CreateProjectEarning.php` is a custom `Page` implementing `HasForms` with a `TableRepeater` that creates multiple `ProjectEarning` rows in one submission. The Blade view at `resources/views/filament/resources/project-earning-resource/pages/create-project-earning.blade.php` uses `wire:submit="save"`.

### Shared BonusRecord
`BonusContribution` and `TargetRealization` pages both read/write `BonusRecord` (unique per `team_id + month + year`). Saving on either page updates the same record. `BonusSetting` stores per-team default percentages; `BonusPoolTier` defines profit-range → bonus pool mappings.

### Bonus Pool Auto-Computation
On `BonusContribution`, outcome is auto-fetched from `FinanceReport.total_expense` for the selected month/year. Bonus pool is auto-computed by matching profit against `BonusPoolTier` ranges. Both override when a saved `BonusRecord` exists for the period.

### Shared Model — Proposal/Lead
`Proposal` model serves both `ProposalResource` and `LeadResource`. Differentiated by `lead_only` and `check_masuk_lead` boolean fields filtered at `getEloquentQuery()`.

### Finance Report Form
Single `transactions` TableRepeater with a virtual `type` ToggleButton (income/expense, `dehydrated(false)`). Type is eager-loaded via `->with('transaction_category')` to populate the category dropdown on edit. Totals auto-calculated via "Calculate Totals" action. View page uses Alpine.js tabs.

### Three-Scenario Forecasting
`Forecasting` model has 12 computed fields (3 scenarios × 4 metrics): Real, Target, Real Income vs Estimated Expenses.

### Money Formatting
Currency inputs use `mask('999,999,999')` — Indonesian format (dot = thousands, comma = decimal). `Transaction.amount` is decimal(15,2).

### Weeks-in-Month Helper
Both `BonusContribution` and `TargetRealization` use a `weeksInMonth(): array` private method that returns all ISO week numbers falling within the selected month — not just weeks that have earnings data.

### Cascade Deletes (via `boot()`)
- `Project` → documents, sprints, tasks, earnings
- `FinanceReport` → incomes, expenses (transactions)

## Projek Navigation Group (App Panel)

| Page/Resource | Path | Purpose |
|---|---|---|
| `ProjectResource` | `/app/{tenant}/projects` | Project CRUD; form has 4 sections: Project Details, Team, Timeline & Target (`total_hour_per_week`), Finance & Status |
| `ProjectEarningResource` | `/app/{tenant}/project-earnings` | Earnings per project per week; batch-entry Create page |
| `BonusContribution` | `/app/{tenant}/bonus-contribution` | Monthly bonus calculation report; saves to `BonusRecord` |
| `TargetRealization` | `/app/{tenant}/target-realization` | Project target vs realization pivot by week; filters by project status |
| `BonusPoolSettings` | `/app/{tenant}/bonus-pool-settings` | Edit per-team allocation % (PM/Dev/Marketing/Koordinator) |
| `ManageBonusPool` | `/app/{tenant}/manage-bonus-pool` | Define profit-range → bonus pool tiers |

## Export/Import System

**PDF exports** (DomPDF) via controllers:
- `GET /forecasting/{id}/export-pdf`
- `GET /finance-report/{id}/export-pdf`
- `GET /salary/{id}/export-pdf`
- `GET /marketing-reports/export-pdf`

**Excel exports** via Maatwebsite Excel (`app/Exports/`, `app/Filament/Exports/`)

**CSV imports** via Filament Importer (`app/Filament/Imports/`) for: Client, FinanceReport, Forecasting, MarketingReport, Proposal

All export routes require `auth` middleware.

## Stack

- **PHP 8.2+**, Laravel 11, Filament v3
- **Frontend**: Tailwind CSS v3, Vite v6, Livewire (via Filament), Alpine.js
- **DB**: MySQL (`objidev_app`); Session/Queue/Cache all use `database` driver
- **Key packages**: `filament-shield`, `awcodes/filament-table-repeater`, `dompdf`, `maatwebsite/excel`, `flowframe/laravel-trend`, `mokhosh/filament-kanban`, `parallax/filament-comments`, `lwwcas/laravel-countries`

## Key Models Added Since Initial Build

| Model | Table | Purpose |
|---|---|---|
| `ProjectEarning` | `project_earnings` | Tracks per-project earnings: amount (USD), developer, PM, marketing, rate_idr; `earning`/`potential` (legacy, nullable) |
| `BonusSetting` | `bonus_settings` | Per-team allocation percentages (unique per team) |
| `BonusRecord` | `bonus_records` | Monthly bonus snapshot per team; unique on team+month+year |
| `BonusPoolTier` | `bonus_pool_tiers` | Profit range → bonus pool amount mapping per team |
