Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
bdda634
fix(forms): filter numbering selector by document type (#152)
nielsdrost7 Jun 12, 2026
273c2f2
Add CLAUDE.md with full project knowledge map
claude Jun 13, 2026
73348d8
Add AI agent context files across all major tools
claude Jun 13, 2026
e78f2ae
Remove Pest references and add pest-control skill
claude Jun 13, 2026
4f49038
fix: allow empty MariaDB root password in docker-compoer. Closes #547
KhawarMehfooz Jun 18, 2026
9fed92b
fix: remove MARIADB_USER and MARIADB_PASSWORD from docker-compose
KhawarMehfooz Jun 18, 2026
1863270
Fix login: wire custom Login page, fix company redirect for elevated …
claude Jun 18, 2026
166e24f
Add LoginResponseTest covering company selection and session edge cases
claude Jun 18, 2026
6950c56
Simplify login redirect to use company attachments instead of roles
claude Jun 18, 2026
3640dbf
Add APP_KEY to phpunit.xml and .env.testing so Livewire tests can run
claude Jun 18, 2026
4ece165
chore(skills): audit and repair all 25 skill files
claude Jun 18, 2026
b35d91f
chore(skills): rewrite laravel-modules skill to target internachi/mod…
claude Jun 18, 2026
865b53b
fix(skills): use Modules\{Name}\ namespace in laravel-modules skill
claude Jun 18, 2026
45eda86
chore(skills): enforce Arrange/Act/Assert phase comments in all tests
claude Jun 18, 2026
51699bf
fix(skills): use /* Arrange */ /* Act */ /* Assert */ block comments
claude Jun 18, 2026
6457750
chore(skills): add it_{verb}_{object} test naming convention as Rule 7
claude Jun 18, 2026
62a47e1
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Jun 18, 2026
272225e
Fix pre-existing naming and AAA structure violations in QuotesTest files
claude Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/skills/autonomous-coding-workflow/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: autonomous-coding-workflow
description: Governs safe, incremental, repository-wide development workflow with continuous validation gates
---

# Autonomous Coding Workflow

## Goal
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/ci-schema-invariant-gate/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: ci-schema-gate
name: ci-schema-invariant-gate
description: Ensures correct execution order of migrations, seeders, and tests in CI
---

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dto-contract/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: dto-contracts
name: dto-contract
description: Defines DTO structure, lifecycle, and transformation rules across the application
license: MIT
metadata:
Expand Down
18 changes: 3 additions & 15 deletions .claude/skills/filament-multi-tenancy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,9 @@ Filament::setTenant($companyA, isQuiet: true);

## Tenant Middleware Stack

The company panel uses three tenant middlewares (in order):

1. `SetTenantFromQueryString` — reads `?tenant=search_code`, validates access
2. `ConfigureTenant` — resolves tenant from route/query/session/user default, writes to session
3. `EnsureUserCanAccessCompany` — enforces authorization (elevated roles bypass; regular users must own the company)

These are registered as persistent tenant middleware in `CompanyPanelProvider`:

```php
->tenantMiddleware([
SetTenantFromQueryString::class,
ConfigureTenant::class,
EnsureUserCanAccessCompany::class,
], isPersistent: true)
```
See the `tenant-middleware` skill for the full middleware chain. In short: three
persistent middlewares run on every company panel request in this order:
`SetTenantFromQueryString` → `ConfigureTenant` → `EnsureUserCanAccessCompany`.

## Tenant in Tests Setup

Expand Down
13 changes: 8 additions & 5 deletions .claude/skills/filament-panel-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ metadata:

# Filament Panel Setup

This app has two panels:
This app has three panels:

| Panel | Provider | Id | Default? | Tenant |
|-------|----------|----|----------|--------|
| Company | `CompanyPanelProvider` | `company` | No | `Company::class` |
| Admin | `AdminPanelProvider` | `admin` | Yes | None |
| Panel | Provider | Id | Default? | Tenant | Access |
|-------|----------|----|----------|--------|--------|
| Company | `CompanyPanelProvider` | `company` | Yes (root) | `Company::class` | `client_admin`, `client` |
| Admin | `AdminPanelProvider` | `admin` | No | None | `super_admin`, `admin`, `assist` |
| User | `UserPanelProvider` | `user` | No | None | minimal, future use |

All three providers live at `Modules/Core/Providers/`.

## Registering Module Resources

Expand Down
230 changes: 150 additions & 80 deletions .claude/skills/filament-resource-pages/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,126 +1,196 @@
---
name: service-layer
description: Defines application service structure and business orchestration boundaries
name: filament-resource-pages
description: "Defines the Filament v4 resource page structure used in this project: Resource + Pages + Schemas + Tables split, action patterns, and BaseResource conventions."
license: MIT
metadata:
author: project
---

# Service Layer
# Filament Resource Pages

Services define business orchestration and are the primary boundary for business logic execution.
## Resource Directory Layout

They are framework-agnostic and represent application behavior independent of UI or transport layers.
Every resource lives under `Modules/{Name}/Filament/{Panel}/Resources/{Model}/`:

---

# 1. Responsibility

Services MUST:

- contain business logic
- coordinate models and repositories
- enforce domain rules
- return models or internally used DTOs
- encapsulate persistence logic

Services MUST NOT:
```
{Model}Resource.php ← extends BaseResource; declares model, nav, pages
Pages/
List{Model}.php ← extends ListRecords
Create{Model}.php ← extends CreateRecord
Edit{Model}.php ← extends EditRecord
Schemas/
{Model}Form.php ← static configure(Schema $schema): Schema
Tables/
{Model}sTable.php ← static configure(Table $table): Table
RelationManagers/ ← optional
```

- use Filament
- depend on HTTP layer (requests/responses)
- contain UI logic
- use service locators (`app()`, `resolve()`)
- depend on transport concerns
Schemas and Tables are **separate classes**, never defined inline inside the Resource.

---

# 2. Dependency Rule

Services MUST use constructor injection:
## Resource Class

```php
public function __construct(
private InvoiceRepository $repository
) {}
class InvoiceResource extends BaseResource
{
protected static ?string $model = Invoice::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBanknotes;
protected static ?int $navigationSort = 10;
protected static bool $isScopedToTenant = true;

public static function form(Schema $schema): Schema
{
return InvoiceForm::configure($schema);
}

public static function table(Table $table): Table
{
return InvoicesTable::configure($table);
}

public static function getPages(): array
{
return [
'index' => Pages\ListInvoices::route('/'),
'create' => Pages\CreateInvoice::route('/create'),
'edit' => Pages\EditInvoice::route('/{record}/edit'),
];
}
}
```

No service locator usage is allowed inside services.
`BaseResource` handles tenant-scoped queries automatically — do NOT add manual `company_id` filters.

---

# 3. DTO Rule (Refined)

DTOs are NOT required for UI → Service communication.

DTO usage depends on coupling, not layer type:

## DTOs are required when:
- crossing system boundaries (API, external integrations, queues)
- multiple consumers share a contract
- transformation logic must be standardized
- stability across versions is required
## List Page

## DTOs are NOT required when:
- input originates from trusted UI layer (e.g. Filament Forms)
- single consumer exists
- payload is short-lived and not reused elsewhere

### Rule of thumb:
> DTOs exist to stabilize unstable or shared contracts, not to formalize trusted UI input.

Services MAY still use DTOs internally if they improve clarity or structure.
```php
class ListInvoices extends ListRecords
{
protected static string $resource = InvoiceResource::class;

protected function getHeaderActions(): array
{
return [
CreateAction::make()
->modalWidth('full')
->action(function (array $data) {
app(InvoiceService::class)->createInvoice($data);
}),
];
}
}
```

---

# 4. Filament Boundary Rule

Filament is a UI orchestration layer.
## Edit Page

## Allowed usage:
Override `save()` when you need to route the update through the service layer:

### Pages / Resources
- constructor injection preferred
- `app(Service::class)` allowed as fallback when needed
```php
class EditInvoice extends EditRecord
{
protected static string $resource = InvoiceResource::class;

public function save(bool $shouldRedirect = true, bool $shouldSendSavedNotification = true): void
{
$this->authorizeAccess();
$this->callHook('beforeValidate');
$data = $this->form->getState();
$this->callHook('afterValidate');
$data = $this->mutateFormDataBeforeSave($data);
$this->callHook('beforeSave');

app(InvoiceService::class)->updateInvoice($data, $this->getRecord());

$this->callHook('afterSave');

if ($shouldRedirect) {
$this->redirect($this->getRedirectUrl());
}
}

protected function getHeaderActions(): array
{
return [DeleteAction::make()];
}
}
```

### Table Actions / Closures
- `app(Service::class)` is allowed
- constructor injection is not guaranteed in closure scope
---

Example:
## Schema Class

```php
Action::make('create')
->action(function (array $data) {
app(InvoiceService::class)->createInvoice($data);
});
class InvoiceForm
{
public static function configure(Schema $schema): Schema
{
return $schema->components([
Grid::make(2)->schema([
Section::make('Details')->schema([
Select::make('customer_id')->relationship('customer', 'company_name')->required(),
DatePicker::make('invoice_date')->required(),
]),
]),
]);
}
}
```

This is acceptable boundary-layer behavior.

---

# 5. Standard Service Shape
## Table Class

```
Modules/{Name}/src/Services/{Model}Service.php
```php
class InvoicesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('invoice_number')->searchable()->sortable(),
TextColumn::make('invoice_status')->badge(),
])
->actions([
EditAction::make(),
DeleteAction::make(),
])
->bulkActions([
BulkActionGroup::make([DeleteBulkAction::make()]),
]);
}
}
```

---

# 6. Standard Methods
## Action Closure Rule

Filament action closures do NOT support constructor injection. Always use `app()`:

- createX
- updateX
- deleteX
- findOrFail
- listForCompany
```php
->action(function (array $data) {
app(InvoiceService::class)->createInvoice($data);
})
```

This is the only place `app()` is acceptable. Services themselves must never use it.

---

# 7. Core Principle
## Panel Registration

Services are pure business units.
Resources are discovered per module in `CompanyPanelProvider`:

They MUST remain independent of framework execution context.
```php
->discoverResources(
in: base_path('modules/invoices/src/Filament/Company/Resources'),
for: 'Modules\\Invoices\\Filament\\Company\\Resources'
)
```

UI layers may use service locator as a pragmatic escape hatch where DI is not available.
The `in` parameter uses the filesystem path (lowercase with `src/`), while `for` uses the PHP namespace.
Loading
Loading