mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
feat(tables): filter tables by clicking a cell value
Click a type, status, server, source, or role value in a table row to filter the table by that value. Click it again to remove the filter. Covers the environment resources, deployments, application and service backups, and team members tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a3e2b9347c
commit
cd992e72e1
@@ -3070,6 +3070,38 @@ input[type="search"]::-webkit-search-results-decoration {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
/* A table cell value that filters the table by that value on click. */
|
||||
.table-filter-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 100%;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-color: transparent;
|
||||
text-underline-offset: 2px;
|
||||
transition: color 0.15s, border-color 0.15s, text-decoration-color 0.15s;
|
||||
}
|
||||
|
||||
.table-filter-value:hover {
|
||||
color: var(--color-black);
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
.dark .table-filter-value:hover {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.table-filter-value.table-filter-badge:hover {
|
||||
text-decoration-color: transparent;
|
||||
border-color: var(--color-neutral-400);
|
||||
}
|
||||
|
||||
.dark .table-filter-value.table-filter-badge:hover {
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.env-table-grid {
|
||||
grid-template-columns: minmax(14rem, 2.5fr) 4.8rem 6rem 4rem 4.5rem 4.8rem 4.2rem 3rem;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,12 @@
|
||||
title="{{ $backup->targetName() }}">
|
||||
{{ $backup->targetName() }}
|
||||
</span>
|
||||
<span>{{ $backup->targetType() }}</span>
|
||||
<span class="min-w-0">
|
||||
<span role="button" tabindex="0" class="table-filter-value"
|
||||
title="Filter by {{ $backup->targetType() }}"
|
||||
x-on:click.prevent.stop="typeFilter = typeFilter === @js(strtolower($backup->targetType())) ? 'all' : @js(strtolower($backup->targetType()))"
|
||||
x-on:keydown.enter.prevent.stop="$el.click()">{{ $backup->targetType() }}</span>
|
||||
</span>
|
||||
<span>{{ $backup->frequency }}</span>
|
||||
<span><x-status-badge :status="$statusLabel" :type="$statusType" /></span>
|
||||
<span title="{{ $backup->save_s3 ? ($s3StorageNames->isNotEmpty() ? $s3StorageNames->join(', ') : 'S3 storage unavailable') : 'S3 storage is not configured' }}">
|
||||
|
||||
@@ -236,6 +236,15 @@
|
||||
(bool) data_get($deployment, 'is_api') => 'API',
|
||||
default => 'Manual',
|
||||
};
|
||||
$sourceKey = match (true) {
|
||||
$pullRequestId > 0 => 'pull-request',
|
||||
(bool) data_get($deployment, 'is_webhook') => 'webhook',
|
||||
(bool) data_get($deployment, 'rollback') => 'rollback',
|
||||
(bool) data_get($deployment, 'is_api') => 'api',
|
||||
default => 'manual',
|
||||
};
|
||||
$serverId = data_get($deployment, 'server_id');
|
||||
$serverName = data_get($deployment, 'server_name') ?: data_get($application, 'destination.server.name', '-');
|
||||
$duration = match ($deploymentStatus) {
|
||||
'queued' => 'Waiting',
|
||||
'in_progress' => calculateDuration(data_get($deployment, 'created_at'), now()),
|
||||
@@ -264,9 +273,21 @@
|
||||
x-bind:aria-busy="selecting === '{{ data_get($deployment, 'deployment_uuid') }}'"
|
||||
@endif>
|
||||
@unless ($embedded)
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!}><x-status-badge :status="$statusLabel" :type="$statusType" /></a>
|
||||
<span class="min-w-0">
|
||||
<x-status-badge as="button" :status="$statusLabel" :type="$statusType"
|
||||
class="table-filter-value table-filter-badge" title="Filter by {{ $statusLabel }}"
|
||||
wire:click="toggleDeploymentFilter('status:{{ $deploymentStatus }}')" />
|
||||
</span>
|
||||
@endunless
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!}>{{ $sourceLabel }}</a>
|
||||
@if ($embedded)
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!}>{{ $sourceLabel }}</a>
|
||||
@else
|
||||
<span class="min-w-0">
|
||||
<button type="button" class="table-filter-value truncate"
|
||||
title="Filter by {{ $sourceLabel }}"
|
||||
wire:click="toggleDeploymentFilter('source:{{ $sourceKey }}')">{{ $sourceLabel }}</button>
|
||||
</span>
|
||||
@endif
|
||||
<span class="min-w-0">
|
||||
@if (data_get($deployment, 'commit'))
|
||||
<span class="flex min-w-0 items-center gap-2">
|
||||
@@ -285,7 +306,7 @@
|
||||
@endif
|
||||
@if ($embedded)
|
||||
<span class="mt-0.5 hidden truncate text-xs text-neutral-500 group-hover:text-neutral-950 dark:text-fg-faint dark:group-hover:text-white 2xl:block">
|
||||
{{ $sourceLabel }} · <span class="tabular-nums">{{ $duration }}</span> · {{ data_get($deployment, 'server_name') ?: data_get($application, 'destination.server.name', '-') }}
|
||||
{{ $sourceLabel }} · <span class="tabular-nums">{{ $duration }}</span> · {{ $serverName }}
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
@@ -295,9 +316,15 @@
|
||||
</a>
|
||||
@endunless
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!} class="tabular-nums">{{ $duration }}</a>
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!} class="truncate">
|
||||
{{ data_get($deployment, 'server_name') ?: data_get($application, 'destination.server.name', '-') }}
|
||||
</a>
|
||||
@if (! $embedded && $serverId)
|
||||
<span class="min-w-0">
|
||||
<button type="button" class="table-filter-value truncate"
|
||||
title="Filter by {{ $serverName }}"
|
||||
wire:click="toggleDeploymentFilter('server:{{ (int) $serverId }}')">{{ $serverName }}</button>
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!} class="truncate">{{ $serverName }}</a>
|
||||
@endif
|
||||
@if ($embedded)
|
||||
<a href="{{ $deploymentUrl }}" {!! $deploymentLinkAttributes !!}
|
||||
class="flex items-center justify-end gap-2 pr-1 tabular-nums"
|
||||
|
||||
@@ -206,11 +206,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-type truncate text-[12px] text-neutral-600 dark:text-fg-dim"
|
||||
x-text="item.typeLabel"></div>
|
||||
<div class="resource-type min-w-0">
|
||||
<button type="button"
|
||||
class="table-filter-value truncate text-[12px] text-neutral-600 dark:text-fg-dim"
|
||||
x-on:click="toggleFilter('typeFilters', item.type)"
|
||||
x-bind:title="`Filter by ${item.typeLabel}`" x-text="item.typeLabel"></button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-status-badge dynamic x-bind:title="statusTitle(item)">
|
||||
<div class="min-w-0">
|
||||
<x-status-badge dynamic as="button" class="table-filter-value table-filter-badge"
|
||||
x-on:click="toggleFilter('statusFilters', statusState(item))"
|
||||
x-bind:title="`${statusTitle(item)} · Filter by ${statusLabel(item)}`">
|
||||
<span class="size-1.5 shrink-0 rounded-full"
|
||||
x-bind:class="statusDotClass(item)"></span>
|
||||
<span class="truncate" x-text="statusLabel(item)"></span>
|
||||
@@ -226,8 +232,13 @@
|
||||
<span x-show="!item.fqdn" class="text-[12px] text-neutral-400 dark:text-fg-faint">-</span>
|
||||
</div>
|
||||
|
||||
<div class="resource-server truncate text-[12px] text-neutral-600 dark:text-fg-dim"
|
||||
x-text="item.destination?.server?.name || 'Unknown'"></div>
|
||||
<div class="resource-server min-w-0">
|
||||
<button type="button"
|
||||
class="table-filter-value truncate text-[12px] text-neutral-600 dark:text-fg-dim"
|
||||
x-on:click="toggleFilter('serverFilters', item.destination?.server?.name || 'Unknown')"
|
||||
x-bind:title="`Filter by ${item.destination?.server?.name || 'Unknown'}`"
|
||||
x-text="item.destination?.server?.name || 'Unknown'"></button>
|
||||
</div>
|
||||
|
||||
<div class="resource-tags flex min-w-0 items-center gap-1 overflow-hidden">
|
||||
<template x-for="tag in item.tags.slice(0, 2)" :key="tag.id">
|
||||
|
||||
@@ -279,7 +279,12 @@
|
||||
<span class="min-w-0 truncate font-medium text-neutral-950 dark:text-fg">
|
||||
{{ $databaseBackup->database->human_name ?: $databaseBackup->database->name }}
|
||||
</span>
|
||||
<span>Database</span>
|
||||
<span class="min-w-0">
|
||||
<button type="button" class="table-filter-value"
|
||||
title="Filter by Database"
|
||||
x-on:click.stop="typeFilter = typeFilter === 'database' ? 'all' : 'database'"
|
||||
x-on:keydown.enter.stop>Database</button>
|
||||
</span>
|
||||
<span>{{ $databaseBackup->frequency }}</span>
|
||||
<span><x-status-badge :status="$statusLabel" :type="$statusType" /></span>
|
||||
<span>
|
||||
@@ -333,7 +338,12 @@
|
||||
title="{{ $backup->targetName() }}">
|
||||
{{ $backup->targetName() }}
|
||||
</span>
|
||||
<span>{{ $backup->targetType() }}</span>
|
||||
<span class="min-w-0">
|
||||
<button type="button" class="table-filter-value"
|
||||
title="Filter by {{ $backup->targetType() }}"
|
||||
x-on:click.stop="typeFilter = typeFilter === @js(strtolower($backup->targetType())) ? 'all' : @js(strtolower($backup->targetType()))"
|
||||
x-on:keydown.enter.stop>{{ $backup->targetType() }}</button>
|
||||
</span>
|
||||
<span>{{ $backup->frequency }}</span>
|
||||
<span><x-status-badge :status="$statusLabel" :type="$statusType" /></span>
|
||||
<span>
|
||||
|
||||
@@ -27,10 +27,12 @@
|
||||
</div>
|
||||
<div class="truncate text-[12px] text-neutral-500 dark:text-fg-dim">{{ $member->email }}</div>
|
||||
<div>
|
||||
<span
|
||||
class="inline-flex rounded-full bg-neutral-100 px-2 py-0.5 text-[10px] font-medium capitalize text-neutral-600 dark:bg-white/[0.06] dark:text-fg-dim">
|
||||
<button type="button"
|
||||
class="table-filter-value table-filter-badge inline-flex rounded-full border border-transparent bg-neutral-100 px-2 py-0.5 text-[10px] font-medium capitalize text-neutral-600 dark:bg-white/[0.06] dark:text-fg-dim"
|
||||
title="Filter by {{ data_get($member, 'pivot.role') }}"
|
||||
x-on:click="roleFilter = roleFilter === @js(data_get($member, 'pivot.role')) ? 'all' : @js(data_get($member, 'pivot.role')); page = 1">
|
||||
{{ data_get($member, 'pivot.role') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@can('manageMembers', currentTeam())
|
||||
<div class="flex items-center">
|
||||
|
||||
Reference in New Issue
Block a user