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:
parent
051db1842d
commit
9b02cc4041
@ -1,10 +1,4 @@
|
||||
<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;
|
||||
@ -23,62 +17,18 @@
|
||||
'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.
|
||||
</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
|
||||
$roleIconMap = [
|
||||
'owner' => 'heroicon-m-shield-check',
|
||||
'manager' => 'heroicon-m-cog-6-tooth',
|
||||
'operator' => 'heroicon-m-wrench-screwdriver',
|
||||
'readonly' => 'heroicon-m-eye',
|
||||
];
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</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') {
|
||||
if (($workspaceRoles[(int) $ws->id] ?? null) === 'owner') {
|
||||
$canManageWorkspaces = true;
|
||||
break;
|
||||
}
|
||||
@ -86,14 +36,120 @@ class="cursor-pointer rounded-lg border p-4 transition hover:shadow-sm dark:bord
|
||||
}
|
||||
@endphp
|
||||
|
||||
@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>
|
||||
<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 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>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 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-2 text-center">
|
||||
<a href="/admin/workspaces" class="text-sm text-primary-600 hover:underline dark:text-primary-400">
|
||||
<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>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</x-filament::section>
|
||||
@endif
|
||||
</x-filament-panels::page>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user