Skip to main content

Infrastructure

Forge Permission Matrix code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/forge-permission-matrix.blade.php

@props([
    'members' => [],
    'permissions' => [],
])

@php
    /*
    | Team permission matrix for Forge-style organization and team settings.
    | Members accept name, email, role and permissions keys.
    |
    | Usage:
    |   <x-ui.forge-permission-matrix :members="$members" :permissions="$permissions" />
    */
    $permissions = collect($permissions)->map(function ($permission, $key): array {
        if (is_array($permission)) {
            return [
                'key' => (string) ($permission['key'] ?? $key),
                'label' => $permission['label'] ?? $permission['key'] ?? $key,
            ];
        }

        return [
            'key' => (string) $key,
            'label' => (string) $permission,
        ];
    })->values();

    $members = collect($members)->map(function (array $member): array {
        return [
            'name' => $member['name'] ?? 'Team member',
            'email' => $member['email'] ?? null,
            'role' => $member['role'] ?? null,
            'permissions' => collect($member['permissions'] ?? [])->map(fn ($permission): string => (string) $permission)->all(),
        ];
    })->values();
@endphp

<section {{ $attributes->merge(['class' => 'min-w-0 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900']) }}>
    <div class="border-b border-gray-200 px-4 py-3 dark:border-gray-800">
        <h3 class="text-sm font-semibold text-gray-900 dark:text-white">Permission matrix</h3>
        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Compare member access across Forge resources.</p>
    </div>

    <div class="overflow-x-auto">
        <table class="w-full min-w-[42rem] table-fixed divide-y divide-gray-200 text-sm dark:divide-gray-800">
            <thead class="bg-gray-50 text-xs text-gray-500 dark:bg-gray-950/40 dark:text-gray-400">
                <tr>
                    <th scope="col" class="sticky left-0 z-10 w-52 bg-gray-50 px-4 py-3 text-left font-medium dark:bg-gray-950">Member</th>
                    @foreach ($permissions as $permission)
                        <th scope="col" class="w-32 px-4 py-3 text-center font-medium whitespace-nowrap">{{ $permission['label'] }}</th>
                    @endforeach
                </tr>
            </thead>
            <tbody class="divide-y divide-gray-200 dark:divide-gray-800">
                @forelse ($members as $member)
                    <tr>
                        <td class="sticky left-0 z-10 w-52 max-w-52 bg-white px-4 py-3 dark:bg-gray-900">
                            <p class="truncate font-medium text-gray-900 dark:text-white">{{ $member['name'] }}</p>
                            <p class="mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400">{{ $member['email'] ?? $member['role'] }}</p>
                        </td>
                        @foreach ($permissions as $permission)
                            <td class="w-32 px-4 py-3 text-center">
                                @if (in_array($permission['key'], $member['permissions'], true))
                                    <x-ui.icon name="check-circle" weight="fill" size="lg" class="mx-auto text-green-500" />
                                    <span class="sr-only">Allowed</span>
                                @else
                                    <x-ui.icon name="minus-circle" size="lg" class="mx-auto text-gray-300 dark:text-gray-600" />
                                    <span class="sr-only">Not allowed</span>
                                @endif
                            </td>
                        @endforeach
                    </tr>
                @empty
                    <tr>
                        <td colspan="{{ $permissions->count() + 1 }}" class="px-4 py-10 text-center text-sm text-gray-500 dark:text-gray-400">No members found.</td>
                    </tr>
                @endforelse
            </tbody>
        </table>
    </div>
</section>