style(107): enterprise redesign of workspace chooser UI

- Replace flat div cards with semantic <button> elements
- Add workspace icon (building-office-2) per card
- Add role-specific icons (shield, cog, wrench, eye)
- Use primary color scheme for 'Last used' highlight (was amber)
- Add loading spinner overlay on workspace selection
- Add hover arrow indicator + shadow elevation on hover
- Add focus ring for keyboard accessibility
- Better empty state with icon + actionable copy
- Proper dark mode support throughout
- Responsive grid (1→2→3 columns)
- Manage workspaces link with cog icon
This commit is contained in:
Ahmed Darrazi 2026-02-22 17:27:54 +01:00
parent 051db1842d
commit 9b02cc4041

View File

@ -1,99 +1,155 @@
<x-filament-panels::page>
<x-filament::section>
<div class="flex flex-col gap-4">
<div class="text-sm text-gray-600 dark:text-gray-300">
Select a workspace to continue.
</div>
@php
$workspaces = $this->getWorkspaces();
$workspaceRoles = $this->workspaceRoles;
@php
$workspaces = $this->getWorkspaces();
$workspaceRoles = $this->workspaceRoles;
$user = auth()->user();
$recommendedWorkspaceId = $user instanceof \App\Models\User ? (int) ($user->last_workspace_id ?? 0) : 0;
$user = auth()->user();
$recommendedWorkspaceId = $user instanceof \App\Models\User ? (int) ($user->last_workspace_id ?? 0) : 0;
if ($recommendedWorkspaceId > 0) {
[$recommended, $other] = $workspaces->partition(fn ($workspace) => (int) $workspace->id === $recommendedWorkspaceId);
$workspaces = $recommended->concat($other)->values();
}
if ($recommendedWorkspaceId > 0) {
[$recommended, $other] = $workspaces->partition(fn ($workspace) => (int) $workspace->id === $recommendedWorkspaceId);
$workspaces = $recommended->concat($other)->values();
$roleColorMap = [
'owner' => 'primary',
'manager' => 'info',
'operator' => 'gray',
'readonly' => 'gray',
];
$roleIconMap = [
'owner' => 'heroicon-m-shield-check',
'manager' => 'heroicon-m-cog-6-tooth',
'operator' => 'heroicon-m-wrench-screwdriver',
'readonly' => 'heroicon-m-eye',
];
$canManageWorkspaces = false;
if ($user instanceof \App\Models\User && $workspaces->count() > 0) {
foreach ($workspaces as $ws) {
if (($workspaceRoles[(int) $ws->id] ?? null) === 'owner') {
$canManageWorkspaces = true;
break;
}
}
}
@endphp
$roleColorMap = [
'owner' => 'primary',
'manager' => 'info',
'operator' => 'gray',
'readonly' => 'gray',
];
@endphp
@if ($workspaces->isEmpty())
<div class="rounded-md border border-gray-200 bg-gray-50 p-4 text-center text-sm text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-200">
You don't have access to any workspace yet.
@if ($workspaces->isEmpty())
<div class="mx-auto max-w-md">
<div class="rounded-xl border border-gray-200 bg-white p-8 text-center shadow-sm dark:border-white/10 dark:bg-white/5">
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 dark:bg-white/10">
<x-filament::icon
icon="heroicon-o-building-office-2"
class="h-6 w-6 text-gray-400 dark:text-gray-500"
/>
</div>
@else
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
@foreach ($workspaces as $workspace)
@php
$isRecommended = $recommendedWorkspaceId > 0 && (int) $workspace->id === $recommendedWorkspaceId;
$role = $workspaceRoles[(int) $workspace->id] ?? null;
$tenantCount = (int) ($workspace->tenants_count ?? 0);
@endphp
<h3 class="text-base font-semibold text-gray-900 dark:text-white">No workspaces available</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
You don't have access to any workspace yet. Contact your administrator to get started.
</p>
</div>
</div>
@else
<div class="mx-auto max-w-3xl">
<p class="mb-6 text-center text-sm text-gray-500 dark:text-gray-400">
Select a workspace to continue.
</p>
<div
wire:key="workspace-{{ $workspace->id }}"
wire:click="selectWorkspace({{ (int) $workspace->id }})"
class="cursor-pointer rounded-lg border p-4 transition hover:shadow-sm dark:border-gray-800 {{ $isRecommended ? 'border-amber-300 bg-amber-50 dark:border-amber-700 dark:bg-amber-950/30' : 'border-gray-200 hover:border-gray-300 dark:hover:border-gray-700' }}"
>
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-2">
<div class="font-medium text-gray-900 dark:text-gray-100">
{{ $workspace->name }}
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-{{ min($workspaces->count(), 3) }}">
@foreach ($workspaces as $workspace)
@php
$isRecommended = $recommendedWorkspaceId > 0 && (int) $workspace->id === $recommendedWorkspaceId;
$role = $workspaceRoles[(int) $workspace->id] ?? null;
$tenantCount = (int) ($workspace->tenants_count ?? 0);
@endphp
<button
type="button"
wire:key="workspace-{{ $workspace->id }}"
wire:click="selectWorkspace({{ (int) $workspace->id }})"
@class([
'group relative flex flex-col rounded-xl border p-5 text-left transition-all duration-150',
'focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900',
// Recommended (last used)
'border-primary-300 bg-primary-50/50 shadow-sm ring-1 ring-primary-200 hover:border-primary-400 hover:shadow-md dark:border-primary-500/40 dark:bg-primary-950/20 dark:ring-primary-500/20 dark:hover:border-primary-400/60' => $isRecommended,
// Default
'border-gray-200 bg-white shadow-sm hover:border-gray-300 hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-white/20' => ! $isRecommended,
])
>
{{-- Loading overlay --}}
<div wire:loading wire:target="selectWorkspace({{ (int) $workspace->id }})"
class="absolute inset-0 z-10 flex items-center justify-center rounded-xl bg-white/80 dark:bg-gray-900/80">
<x-filament::loading-indicator class="h-5 w-5 text-primary-500" />
</div>
{{-- Header: icon + name --}}
<div class="flex items-start gap-3">
<div @class([
'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',
'bg-primary-100 dark:bg-primary-900/50' => $isRecommended,
'bg-gray-100 group-hover:bg-gray-200 dark:bg-white/10 dark:group-hover:bg-white/15' => ! $isRecommended,
])>
<x-filament::icon
icon="heroicon-o-building-office-2"
@class([
'h-5 w-5',
'text-primary-600 dark:text-primary-400' => $isRecommended,
'text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300' => ! $isRecommended,
])
/>
</div>
<div class="min-w-0 flex-1">
<h3 class="truncate text-sm font-semibold text-gray-900 dark:text-white">
{{ $workspace->name }}
</h3>
@if ($role)
<div class="mt-0.5 flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
<x-filament::icon
:icon="$roleIconMap[$role] ?? 'heroicon-m-user'"
class="h-3.5 w-3.5"
/>
{{ ucfirst($role) }}
</div>
<div class="flex flex-wrap items-center gap-2">
@if ($isRecommended)
<x-filament::badge color="warning" size="sm">
Last used
</x-filament::badge>
@endif
@if ($role)
<x-filament::badge :color="$roleColorMap[$role] ?? 'gray'" size="sm">
{{ ucfirst($role) }}
</x-filament::badge>
@endif
</div>
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{{ $tenantCount }} {{ \Illuminate\Support\Str::plural('tenant', $tenantCount) }}
</div>
@endif
</div>
</div>
@endforeach
{{-- Metadata --}}
<div class="mt-4 flex items-center justify-between border-t border-gray-100 pt-3 dark:border-white/5">
<div class="flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400">
<x-filament::icon icon="heroicon-m-server-stack" class="h-3.5 w-3.5" />
{{ $tenantCount }} {{ \Illuminate\Support\Str::plural('tenant', $tenantCount) }}
</div>
@if ($isRecommended)
<x-filament::badge color="primary" size="sm">
Last used
</x-filament::badge>
@endif
</div>
{{-- Hover arrow indicator --}}
<div class="absolute right-4 top-5 opacity-0 transition-opacity group-hover:opacity-100">
<x-filament::icon
icon="heroicon-m-arrow-right"
class="h-4 w-4 text-gray-400 dark:text-gray-500"
/>
</div>
</button>
@endforeach
</div>
@if ($canManageWorkspaces)
<div class="mt-6 flex justify-center">
<a href="/admin/workspaces"
class="inline-flex items-center gap-1.5 text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
<x-filament::icon icon="heroicon-m-cog-6-tooth" class="h-4 w-4" />
Manage workspaces
</a>
</div>
@php
$canManageWorkspaces = false;
if ($user instanceof \App\Models\User && $workspaces->count() > 0) {
foreach ($workspaces as $ws) {
$wsRole = $workspaceRoles[(int) $ws->id] ?? null;
if ($wsRole === 'owner') {
$canManageWorkspaces = true;
break;
}
}
}
@endphp
@if ($canManageWorkspaces)
<div class="mt-2 text-center">
<a href="/admin/workspaces" class="text-sm text-primary-600 hover:underline dark:text-primary-400">
Manage workspaces
</a>
</div>
@endif
@endif
</div>
</x-filament::section>
@endif
</x-filament-panels::page>