Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/skills/blaze-optimize/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,4 @@ Then present the advanced optimization options. Use the SAME formatting as writt

## STOP

You are done. Do NOT proceed with advanced optimization (memoization, folding) unless the user explicitly asks for it. When they do, read [references/advanced-optimization.md](references/advanced-optimization.md) for instructions.
You are done. Do NOT proceed with advanced optimization (memoization, folding) unless the user explicitly asks for it. When they do, read [references/advanced-optimization.md](references/advanced-optimization.md) for instructions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@ After applying any advanced optimizations:
- Memoizing components that have slots — memoization requires slot-free components
- Folding components that access global state (auth, session, request, errors, time, CSRF) — produces stale HTML
- Folding components where props drive internal logic without marking them `safe`
- Forgetting to run `php artisan view:clear` after changes
- Forgetting to run `php artisan view:clear` after changes
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,4 @@ Works because: `href` and `label` are passed through directly.

- The project doesn't have measured performance issues — premature optimization
- The component is frequently modified during development — folding requires `view:clear` after changes and adds cognitive overhead for developers
- The component has complex internal logic — harder to reason about safety
- The component has complex internal logic — harder to reason about safety
2 changes: 1 addition & 1 deletion .github/skills/debug-using-debugbar/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ Duplicate queries are a strong N+1 signal. Use `--statement=N` to get the backtr
- The `{id}` is the request ID from the `debugbar:find` output, or use `latest` to inspect the most recent request.
- Collector availability depends on the app's debugbar config — the summary from `debugbar:get` shows which collectors have data.
- `--explain` and `--result` only work on SELECT queries. They re-execute against the current database, so results may differ from the original request.
- `debugbar:clear` removes all stored data — use it to reset between debugging sessions, not mid-investigation.
- `debugbar:clear` removes all stored data — use it to reset between debugging sessions, not mid-investigation.
4 changes: 2 additions & 2 deletions .github/skills/fluxui-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Use Flux UI components when available. Fall back to standard Blade components wh

## Available Components (Pro Edition)

Available: accordion, autocomplete, avatar, badge, brand, breadcrumbs, button, calendar, callout, card, chart, checkbox, command, composer, context, date-picker, dropdown, editor, field, file-upload, heading, icon, input, kanban, modal, navbar, otp-input, pagination, pillbox, popover, profile, radio, select, separator, skeleton, slider, switch, table, tabs, text, textarea, time-picker, toast, tooltip
Available: accordion, autocomplete, avatar, badge, brand, breadcrumbs, button, calendar, callout, card, chart, checkbox, color-picker, command, composer, context, date-picker, dropdown, editor, field, file-upload, heading, icon, input, kanban, modal, navbar, otp-input, pagination, pillbox, popover, profile, progress, radio, select, separator, skeleton, slider, switch, table, tabs, text, textarea, time-picker, timeline, toast, tooltip

## Icons

Expand Down Expand Up @@ -81,4 +81,4 @@ php artisan flux:icon crown grip-vertical github

- Not checking if a Flux component exists before creating custom implementations
- Forgetting to use the `search-docs` tool for component-specific documentation
- Not following existing project patterns for Flux usage
- Not following existing project patterns for Flux usage
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,4 @@ Always use a sub-agent to read rule files and explore this skill's content.

1. Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)
2. Check sibling files for existing patterns — follow those first per Consistency First
3. Verify API syntax with `search-docs` for the installed Laravel version
3. Verify API syntax with `search-docs` for the installed Laravel version
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,4 @@ public function scopeOrderByLastLogin($query): void
->take(1)
);
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,4 @@ class Customer extends Model
return $this->belongsToMany(Role::class);
}
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ return view('dashboard', compact('users'))

## Use `@aware` for Deeply Nested Component Props

Avoids re-passing parent props through every level of nested components.
Avoids re-passing parent props through every level of nested components.
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ If Redis goes down, the app falls back to a secondary store automatically.

```php
'failover' => ['driver' => 'failover', 'stores' => ['redis', 'database']],
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ More declarative than overriding `newCollection()`.
```php
#[CollectedBy(UserCollection::class)]
class User extends Model {}
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ If the application already uses language files for localization, use `__()` for
```php
// Only when lang files already exist in the project
return back()->with('message', __('app.article_added'));
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,4 @@ return view('users.index', compact('users'));
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/eloquent.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,4 @@ Order::where('status', 'pending')->get();

Prefer Eloquent queries and relationships over `DB::table()` whenever possible — they already reference the model's table. When `DB::table()` or raw joins are unavoidable, always use `(new Model)->getTable()` to keep the reference traceable.

**Exception — migrations:** In migrations, hardcoded table names via `DB::table('settings')` are acceptable and preferred. Models change over time but migrations are frozen snapshots — referencing a model that is later renamed or deleted would break the migration.
**Exception — migrations:** In migrations, hardcoded table names via `DB::table('settings')` are acceptable and preferred. Models change over time but migrations are frozen snapshots — referencing a model that is later renamed or deleted would break the migration.
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ class InvalidOrderException extends Exception
return ['order_id' => $this->orderId];
}
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ Notification::route('mail', 'admin@example.com')->notify(new SystemAlert());

## Implement `HasLocalePreference` on Notifiable Models

Laravel automatically uses the user's preferred locale for all notifications and mailables — no per-call `locale()` needed.
Laravel automatically uses the user's preferred locale for all notifications and mailables — no per-call `locale()` needed.
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@ Test failure scenarios too:
Http::fake([
'api.example.com/*' => Http::failedConnection(),
]);
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/mail.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Markdown mailables auto-generate both HTML and plain-text versions, use responsi

Content tests: instantiate the mailable directly, call `assertSeeInHtml()`.
Sending tests: use `Mail::fake()` and `assertSent()`/`assertQueued()`.
Don't mix them — it conflates concerns and makes tests brittle.
Don't mix them — it conflates concerns and makes tests brittle.
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,4 @@ Schema::create('settings', function (Blueprint $table) { ... });

// Migration 2: seed_default_settings
DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']);
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,4 @@ Use Laravel Horizon when you need monitoring, auto-scaling, failure tracking, or
],
],
],
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ public function store(StorePostRequest $request): RedirectResponse

return redirect()->route('posts.index');
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ Schedule::daily()
Schedule::command('emails:send --force');
Schedule::command('emails:prune');
});
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,4 @@ class Integration extends Model
];
}
}
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/style.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,4 @@ if (count((array) $builder->getQuery()->joins) > 0)
Correct:
```php
if ($this->hasJoins())
```
```
2 changes: 1 addition & 1 deletion .github/skills/laravel-best-practices/rules/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ Without `recycle()`, nested factories create separate instances of the same conc
Ticket::factory()
->recycle(Airline::factory()->create())
->create();
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ public function after(): array
},
];
}
```
```
5 changes: 3 additions & 2 deletions .github/skills/livewire-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ Namespaced components map to subdirectories: `make:livewire Posts/CreatePost` cr
<?php
use Livewire\Component;

new class extends Component {
new class extends Component
{
public int $count = 0;

public function increment(): void
Expand Down Expand Up @@ -172,4 +173,4 @@ Livewire::test(Counter::class)
- Expecting `wire:model` real-time → use `wire:model.live`
- Unclosed component tags → syntax errors in v4
- Using deprecated config keys or JS hooks
- Including Alpine.js separately (already bundled in Livewire 4)
- Including Alpine.js separately (already bundled in Livewire 4)
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ Livewire.interceptRequest(({ request, onResponse, onSuccess, onError, onFailure
## Magic Properties

- `$errors` - Access validation errors from JavaScript
- `$intercept` - Component-scoped interceptors
- `$intercept` - Component-scoped interceptors
9 changes: 8 additions & 1 deletion .github/skills/pest-testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ Use `search-docs` for detailed Pest 4 patterns and documentation.

All tests must be written using Pest. Use `php artisan make:test --pest {name}`.

The `{name}` argument should include only the path and test name, but should not include the test suite.
- Incorrect: `php artisan make:test --pest Feature/SomeFeatureTest` will generate `tests/Feature/Feature/SomeFeatureTest.php`
- Correct: `php artisan make:test --pest SomeControllerTest` will generate `tests/Feature/SomeControllerTest.php`
- Incorrect: `php artisan make:test --pest --unit Unit/SomeServiceTest` will generate `tests/Unit/Unit/SomeServiceTest.php`
- Correct: `php artisan make:test --pest --unit SomeServiceTest` will generate `tests/Unit/SomeServiceTest.php`

### Test Organization

- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
Expand Down Expand Up @@ -156,4 +162,5 @@ arch('controllers')
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests
- Forgetting `assertNoJavaScriptErrors()` in browser tests
- Prefixing `Feature/` or `Unit/` in `{name}` when using `make:test`
2 changes: 1 addition & 1 deletion .github/skills/pulse-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,4 @@ Register the recorder in the `recorders` array in `config/pulse.php`.
- Multiple `Authenticatable` models can cause incorrect user tracking. Use `Pulse::resolveAuthenticatedUserId()` when recording user-keyed entries.
- SQS queues may appear duplicated in the Queue card. Use `ignore` regex patterns to suppress them.
- Sampled dashboard values are approximate and prefixed with `~`. They are not suitable for financial or audit reporting.
- Always use `search-docs` for the latest Pulse documentation rather than relying on this skill alone.
- Always use `search-docs` for the latest Pulse documentation rather than relying on this skill alone.
2 changes: 1 addition & 1 deletion .github/skills/tailwindcss-development/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ If existing pages and components support dark mode, new pages and components mus
- Using `@tailwind` directives instead of `@import "tailwindcss"`
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
- Using margins for spacing between siblings instead of gap utilities
- Forgetting to add dark mode variants when the project uses dark mode
- Forgetting to add dark mode variants when the project uses dark mode
6 changes: 3 additions & 3 deletions app/Components/AdminAssociationDonationInvoiceForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ public function submit(CreateAssociationDonationInvoiceAction $createAssociation
$pdf = $invoice['pdf'];
$filename = $invoice['filename'];

$response = response()->streamDownload(function () use ($pdf) {
echo $pdf->stream();
}, $filename);
$response = response()->streamDownload(function () use ($pdf): void {
echo $pdf->output();
}, $filename, ['Content-Type' => 'application/pdf']);

} catch (Exception $exception) {

Expand Down
14 changes: 7 additions & 7 deletions app/Console/Commands/MakeDatatableCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace App\Console\Commands;

use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -21,9 +23,8 @@
use function Laravel\Prompts\suggest;
use function Laravel\Prompts\warning;

class MakeDatatableCommand extends Command implements PromptsForMissingInput
{
protected $signature = 'make:datatable
#[Description('Generate an opinionated Livewire datatable with smart defaults and prompts')]
#[Signature('make:datatable
{context : Datatable context (admin, public, or shared)}
{name : Datatable component class (e.g. AdminPartnersTable)}
{--model= : Model class (e.g. Partner or App\\Models\\Partner)}
Expand All @@ -34,10 +35,9 @@ class MakeDatatableCommand extends Command implements PromptsForMissingInput
{--visible= : Comma-separated default visible columns}
{--export : Include export actions and methods}
{--test : Create a Pest feature test}
{--force : Overwrite existing files}';

protected $description = 'Generate an opinionated Livewire datatable with smart defaults and prompts';

{--force : Overwrite existing files}')]
class MakeDatatableCommand extends Command implements PromptsForMissingInput
{
public function __construct(public Filesystem $files)
{
parent::__construct();
Expand Down
10 changes: 3 additions & 7 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
namespace App\Providers;

use App\Actions\GetDashboardDataAction;
use App\Services\AthleteDocumentService;
use App\Services\CurrentDonationEventService;
use App\Services\DonationService;
use App\Services\SettingsService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User;
use Illuminate\Mail\Events\MessageSending;
Expand All @@ -31,10 +28,9 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->app->singleton(AthleteDocumentService::class);
$this->app->singleton(CurrentDonationEventService::class);
$this->app->singleton(SettingsService::class);
$this->app->singleton(DonationService::class);
// Scoped binding is required because the service uses once() to memoize
// current event resolution across repeated view composer resolutions.
$this->app->scoped(CurrentDonationEventService::class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

it('can be filled with all inputs', function () {

Livewire::test(AdminAssociationDonationInvoiceForm::class)
$component = Livewire::test(AdminAssociationDonationInvoiceForm::class)
->set('company_name', 'Test Company')
->set('first_name', 'John')
->set('last_name', 'Doe')
Expand All @@ -22,6 +22,12 @@
->assertHasNoErrors()
->assertFileDownloaded('Spendenrechnung_John_Doe_VereinFuerMenschen.pdf');

$content = base64_decode(data_get($component->effects, 'download.content'));
Comment thread
smnmsr marked this conversation as resolved.

expect($content)->toStartWith('%PDF');
expect(str_contains($content, 'Content-Type'))->toBeFalse();
expect(str_contains($content, 'Content-Disposition'))->toBeFalse();

});

it('can be set without a company name', function () {
Expand Down
9 changes: 8 additions & 1 deletion tests/Feature/Livewire/AssociationDonationFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@
->call('redirectHelper')
->assertRedirect(route('home'));

Notification::assertSentToTimes(
Notification::assertSentTo(
Notification::route('mail', 'john.doe@example.com'),
AssociationDonationMessage::class,
function (AssociationDonationMessage $notification): bool {
$pdf = base64_decode($notification->pdf, true);

return is_string($pdf)
&& str_starts_with($pdf, '%PDF')
&& $notification->filename === 'Spendenrechnung_John_Doe_VereinFuerMenschen.pdf';
},
);

});
Expand Down
Loading