major update

This commit is contained in:
Mark Gregory
2026-06-10 15:14:55 +01:00
parent ca3be4c887
commit 8866e8438e
89 changed files with 0 additions and 3969 deletions
-198
View File
@@ -1,198 +0,0 @@
<laravel-boost-guidelines>
=== foundation rules ===
# Laravel Boost Guidelines
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
## Foundational Context
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
- php - 8.3
- filament/filament (FILAMENT) - v5
- laravel/breeze (BREEZE) - v2
- laravel/fortify (FORTIFY) - v1
- laravel/framework (LARAVEL) - v13
- laravel/horizon (HORIZON) - v5
- laravel/octane (OCTANE) - v2
- laravel/prompts (PROMPTS) - v0
- livewire/flux (FLUXUI_FREE) - v2
- livewire/livewire (LIVEWIRE) - v4
- laravel/boost (BOOST) - v2
- laravel/mcp (MCP) - v0
- laravel/pail (PAIL) - v1
- laravel/pint (PINT) - v1
- laravel/sail (SAIL) - v1
- phpunit/phpunit (PHPUNIT) - v12
- tailwindcss (TAILWINDCSS) - v4
## Skills Activation
This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
## Conventions
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.
## Verification Scripts
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
## Application Structure & Architecture
- Stick to existing directory structure; don't create new base folders without approval.
- Do not change the application's dependencies without approval.
## Frontend Bundling
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
## Replies
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
=== boost rules ===
# Laravel Boost
## Tools
- Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads.
- Use `database-query` to run read-only queries against the database instead of writing raw SQL in tinker.
- Use `database-schema` to inspect table structure before writing migrations or models.
- Use `get-absolute-url` to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.
- Use `browser-logs` to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.
## Searching Documentation (IMPORTANT)
- Always use `search-docs` before making code changes. Do not skip this step. It returns version-specific docs based on installed packages automatically.
- Pass a `packages` array to scope results when you know which packages are relevant.
- Use multiple broad, topic-based queries: `['rate limiting', 'routing rate limiting', 'routing']`. Expect the most relevant results first.
- Do not add package names to queries because package info is already shared. Use `test resource table`, not `filament 4 test resource table`.
### Search Syntax
1. Use words for auto-stemmed AND logic: `rate limit` matches both "rate" AND "limit".
2. Use `"quoted phrases"` for exact position matching: `"infinite scroll"` requires adjacent words in order.
3. Combine words and phrases for mixed queries: `middleware "rate limit"`.
4. Use multiple queries for OR logic: `queries=["authentication", "middleware"]`.
## Artisan
- Run Artisan commands directly via the command line (e.g., `php artisan route:list`). Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters.
- Inspect routes with `php artisan route:list`. Filter with: `--method=GET`, `--name=users`, `--path=api`, `--except-vendor`, `--only-vendor`.
- Read configuration values using dot notation: `php artisan config:show app.name`, `php artisan config:show database.default`. Or read config files directly from the `config/` directory.
## Tinker
- Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code.
- Always use single quotes to prevent shell expansion: `php artisan tinker --execute 'Your::code();'`
- Double quotes for PHP strings inside: `php artisan tinker --execute 'User::where("active", true)->count();'`
=== php rules ===
# PHP
- Always use curly braces for control structures, even for single-line bodies.
- Use PHP 8 constructor property promotion: `public function __construct(public GitHub $github) { }`. Do not leave empty zero-parameter `__construct()` methods unless the constructor is private.
- Use explicit return type declarations and type hints for all method parameters: `function isAccessible(User $user, ?string $path = null): bool`
- Use TitleCase for Enum keys: `FavoritePerson`, `BestLake`, `Monthly`.
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
- Use array shape type definitions in PHPDoc blocks.
=== deployments rules ===
# Deployment
- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.
=== tests rules ===
# Test Enforcement
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter.
=== laravel/core rules ===
# Do Things the Laravel Way
- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using `php artisan list` and check their parameters with `php artisan [command] --help`.
- If you're creating a generic PHP class, use `php artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
### Model Creation
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `php artisan make:model --help` to check the available options.
## APIs & Eloquent Resources
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
## URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
## Testing
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
## Vite Error
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
=== octane/core rules ===
# Laravel Octane
This application uses Laravel Octane, a long-running PHP server. The application bootstraps once and handles many requests within the same process.
- Never store request-specific state in singletons or static properties, because it can leak across requests.
- Use `config('octane.server')` to detect the active driver (`swoole`, `roadrunner`, or `frankenphp`).
- Prefer scoped bindings (`$this->app->scoped()`) over singletons for per-request services.
When working on Octane-specific features (concurrency, shared tables, memory, driver configuration, testing), invoke `octane-development` for detailed rules.
=== livewire/core rules ===
# Livewire
- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript.
- You can use Alpine.js for client-side interactions instead of JavaScript frameworks.
- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests.
=== pint/core rules ===
# Laravel Pint Code Formatter
- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.
=== phpunit/core rules ===
# PHPUnit
- This application uses PHPUnit for testing. All tests must be written as PHPUnit classes. Use `php artisan make:test --phpunit {name}` to create a new test.
- If you see a test using "Pest", convert it to PHPUnit.
- Every time a test has been updated, run that singular test.
- When the tests relating to your feature are passing, ask the user if they would like to also run the entire test suite to make sure everything is still passing.
- Tests should cover all happy paths, failure paths, and edge cases.
- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files; these are core to the application.
## Running Tests
- Run the minimal number of tests, using an appropriate filter, before finalizing.
- To run all tests: `php artisan test --compact`.
- To run all tests in a file: `php artisan test --compact tests/Feature/ExampleTest.php`.
- To filter on a particular test name: `php artisan test --compact --filter=testName` (recommended after making a change to a related file).
</laravel-boost-guidelines>
-22
View File
@@ -1,22 +0,0 @@
# WMS Fix Notes
This build patches only the current uploaded code.
## Fixed
- Duplicate two-factor migration made safe.
- Primary two-factor migration adds columns one at a time for SQLite safety.
- Module page slot is wrapped in a visible `.wms-content` main container.
- Sidebar remains traditional: desktop/tablet icon rail, mobile drawer.
- Sidebar widened for "Warehouse Management System".
- Laravel cache folders are included so "Please provide a valid cache path" does not recur after unzip.
## After unzip
```bash
composer install
npm install
php artisan optimize:clear
php artisan test
npm run build
```
-35
View File
@@ -1,35 +0,0 @@
# Platform config and module menu fix
Applied fixes:
- Added `Modules/Platform/config/platform.php`.
- Hardened `Modules/Platform/Providers/PlatformServiceProvider.php` so config loading is safe.
- Removed stale generated files from `bootstrap/cache`.
- Updated `resources/views/layouts/app.blade.php` so it works for both:
- component pages using `<x-layouts::app>`
- legacy placeholder module pages using `@extends('layouts.app')` and `@section('content')`
- Added placeholder module entries to the left navigation:
- Inventory Management
- Shipping
- Carrier Management
- Transportation
- Replenishment
- M3 Integration
- Document Archive
- Analytics
- Reporting
- Audit
- Security
- Configuration
- Added compatibility CSS for the placeholder module pages that use Bootstrap-style classes.
- Ensured cache/runtime directories exist.
Run:
```bash
composer install
npm install
php artisan optimize:clear
php artisan test
npm run build
```
-81
View File
@@ -1,81 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Fortify\Features;
use Tests\TestCase;
class AuthenticationTest extends TestCase
{
use RefreshDatabase;
public function test_login_screen_can_be_rendered(): void
{
$response = $this->get(route('login'));
$response->assertOk();
}
public function test_users_can_authenticate_using_the_login_screen(): void
{
$user = User::factory()->create();
$response = $this->csrfPost(route('login.store'), [
'email' => $user->email,
'password' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticated();
}
public function test_users_can_not_authenticate_with_invalid_password(): void
{
$user = User::factory()->create();
$response = $this->csrfPost(route('login.store'), [
'email' => $user->email,
'password' => 'wrong-password',
]);
$response->assertSessionHasErrorsIn('email');
$this->assertGuest();
}
public function test_users_with_two_factor_enabled_are_redirected_to_two_factor_challenge(): void
{
$this->skipUnlessFortifyHas(Features::twoFactorAuthentication());
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->withTwoFactor()->create();
$response = $this->csrfPost(route('login.store'), [
'email' => $user->email,
'password' => 'password',
]);
$response->assertRedirect(route('two-factor.login'));
$this->assertGuest();
}
public function test_users_can_logout(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->csrfPost(route('logout'));
$response->assertRedirect(route('home'));
$this->assertGuest();
}
}
-88
View File
@@ -1,88 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\URL;
use Laravel\Fortify\Features;
use Tests\TestCase;
class EmailVerificationTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyHas(Features::emailVerification());
}
public function test_email_verification_screen_can_be_rendered(): void
{
$user = User::factory()->unverified()->create();
$response = $this->actingAs($user)->get(route('verification.notice'));
$response->assertOk();
}
public function test_email_can_be_verified(): void
{
$user = User::factory()->unverified()->create();
Event::fake();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)],
);
$response = $this->actingAs($user)->get($verificationUrl);
Event::assertDispatched(Verified::class);
$this->assertTrue($user->fresh()->hasVerifiedEmail());
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
}
public function test_email_is_not_verified_with_invalid_hash(): void
{
$user = User::factory()->unverified()->create();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1('wrong-email')],
);
$this->actingAs($user)->get($verificationUrl);
$this->assertFalse($user->fresh()->hasVerifiedEmail());
}
public function test_already_verified_user_visiting_verification_link_is_redirected_without_firing_event_again(): void
{
$user = User::factory()->create([
'email_verified_at' => now(),
]);
Event::fake();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)],
);
$this->actingAs($user)->get($verificationUrl)
->assertRedirect(route('dashboard', absolute: false).'?verified=1');
$this->assertTrue($user->fresh()->hasVerifiedEmail());
Event::assertNotDispatched(Verified::class);
}
}
-21
View File
@@ -1,21 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PasswordConfirmationTest extends TestCase
{
use RefreshDatabase;
public function test_confirm_password_screen_can_be_rendered(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('password.confirm'));
$response->assertOk();
}
}
-81
View File
@@ -1,81 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Laravel\Fortify\Features;
use Tests\TestCase;
class PasswordResetTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyHas(Features::resetPasswords());
}
public function test_reset_password_link_screen_can_be_rendered(): void
{
$response = $this->get(route('password.request'));
$response->assertOk();
}
public function test_reset_password_link_can_be_requested(): void
{
Notification::fake();
$user = User::factory()->create();
$this->csrfPost(route('password.email'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class);
}
public function test_reset_password_screen_can_be_rendered(): void
{
Notification::fake();
$user = User::factory()->create();
$this->csrfPost(route('password.email'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function ($notification) {
$response = $this->get(route('password.reset', $notification->token));
$response->assertOk();
return true;
});
}
public function test_password_can_be_reset_with_valid_token(): void
{
Notification::fake();
$user = User::factory()->create();
$this->csrfPost(route('password.email'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
$response = $this->csrfPost(route('password.update'), [
'token' => $notification->token,
'email' => $user->email,
'password' => 'password',
'password_confirmation' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('login', absolute: false));
return true;
});
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Fortify\Features;
use Tests\TestCase;
class RegistrationTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyHas(Features::registration());
}
public function test_registration_screen_can_be_rendered(): void
{
$response = $this->get(route('register'));
$response->assertOk();
}
public function test_new_users_can_register(): void
{
$response = $this->csrfPost(route('register.store'), [
'name' => 'John Doe',
'email' => 'test@example.com',
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticated();
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Fortify\Features;
use Tests\TestCase;
class TwoFactorChallengeTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyHas(Features::twoFactorAuthentication());
}
public function test_two_factor_challenge_redirects_to_login_when_not_authenticated(): void
{
$response = $this->get(route('two-factor.login'));
$response->assertRedirect(route('login'));
}
public function test_two_factor_challenge_can_be_rendered(): void
{
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->withTwoFactor()->create();
$this->csrfPost(route('login.store'), [
'email' => $user->email,
'password' => 'password',
])->assertRedirect(route('two-factor.login'));
}
}
-27
View File
@@ -1,27 +0,0 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class DashboardTest extends TestCase
{
use RefreshDatabase;
public function test_guests_are_redirected_to_the_login_page(): void
{
$response = $this->get(route('dashboard'));
$response->assertRedirect(route('login'));
}
public function test_authenticated_users_can_visit_the_dashboard(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('dashboard'));
$response->assertOk();
}
}
-18
View File
@@ -1,18 +0,0 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
public function test_returns_a_successful_response(): void
{
$response = $this->get(route('home'));
$response->assertOk();
}
}
-90
View File
@@ -1,90 +0,0 @@
<?php
namespace Tests\Feature\Settings;
use App\Livewire\Settings\Profile;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
class ProfileUpdateTest extends TestCase
{
use RefreshDatabase;
public function test_profile_page_is_displayed(): void
{
$this->actingAs($user = User::factory()->create());
$this->get('/settings/profile')->assertOk();
}
public function test_profile_information_can_be_updated(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test(Profile::class)
->set('name', 'Test User')
->set('email', 'test@example.com')
->call('updateProfileInformation');
$response->assertHasNoErrors();
$user->refresh();
$this->assertEquals('Test User', $user->name);
$this->assertEquals('test@example.com', $user->email);
$this->assertNull($user->email_verified_at);
}
public function test_email_verification_status_is_unchanged_when_email_address_is_unchanged(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test(Profile::class)
->set('name', 'Test User')
->set('email', $user->email)
->call('updateProfileInformation');
$response->assertHasNoErrors();
$this->assertNotNull($user->refresh()->email_verified_at);
}
public function test_user_can_delete_their_account(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test('settings.delete-user-form')
->set('password', 'password')
->call('deleteUser');
$response
->assertHasNoErrors()
->assertRedirect('/');
$this->assertNull($user->fresh());
$this->assertFalse(auth()->check());
}
public function test_correct_password_must_be_provided_to_delete_account(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test('settings.delete-user-form')
->set('password', 'wrong-password')
->call('deleteUser');
$response->assertHasErrors(['password']);
$this->assertNotNull($user->fresh());
}
}
-132
View File
@@ -1,132 +0,0 @@
<?php
namespace Tests\Feature\Settings;
use App\Livewire\Settings\Security;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Laravel\Fortify\Features;
use Livewire\Livewire;
use Tests\TestCase;
class SecurityTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyHas(Features::twoFactorAuthentication());
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
Features::passkeys([
'confirmPassword' => true,
]);
}
public function test_security_settings_page_can_be_rendered(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->withSession(['auth.password_confirmed_at' => time()])
->get(route('security.edit'));
$response->assertOk();
$response->assertSee('Passkeys');
$response->assertSee('No passkeys yet');
$response->assertSee('Two-factor authentication');
$response->assertSee('Enable 2FA');
}
public function test_security_settings_page_requires_password_confirmation_when_enabled(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->get(route('security.edit'));
$response->assertRedirect(route('password.confirm'));
}
public function test_security_settings_page_renders_without_two_factor_when_feature_is_disabled(): void
{
config(['fortify.features' => []]);
$user = User::factory()->create();
$this->actingAs($user)
->withSession(['auth.password_confirmed_at' => time()])
->get(route('security.edit'))
->assertOk()
->assertSee('Update password')
->assertDontSee('Manage your passkeys for passwordless sign-in')
->assertDontSee('Add a passkey to sign in without a password')
->assertDontSee('Two-factor authentication');
}
public function test_two_factor_authentication_disabled_when_confirmation_abandoned_between_requests(): void
{
$user = User::factory()->create();
$user->forceFill([
'two_factor_secret' => encrypt('test-secret'),
'two_factor_recovery_codes' => encrypt(json_encode(['code1', 'code2'])),
'two_factor_confirmed_at' => null,
])->save();
$this->actingAs($user);
$component = Livewire::test(Security::class);
$component->assertSet('twoFactorEnabled', false);
$this->assertDatabaseHas('users', [
'id' => $user->id,
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
]);
}
public function test_password_can_be_updated(): void
{
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
$this->actingAs($user);
$response = Livewire::test(Security::class)
->set('current_password', 'password')
->set('password', 'new-password')
->set('password_confirmation', 'new-password')
->call('updatePassword');
$response->assertHasNoErrors();
$this->assertTrue(Hash::check('new-password', $user->refresh()->password));
}
public function test_correct_password_must_be_provided_to_update_password(): void
{
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
$this->actingAs($user);
$response = Livewire::test(Security::class)
->set('current_password', 'wrong-password')
->set('password', 'new-password')
->set('password_confirmation', 'new-password')
->call('updatePassword');
$response->assertHasErrors(['current_password']);
}
}
-44
View File
@@ -1,44 +0,0 @@
<?php
namespace Tests;
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Laravel\Fortify\Features;
abstract class TestCase extends BaseTestCase
{
protected function setUp(): void
{
parent::setUp();
config([
'app.debug' => false,
'session.driver' => 'array',
'cache.default' => 'array',
'mail.default' => 'array',
]);
$this->withoutMiddleware(ValidateCsrfToken::class);
}
/**
* POST with a real CSRF token for Fortify/web form tests.
*
* @param array<string, mixed> $data
*/
protected function csrfPost(string $uri, array $data = []): \Illuminate\Testing\TestResponse
{
$token = 'test-csrf-token';
return $this->withSession(['_token' => $token])
->post($uri, ['_token' => $token, ...$data]);
}
protected function skipUnlessFortifyHas(string $feature, ?string $message = null): void
{
if (! Features::enabled($feature)) {
$this->markTestSkipped($message ?? "Fortify feature [{$feature}] is not enabled.");
}
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
namespace Tests\Unit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
public function test_that_true_is_true(): void
{
$this->assertTrue(true);
}
}
-3
View File
@@ -1,3 +0,0 @@
<div {{ $getExtraAttributeBag() }}>
{{ $getState() }}
</div>
-3
View File
@@ -1,3 +0,0 @@
<div>
{{-- Use $getChildSchema() to render the child schema() of this component. --}}
</div>
-8
View File
@@ -1,8 +0,0 @@
<x-dynamic-component
:component="$getEntryWrapperView()"
:entry="$entry"
>
<div {{ $getExtraAttributeBag() }}>
{{ $getState() }}
</div>
</x-dynamic-component>
-11
View File
@@ -1,11 +0,0 @@
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
x-data="{ state: $wire.$entangle(@js($getStatePath())) }"
{{ $getExtraAttributeBag() }}
>
{{-- Interact with the `state` property in Alpine.js --}}
</div>
</x-dynamic-component>
-11
View File
@@ -1,11 +0,0 @@
<div>
<form wire:submit="{{ submitAction }}">
{{ $this->form }}
<button type="submit">
Submit
</button>
</form>
<x-filament-actions::modals />
</div>
-5
View File
@@ -1,5 +0,0 @@
<div>
{{ $this->content }}
<x-filament-actions::modals />
</div>
-3
View File
@@ -1,3 +0,0 @@
<div>
{{ $this->table }}
</div>
-3
View File
@@ -1,3 +0,0 @@
<x-filament-panels::page>
{{-- Page content --}}
</x-filament-panels::page>
@@ -1,3 +0,0 @@
<div>
{{-- Custom block preview --}}
</div>
-3
View File
@@ -1,3 +0,0 @@
<div>
{{-- Custom block --}}
</div>
-4
View File
@@ -1,4 +0,0 @@
@import '../../../../vendor/filament/filament/resources/css/theme.css';
@source '../../../../app/Filament/{{ classDirectory }}**/*';
@source '../../../../resources/views/filament/{{ viewDirectory }}**/*';
-5
View File
@@ -1,5 +0,0 @@
<x-filament-widgets::widget>
<x-filament::section>
{{-- Widget content --}}
</x-filament::section>
</x-filament-widgets::widget>
-24
View File
@@ -1,24 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;
#[Description('A description of what this app resource does.')]
#[AppMeta]
class {{ class }} extends AppResource
{
/**
* Handle the app resource request.
*/
public function handle(Request $request): Response
{
return Response::view('{{ view }}', [
'title' => $this->title(),
]);
}
}
-17
View File
@@ -1,17 +0,0 @@
<x-mcp::app :title="$title">
<x-slot:head>
<script type="module">
createMcpApp(async (app) => {
document.getElementById('run-btn').addEventListener('click', async () => {
const result = await app.callServerTool('tool-name', {});
document.getElementById('output').textContent = result.content[0]?.text ?? '';
});
});
</script>
</x-slot:head>
<div id="app">
<button id="run-btn">Run</button>
<p id="output"></p>
</div>
</x-mcp::app>
-35
View File
@@ -1,35 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;
#[Description('A description of what this prompt does.')]
class {{ class }} extends Prompt
{
/**
* Handle the prompt request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The content generated by the prompt.');
}
/**
* Get the prompt's arguments.
*
* @return array<int, Argument>
*/
public function arguments(): array
{
return [
//
];
}
}
-22
View File
@@ -1,22 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Resource;
#[Description('A description of what this resource contains.')]
class {{ class }} extends Resource
{
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The resource content.');
}
}
-26
View File
@@ -1,26 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Server;
use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
#[Name('{{ serverDisplayName }}')]
#[Version('0.0.1')]
#[Instructions('Instructions describing how to use the server and its features.')]
class {{ class }} extends Server
{
protected array $tools = [
//
];
protected array $resources = [
//
];
protected array $prompts = [
//
];
}
-35
View File
@@ -1,35 +0,0 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('A description of what this tool does.')]
class {{ class }} extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The content generated by the tool.');
}
/**
* Get the tool's input schema.
*
* @return array<string, JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
//
];
}
}
@@ -1 +0,0 @@
{!! value($html) !!}
-19
View File
@@ -1,19 +0,0 @@
@if (isset($data))
<script>
window.filamentData = @js($data)
</script>
@endif
@foreach ($assets as $asset)
@if (! $asset->isLoadedOnRequest())
{{ $asset->getHtml() }}
@endif
@endforeach
<style>
:root {
@foreach ($cssVariables ?? [] as $cssVariableName => $cssVariableValue) --{{ $cssVariableName }}:{{ $cssVariableValue }}; @endforeach
}
@foreach ($customColors ?? [] as $customColorName => $customColorShades) .fi-color-{{ $customColorName }} { @foreach ($customColorShades as $customColorShade) --color-{{ $customColorShade }}:var(--{{ $customColorName }}-{{ $customColorShade }}); @endforeach } @endforeach
</style>
@@ -1,57 +0,0 @@
@php
use Filament\Support\Enums\Alignment;
@endphp
@props([
'actions' => [],
'alignment' => Alignment::Start,
'fullWidth' => false,
])
@php
if (is_array($actions)) {
$actions = array_filter(
$actions,
fn ($action): bool => $action->isVisible(),
);
}
if (! $alignment instanceof Alignment) {
$alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null;
}
$hasActions = false;
$hasSlot = ! \Filament\Support\is_slot_empty($slot);
$actionsAreHtmlable = $actions instanceof \Illuminate\Contracts\Support\Htmlable;
if ($hasSlot) {
$hasActions = true;
} elseif ($actionsAreHtmlable) {
$hasActions = ! \Filament\Support\is_slot_empty($actions);
} else {
$hasActions = filled($actions);
}
@endphp
@if ($hasActions)
<div
{{
$attributes->class([
'fi-ac',
'fi-width-full' => $fullWidth,
($alignment instanceof Alignment) ? "fi-align-{$alignment->value}" : (is_string($alignment) ? $alignment : null) => ! $fullWidth,
])
}}
>
@if ($hasSlot)
{{ $slot }}
@elseif ($actionsAreHtmlable)
{{ $actions }}
@else
@foreach ($actions as $action)
{{ $action }}
@endforeach
@endif
</div>
@endif
@@ -1,18 +0,0 @@
@props([
'circular' => true,
'size' => 'md',
])
<img
{{
$attributes
->class([
'fi-avatar',
'fi-circular' => $circular,
match ($size) {
'sm', 'md', 'lg' => "fi-size-{$size}",
default => $size,
},
])
}}
/>
@@ -1,183 +0,0 @@
@php
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
use Filament\Support\Enums\Size;
use Filament\Support\View\Components\BadgeComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'color' => 'primary',
'deleteButton' => null,
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => null,
'keyBindings' => null,
'loadingIndicator' => true,
'size' => Size::Medium,
'spaMode' => null,
'tag' => 'span',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $size instanceof Size) {
$size = filled($size) ? (Size::tryFrom($size) ?? $size) : null;
}
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$isDeletable = count($deleteButton?->attributes->getAttributes() ?? []) > 0;
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
<{{ $tag }}
@if (($tag === 'a') && (! ($disabled && $hasTooltip)))
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id)?.click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
{{
$attributes
->merge([
'aria-disabled' => $disabled ? 'true' : null,
'disabled' => $disabled && blank($tooltip),
'form' => $tag === 'button' ? $formId : null,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->when(
$disabled && $hasTooltip,
fn (ComponentAttributeBag $attributes) => $attributes->filter(
fn (mixed $value, string $key): bool => ! str($key)->startsWith(['href', 'x-on:', 'wire:click']),
),
)
->class([
'fi-badge',
'fi-disabled' => $disabled,
($size instanceof Size) ? "fi-size-{$size->value}" : (is_string($size) ? $size : ''),
])
->color(BadgeComponent::class, $color)
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize ?? \Filament\Support\Enums\IconSize::Small)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize ?? \Filament\Support\Enums\IconSize::Small)
}}
@endif
@endif
<span class="fi-badge-label-ctn">
<span class="fi-badge-label">
{{ $slot }}
</span>
</span>
@if ($isDeletable)
@php
$deleteButtonWireTarget = $deleteButton->attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first();
$deleteButtonHasLoadingIndicator = filled($deleteButtonWireTarget);
if ($deleteButtonHasLoadingIndicator) {
$deleteButtonLoadingIndicatorTarget = html_entity_decode($deleteButtonWireTarget, ENT_QUOTES);
}
@endphp
<button
type="button"
{{
$deleteButton->attributes
->except(['label'])
->class([
'fi-badge-delete-btn',
])
}}
>
{{
\Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::XMark, alias: \Filament\Support\View\SupportIconAlias::BADGE_DELETE_BUTTON, attributes: (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $deleteButtonHasLoadingIndicator,
'wire:target' => $deleteButtonHasLoadingIndicator ? $deleteButtonLoadingIndicatorTarget : false,
])), size: \Filament\Support\Enums\IconSize::ExtraSmall)
}}
@if ($deleteButtonHasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $deleteButtonLoadingIndicatorTarget,
])), size: \Filament\Support\Enums\IconSize::ExtraSmall)
}}
@endif
@if (filled($label = $deleteButton->attributes->get('label')))
<span class="fi-sr-only">
{{ $label }}
</span>
@endif
</button>
@elseif ($iconPosition === IconPosition::After)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize ?? \Filament\Support\Enums\IconSize::Small)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize ?? \Filament\Support\Enums\IconSize::Small)
}}
@endif
@endif
</{{ $tag }}>
@@ -1,44 +0,0 @@
@php
use Illuminate\View\ComponentAttributeBag;
use function Filament\Support\generate_icon_html;
@endphp
@props([
'breadcrumbs' => [],
])
<nav {{ $attributes->class(['fi-breadcrumbs']) }}>
<ol class="fi-breadcrumbs-list">
@foreach ($breadcrumbs as $url => $label)
<li class="fi-breadcrumbs-item">
@if (! $loop->first)
{{
generate_icon_html(\Filament\Support\Icons\Heroicon::ChevronRight, alias: \Filament\Support\View\SupportIconAlias::BREADCRUMBS_SEPARATOR, attributes: (new ComponentAttributeBag)->class([
'fi-breadcrumbs-item-separator fi-ltr',
]))
}}
{{
generate_icon_html(\Filament\Support\Icons\Heroicon::ChevronLeft, alias: \Filament\Support\View\SupportIconAlias::BREADCRUMBS_SEPARATOR_RTL, attributes: (new ComponentAttributeBag)->class([
'fi-breadcrumbs-item-separator fi-rtl',
]))
}}
@endif
@if (is_int($url))
<span class="fi-breadcrumbs-item-label">
{{ $label }}
</span>
@else
<a
{{ \Filament\Support\generate_href_html($url) }}
class="fi-breadcrumbs-item-label"
>
{{ $label }}
</a>
@endif
</li>
@endforeach
</ol>
</nav>
@@ -1,3 +0,0 @@
<div {{ $attributes->class(['fi-btn-group']) }}>
{{ $slot }}
</div>
@@ -1,238 +0,0 @@
@php
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
use Filament\Support\Enums\Size;
use Filament\Support\View\Components\BadgeComponent;
use Filament\Support\View\Components\ButtonComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => Size::ExtraSmall,
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => null,
'keyBindings' => null,
'labeledFrom' => null,
'labelSrOnly' => false,
'loadingIndicator' => true,
'outlined' => false,
'size' => Size::Medium,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $size instanceof Size) {
$size = filled($size) ? (Size::tryFrom($size) ?? $size) : null;
}
if (! $badgeSize instanceof Size) {
$badgeSize = filled($badgeSize) ? (Size::tryFrom($badgeSize) ?? $badgeSize) : null;
}
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$iconSize ??= match ($size) {
Size::ExtraSmall, Size::Small => IconSize::Small,
default => null,
};
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasFormProcessingLoadingIndicator = $type === 'submit' && filled($form);
$hasLoadingIndicator = filled($wireTarget) || $hasFormProcessingLoadingIndicator;
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
@if ($labeledFrom)
<x-filament::icon-button
:badge="$badge"
:badge-color="$badgeColor"
:badge-size="$badgeSize"
:color="$color"
:disabled="$disabled"
:form="$form"
:form-id="$formId"
:href="$href"
:icon="$icon"
:icon-alias="$iconAlias"
:icon-size="$iconSize"
:key-bindings="$keyBindings"
:label="$slot"
:loading-indicator="$loadingIndicator"
:size="$size"
:spa-mode="$spaMode"
:tag="$tag"
:target="$target"
:tooltip="$tooltip"
:type="$type"
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)"
/>
@endif
<{{ $tag }}
@if (($tag === 'a') && (! ($disabled && $hasTooltip)))
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id)?.click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
@if ($hasFormProcessingLoadingIndicator)
x-data="filamentFormButton"
x-bind:class="{ 'fi-processing': isProcessing }"
@endif
{{
$attributes
->merge([
'aria-disabled' => $disabled ? 'true' : null,
'aria-label' => $labelSrOnly ? trim(strip_tags($slot->toHtml())) : null,
'disabled' => $disabled && blank($tooltip),
'form' => $formId,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
'x-bind:disabled' => $hasFormProcessingLoadingIndicator ? 'isProcessing' : null,
'x-bind:aria-label' => ($labelSrOnly && $hasFormProcessingLoadingIndicator) ? ('isProcessing ? processingMessage : ' . \Illuminate\Support\Js::from(trim(strip_tags($slot->toHtml())))) : null,
], escape: false)
->when(
$disabled && $hasTooltip,
fn (ComponentAttributeBag $attributes) => $attributes->filter(
fn (mixed $value, string $key): bool => ! str($key)->startsWith(['href', 'x-on:', 'wire:click']),
),
)
->class([
'fi-btn',
'fi-disabled' => $disabled,
'fi-outlined' => $outlined,
($size instanceof Size) ? "fi-size-{$size->value}" : (is_string($size) ? $size : ''),
is_string($labeledFrom) ? "fi-labeled-from-{$labeledFrom}" : null,
])
->color(app(ButtonComponent::class, ['isOutlined' => $outlined]), $color)
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize)
}}
@endif
@if ($hasFormProcessingLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'x-cloak' => 'x-cloak',
'x-show' => 'isProcessing',
])), size: $iconSize)
}}
@endif
@endif
@if (! $labelSrOnly)
@if ($hasFormProcessingLoadingIndicator)
<span x-show="! isProcessing">
{{ $slot }}
</span>
@else
{{ $slot }}
@endif
@endif
@if ($hasFormProcessingLoadingIndicator && (! $labelSrOnly))
<span
x-cloak
x-show="isProcessing"
x-text="processingMessage"
></span>
@endif
@if ($iconPosition === IconPosition::After)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize)
}}
@endif
@if ($hasFormProcessingLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'x-cloak' => 'x-cloak',
'x-show' => 'isProcessing',
])), size: $iconSize)
}}
@endif
@endif
@if (filled($badge))
<div class="fi-btn-badge-ctn">
@if ($badge instanceof \Illuminate\View\ComponentSlot)
{{ $badge }}
@else
<span
{{
(new ComponentAttributeBag)->color(BadgeComponent::class, $badgeColor)->class([
'fi-badge',
($badgeSize instanceof Size) ? "fi-size-{$badgeSize->value}" : (is_string($badgeSize) ? $badgeSize : ''),
])
}}
>
{{ $badge }}
</span>
@endif
</div>
@endif
</{{ $tag }}>
@@ -1,84 +0,0 @@
@php
use Filament\Support\Enums\IconSize;
use Filament\Support\View\Components\CalloutComponent\IconComponent;
use function Filament\Support\generate_icon_html;
use function Filament\Support\is_slot_empty;
@endphp
@props([
'color' => 'gray',
'controls' => null,
'description' => null,
'footer' => null,
'heading' => null,
'icon' => null,
'iconColor' => null,
'iconSize' => null,
])
@php
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$iconColor ??= $color;
$hasDescription = filled((string) $description);
$hasHeading = filled($heading);
$hasFooter = ! is_slot_empty($footer);
$hasIcon = filled($icon);
$hasControls = ! is_slot_empty($controls);
@endphp
<div
{{
$attributes
->color(\Filament\Support\View\Components\CalloutComponent::class, $color)
->class(['fi-callout'])
}}
>
@if ($hasIcon)
{{
generate_icon_html(
$icon,
attributes: (new \Illuminate\View\ComponentAttributeBag)
->color(IconComponent::class, $iconColor)
->class(['fi-callout-icon']),
size: $iconSize ?? IconSize::Large,
)
}}
@endif
@if ($hasHeading || $hasDescription || $hasFooter)
<div class="fi-callout-main">
@if ($hasHeading || $hasDescription)
<div class="fi-callout-text">
@if ($hasHeading)
<h4 class="fi-callout-heading">
{{ $heading }}
</h4>
@endif
@if ($hasDescription)
<p class="fi-callout-description">
{{ $description }}
</p>
@endif
</div>
@endif
@if ($hasFooter)
<div class="fi-callout-footer">
{{ $footer }}
</div>
@endif
</div>
@endif
@if ($hasControls)
<div class="fi-callout-controls">
{{ $controls }}
</div>
@endif
</div>
@@ -1,5 +0,0 @@
<x-filament::section
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)"
>
{{ $slot }}
</x-filament::section>
@@ -1,34 +0,0 @@
@php
use Filament\Support\Enums\IconSize;
use Filament\Support\View\Components\DropdownComponent\HeaderComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'color' => 'gray',
'icon' => null,
'iconSize' => null,
'tag' => 'div',
])
@php
if (! ($iconSize instanceof IconSize)) {
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
}
@endphp
<{{ $tag }}
{{
$attributes
->class([
'fi-dropdown-header',
])
->color(HeaderComponent::class, $color)
}}
>
{{ \Filament\Support\generate_icon_html($icon, size: $iconSize) }}
<span>
{{ $slot }}
</span>
</{{ $tag }}>
@@ -1,66 +0,0 @@
@props([
'availableHeight' => null,
'availableWidth' => null,
'flip' => true,
'maxHeight' => null,
'offset' => 8,
'placement' => null,
'shift' => false,
'size' => false,
'sizePadding' => 16,
'teleport' => false,
'trigger' => null,
'width' => null,
])
@php
use Filament\Support\Enums\Width;
$sizeConfig = collect([
'availableHeight' => $availableHeight,
'availableWidth' => $availableWidth,
'padding' => $sizePadding,
])->filter()->toJson();
if (is_string($width)) {
$width = Width::tryFrom($width) ?? $width;
}
@endphp
<div
x-data="filamentDropdown"
{{ $attributes->class(['fi-dropdown']) }}
>
<div
x-on:keyup.enter="toggle($event)"
x-on:keyup.space="toggle($event)"
x-on:mousedown="if ($event.button === 0) toggle($event)"
{{ $trigger->attributes->class(['fi-dropdown-trigger']) }}
>
{{ $trigger }}
</div>
@if (! \Filament\Support\is_slot_empty($slot))
<div
x-cloak
x-float{{ $placement ? ".placement.{$placement}" : '' }}{{ $size ? '.size' : '' }}{{ $flip ? '.flip' : '' }}{{ $shift ? '.shift' : '' }}{{ $teleport ? '.teleport' : '' }}{{ $offset ? '.offset' : '' }}="{ offset: {{ $offset }}, {{ $size ? ('size: ' . $sizeConfig) : '' }} }"
x-ref="panel"
x-transition:enter-start="fi-opacity-0"
x-transition:leave-end="fi-opacity-0"
@if ($attributes->has('wire:key'))
wire:ignore.self
wire:key="{{ $attributes->get('wire:key') }}.panel"
@endif
@class([
'fi-dropdown-panel',
($width instanceof Width) ? "fi-width-{$width->value}" : (is_string($width) ? $width : ''),
'fi-scrollable' => $maxHeight || $size,
])
@style([
('max-height: ' . e($maxHeight)) => $maxHeight,
])
>
{{ $slot }}
</div>
@endif
</div>
@@ -1,3 +0,0 @@
<div {{ $attributes->class(['fi-dropdown-list']) }}>
{{ $slot }}
</div>
@@ -1,191 +0,0 @@
@php
use Filament\Support\Enums\IconSize;
use Filament\Support\Enums\Size;
use Filament\Support\View\Components\BadgeComponent;
use Filament\Support\View\Components\DropdownComponent\ItemComponent;
use Filament\Support\View\Components\DropdownComponent\ItemComponent\IconComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'alpineDeferredBadgeData' => null,
'alpineDeferredBadgeLoading' => null,
'badge' => null,
'badgeColor' => 'primary',
'badgeTooltip' => null,
'color' => 'gray',
'disabled' => false,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconColor' => null,
'iconSize' => null,
'image' => null,
'keyBindings' => null,
'loadingIndicator' => true,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
])
@php
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$iconColor ??= $color;
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget);
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES);
}
$hasDeferredBadge = filled($alpineDeferredBadgeData);
$hasTooltip = filled($tooltip);
@endphp
{!! ($tag === 'form') ? ('<form ' . $attributes->only(['action', 'class', 'method', 'wire:submit'])->toHtml() . '>') : '' !!}
@if ($tag === 'form')
@csrf
@endif
<{{ ($tag === 'form') ? 'button' : $tag }}
@if (($tag === 'a') && (! ($disabled && $hasTooltip)))
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id)?.click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
{{
$attributes
->when(
$tag === 'form',
fn (ComponentAttributeBag $attributes) => $attributes->except(['action', 'class', 'method', 'wire:submit']),
)
->merge([
'aria-disabled' => $disabled ? 'true' : null,
'disabled' => $disabled && blank($tooltip),
'type' => match ($tag) {
'button' => 'button',
'form' => 'submit',
default => null,
},
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->when(
$disabled && $hasTooltip,
fn (ComponentAttributeBag $attributes) => $attributes->filter(
fn (mixed $value, string $key): bool => ! str($key)->startsWith(['href', 'x-on:', 'wire:click']),
),
)
->class([
'fi-dropdown-list-item',
'fi-disabled' => $disabled,
])
->color(ItemComponent::class, $color)
}}
>
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
]))->color(IconComponent::class, $iconColor), size: $iconSize)
}}
@endif
@if ($image)
<div
class="fi-dropdown-list-item-image"
style="background-image: url('{{ $image }}')"
@if ($hasLoadingIndicator)
wire:loading.remove.delay.{{ config('filament.livewire_loading_delay', 'default') }}
wire:target="{{ $loadingIndicatorTarget }}"
@endif
></div>
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
]))->color(IconComponent::class, $iconColor), size: $iconSize)
}}
@endif
<span class="fi-dropdown-list-item-label">
{{ $slot }}
</span>
@if (filled($badge))
@if ($badge instanceof \Illuminate\View\ComponentSlot)
{{ $badge }}
@else
<span
@if ($badgeTooltip)
x-tooltip="{
content: @js($badgeTooltip),
theme: $store.theme,
allowHTML: @js($badgeTooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
{{ (new ComponentAttributeBag)->color(BadgeComponent::class, $badgeColor)->class(['fi-badge']) }}
>
{{ $badge }}
</span>
@endif
@elseif ($hasDeferredBadge)
<span
x-show="{{ $alpineDeferredBadgeLoading }}"
x-cloak
class="fi-dropdown-list-item-badge-placeholder"
>
{{ \Filament\Support\generate_loading_indicator_html(size: \Filament\Support\Enums\IconSize::Small) }}
</span>
<template
x-if="
! {{ $alpineDeferredBadgeLoading }} &&
{{ $alpineDeferredBadgeData }}?.badge != null
"
>
<span
x-bind:class="'fi-badge ' + ({{ $alpineDeferredBadgeData }}?.badgeColorClasses ?? '')"
x-bind:style="{{ $alpineDeferredBadgeData }}?.badgeColorStyles ?? ''"
x-init="
let tooltip = {{ $alpineDeferredBadgeData }}?.badgeTooltip
if (tooltip) {
window.tippy?.($el, {
content: tooltip,
theme: $store.theme,
})
}
"
>
<span class="fi-badge-label-ctn">
<span
class="fi-badge-label"
x-text="{{ $alpineDeferredBadgeData }}?.badge"
></span>
</span>
</span>
</template>
@endif
</{{ ($tag === 'form') ? 'button' : $tag }}>
{!! ($tag === 'form') ? '</form>' : '' !!}
@@ -1,70 +0,0 @@
@php
use Filament\Support\Enums\IconSize;
use Filament\Support\View\Components\SectionComponent\IconComponent;
@endphp
@props([
'compact' => false,
'contained' => true,
'description' => null,
'footer' => null,
'heading',
'headingTag' => 'h2',
'icon' => null,
'iconColor' => 'primary',
'iconSize' => null,
])
@php
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$hasDescription = filled((string) $description);
$hasFooter = filled((string) $footer);
$hasIcon = filled($icon);
@endphp
<section
{{
$attributes->class([
'fi-empty-state',
'fi-compact' => $compact,
'fi-empty-state-not-contained' => ! $contained,
])
}}
>
<div class="fi-empty-state-content">
@if ($hasIcon)
<div
@class([
'fi-empty-state-icon-bg',
'fi-color ' . ('fi-color-' . $iconColor) => $iconColor !== 'gray',
])
>
{{
\Filament\Support\generate_icon_html($icon, attributes: (new \Illuminate\View\ComponentAttributeBag)
->color(IconComponent::class, $iconColor), size: $iconSize ?? IconSize::Large)
}}
</div>
@endif
<div class="fi-empty-state-text-ctn">
<{{ $headingTag }} class="fi-empty-state-heading">
{{ $heading }}
</{{ $headingTag }}>
@if ($hasDescription)
<p class="fi-empty-state-description">
{{ $description }}
</p>
@endif
@if ($hasFooter)
<footer class="fi-empty-state-footer">
{{ $footer }}
</footer>
@endif
</div>
</div>
</section>
@@ -1,25 +0,0 @@
@props([
'contained' => true,
'label' => null,
'labelHidden' => false,
'required' => false,
])
<fieldset
{{
$attributes->class([
'fi-fieldset',
'fi-fieldset-label-hidden' => $labelHidden,
'fi-fieldset-not-contained' => ! $contained,
])
}}
>
@if (filled($label))
<legend>
{{ $label }}@if ($required)<sup class="fi-fieldset-label-required-mark">*</sup>
@endif
</legend>
@endif
{{ $slot }}
</fieldset>
@@ -1,139 +0,0 @@
@php
use Filament\Support\Enums\IconSize;
use Filament\Support\Enums\Size;
use Filament\Support\View\Components\BadgeComponent;
use Filament\Support\View\Components\IconButtonComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => Size::ExtraSmall,
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconSize' => null,
'keyBindings' => null,
'label' => null,
'loadingIndicator' => true,
'size' => Size::Medium,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $size instanceof Size) {
$size = filled($size) ? (Size::tryFrom($size) ?? $size) : null;
}
if (! $badgeSize instanceof Size) {
$badgeSize = filled($badgeSize) ? (Size::tryFrom($badgeSize) ?? $badgeSize) : null;
}
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$iconSize ??= match ($size) {
Size::ExtraSmall => IconSize::Small,
Size::Large, Size::ExtraLarge => IconSize::Large,
default => null,
};
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = $hasTooltip = filled($tooltip);
@endphp
<{{ $tag }}
@if (($tag === 'a') && (! ($disabled && $hasTooltip)))
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id)?.click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
{{
$attributes
->merge([
'aria-disabled' => $disabled ? 'true' : null,
'aria-label' => e($label),
'disabled' => $disabled && blank($tooltip),
'form' => $formId,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->merge([
'title' => $hasTooltip ? null : $label,
], escape: true)
->when(
$disabled && $hasTooltip,
fn (ComponentAttributeBag $attributes) => $attributes->filter(
fn (mixed $value, string $key): bool => ! str($key)->startsWith(['href', 'x-on:', 'wire:click']),
),
)
->class([
'fi-icon-btn',
'fi-disabled' => $disabled,
($size instanceof Size) ? "fi-size-{$size->value}" : (is_string($size) ? $size : ''),
])
->color(IconButtonComponent::class, $color)
}}
>
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize)
}}
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize)
}}
@endif
@if (filled($badge))
<div class="fi-icon-btn-badge-ctn">
@if ($badge instanceof \Illuminate\View\ComponentSlot)
{{ $badge }}
@else
<span
{{
(new ComponentAttributeBag)->color(BadgeComponent::class, $badgeColor)->class([
'fi-badge',
($badgeSize instanceof Size) ? "fi-size-{$badgeSize->value}" : (is_string($badgeSize) ? $badgeSize : ''),
])
}}
>
{{ $badge }}
</span>
@endif
</div>
@endif
</{{ $tag }}>
@@ -1,7 +0,0 @@
@props([
'alias' => null,
'icon' => null,
'size' => null,
])
{{ \Filament\Support\generate_icon_html($icon, $alias, $attributes, $size) }}
@@ -1,26 +0,0 @@
@props([
'alpineValid' => null,
'valid' => true,
])
@php
$hasAlpineValidClasses = filled($alpineValid);
@endphp
<input
type="checkbox"
@if ($hasAlpineValidClasses)
x-bind:class="{
'fi-valid': {{ $alpineValid }},
'fi-invalid': {{ "(! {$alpineValid})" }},
}"
@endif
{{
$attributes
->class([
'fi-checkbox-input',
'fi-valid' => (! $hasAlpineValidClasses) && $valid,
'fi-invalid' => (! $hasAlpineValidClasses) && (! $valid),
])
}}
/>
@@ -1,14 +0,0 @@
@props([
'inlinePrefix' => false,
'inlineSuffix' => false,
])
<input
{{
$attributes->class([
'fi-input',
'fi-input-has-inline-prefix' => $inlinePrefix,
'fi-input-has-inline-suffix' => $inlineSuffix,
])
}}
/>
@@ -1,43 +0,0 @@
@props([
'length' => 6,
])
<div
x-data="{ currentNumberOfDigits: null }"
{{
$attributes
->class([
'fi-one-time-code-input-ctn',
])
}}
>
@foreach (range(1, $length) as $digit)
<div
x-bind:class="{
'fi-active':
currentNumberOfDigits !== null &&
currentNumberOfDigits >= {{ $digit }},
}"
class="fi-one-time-code-input-digit-field"
></div>
@endforeach
<input
autocomplete="one-time-code"
inputmode="numeric"
minlength="{{ $length }}"
maxlength="{{ $length }}"
pattern="\d{{ '{' . $length . '}' }}"
type="text"
x-data="{}"
x-on:focus="currentNumberOfDigits = $el.value.length"
x-on:blur="currentNumberOfDigits = null"
x-on:input="
$el.value = $el.value.replace(/\D/g, '')
currentNumberOfDigits = $el.value.length
"
x-bind:class="{ 'fi-valid': currentNumberOfDigits >= {{ $length }} }"
{{ $input?->attributes }}
class="fi-one-time-code-input"
/>
</div>
@@ -1,14 +0,0 @@
@props([
'valid' => true,
])
<input
type="radio"
{{
$attributes
->class([
'fi-radio-input',
'fi-invalid' => ! $valid,
])
}}
/>
@@ -1,15 +0,0 @@
@props([
'inlinePrefix' => false,
'inlineSuffix' => false,
])
<select
{{
$attributes->class([
'fi-select-input',
'fi-select-input-has-inline-prefix' => $inlinePrefix,
])
}}
>
{{ $slot }}
</select>
@@ -1,179 +0,0 @@
@props([
'alpineDisabled' => null,
'alpineValid' => null,
'disabled' => false,
'inlinePrefix' => false,
'inlineSuffix' => false,
'prefix' => null,
'prefixActions' => [],
'prefixIcon' => null,
'prefixIconColor' => 'gray',
'prefixIconAlias' => null,
'suffix' => null,
'suffixActions' => [],
'suffixIcon' => null,
'suffixIconColor' => 'gray',
'suffixIconAlias' => null,
'valid' => true,
])
@php
use Filament\Support\View\Components\InputComponent\WrapperComponent\IconComponent;
use Illuminate\View\ComponentAttributeBag;
$prefixActions = array_filter(
$prefixActions,
fn (\Filament\Actions\Action $prefixAction): bool => $prefixAction->isVisible(),
);
$suffixActions = array_filter(
$suffixActions,
fn (\Filament\Actions\Action $suffixAction): bool => $suffixAction->isVisible(),
);
$hasPrefix = count($prefixActions) || $prefixIcon || filled($prefix);
$hasSuffix = count($suffixActions) || $suffixIcon || filled($suffix);
$hasAlpineDisabledClasses = filled($alpineDisabled);
$hasAlpineValidClasses = filled($alpineValid);
$hasAlpineClasses = $hasAlpineDisabledClasses || $hasAlpineValidClasses;
$wireTarget = $attributes->whereStartsWith(['wire:target'])->first();
$hasLoadingIndicator = filled($wireTarget);
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES);
}
$hasFocusInputListener = $attributes->has('x-on:focus-input.stop');
$canClickPrefixAffix = $hasFocusInputListener && ($prefixIcon || filled($prefix));
$canClickSuffixAffix = $hasFocusInputListener && ($suffixIcon || filled($suffix));
@endphp
<div
@if ($hasAlpineClasses)
x-bind:class="{
{{ $hasAlpineDisabledClasses ? "'fi-disabled': {$alpineDisabled}," : null }}
{{ $hasAlpineValidClasses ? "'fi-invalid': ! ({$alpineValid})," : null }}
}"
@endif
{{
$attributes
->except(['wire:target', 'tabindex'])
->class([
'fi-input-wrp',
'fi-disabled' => (! $hasAlpineClasses) && $disabled,
'fi-invalid' => (! $hasAlpineClasses) && (! $valid),
])
}}
>
@if ($hasPrefix || $hasLoadingIndicator)
<div
@if (! $hasPrefix)
wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.flex
wire:target="{{ $loadingIndicatorTarget }}"
wire:key="{{ \Illuminate\Support\Str::random() }}" {{-- Makes sure the loading indicator gets hidden again. --}}
@endif
@if ($canClickPrefixAffix)
x-on:click="$dispatch('focus-input')"
@endif
@class([
'fi-input-wrp-prefix',
'fi-input-wrp-prefix-has-content' => $hasPrefix,
'fi-inline' => $inlinePrefix,
'fi-input-wrp-prefix-has-label' => filled($prefix),
])
>
@if (count($prefixActions))
<div
@class(['fi-input-wrp-actions'])
@if ($canClickPrefixAffix) x-on:click.stop @endif
>
@foreach ($prefixActions as $prefixAction)
{{ $prefixAction }}
@endforeach
</div>
@endif
{{
\Filament\Support\generate_icon_html($prefixIcon, $prefixIconAlias, (new \Illuminate\View\ComponentAttributeBag)
->merge([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
], escape: false)
->color(IconComponent::class, $prefixIconColor))
}}
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => $hasPrefix,
'wire:target' => $hasPrefix ? $loadingIndicatorTarget : null,
]))->color(IconComponent::class, 'gray'))
}}
@endif
@if (filled($prefix))
<span class="fi-input-wrp-label">
{{ $prefix }}
</span>
@endif
</div>
@endif
<div
@if ($hasLoadingIndicator && (! $hasPrefix))
@if ($inlinePrefix)
wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.class.remove="ps-3"
@endif
wire:target="{{ $loadingIndicatorTarget }}"
@endif
@class([
'fi-input-wrp-content-ctn',
'fi-input-wrp-content-ctn-ps' => $hasLoadingIndicator && (! $hasPrefix) && $inlinePrefix,
])
>
{{ $slot }}
</div>
@if ($hasSuffix)
<div
@if ($canClickSuffixAffix)
x-on:click="$dispatch('focus-input')"
@endif
@class([
'fi-input-wrp-suffix',
'fi-inline' => $inlineSuffix,
'fi-input-wrp-suffix-has-label' => filled($suffix),
])
>
@if (filled($suffix))
<span class="fi-input-wrp-label">
{{ $suffix }}
</span>
@endif
{{
\Filament\Support\generate_icon_html($suffixIcon, $suffixIconAlias, (new \Illuminate\View\ComponentAttributeBag)
->merge([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
], escape: false)
->color(IconComponent::class, $suffixIconColor))
}}
@if (count($suffixActions))
<div
@class(['fi-input-wrp-actions'])
@if ($canClickSuffixAffix) x-on:click.stop @endif
>
@foreach ($suffixActions as $suffixAction)
{{ $suffixAction }}
@endforeach
</div>
@endif
</div>
@endif
</div>
@@ -1,176 +0,0 @@
@php
use Filament\Support\Enums\FontWeight;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
use Filament\Support\Enums\Size;
use Filament\Support\View\Components\BadgeComponent;
use Filament\Support\View\Components\LinkComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => Size::ExtraSmall,
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => null,
'keyBindings' => null,
'labelSrOnly' => false,
'loadingIndicator' => true,
'size' => Size::Medium,
'spaMode' => null,
'tag' => 'a',
'target' => null,
'tooltip' => null,
'type' => 'button',
'weight' => null,
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $badgeSize instanceof Size) {
$badgeSize = filled($badgeSize) ? (Size::tryFrom($badgeSize) ?? $badgeSize) : null;
}
if (! $size instanceof Size) {
$size = filled($size) ? (Size::tryFrom($size) ?? $size) : null;
}
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$iconSize ??= match ($size) {
Size::ExtraSmall, Size::Small => IconSize::Small,
default => null,
};
if (! $weight instanceof FontWeight) {
$weight = filled($weight) ? (FontWeight::tryFrom($weight) ?? $weight) : null;
}
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
<{{ $tag }}
@if (($tag === 'a') && (! ($disabled && $hasTooltip)))
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id)?.click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
allowHTML: @js($tooltip instanceof \Illuminate\Contracts\Support\Htmlable),
}"
@endif
{{
$attributes
->merge([
'aria-disabled' => $disabled ? 'true' : null,
'disabled' => $disabled && blank($tooltip),
'form' => $formId,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->when(
$disabled && $hasTooltip,
fn (ComponentAttributeBag $attributes) => $attributes->filter(
fn (mixed $value, string $key): bool => ! str($key)->startsWith(['href', 'x-on:', 'wire:click']),
),
)
->class([
'fi-link',
'fi-disabled' => $disabled,
($size instanceof Size) ? "fi-size-{$size->value}" : (is_string($size) ? $size : ''),
($weight instanceof FontWeight) ? "fi-font-{$weight->value}" : (is_string($weight) ? $weight : ''),
])
->color(LinkComponent::class, $color)
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize)
}}
@endif
@endif
@if (! $labelSrOnly)
{{ $slot }}
@endif
@if ($iconPosition === IconPosition::After)
@if ($icon)
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, (new \Illuminate\View\ComponentAttributeBag([
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,
])), size: $iconSize)
}}
@endif
@if ($hasLoadingIndicator)
{{
\Filament\Support\generate_loading_indicator_html((new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])), size: $iconSize)
}}
@endif
@endif
@if (filled($badge))
<div class="fi-link-badge-ctn">
@if ($badge instanceof \Illuminate\View\ComponentSlot)
{{ $badge }}
@else
<span
{{
(new ComponentAttributeBag)->color(BadgeComponent::class, $badgeColor)->class([
'fi-badge',
($badgeSize instanceof Size) ? "fi-size-{$badgeSize->value}" : (is_string($badgeSize) ? $badgeSize : ''),
])
}}
>
{{ $badge }}
</span>
@endif
</div>
@endif
</{{ $tag }}>
@trim
@@ -1 +0,0 @@
{{ \Filament\Support\generate_loading_indicator_html($attributes) }}
@@ -1,14 +0,0 @@
@props([
'columnSpan' => [],
'columnStart' => [],
'height' => null,
])
<div
{{
($attributes ?? new \Illuminate\View\ComponentAttributeBag)
->gridColumn($columnSpan, $columnStart)
->class(['fi-section fi-loading-section'])
->style(['height: ' . e($height ?? '8rem')])
}}
></div>
@@ -1,5 +0,0 @@
<p
{{ $attributes->class(['fi-modal-description']) }}
>
{{ $slot }}
</p>
@@ -1,5 +0,0 @@
<h2
{{ $attributes->class(['fi-modal-heading']) }}
>
{{ $slot }}
</h2>
@@ -1,281 +0,0 @@
@php
use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\SlideOverPosition;
use Filament\Support\Enums\Width;
use Filament\Support\View\Components\ModalComponent\IconComponent;
use Illuminate\View\ComponentAttributeBag;
@endphp
@props([
'alignment' => Alignment::Start,
'ariaLabelledby' => null,
'autofocus' => \Filament\Support\View\Components\ModalComponent::$isAutofocused,
'closeButton' => \Filament\Support\View\Components\ModalComponent::$hasCloseButton,
'closeByClickingAway' => \Filament\Support\View\Components\ModalComponent::$isClosedByClickingAway,
'closeByEscaping' => \Filament\Support\View\Components\ModalComponent::$isClosedByEscaping,
'closeEventName' => 'close-modal',
'closeQuietlyEventName' => 'close-modal-quietly',
'description' => null,
'extraModalWindowAttributeBag' => null,
'extraModalOverlayAttributeBag' => null,
'footer' => null,
'footerActions' => [],
'footerActionsAlignment' => Alignment::Start,
'header' => null,
'heading' => null,
'icon' => null,
'iconAlias' => null,
'iconColor' => 'primary',
'id' => null,
'openEventName' => 'open-modal',
'slideOver' => false,
'slideOverPosition' => SlideOverPosition::End,
'stickyFooter' => false,
'stickyHeader' => false,
'teleport' => null,
'trigger' => null,
'visible' => true,
'width' => 'sm',
])
@php
$hasContent = ! \Filament\Support\is_slot_empty($slot);
$hasDescription = filled($description);
$hasFooter = (! \Filament\Support\is_slot_empty($footer)) || (is_array($footerActions) && count($footerActions)) || (! is_array($footerActions) && (! \Filament\Support\is_slot_empty($footerActions)));
$hasHeading = filled($heading);
$hasIcon = filled($icon);
if (! $alignment instanceof Alignment) {
$alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null;
}
if (! $footerActionsAlignment instanceof Alignment) {
$footerActionsAlignment = filled($footerActionsAlignment) ? (Alignment::tryFrom($footerActionsAlignment) ?? $footerActionsAlignment) : null;
}
if (is_string($width)) {
$width = Width::tryFrom($width) ?? $width;
}
$closeEventHandler = filled($id) ? '$dispatch(' . \Illuminate\Support\Js::from($closeEventName) . ', { id: ' . \Illuminate\Support\Js::from($id) . ' })' : 'close()';
$wireSubmitHandler = $attributes->get('wire:submit.prevent');
$attributes = $attributes->except(['wire:submit.prevent']);
@endphp
@if ($trigger)
{!! '<div>' !!}
{{-- Avoid formatting issues with unclosed elements --}}
<div
@if (! $trigger->attributes->get('disabled'))
@if ($id)
x-on:click="$dispatch(@js($openEventName), { id: @js($id) })"
@else
x-on:click="$el.nextElementSibling.dispatchEvent(new CustomEvent(@js($openEventName)))"
@endif
@endif
{{ $trigger->attributes->except(['disabled'])->class(['fi-modal-trigger']) }}
>
{{ $trigger }}
</div>
@endif
@if (filled($teleport))
{!! "<template x-teleport=\"{$teleport}\">" !!}
{{-- Avoid formatting issues with unclosed elements --}}
@endif
<div
@if ($ariaLabelledby)
aria-labelledby="{{ $ariaLabelledby }}"
@elseif ($heading)
aria-labelledby="{{ "{$id}.heading" }}"
@endif
aria-modal="true"
id="{{ $id }}"
role="dialog"
x-data="filamentModal({
id: @js($id),
})"
@if ($id)
data-fi-modal-id="{{ $id }}"
x-on:{{ $closeEventName }}.window="if (($event.detail.id === @js($id)) && isOpen) close()"
x-on:{{ $closeQuietlyEventName }}.window="if (($event.detail.id === @js($id)) && isOpen) closeQuietly()"
x-on:{{ $openEventName }}.window="if (($event.detail.id === @js($id)) && (! isOpen)) open()"
@else
x-on:{{ $closeEventName }}.stop="if (isOpen) close()"
x-on:{{ $closeQuietlyEventName }}.stop="if (isOpen) closeQuietly()"
x-on:{{ $openEventName }}.stop="if (! isOpen) open()"
@endif
x-bind:class="{
'fi-modal-open': isOpen,
}"
x-cloak
x-show="isOpen"
x-trap.noscroll{{ $autofocus ? '' : '.noautofocus' }}="isOpen"
{{
$attributes->class([
'fi-modal',
'fi-absolute-positioning-context',
'fi-modal-slide-over' => $slideOver,
'fi-modal-slide-over-from-start' => $slideOver && $slideOverPosition === SlideOverPosition::Start,
'fi-modal-slide-over-from-end' => $slideOver && $slideOverPosition === SlideOverPosition::End,
'fi-modal-has-sticky-header' => $stickyHeader,
'fi-modal-has-sticky-footer' => $stickyFooter,
'fi-width-screen' => $width === Width::Screen,
])
}}
>
<div
aria-hidden="true"
x-show="isOpen"
x-transition.duration.300ms.opacity
{{
($extraModalOverlayAttributeBag ?? new \Illuminate\View\ComponentAttributeBag)->class([
'fi-modal-close-overlay',
])
}}
></div>
<div
@if ($closeByClickingAway)
x-on:click.self="{{ $closeEventHandler }}"
@endif
@class([
'fi-modal-window-ctn',
'fi-clickable' => $closeByClickingAway,
])
>
<{{ filled($wireSubmitHandler) ? 'form' : 'div' }}
@if ($closeByEscaping)
x-on:keydown.window.escape="if (isTopmost()) {{ $closeEventHandler }}"
@endif
x-show="isWindowVisible"
x-transition:enter="fi-transition-enter"
x-transition:leave="fi-transition-leave"
@if ($width !== Width::Screen)
x-transition:enter-start="fi-transition-enter-start"
x-transition:enter-end="fi-transition-enter-end"
x-transition:leave-start="fi-transition-leave-start"
x-transition:leave-end="fi-transition-leave-end"
@endif
@if (filled($wireSubmitHandler))
wire:submit.prevent="{!! $wireSubmitHandler !!}"
@endif
@if (filled($id))
wire:key="{{ isset($this) ? "{$this->getId()}." : '' }}modal.{{ $id }}.window"
@endif
{{
($extraModalWindowAttributeBag ?? new \Illuminate\View\ComponentAttributeBag)->class([
'fi-modal-window',
'fi-modal-window-has-close-btn' => $closeButton,
'fi-modal-window-has-content' => $hasContent,
'fi-modal-window-has-footer' => $hasFooter,
'fi-modal-window-has-icon' => $hasIcon,
'fi-hidden' => ! $visible,
($alignment instanceof Alignment) ? "fi-align-{$alignment->value}" : null,
($width instanceof Width) ? "fi-width-{$width->value}" : (is_string($width) ? $width : null),
])
}}
>
@if ($heading || $header)
<div
@if (filled($id))
wire:key="{{ isset($this) ? "{$this->getId()}." : '' }}modal.{{ $id }}.header"
@endif
@class([
'fi-modal-header',
'fi-vertical-align-center' => $hasIcon && $hasHeading && (! $hasDescription) && in_array($alignment, [Alignment::Start, Alignment::Left]),
])
>
@if ($closeButton)
<x-filament::icon-button
color="gray"
:icon="\Filament\Support\Icons\Heroicon::OutlinedXMark"
:icon-alias="\Filament\Support\View\SupportIconAlias::MODAL_CLOSE_BUTTON"
icon-size="lg"
:label="__('filament::components/modal.actions.close.label')"
tabindex="-1"
:x-on:click="$closeEventHandler"
class="fi-modal-close-btn"
/>
@endif
@if ($header)
{{ $header }}
@else
@if ($hasIcon)
<div class="fi-modal-icon-ctn">
<div
{{ (new ComponentAttributeBag)->color(IconComponent::class, $iconColor)->class(['fi-modal-icon-bg']) }}
>
{{ \Filament\Support\generate_icon_html($icon, $iconAlias, size: \Filament\Support\Enums\IconSize::Large) }}
</div>
</div>
@endif
<div>
<h2 class="fi-modal-heading">
{{ $heading }}
</h2>
@if ($hasDescription)
<p class="fi-modal-description">
{{ $description }}
</p>
@endif
</div>
@endif
</div>
@endif
@if ($hasContent)
<div
@if (filled($id))
wire:key="{{ isset($this) ? "{$this->getId()}." : '' }}modal.{{ $id }}.content"
@endif
class="fi-modal-content"
>
{{ $slot }}
</div>
@endif
@if ($hasFooter)
<div
@if (filled($id))
wire:key="{{ isset($this) ? "{$this->getId()}." : '' }}modal.{{ $id }}.footer"
@endif
@class([
'fi-modal-footer',
($footerActionsAlignment instanceof Alignment) ? "fi-align-{$footerActionsAlignment->value}" : null,
])
>
@if (! \Filament\Support\is_slot_empty($footer))
{{ $footer }}
@else
<div class="fi-modal-footer-actions">
@if (is_array($footerActions))
@foreach ($footerActions as $action)
{{ $action }}
@endforeach
@else
{{ $footerActions }}
@endif
</div>
@endif
</div>
@endif
</{{ filled($wireSubmitHandler) ? 'form' : 'div' }}>
</div>
</div>
@if (filled($teleport))
{!! '</template>' !!}
{{-- Avoid formatting issues with unclosed elements --}}
@endif
@if ($trigger)
{!! '</div>' !!}
{{-- Avoid formatting issues with unclosed elements --}}
@endif
@@ -1,208 +0,0 @@
@props([
'currentPageOptionProperty' => 'tableRecordsPerPage',
'extremeLinks' => false,
'paginator',
'pageOptions' => [],
])
@php
use Illuminate\Contracts\Pagination\CursorPaginator;
$isRtl = __('filament-panels::layout.direction') === 'rtl';
$isSimple = ! $paginator instanceof \Illuminate\Pagination\LengthAwarePaginator;
@endphp
<nav
aria-label="{{ __('filament::components/pagination.label') }}"
role="navigation"
{{
$attributes->class([
'fi-pagination',
'fi-simple' => $isSimple,
])
}}
>
@if (! $paginator->onFirstPage())
@php
if ($paginator instanceof CursorPaginator) {
$wireClickAction = "setPage('{$paginator->previousCursor()->encode()}', '{$paginator->getCursorName()}')";
} else {
$wireClickAction = "previousPage('{$paginator->getPageName()}')";
}
@endphp
<x-filament::button
color="gray"
rel="prev"
:wire:click="$wireClickAction"
:wire:key="$this->getId() . '.pagination.previous'"
class="fi-pagination-previous-btn"
>
{{ __('filament::components/pagination.actions.previous.label') }}
</x-filament::button>
@endif
@if (! $isSimple)
<span class="fi-pagination-overview">
{{
trans_choice(
'filament::components/pagination.overview',
$paginator->total(),
[
'first' => \Illuminate\Support\Number::format($paginator->firstItem() ?? 0),
'last' => \Illuminate\Support\Number::format($paginator->lastItem() ?? 0),
'total' => \Illuminate\Support\Number::format($paginator->total()),
],
)
}}
</span>
@endif
@if (count($pageOptions) > 1)
<div class="fi-pagination-records-per-page-select-ctn">
<label class="fi-pagination-records-per-page-select fi-compact">
<x-filament::input.wrapper>
<x-filament::input.select
:wire:model.live="$currentPageOptionProperty"
>
@foreach ($pageOptions as $option)
<option value="{{ $option }}">
{{ $option === 'all' ? __('filament::components/pagination.fields.records_per_page.options.all') : $option }}
</option>
@endforeach
</x-filament::input.select>
</x-filament::input.wrapper>
<span class="fi-sr-only">
{{ __('filament::components/pagination.fields.records_per_page.label') }}
</span>
</label>
<label class="fi-pagination-records-per-page-select">
<x-filament::input.wrapper
:prefix="__('filament::components/pagination.fields.records_per_page.label')"
>
<x-filament::input.select
:wire:model.live="$currentPageOptionProperty"
>
@foreach ($pageOptions as $option)
<option value="{{ $option }}">
{{ $option === 'all' ? __('filament::components/pagination.fields.records_per_page.options.all') : $option }}
</option>
@endforeach
</x-filament::input.select>
</x-filament::input.wrapper>
</label>
</div>
@endif
@if ($paginator->hasMorePages())
@php
if ($paginator instanceof CursorPaginator) {
$wireClickAction = "setPage('{$paginator->nextCursor()->encode()}', '{$paginator->getCursorName()}')";
} else {
$wireClickAction = "nextPage('{$paginator->getPageName()}')";
}
@endphp
<x-filament::button
color="gray"
rel="next"
:wire:click="$wireClickAction"
:wire:key="$this->getId() . '.pagination.next'"
class="fi-pagination-next-btn"
>
{{ __('filament::components/pagination.actions.next.label') }}
</x-filament::button>
@endif
@if ((! $isSimple) && $paginator->hasPages())
<ol class="fi-pagination-items">
@if (! $paginator->onFirstPage())
@if ($extremeLinks)
<x-filament::pagination.item
:aria-label="__('filament::components/pagination.actions.first.label')"
:icon="$isRtl ? \Filament\Support\Icons\Heroicon::ChevronDoubleRight : \Filament\Support\Icons\Heroicon::ChevronDoubleLeft"
:icon-alias="
$isRtl
? \Filament\Support\View\SupportIconAlias::PAGINATION_FIRST_BUTTON_RTL
: \Filament\Support\View\SupportIconAlias::PAGINATION_FIRST_BUTTON
"
rel="first"
:wire:click="'gotoPage(1, \'' . $paginator->getPageName() . '\')'"
:wire:key="$this->getId() . '.pagination.first'"
/>
@endif
<x-filament::pagination.item
:aria-label="__('filament::components/pagination.actions.previous.label')"
:icon="$isRtl ? \Filament\Support\Icons\Heroicon::ChevronRight : \Filament\Support\Icons\Heroicon::ChevronLeft"
{{-- @deprecated Use `SupportIconAlias::PAGINATION_PREVIOUS_BUTTON_RTL` instead of `SupportIconAlias::PAGINATION_PREVIOUS_BUTTON` for RTL. --}}
:icon-alias="
$isRtl
? [
\Filament\Support\View\SupportIconAlias::PAGINATION_PREVIOUS_BUTTON_RTL,
\Filament\Support\View\SupportIconAlias::PAGINATION_PREVIOUS_BUTTON,
]
: \Filament\Support\View\SupportIconAlias::PAGINATION_PREVIOUS_BUTTON
"
rel="prev"
:wire:click="'previousPage(\'' . $paginator->getPageName() . '\')'"
:wire:key="$this->getId() . '.pagination.previous'"
/>
@endif
@foreach ($paginator->render()->offsetGet('elements') as $element)
@if (is_string($element))
<x-filament::pagination.item disabled :label="$element" />
@endif
@if (is_array($element))
@foreach ($element as $page => $url)
<x-filament::pagination.item
:active="$page === $paginator->currentPage()"
:aria-label="trans_choice('filament::components/pagination.actions.go_to_page.label', $page, ['page' => \Illuminate\Support\Number::format($page)])"
:label="\Illuminate\Support\Number::format($page)"
:wire:click="'gotoPage(' . $page . ', \'' . $paginator->getPageName() . '\')'"
:wire:key="$this->getId() . '.pagination.' . $paginator->getPageName() . '.' . $page"
/>
@endforeach
@endif
@endforeach
@if ($paginator->hasMorePages())
<x-filament::pagination.item
:aria-label="__('filament::components/pagination.actions.next.label')"
:icon="$isRtl ? \Filament\Support\Icons\Heroicon::ChevronLeft : \Filament\Support\Icons\Heroicon::ChevronRight"
{{-- @deprecated Use `SupportIconAlias::PAGINATION_NEXT_BUTTON_RTL` instead of `SupportIconAlias::PAGINATION_NEXT_BUTTON` for RTL. --}}
:icon-alias="
$isRtl
? [
\Filament\Support\View\SupportIconAlias::PAGINATION_NEXT_BUTTON_RTL,
\Filament\Support\View\SupportIconAlias::PAGINATION_NEXT_BUTTON,
]
: \Filament\Support\View\SupportIconAlias::PAGINATION_NEXT_BUTTON
"
rel="next"
:wire:click="'nextPage(\'' . $paginator->getPageName() . '\')'"
:wire:key="$this->getId() . '.pagination.next'"
/>
@if ($extremeLinks)
<x-filament::pagination.item
:aria-label="__('filament::components/pagination.actions.last.label')"
:icon="$isRtl ? \Filament\Support\Icons\Heroicon::ChevronDoubleLeft : \Filament\Support\Icons\Heroicon::ChevronDoubleRight"
:icon-alias="
$isRtl
? \Filament\Support\View\SupportIconAlias::PAGINATION_LAST_BUTTON_RTL
: \Filament\Support\View\SupportIconAlias::PAGINATION_LAST_BUTTON
"
rel="last"
:wire:click="'gotoPage(' . $paginator->lastPage() . ', \'' . $paginator->getPageName() . '\')'"
:wire:key="$this->getId() . '.pagination.last'"
/>
@endif
@endif
</ol>
@endif
</nav>
@@ -1,39 +0,0 @@
@props([
'active' => false,
'ariaLabel' => null,
'disabled' => false,
'icon' => null,
'iconAlias' => null,
'label' => null,
])
<li
{{
$attributes->class([
'fi-pagination-item',
'fi-disabled' => $disabled,
'fi-active' => $active,
])
}}
>
<button
aria-label="{{ $ariaLabel }}"
@disabled($disabled)
type="button"
class="fi-pagination-item-btn"
>
@if (filled($icon))
{{
\Filament\Support\generate_icon_html($icon, $iconAlias, attributes: (new \Illuminate\View\ComponentAttributeBag)->class([
'fi-pagination-item-icon',
]))
}}
@endif
@if (filled($label))
<span class="fi-pagination-item-label">
{{ is_numeric($label) ? \Illuminate\Support\Number::format($label) : ($label ?? '...') }}
</span>
@endif
</button>
</li>
@@ -1,5 +0,0 @@
<p
{{ $attributes->class(['fi-section-header-description']) }}
>
{{ $slot }}
</p>
@@ -1,5 +0,0 @@
<h2
{{ $attributes->class(['fi-section-header-heading']) }}
>
{{ $slot }}
</h2>
@@ -1,140 +0,0 @@
@php
use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\IconSize;
use Filament\Support\View\Components\SectionComponent\IconComponent;
use function Filament\Support\is_slot_empty;
@endphp
@props([
'afterHeader' => null,
'aside' => false,
'collapsed' => false,
'collapseId' => null,
'collapsible' => false,
'compact' => false,
'contained' => true,
'contentBefore' => false,
'description' => null,
'divided' => false,
'footer' => null,
'hasContentEl' => true,
'heading' => null,
'headingTag' => 'h2',
'icon' => null,
'iconColor' => 'gray',
'iconSize' => null,
'persistCollapsed' => false,
'secondary' => false,
])
@php
if (filled($iconSize) && (! $iconSize instanceof IconSize)) {
$iconSize = IconSize::tryFrom($iconSize) ?? $iconSize;
}
$hasDescription = filled((string) $description);
$hasHeading = filled($heading);
$hasIcon = filled($icon);
$hasHeader = $hasIcon || $hasHeading || $hasDescription || $collapsible || (! is_slot_empty($afterHeader));
@endphp
<section
{{-- TODO: Investigate Livewire bug - https://github.com/filamentphp/filament/pull/8511 --}}
x-data="{
isCollapsed: @if ($persistCollapsed) $persist(@js($collapsed)).as(`section-${@js($collapseId) ?? $el.id}-isCollapsed`) @else @js($collapsed) @endif,
}"
@if ($collapsible)
x-on:collapse-section.window="if ($event.detail.id == @js($collapseId) ?? $el.id) isCollapsed = true"
x-on:expand="isCollapsed = false"
x-on:expand-section.window="if ($event.detail.id == @js($collapseId) ?? $el.id) isCollapsed = false"
x-on:open-section.window="if ($event.detail.id == @js($collapseId) ?? $el.id) isCollapsed = false"
x-on:toggle-section.window="if ($event.detail.id == @js($collapseId) ?? $el.id) isCollapsed = ! isCollapsed"
x-bind:class="isCollapsed && 'fi-collapsed'"
@endif
{{
$attributes->class([
'fi-section',
'fi-section-not-contained' => ! $contained,
'fi-section-has-content-before' => $contentBefore,
'fi-section-has-header' => $hasHeader,
'fi-aside' => $aside,
'fi-compact' => $compact,
'fi-collapsible' => $collapsible,
'fi-divided' => $divided,
'fi-secondary' => $secondary,
])
}}
>
@if ($hasHeader)
<header
@if ($collapsible)
x-on:click="if (! $event.target.closest('.fi-section-header-after-ctn')) isCollapsed = ! isCollapsed"
@endif
class="fi-section-header"
>
{{
\Filament\Support\generate_icon_html($icon, attributes: (new \Illuminate\View\ComponentAttributeBag)
->color(IconComponent::class, $iconColor), size: $iconSize ?? IconSize::Large)
}}
@if ($hasHeading || $hasDescription)
<div class="fi-section-header-text-ctn">
@if ($hasHeading)
<{{ $headingTag }} class="fi-section-header-heading">
{{ $heading }}
</{{ $headingTag }}>
@endif
@if ($hasDescription)
<p class="fi-section-header-description">
{{ $description }}
</p>
@endif
</div>
@endif
@if (! is_slot_empty($afterHeader))
<div class="fi-section-header-after-ctn">
{{ $afterHeader }}
</div>
@endif
@if ($collapsible)
<x-filament::icon-button
color="gray"
:icon="\Filament\Support\Icons\Heroicon::ChevronUp"
:icon-alias="\Filament\Support\View\SupportIconAlias::SECTION_COLLAPSE_BUTTON"
x-on:click.stop="isCollapsed = ! isCollapsed"
class="fi-section-collapse-btn"
/>
@endif
</header>
@endif
@if ((! is_slot_empty($slot)) || (! is_slot_empty($footer)))
<div
@if ($collapsible)
x-bind:aria-expanded="(! isCollapsed).toString()"
@if ($collapsed || $persistCollapsed)
x-cloak
@endif
@endif
class="fi-section-content-ctn"
>
@if ($hasContentEl)
<div class="fi-section-content">
{{ $slot }}
</div>
@else
{{ $slot }}
@endif
@if (! is_slot_empty($footer))
<footer class="fi-section-footer">
{{ $footer }}
</footer>
@endif
</div>
@endif
</section>
@@ -1,22 +0,0 @@
@props([
'contained' => false,
'label' => null,
'vertical' => false,
])
<nav
{{
$attributes
->merge([
'aria-label' => $label,
'role' => 'tablist',
])
->class([
'fi-tabs',
'fi-contained' => $contained,
'fi-vertical' => $vertical,
])
}}
>
{{ $slot }}
</nav>
@@ -1,145 +0,0 @@
@php
use Filament\Support\Enums\IconPosition;
@endphp
@props([
'active' => false,
'alpineActive' => null,
'alpineDeferredBadgeData' => null,
'alpineDeferredBadgeLoading' => null,
'badge' => null,
'badgeColor' => null,
'badgeTooltip' => null,
'badgeIcon' => null,
'badgeIconPosition' => IconPosition::Before,
'href' => null,
'icon' => null,
'iconColor' => 'gray',
'iconPosition' => IconPosition::Before,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'type' => 'button',
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
$hasAlpineActiveClasses = filled($alpineActive);
$hasDeferredBadge = filled($alpineDeferredBadgeData);
@endphp
<{{ $tag }}
@if ($tag === 'button')
type="{{ $type }}"
@elseif ($tag === 'a')
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($hasAlpineActiveClasses)
x-bind:class="{
'fi-active': {{ $alpineActive }},
}"
@endif
{{
$attributes
->merge([
'aria-selected' => $active,
'role' => 'tab',
])
->class([
'fi-tabs-item',
'fi-active' => (! $hasAlpineActiveClasses) && $active,
])
}}
>
@if ($icon && $iconPosition === IconPosition::Before)
{{ \Filament\Support\generate_icon_html($icon) }}
@endif
<span class="fi-tabs-item-label">
{{ $slot }}
</span>
@if ($icon && $iconPosition === IconPosition::After)
{{ \Filament\Support\generate_icon_html($icon) }}
@endif
@if (filled($badge))
@if ($badge instanceof \Illuminate\View\ComponentSlot)
{{ $badge }}
@else
<x-filament::badge
:color="$badgeColor"
:icon="$badgeIcon"
:icon-position="$badgeIconPosition"
size="sm"
:tooltip="$badgeTooltip"
>
{{ $badge }}
</x-filament::badge>
@endif
@elseif ($hasDeferredBadge)
<span
x-show="{{ $alpineDeferredBadgeLoading }}"
x-cloak
class="fi-tabs-item-badge-placeholder"
>
{{ \Filament\Support\generate_loading_indicator_html(size: \Filament\Support\Enums\IconSize::Small) }}
</span>
<template
x-if="
! {{ $alpineDeferredBadgeLoading }} &&
{{ $alpineDeferredBadgeData }}?.badge != null
"
>
<span
x-bind:class="
'fi-badge fi-size-sm ' +
({{ $alpineDeferredBadgeData }}?.badgeColorClasses ?? '')
"
x-bind:style="{{ $alpineDeferredBadgeData }}?.badgeColorStyles ?? ''"
x-init="
let tooltip = {{ $alpineDeferredBadgeData }}?.badgeTooltip
if (tooltip) {
window.tippy?.($el, {
content: tooltip,
theme: $store.theme,
})
}
"
>
<template
x-if="
{{ $alpineDeferredBadgeData }}?.badgeIconHtml &&
{{ $alpineDeferredBadgeData }}?.badgeIconPosition !== 'after'
"
>
<span
x-html="{{ $alpineDeferredBadgeData }}.badgeIconHtml"
></span>
</template>
<span class="fi-badge-label-ctn">
<span
class="fi-badge-label"
x-text="{{ $alpineDeferredBadgeData }}?.badge"
></span>
</span>
<template
x-if="
{{ $alpineDeferredBadgeData }}?.badgeIconHtml &&
{{ $alpineDeferredBadgeData }}?.badgeIconPosition === 'after'
"
>
<span
x-html="{{ $alpineDeferredBadgeData }}.badgeIconHtml"
></span>
</template>
</span>
</template>
@endif
</{{ $tag }}>
@@ -1,67 +0,0 @@
@php
use Filament\Support\View\Components\ToggleComponent;
use Illuminate\Support\Arr;
@endphp
@props([
'state',
'offColor' => 'gray',
'offIcon' => null,
'onColor' => 'primary',
'onIcon' => null,
])
<button
x-data="{ state: {{ $state }} }"
x-bind:aria-checked="state?.toString()"
x-on:click="state = ! state"
x-bind:class="
state ? @js(Arr::toCssClasses([
'fi-toggle-on',
...\Filament\Support\get_component_color_classes(ToggleComponent::class, $onColor),
])) : @js(Arr::toCssClasses([
'fi-toggle-off',
...\Filament\Support\get_component_color_classes(ToggleComponent::class, $offColor),
]))
"
@if ($state)
x-cloak
@endif
{{
$attributes
->merge([
'role' => 'switch',
'type' => 'button',
], escape: false)
->class(['fi-toggle'])
}}
>
<div>
<div aria-hidden="true">
{{ \Filament\Support\generate_icon_html($offIcon, size: \Filament\Support\Enums\IconSize::ExtraSmall) }}
</div>
<div aria-hidden="true">
{{ \Filament\Support\generate_icon_html($onIcon, size: \Filament\Support\Enums\IconSize::ExtraSmall) }}
</div>
</div>
</button>
@if ($state)
<div
x-cloak="inline-flex"
wire:ignore
@class([
'fi-toggle fi-toggle-on fi-hidden',
...\Filament\Support\get_component_color_classes(ToggleComponent::class, $onColor),
])
>
<div>
<div aria-hidden="true"></div>
<div aria-hidden="true">
{{ \Filament\Support\generate_icon_html($onIcon, size: \Filament\Support\Enums\IconSize::ExtraSmall) }}
</div>
</div>
</div>
@endif
-3
View File
@@ -1,3 +0,0 @@
<div {{ $getExtraAttributeBag() }}>
{{ $getState() }}
</div>
-3
View File
@@ -1,3 +0,0 @@
<div>
{{-- Use $getChildSchema() to render the child schema() of this component. --}}
</div>
-8
View File
@@ -1,8 +0,0 @@
<x-dynamic-component
:component="$getEntryWrapperView()"
:entry="$entry"
>
<div {{ $getExtraAttributeBag() }}>
{{ $getState() }}
</div>
</x-dynamic-component>
-11
View File
@@ -1,11 +0,0 @@
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
x-data="{ state: $wire.$entangle(@js($getStatePath())) }"
{{ $getExtraAttributeBag() }}
>
{{-- Interact with the `state` property in Alpine.js --}}
</div>
</x-dynamic-component>
-11
View File
@@ -1,11 +0,0 @@
<div>
<form wire:submit="{{ submitAction }}">
{{ $this->form }}
<button type="submit">
Submit
</button>
</form>
<x-filament-actions::modals />
</div>
-5
View File
@@ -1,5 +0,0 @@
<div>
{{ $this->content }}
<x-filament-actions::modals />
</div>
-3
View File
@@ -1,3 +0,0 @@
<div>
{{ $this->table }}
</div>
-3
View File
@@ -1,3 +0,0 @@
<x-filament-panels::page>
{{-- Page content --}}
</x-filament-panels::page>
@@ -1,3 +0,0 @@
<div>
{{-- Custom block preview --}}
</div>
@@ -1,3 +0,0 @@
<div>
{{-- Custom block --}}
</div>
-4
View File
@@ -1,4 +0,0 @@
@import '../../../../vendor/filament/filament/resources/css/theme.css';
@source '../../../../app/Filament/{{ classDirectory }}**/*';
@source '../../../../resources/views/filament/{{ viewDirectory }}**/*';
-5
View File
@@ -1,5 +0,0 @@
<x-filament-widgets::widget>
<x-filament::section>
{{-- Widget content --}}
</x-filament::section>
</x-filament-widgets::widget>
-24
View File
@@ -1,24 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;
#[Description('A description of what this app resource does.')]
#[AppMeta]
class {{ class }} extends AppResource
{
/**
* Handle the app resource request.
*/
public function handle(Request $request): Response
{
return Response::view('{{ view }}', [
'title' => $this->title(),
]);
}
}
-17
View File
@@ -1,17 +0,0 @@
<x-mcp::app :title="$title">
<x-slot:head>
<script type="module">
createMcpApp(async (app) => {
document.getElementById('run-btn').addEventListener('click', async () => {
const result = await app.callServerTool('tool-name', {});
document.getElementById('output').textContent = result.content[0]?.text ?? '';
});
});
</script>
</x-slot:head>
<div id="app">
<button id="run-btn">Run</button>
<p id="output"></p>
</div>
</x-mcp::app>
-35
View File
@@ -1,35 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;
#[Description('A description of what this prompt does.')]
class {{ class }} extends Prompt
{
/**
* Handle the prompt request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The content generated by the prompt.');
}
/**
* Get the prompt's arguments.
*
* @return array<int, Argument>
*/
public function arguments(): array
{
return [
//
];
}
}
-22
View File
@@ -1,22 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Resource;
#[Description('A description of what this resource contains.')]
class {{ class }} extends Resource
{
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The resource content.');
}
}
-26
View File
@@ -1,26 +0,0 @@
<?php
namespace {{ namespace }};
use Laravel\Mcp\Server;
use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
#[Name('{{ serverDisplayName }}')]
#[Version('0.0.1')]
#[Instructions('Instructions describing how to use the server and its features.')]
class {{ class }} extends Server
{
protected array $tools = [
//
];
protected array $resources = [
//
];
protected array $prompts = [
//
];
}
-35
View File
@@ -1,35 +0,0 @@
<?php
namespace {{ namespace }};
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('A description of what this tool does.')]
class {{ class }} extends Tool
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
//
return Response::text('The content generated by the tool.');
}
/**
* Get the tool's input schema.
*
* @return array<string, JsonSchema>
*/
public function schema(JsonSchema $schema): array
{
return [
//
];
}
}