feature: add user assignments component
- add title, placeholder and subtitle in avatar component - add role collection in users dto - make show empty text on table by default
This commit is contained in:
parent
9443ac181c
commit
c8e950bb79
@ -4,9 +4,19 @@
|
|||||||
|
|
||||||
namespace App\Data\Users;
|
namespace App\Data\Users;
|
||||||
|
|
||||||
use Spatie\LaravelData\Data;
|
use App\Data\Ui\ManageRoles\RoleData;
|
||||||
|
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||||
|
use Spatie\LaravelData\{Data, DataCollection};
|
||||||
|
|
||||||
final class UserData extends Data
|
final class UserData extends Data
|
||||||
{
|
{
|
||||||
public function __construct(public string $name, public string $email) {}
|
public function __construct(
|
||||||
|
public string $name,
|
||||||
|
public string $email,
|
||||||
|
public bool $active = false,
|
||||||
|
|
||||||
|
/** @var DataCollection<int, RoleData>|null */
|
||||||
|
#[DataCollectionOf(class: RoleData::class)]
|
||||||
|
public ?DataCollection $roles = null,
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
use Illuminate\Support\{Arr, ServiceProvider};
|
use Illuminate\Support\{Arr, ServiceProvider};
|
||||||
use Illuminate\Support\Facades\Blade;
|
use Illuminate\Support\Facades\Blade;
|
||||||
|
|
||||||
final class ScopeServiceProvider extends ServiceProvider
|
final class DirectiveServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Bootstrap services.
|
* Bootstrap services.
|
||||||
@ -15,9 +15,19 @@ final class ScopeServiceProvider extends ServiceProvider
|
|||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->registerScopeDirective();
|
$this->registerScopeDirective();
|
||||||
|
$this->registerInitialsDirective();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerScopeDirective(): void
|
private function registerInitialsDirective(): void
|
||||||
|
{
|
||||||
|
Blade::directive('initials', fn ($expression) => "<?php
|
||||||
|
echo implode('', array_map(function(\$w) {
|
||||||
|
return strtoupper(\$w[0] ?? '');
|
||||||
|
}, explode(' ', $expression)));
|
||||||
|
?>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function registerScopeDirective(): void
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* All credits from this blade directive goes to Konrad Kalemba.
|
* All credits from this blade directive goes to Konrad Kalemba.
|
||||||
@ -23,7 +23,7 @@ public function __construct(
|
|||||||
public bool $withPagination = false,
|
public bool $withPagination = false,
|
||||||
public array $rowDecoration = [],
|
public array $rowDecoration = [],
|
||||||
public array $cellDecoration = [],
|
public array $cellDecoration = [],
|
||||||
public bool $showEmptyText = false,
|
public bool $showEmptyText = true,
|
||||||
public string $emptyText = 'No records found.',
|
public string $emptyText = 'No records found.',
|
||||||
public string $containerClass = 'relative overflow-x-auto rounded-2xl -pb-2',
|
public string $containerClass = 'relative overflow-x-auto rounded-2xl -pb-2',
|
||||||
public bool $noHover = false,
|
public bool $noHover = false,
|
||||||
@ -173,6 +173,7 @@ public function render(): View|Closure|string
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
@isset($rows)
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach($rows as $k => $row)
|
@foreach($rows as $k => $row)
|
||||||
<tr
|
<tr
|
||||||
@ -230,7 +231,7 @@ public function render(): View|Closure|string
|
|||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@endisset
|
||||||
@isset ($footer)
|
@isset ($footer)
|
||||||
<tfoot @class([
|
<tfoot @class([
|
||||||
"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
|
"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
|
||||||
@ -241,7 +242,7 @@ public function render(): View|Closure|string
|
|||||||
@endisset
|
@endisset
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@if(count($rows) === 0)
|
@if(!$rows || count($rows) === 0)
|
||||||
@if($showEmptyText)
|
@if($showEmptyText)
|
||||||
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
||||||
{{ $emptyText }}
|
{{ $emptyText }}
|
||||||
|
|||||||
@ -5,5 +5,5 @@
|
|||||||
return [
|
return [
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
App\Providers\FortifyServiceProvider::class,
|
App\Providers\FortifyServiceProvider::class,
|
||||||
App\Providers\ScopeServiceProvider::class,
|
App\Providers\DirectiveServiceProvider::class,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -85,11 +85,7 @@ public function mount(): void
|
|||||||
@php /** @var RoleData $role */ @endphp
|
@php /** @var RoleData $role */ @endphp
|
||||||
<div class="flex -space-x-3">
|
<div class="flex -space-x-3">
|
||||||
@foreach($role->users as $user)
|
@foreach($role->users as $user)
|
||||||
<x-shared.avatar>
|
<x-shared.avatar class="text-blue-800" :placeholder="$user->name" />
|
||||||
<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
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
@endscope
|
@endscope
|
||||||
|
|||||||
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Data\Ui\TableHeader;
|
||||||
|
use App\Data\Users\UserData;
|
||||||
|
use App\Data\Ui\ManageRoles\RoleData;
|
||||||
|
use App\Data\Ui\ManageRoles\ServiceData;
|
||||||
|
use App\Enums\RolesStatus;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||||
|
use Spatie\LaravelData\DataCollection;
|
||||||
|
|
||||||
|
new class extends Component {
|
||||||
|
/** @var DataCollection<int, TableHeader> */
|
||||||
|
#[DataCollectionOf(TableHeader::class)]
|
||||||
|
public DataCollection $header;
|
||||||
|
|
||||||
|
/** @var DataCollection<int, UserData>|null */
|
||||||
|
#[DataCollectionOf(UserData::class)]
|
||||||
|
public ?DataCollection $users = null;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->header = TableHeader::collect([
|
||||||
|
new TableHeader('name', 'User'),
|
||||||
|
new TableHeader('role.name', 'Role'),
|
||||||
|
new TableHeader('role.service', 'Service Access'),
|
||||||
|
new TableHeader('status', 'Status'),
|
||||||
|
], DataCollection::class);
|
||||||
|
|
||||||
|
$this->users = UserData::collect([
|
||||||
|
new UserData(
|
||||||
|
name: 'Priya Rao',
|
||||||
|
email: 'priya@example.com',
|
||||||
|
active: true,
|
||||||
|
roles: RoleData::collect([
|
||||||
|
new RoleData(
|
||||||
|
name: 'Super Admin',
|
||||||
|
users: UserData::collect([], DataCollection::class),
|
||||||
|
services: ServiceData::collect([
|
||||||
|
new ServiceData(name: 'AWS Core'),
|
||||||
|
new ServiceData(name: 'GitHub Enterprise'),
|
||||||
|
], DataCollection::class),
|
||||||
|
status: RolesStatus::Active,
|
||||||
|
),
|
||||||
|
new RoleData(
|
||||||
|
name: 'HR Manager',
|
||||||
|
users: UserData::collect([], DataCollection::class),
|
||||||
|
services: ServiceData::collect([
|
||||||
|
new ServiceData(name: 'Keka'),
|
||||||
|
new ServiceData(name: 'Outlook'),
|
||||||
|
], DataCollection::class),
|
||||||
|
status: RolesStatus::Active,
|
||||||
|
)
|
||||||
|
], DataCollection::class)
|
||||||
|
),
|
||||||
|
|
||||||
|
new UserData(
|
||||||
|
name: 'Kushal Saha',
|
||||||
|
email: 'kushal@example.com',
|
||||||
|
active: true,
|
||||||
|
roles: RoleData::collect([
|
||||||
|
new RoleData(
|
||||||
|
name: 'Backend Developer',
|
||||||
|
users: UserData::collect([], DataCollection::class),
|
||||||
|
services: ServiceData::collect([
|
||||||
|
new ServiceData(name: 'Forge'),
|
||||||
|
], DataCollection::class),
|
||||||
|
status: RolesStatus::Active,
|
||||||
|
)
|
||||||
|
], DataCollection::class)
|
||||||
|
),
|
||||||
|
|
||||||
|
new UserData(
|
||||||
|
name: 'Alex Johnson',
|
||||||
|
email: 'alex@example.com',
|
||||||
|
active: false,
|
||||||
|
roles: RoleData::collect([
|
||||||
|
new RoleData(
|
||||||
|
name: 'Guest Viewer',
|
||||||
|
users: UserData::collect([], DataCollection::class),
|
||||||
|
services: ServiceData::collect([
|
||||||
|
new ServiceData(name: 'Analytics Dashboard'),
|
||||||
|
], DataCollection::class),
|
||||||
|
status: RolesStatus::Active,
|
||||||
|
)
|
||||||
|
], DataCollection::class)
|
||||||
|
)
|
||||||
|
], DataCollection::class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<x-shared.card title="User Assignments" icon="lucide-users" class="p-0">
|
||||||
|
<x-slot:actions>
|
||||||
|
<x-shared.button icon="lucide-plus">
|
||||||
|
{{ __('Assign user') }}
|
||||||
|
</x-shared.button>
|
||||||
|
</x-slot:actions>
|
||||||
|
|
||||||
|
<x-table :headers="$header->toArray()" :rows="$users">
|
||||||
|
@scope('cell_name', $row)
|
||||||
|
<x-shared.avatar :placeholder="$row->name" :title="$row->name" :subtitle="$row->email" />
|
||||||
|
@endscope
|
||||||
|
@scope('cell_role.name', $row)
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
@foreach ($row->roles as $role)
|
||||||
|
<x-shared.badge>
|
||||||
|
{{$role->name}}
|
||||||
|
</x-shared.badge>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endscope
|
||||||
|
|
||||||
|
@scope('cell_role.service', $row)
|
||||||
|
<div class="flex space-x-2 text-sm">
|
||||||
|
@foreach ($row->roles as $role)
|
||||||
|
@foreach($role->services as $service)
|
||||||
|
{{$service->name}},
|
||||||
|
@endforeach
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endscope
|
||||||
|
|
||||||
|
@scope('cell_status', $row)
|
||||||
|
@if($row->active)
|
||||||
|
<x-shared.badge class="bg-lime-200 text-lime-800 font-bold">Active</x-shared.badge>
|
||||||
|
@else
|
||||||
|
<x-shared.badge class="bg-orange-200 text-orange-800 font-bold">Not Active</x-shared.badge>
|
||||||
|
@endif
|
||||||
|
@endscope
|
||||||
|
|
||||||
|
@scope('actions', $row)
|
||||||
|
<x-shared.button icon="lucide-square-pen"/>
|
||||||
|
@endscope
|
||||||
|
</x-table>
|
||||||
|
</x-shared.card>
|
||||||
|
</div>
|
||||||
@ -1,3 +1,18 @@
|
|||||||
<span {{$attributes->twMerge(['class' => 'bg-blue-300 w-8 h-8 border border-white rounded-full flex items-center justify-center'])}}>
|
@props(['placeholder', 'title', 'subtitle'])
|
||||||
{{$slot}}
|
<div class="flex space-x-2 items-center">
|
||||||
|
<span {{$attributes->twMerge(['class' => 'bg-blue-300 text-blue-800 w-8 h-8 border border-white rounded-full flex items-center justify-center'])}}>
|
||||||
|
@isset($placeholder)
|
||||||
|
@initials($placeholder)
|
||||||
|
@endisset
|
||||||
</span>
|
</span>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
@isset($title)
|
||||||
|
<p class="font-semibold">
|
||||||
|
{{$title}}
|
||||||
|
</p>
|
||||||
|
@endisset
|
||||||
|
@isset($subtitle)
|
||||||
|
{{$subtitle}}
|
||||||
|
@endisset
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@ -18,5 +18,6 @@ class extends Component {
|
|||||||
<livewire:dashboard.connected-services />
|
<livewire:dashboard.connected-services />
|
||||||
<livewire:dashboard.roles.manager />
|
<livewire:dashboard.roles.manager />
|
||||||
<livewire:dashboard.roles.hr-permission />
|
<livewire:dashboard.roles.hr-permission />
|
||||||
|
<livewire:dashboard.users.assignments />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user