feat(ui): New status page UI (#1198)
* feat(ui): New status page UI * docs: Rename labels to extra-labels * Fix domain expiration test * feat(ui): Add ui.default-sort-by and ui.default-filter-by * Change ui.header default value to Gatus * Re-use EndpointCard in Details.vue as well to avoid duplicate code * Fix flaky metrics test * Add subtle green color to "Gatus" * Remove duplicate title (tooltip is sufficient, no need for title on top of that) * Fix collapsed group user preferences * Update status page screenshots
This commit is contained in:
100
web/app/src/components/SearchBar.vue
Normal file
100
web/app/src/components/SearchBar.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="flex flex-col lg:flex-row gap-3 lg:gap-4 p-3 sm:p-4 bg-card rounded-lg border">
|
||||
<div class="flex-1">
|
||||
<div class="relative">
|
||||
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<label for="search-input" class="sr-only">Search endpoints</label>
|
||||
<Input
|
||||
id="search-input"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Search endpoints..."
|
||||
class="pl-10 text-sm sm:text-base"
|
||||
@input="$emit('search', searchQuery)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||
<div class="flex items-center gap-2 flex-1 sm:flex-initial">
|
||||
<label class="text-xs sm:text-sm font-medium text-muted-foreground whitespace-nowrap">Filter by:</label>
|
||||
<Select
|
||||
v-model="filterBy"
|
||||
:options="filterOptions"
|
||||
placeholder="Nothing"
|
||||
class="flex-1 sm:w-[140px] md:w-[160px]"
|
||||
@update:model-value="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 flex-1 sm:flex-initial">
|
||||
<label class="text-xs sm:text-sm font-medium text-muted-foreground whitespace-nowrap">Sort by:</label>
|
||||
<Select
|
||||
v-model="sortBy"
|
||||
:options="sortOptions"
|
||||
placeholder="Name"
|
||||
class="flex-1 sm:w-[90px] md:w-[100px]"
|
||||
@update:model-value="handleSortChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Search } from 'lucide-vue-next'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select } from '@/components/ui/select'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const filterBy = ref(localStorage.getItem('gatus:filter-by') || (typeof window !== 'undefined' && window.config?.defaultFilterBy) || 'nothing')
|
||||
const sortBy = ref(localStorage.getItem('gatus:sort-by') || (typeof window !== 'undefined' && window.config?.defaultSortBy) || 'name')
|
||||
|
||||
const filterOptions = [
|
||||
{ label: 'Nothing', value: 'nothing' },
|
||||
{ label: 'Failing', value: 'failing' },
|
||||
{ label: 'Unstable', value: 'unstable' }
|
||||
]
|
||||
|
||||
const sortOptions = [
|
||||
{ label: 'Name', value: 'name' },
|
||||
{ label: 'Group', value: 'group' },
|
||||
{ label: 'Health', value: 'health' }
|
||||
]
|
||||
|
||||
const emit = defineEmits(['search', 'update:showOnlyFailing', 'update:showRecentFailures', 'update:groupByGroup', 'update:sortBy', 'initializeCollapsedGroups'])
|
||||
|
||||
const handleFilterChange = (value) => {
|
||||
filterBy.value = value
|
||||
localStorage.setItem('gatus:filter-by', value)
|
||||
|
||||
// Reset all filter states first
|
||||
emit('update:showOnlyFailing', false)
|
||||
emit('update:showRecentFailures', false)
|
||||
|
||||
// Apply the selected filter
|
||||
if (value === 'failing') {
|
||||
emit('update:showOnlyFailing', true)
|
||||
} else if (value === 'unstable') {
|
||||
emit('update:showRecentFailures', true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSortChange = (value) => {
|
||||
sortBy.value = value
|
||||
localStorage.setItem('gatus:sort-by', value)
|
||||
emit('update:sortBy', value)
|
||||
emit('update:groupByGroup', value === 'group')
|
||||
|
||||
// When switching to group view, initialize collapsed groups
|
||||
if (value === 'group') {
|
||||
emit('initializeCollapsedGroups')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Apply saved filter/sort state on load
|
||||
handleFilterChange(filterBy.value)
|
||||
handleSortChange(sortBy.value)
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user