refactor: simplify data objects and improve role management components

- Removed redundant custom collections and `Wireable` trait from DTOs.
- Replaced custom collection properties with `DataCollection`.
- Updated role management views to use improved components, including avatars and toggle.
- Refined Blade directives for scoped slots.
- Minor cleanup in configuration and dependencies.
This commit is contained in:
= 2026-05-11 05:51:25 +00:00
parent 105123bb74
commit ede0064b54
17 changed files with 153 additions and 253 deletions

View File

@ -4,19 +4,24 @@
namespace App\Data\Ui\ManageRoles; namespace App\Data\Ui\ManageRoles;
use App\Data\Users\UsersData; use App\Data\Users\UserData;
use App\Enums\RolesStatus; use App\Enums\RolesStatus;
use Livewire\Wireable; use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Concerns\WireableData;
use Spatie\LaravelData\Data; use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
final class RoleData extends Data implements Wireable final class RoleData extends Data
{ {
use WireableData;
public function __construct( public function __construct(
public string $name, public string $name,
public UsersData $users,
public ServiceCollection $services, /** @var DataCollection<int, UserData> */
#[DataCollectionOf(class: UserData::class),]
public DataCollection $users,
/** @var DataCollection<int, ServiceData> */
#[DataCollectionOf(class: ServiceData::class),]
public DataCollection $services,
public RolesStatus $status, public RolesStatus $status,
) {} ) {}
} }

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Data\Ui\ManageRoles;
use Illuminate\Support\Collection;
/**
* @extends Collection<int, RoleData>
*/
final class RolesCollection extends Collection {}

View File

@ -1,17 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Data\Ui\ManageRoles;
use Livewire\Wireable;
use Spatie\LaravelData\Concerns\WireableData;
use Spatie\LaravelData\Data;
final class RolesData extends Data implements Wireable
{
use WireableData;
public function __construct(
public RolesCollection $roles,
) {}
}

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Data\Ui\ManageRoles;
use Illuminate\Support\Collection;
/**
* @extends Collection<int, ServiceData>
*/
final class ServiceCollection extends Collection {}

View File

@ -4,15 +4,9 @@
namespace App\Data\Ui\ManageRoles; namespace App\Data\Ui\ManageRoles;
use Livewire\Wireable;
use Spatie\LaravelData\Concerns\WireableData;
use Spatie\LaravelData\Data; use Spatie\LaravelData\Data;
final class ServiceData extends Data implements Wireable final class ServiceData extends Data
{ {
use WireableData; public function __construct(public string $name) {}
public function __construct(
public string $name,
) {}
} }

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Data\Users;
use Illuminate\Support\Collection;
/**
* @extends Collection<int, UserData>
*/
final class UserCollection extends Collection {}

View File

@ -8,9 +8,5 @@
final class UserData extends Data final class UserData extends Data
{ {
public function __construct( public function __construct(public string $name, public string $email) {}
public string $name,
public string $email,
) {}
} }

View File

@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Data\Users;
use Spatie\LaravelData\Data;
final class UsersData extends Data
{
public function __construct(
public UserCollection $users,
) {}
}

View File

@ -25,19 +25,18 @@ public function registerScopeDirective(): void
* *
* https://github.com/konradkalemba/blade-components-scoped-slots * https://github.com/konradkalemba/blade-components-scoped-slots
*/ */
Blade::directive('scope', function ($expression) { Blade::directive("scope", function ($expression) {
// Split the expression by `top-level` commas (not in parentheses) // Split the expression by `top-level` commas (not in parentheses)
$directiveArguments = preg_split("/,(?![^\(\(]*[\)\)])/", $expression); $directiveArguments = preg_split("/,(?![^\(\(]*[\)\)])/", $expression);
$directiveArguments = array_map('trim', $directiveArguments); $directiveArguments = array_map("trim", $directiveArguments);
[$name, $functionArguments] = $directiveArguments; [$name, $functionArguments] = $directiveArguments;
// Build function "uses" to inject extra external variables // Build function "uses" to inject extra external variables
$uses = Arr::except(array_flip($directiveArguments), [$name, $functionArguments]); $uses = Arr::except(array_flip($directiveArguments), [$name, $functionArguments]);
$uses = array_flip($uses); $uses = array_flip($uses);
$uses[] = '$__env'; $uses[] = '$__env';
$uses[] = '$__bladeCompiler'; $uses[] = '$__bladeCompiler';
$uses = implode(',', $uses); $uses = implode(",", $uses);
/** /**
* Slot names can`t contains dot , eg: `user.city`. * Slot names can`t contains dot , eg: `user.city`.
@ -45,11 +44,11 @@ public function registerScopeDirective(): void
* *
* Later, on component it will be replaced back. * Later, on component it will be replaced back.
*/ */
$name = str_replace('.', '___', $name); $name = str_replace(".", "___", $name);
return "<?php \$__bladeCompiler = \$__bladeCompiler ?? null; \$__env->slot({$name}, function({$functionArguments}) use ({$uses}) { \$loop = !empty(\$__env->getLoopStack()) ? (object) \$__env->getLoopStack()[0] : null; ?>"; return "<?php \$__bladeCompiler = \$__bladeCompiler ?? null; \$__env->slot({$name}, function({$functionArguments}) use ({$uses}) { \$loop = !empty(\$__env->getLoopStack()) ? (object) \$__env->getLoopStack()[0] : null; ?>";
}); });
Blade::directive('endscope', fn() => '<?php }); ?>'); Blade::directive("endscope", fn() => "<?php }); ?>");
} }
} }

View File

@ -171,7 +171,7 @@ public function render(): View|Closure|string
@if($actions) @if($actions)
<th scope="col" class="px-6 py-3 w-1 whitespace-nowrap"> <th scope="col" class="px-6 py-3 w-1 whitespace-nowrap">
<span class="sr-only">Actions</span> <span>Actions</span>
</th> </th>
@endif @endif
</tr> </tr>
@ -182,7 +182,8 @@ public function render(): View|Closure|string
<tr <tr
wire:key="{{ $uuid }}-{{ $k }}" wire:key="{{ $uuid }}-{{ $k }}"
@class([ @class([
'bg-white border-b', 'bg-white',
'border-b' => $k < $rows->count() - 1,
$rowClasses($row), $rowClasses($row),
'even:bg-gray-50 ' => $striped, 'even:bg-gray-50 ' => $striped,
'hover:bg-gray-50' => !$noHover, 'hover:bg-gray-50' => !$noHover,

View File

@ -54,7 +54,7 @@
], ],
"dev": [ "dev": [
"Composer\\Config::disableProcessTimeout", "Composer\\Config::disableProcessTimeout",
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
], ],
"lint": [ "lint": [
"pint --parallel" "pint --parallel"

View File

@ -26,36 +26,36 @@
* is an array, it will try to convert from the first format that works, * is an array, it will try to convert from the first format that works,
* and will serialize dates using the first format from the array. * and will serialize dates using the first format from the array.
*/ */
'date_format' => DATE_ATOM, "date_format" => DATE_ATOM,
/* /*
* When transforming or casting dates, the following timezone will be used to * When transforming or casting dates, the following timezone will be used to
* convert the date to the correct timezone. If set to null no timezone will * convert the date to the correct timezone. If set to null no timezone will
* be passed. * be passed.
*/ */
'date_timezone' => null, "date_timezone" => null,
/* /*
* It is possible to enable certain features of the package, these would otherwise * It is possible to enable certain features of the package, these would otherwise
* be breaking changes, and thus they are disabled by default. In the next major * be breaking changes, and thus they are disabled by default. In the next major
* version of the package, these features will be enabled by default. * version of the package, these features will be enabled by default.
*/ */
'features' => [ "features" => [
'cast_and_transform_iterables' => false, "cast_and_transform_iterables" => false,
/* /*
* When trying to set a computed property value, the package will throw an exception. * When trying to set a computed property value, the package will throw an exception.
* You can disable this behaviour by setting this option to true, which will then just * You can disable this behaviour by setting this option to true, which will then just
* ignore the value being passed into the computed property and recalculate it. * ignore the value being passed into the computed property and recalculate it.
*/ */
'ignore_exception_when_trying_to_set_computed_property_value' => false, "ignore_exception_when_trying_to_set_computed_property_value" => false,
], ],
/* /*
* Global transformers will take complex types and transform them into simple * Global transformers will take complex types and transform them into simple
* types. * types.
*/ */
'transformers' => [ "transformers" => [
DateTimeInterface::class => DateTimeInterfaceTransformer::class, DateTimeInterface::class => DateTimeInterfaceTransformer::class,
Arrayable::class => ArrayableTransformer::class, Arrayable::class => ArrayableTransformer::class,
BackedEnum::class => EnumTransformer::class, BackedEnum::class => EnumTransformer::class,
@ -65,7 +65,7 @@
* Global casts will cast values into complex types when creating a data * Global casts will cast values into complex types when creating a data
* object from simple types. * object from simple types.
*/ */
'casts' => [ "casts" => [
DateTimeInterface::class => DateTimeInterfaceCast::class, DateTimeInterface::class => DateTimeInterfaceCast::class,
BackedEnum::class => EnumCast::class, BackedEnum::class => EnumCast::class,
// Enumerable::class => Spatie\LaravelData\Casts\EnumerableCast::class, // Enumerable::class => Spatie\LaravelData\Casts\EnumerableCast::class,
@ -76,7 +76,7 @@
* validation rules to properties of a data object based upon * validation rules to properties of a data object based upon
* the type of the property. * the type of the property.
*/ */
'rule_inferrers' => [ "rule_inferrers" => [
SometimesRuleInferrer::class, SometimesRuleInferrer::class,
NullableRuleInferrer::class, NullableRuleInferrer::class,
RequiredRuleInferrer::class, RequiredRuleInferrer::class,
@ -89,7 +89,7 @@
* it cannot normalize the payload. The normalizers below are used for * it cannot normalize the payload. The normalizers below are used for
* every data object, unless overridden in a specific data object class. * every data object, unless overridden in a specific data object class.
*/ */
'normalizers' => [ "normalizers" => [
ModelNormalizer::class, ModelNormalizer::class,
// Spatie\LaravelData\Normalizers\FormRequestNormalizer::class, // Spatie\LaravelData\Normalizers\FormRequestNormalizer::class,
ArrayableNormalizer::class, ArrayableNormalizer::class,
@ -103,7 +103,7 @@
* this key can be set globally here for all data objects. You can pass in * this key can be set globally here for all data objects. You can pass in
* `null` if you want to disable wrapping. * `null` if you want to disable wrapping.
*/ */
'wrap' => null, "wrap" => null,
/* /*
* Adds a specific caster to the Symphony VarDumper component which hides * Adds a specific caster to the Symphony VarDumper component which hides
@ -111,7 +111,7 @@
* by `dump` or `dd`. Can be 'enabled', 'disabled' or 'development' * by `dump` or `dd`. Can be 'enabled', 'disabled' or 'development'
* which will only enable the caster locally. * which will only enable the caster locally.
*/ */
'var_dumper_caster_mode' => 'development', "var_dumper_caster_mode" => "development",
/* /*
* It is possible to skip the PHP reflection analysis of data objects * It is possible to skip the PHP reflection analysis of data objects
@ -124,18 +124,18 @@
* in seconds if you want the cache to clear after a certain * in seconds if you want the cache to clear after a certain
* timeframe. * timeframe.
*/ */
'structure_caching' => [ "structure_caching" => [
'enabled' => true, "enabled" => true,
'directories' => [app_path('Data')], "directories" => [app_path("Data")],
'cache' => [ "cache" => [
'store' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')), "store" => env("CACHE_STORE", env("CACHE_DRIVER", "file")),
'prefix' => 'laravel-data', "prefix" => "laravel-data",
'duration' => null, "duration" => null,
], ],
'reflection_discovery' => [ "reflection_discovery" => [
'enabled' => true, "enabled" => true,
'base_path' => base_path(), "base_path" => base_path(),
'root_namespace' => null, "root_namespace" => null,
], ],
], ],
@ -144,23 +144,23 @@
* method. By default, only when a request is passed the data is being validated. This * method. By default, only when a request is passed the data is being validated. This
* behaviour can be changed to always validate or to completely disable validation. * behaviour can be changed to always validate or to completely disable validation.
*/ */
'validation_strategy' => ValidationStrategy::OnlyRequests->value, "validation_strategy" => ValidationStrategy::OnlyRequests->value,
/* /*
* A data object can map the names of its properties when transforming (output) or when * A data object can map the names of its properties when transforming (output) or when
* creating (input). By default, the package will not map any names. You can set a * creating (input). By default, the package will not map any names. You can set a
* global strategy here, or override it on a specific data object. * global strategy here, or override it on a specific data object.
*/ */
'name_mapping_strategy' => [ "name_mapping_strategy" => [
'input' => null, "input" => null,
'output' => null, "output" => null,
], ],
/* /*
* When using an invalid include, exclude, only or except partial, the package will * When using an invalid include, exclude, only or except partial, the package will
* throw an exception. You can disable this behaviour by setting this option to true. * throw an exception. You can disable this behaviour by setting this option to true.
*/ */
'ignore_invalid_partials' => false, "ignore_invalid_partials" => false,
/* /*
* When transforming a nested chain of data objects, the package can end up in an infinite * When transforming a nested chain of data objects, the package can end up in an infinite
@ -168,41 +168,39 @@
* set as a safety measure to prevent this from happening. When set to null, the * set as a safety measure to prevent this from happening. When set to null, the
* package will not enforce a maximum depth. * package will not enforce a maximum depth.
*/ */
'max_transformation_depth' => null, "max_transformation_depth" => null,
/* /*
* When the maximum transformation depth is reached, the package will throw an exception. * When the maximum transformation depth is reached, the package will throw an exception.
* You can disable this behaviour by setting this option to true which will return an * You can disable this behaviour by setting this option to true which will return an
* empty array. * empty array.
*/ */
'throw_when_max_transformation_depth_reached' => true, "throw_when_max_transformation_depth_reached" => true,
/* /*
* When using the `make:data` command, the package will use these settings to generate * When using the `make:data` command, the package will use these settings to generate
* the data classes. You can override these settings by passing options to the command. * the data classes. You can override these settings by passing options to the command.
*/ */
'commands' => [ "commands" => [
/* /*
* Provides default configuration for the `make:data` command. These settings can be overridden with options * Provides default configuration for the `make:data` command. These settings can be overridden with options
* passed directly to the `make:data` command for generating single Data classes, or if not set they will * passed directly to the `make:data` command for generating single Data classes, or if not set they will
* automatically fall back to these defaults. See `php artisan make:data --help` for more information * automatically fall back to these defaults. See `php artisan make:data --help` for more information
*/ */
'make' => [ "make" => [
/* /*
* The default namespace for generated Data classes. This exists under the application's root namespace, * The default namespace for generated Data classes. This exists under the application's root namespace,
* so the default 'Data` will end up as '\App\Data', and generated Data classes will be placed in the * so the default 'Data` will end up as '\App\Data', and generated Data classes will be placed in the
* app/Data/ folder. Data classes can live anywhere, but this is where `make:data` will put them. * app/Data/ folder. Data classes can live anywhere, but this is where `make:data` will put them.
*/ */
'namespace' => 'Data', "namespace" => "Data",
/* /*
* This suffix will be appended to all data classes generated by make:data, so that they are less likely * This suffix will be appended to all data classes generated by make:data, so that they are less likely
* to conflict with other related classes, controllers or models with a similar name without resorting * to conflict with other related classes, controllers or models with a similar name without resorting
* to adding an alias for the Data object. Set to a blank string (not null) to disable. * to adding an alias for the Data object. Set to a blank string (not null) to disable.
*/ */
'suffix' => 'Data', "suffix" => "Data",
], ],
], ],
@ -211,7 +209,7 @@
* these synths will automatically handle the data objects and their * these synths will automatically handle the data objects and their
* properties when used in a Livewire component. * properties when used in a Livewire component.
*/ */
'livewire' => [ "livewire" => [
'enable_synths' => false, "enable_synths" => true,
], ],
]; ];

45
package-lock.json generated
View File

@ -208,9 +208,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -227,9 +224,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -246,9 +240,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -265,9 +256,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -284,9 +272,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -303,9 +288,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -394,9 +376,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -528,9 +507,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -547,9 +523,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -566,9 +539,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -585,9 +555,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1182,9 +1149,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -1205,9 +1169,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -1228,9 +1189,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -1251,9 +1209,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [

View File

@ -8,7 +8,10 @@
use App\Data\Users\UserCollection; use App\Data\Users\UserCollection;
use App\Data\Users\UserData; use App\Data\Users\UserData;
use App\Data\Users\UsersData; use App\Data\Users\UsersData;
use App\Enums\RolesStatus;
use Livewire\Component; use Livewire\Component;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\DataCollection;
new class extends Component { new class extends Component {
/** /**
@ -19,79 +22,61 @@
* }> * }>
*/ */
public array $header; public array $header;
public RolesData $rolesData;
#[DataCollectionOf(class: RoleData::class)]
public DataCollection $roles;
public function mount(): void public function mount(): void
{ {
$this->header = [ $this->header = [
[ [
'key' => 'name', "key" => "name",
'label' => 'Role Name', "label" => "Role Name",
], ],
[ [
'key' => 'users', "key" => "users",
'label' => 'User Assigned', "label" => "User Assigned",
], ],
[ [
'key' => 'services', "key" => "services",
'label' => 'Services', "label" => "Services",
], ],
[ [
'key' => 'status', "key" => "status",
'label' => 'Status', "label" => "Status",
] ],
]; ];
$this->roles = RoleData::collect(
[
new RoleData(
name: "Super Admin",
$this->rolesData = new RolesData( users: UserData::collect(
roles: new RolesCollection([
new RoleData(
name: 'Super Admin',
users: new UsersData(
users: new UserCollection([
new UserData(
name: 'Kushal Saha',
email: 'kushal@example.com'
),
new UserData(
name: 'Test Example',
email: 'test@example.com'
)
]
)
),
services: new ServiceCollection(
[ [
new ServiceData( new UserData(
name: 'Teams' name: "Kushal Saha",
) email: "kushal@example.com",
]
),
status: \App\Enums\RolesStatus::Active
),
new RoleData(
name: 'Super Admin',
users: new UsersData(
users: new UserCollection([
new UserData(
name: 'Kushal Saha',
email: 'kushal@example.com'
)
]
)
),
services: new ServiceCollection(
[
new ServiceData(
name: 'Teams'
), ),
new ServiceData( new UserData(
name: 'Outlook' name: "Test Example",
) email: "test@example.com",
] ),
],
DataCollection::class,
), ),
status: \App\Enums\RolesStatus::Active
services: ServiceData::collect(
[
new ServiceData(name: "Teams"),
new ServiceData(name: "Outlook"),
],
DataCollection::class,
),
status: RolesStatus::Active,
), ),
]) ],
DataCollection::class,
); );
} }
}; };
@ -101,34 +86,42 @@ public function mount(): void
<div> <div>
<x-shared.card title="Roles" icon="lucide-shield-ellipsis" class="p-0"> <x-shared.card title="Roles" icon="lucide-shield-ellipsis" class="p-0">
<x-slot:actions> <x-slot:actions>
<x-shared.button> <x-shared.button icon="lucide-plus">
<div class="flex items-center gap-x-2"> {{ __('Add role') }}
<x-lucide-plus class="w-5 h-5"/>
{{ __('Add role') }}
</div>
</x-shared.button> </x-shared.button>
</x-slot:actions> </x-slot:actions>
<x-table :headers="$header" :rows="$rolesData->roles">
@scope('cell_users', $row) <x-table :headers="$header" :rows="$roles">
@foreach($row->users->users as $user) @scope('cell_users', $role)
<span @php /** @var RoleData $role */ @endphp
class="inline-flex items-center justify-center w-7 h-7 rounded-full bg-gray-200 text-gray-800 font-bold text-[10px] tracking-tight"> <div class="flex -space-x-3">
{{implode('', array_map(fn($w) => strtoupper($w[0]), explode(' ', $user->name)))}} @foreach($role->users as $user)
</span> <x-shared.avatar>
@endforeach <span class="text-xs font-bold text-center text-blue-800">
{{implode('', array_map(fn($w) => strtoupper($w[0]), explode(' ', $user->name)))}}
</span>
</x-shared.avatar>
@endforeach
</div>
@endscope @endscope
@scope('cell_status', $row) @scope('cell_status', $role)
@php /** @var RoleData $role */ @endphp
<x-shared.badge class="bg-lime-200 text-lime-800 font-bold"> <x-shared.badge class="bg-lime-200 text-lime-800 font-bold">
{{ ucfirst($row->status->name) }} {{ ucfirst($role->status->name) }}
</x-shared.badge> </x-shared.badge>
@endscope @endscope
@scope('cell_services', $row) @scope('cell_services', $role)
@foreach($row->services as $service) @php /** @var RoleData $role */ @endphp
@foreach($role->services as $service)
<x-shared.badge class="bg-indigo-200 text-indigo-800 font-bold"> <x-shared.badge class="bg-indigo-200 text-indigo-800 font-bold">
{{$service->name}} {{$service->name}}
</x-shared.badge> </x-shared.badge>
@endforeach @endforeach
@endscope @endscope
@scope('actions', $role)
<x-shared.button icon="lucide-square-pen"/>
@endscope
</x-table> </x-table>
</x-shared.card> </x-shared.card>
</div> </div>

View File

@ -0,0 +1,3 @@
<span {{$attributes->twMerge(['class' => 'bg-blue-300 w-8 h-8 border border-white rounded-full flex items-center justify-center'])}}>
{{$slot}}
</span>

View File

@ -1,3 +1,11 @@
<button class="rounded-lg border border-gray-300 px-6 py-2 text-gray-700"> @props(['icon' => null])
{{$slot}} <button {{$attributes->twMerge(['class' => 'rounded-lg border border-gray-300 px-4 py-2 text-gray-700'])}}>
<div class="gap-2 flex items-center">
@if($icon)
<span class="block">
@svg($icon, 'w-4 h-4')
</span>
@endif
{{$slot}}
</div>
</button> </button>

View File

@ -0,0 +1,15 @@
@props(['disabled' => false, 'label' => null])
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
{{ $disabled ? 'disabled' : '' }}
{!! $attributes->merge(['class' => 'sr-only peer']) !!}
>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
@if($label)
<span class="ml-3 text-sm font-medium text-gray-900">{{ $label }}</span>
@endif
</label>