Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions web/app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,8 @@ const showTooltip = (result, event, action = 'hover') => {
tooltipIsPersistent.value = true
}
} else if (action === 'hover') {
// Only update tooltip on hover if not in persistent mode
if (!tooltipIsPersistent.value) {
tooltip.value = { result, event }
}
if (tooltipIsPersistent.value) return
tooltip.value = result ? { result, event } : {}
}
}

Expand Down
44 changes: 32 additions & 12 deletions web/app/src/components/EndpointCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@
<div class="flex-1"></div>
<p class="text-xs text-muted-foreground" :title="showAverageResponseTime ? 'Average response time' : 'Minimum and maximum response time'">{{ formattedResponseTime }}</p>
</div>
<div class="flex gap-0.5">
<div class="flex gap-0.5"
@mouseleave="clearTooltip()">
<div
v-for="(result, index) in displayResults"
:key="index"
:class="[
'flex-1 h-6 sm:h-8 rounded-sm transition-all',
result ? 'cursor-pointer' : '',
result && 'cursor-pointer',
result ? (
result.success
? (selectedResultIndex === index ? 'bg-green-700' : 'bg-green-500 hover:bg-green-700')
: (selectedResultIndex === index ? 'bg-red-700' : 'bg-red-500 hover:bg-red-700')
? (isHighlighted(index) ? 'bg-green-700' : 'bg-green-500')
: (isHighlighted(index) ? 'bg-red-700' : 'bg-red-500')
) : 'bg-gray-200 dark:bg-gray-700'
]"
@mouseenter="result && handleMouseEnter(result, $event)"
@mouseleave="result && handleMouseLeave(result, $event)"
@click.stop="result && handleClick(result, $event, index)"
@mouseenter="result ? handleMouseEnter(result, $event, index) : clearTooltip()"
@click="handleClick(result, $event, index)"
/>
</div>
<div class="flex items-center justify-between text-xs text-muted-foreground mt-1">
Expand All @@ -62,7 +62,7 @@
</template>

<script setup>
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { computed, watch, ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import StatusBadge from '@/components/StatusBadge.vue'
Expand Down Expand Up @@ -90,6 +90,8 @@ const emit = defineEmits(['showTooltip'])
// Track selected data point
const selectedResultIndex = ref(null)

const lastHoverIndex = ref(null)

const latestResult = computed(() => {
if (!props.endpoint.results || props.endpoint.results.length === 0) {
return null
Expand Down Expand Up @@ -162,25 +164,32 @@ const newestResultTime = computed(() => {
return generatePrettyTimeAgo(props.endpoint.results[props.endpoint.results.length - 1].timestamp)
})

const isHighlighted = (index) => {
return selectedResultIndex.value === index || lastHoverIndex.value === index
}

const navigateToDetails = () => {
router.push(`/endpoints/${props.endpoint.key}`)
}

const handleMouseEnter = (result, event) => {
const handleMouseEnter = (result, event, index) => {
lastHoverIndex.value = index
emit('showTooltip', result, event, 'hover')
}

const handleMouseLeave = (result, event) => {
emit('showTooltip', null, event, 'hover')
const clearTooltip = () => {
lastHoverIndex.value = null
emit('showTooltip', null, null, 'hover')
}

const handleClick = (result, event, index) => {
// Clear selections in other cards first
window.dispatchEvent(new CustomEvent('clear-data-point-selection'))

// Then toggle this card's selection
if (selectedResultIndex.value === index) {
selectedResultIndex.value = null
emit('showTooltip', null, event, 'click')
emit('showTooltip', null, null, 'click')
} else {
selectedResultIndex.value = index
emit('showTooltip', result, event, 'click')
Expand All @@ -192,6 +201,17 @@ const handleClearSelection = () => {
selectedResultIndex.value = null
}

watch(latestResult, () => {
// Update tooltip if a data point is selected
if (selectedResultIndex.value !== null) {
const result = displayResults.value[selectedResultIndex.value]
emit('showTooltip', result, null, 'click')
} else if (lastHoverIndex.value !== null) {
const result = displayResults.value[lastHoverIndex.value]
emit('showTooltip', result, null, 'hover')
}
})

onMounted(() => {
window.addEventListener('clear-data-point-selection', handleClearSelection)
})
Expand Down
62 changes: 43 additions & 19 deletions web/app/src/components/SuiteCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@
<p class="text-xs text-muted-foreground">Success Rate: {{ successRate }}%</p>
<p class="text-xs text-muted-foreground" v-if="averageDuration !== null">{{ averageDuration }}ms avg</p>
</div>
<div class="flex gap-0.5">
<div :class="['flex gap-0.5', lastHoverIndex !== null && 'cursor-pointer']"
@mouseleave="clearTooltip()">
<div
v-for="(result, index) in displayResults"
:key="index"
:class="[
'flex-1 h-6 sm:h-8 rounded-sm transition-all',
result ? 'cursor-pointer' : '',
result && 'cursor-pointer',
result ? (
result.success
? (selectedResultIndex === index ? 'bg-green-700' : 'bg-green-500 hover:bg-green-700')
: (selectedResultIndex === index ? 'bg-red-700' : 'bg-red-500 hover:bg-red-700')
? (isHighlighted(index) ? 'bg-green-700' : 'bg-green-500')
: (isHighlighted(index) ? 'bg-red-700' : 'bg-red-500')
) : 'bg-gray-200 dark:bg-gray-700'
]"
@mouseenter="result && handleMouseEnter(result, $event)"
@mouseleave="result && handleMouseLeave(result, $event)"
@click.stop="result && handleClick(result, $event, index)"
@mouseenter="result ? handleMouseEnter(result, $event, index) : clearTooltip()"
@click="handleClick(result, $event, index)"
/>
</div>
<div class="flex items-center justify-between text-xs text-muted-foreground mt-1">
Expand All @@ -62,7 +62,7 @@
</template>

<script setup>
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { computed, watch, ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import StatusBadge from '@/components/StatusBadge.vue'
Expand All @@ -86,7 +86,21 @@ const emit = defineEmits(['showTooltip'])
// Track selected data point
const selectedResultIndex = ref(null)

const lastHoverIndex = ref(null)

// Computed properties
const latestResult = computed(() => {
if (!props.suite.results || props.suite.results.length === 0) {
return null
}
return props.suite.results[props.suite.results.length - 1]
})

const currentStatus = computed(() => {
if (!latestResult.value) return 'unknown'
return props.suite.results[props.suite.results.length - 1].success ? 'healthy' : 'unhealthy'
})

const displayResults = computed(() => {
const results = [...(props.suite.results || [])]
while (results.length < props.maxResults) {
Expand All @@ -95,13 +109,6 @@ const displayResults = computed(() => {
return results.slice(-props.maxResults)
})

const currentStatus = computed(() => {
if (!props.suite.results || props.suite.results.length === 0) {
return 'unknown'
}
return props.suite.results[props.suite.results.length - 1].success ? 'healthy' : 'unhealthy'
})

const endpointCount = computed(() => {
if (!props.suite.results || props.suite.results.length === 0) {
return 0
Expand Down Expand Up @@ -147,17 +154,23 @@ const newestResultTime = computed(() => {
return generatePrettyTimeAgo(newestResult.timestamp)
})

const isHighlighted = (index) => {
return selectedResultIndex.value === index || lastHoverIndex.value === index
}

// Methods
const navigateToDetails = () => {
router.push(`/suites/${props.suite.key}`)
}

const handleMouseEnter = (result, event) => {
const handleMouseEnter = (result, event, index) => {
lastHoverIndex.value = index
emit('showTooltip', result, event, 'hover')
}

const handleMouseLeave = (result, event) => {
emit('showTooltip', null, event, 'hover')
const clearTooltip = () => {
lastHoverIndex.value = null
emit('showTooltip', null, null, 'hover')
}

const handleClick = (result, event, index) => {
Expand All @@ -166,7 +179,7 @@ const handleClick = (result, event, index) => {
// Then toggle this card's selection
if (selectedResultIndex.value === index) {
selectedResultIndex.value = null
emit('showTooltip', null, event, 'click')
emit('showTooltip', null, null, 'click')
} else {
selectedResultIndex.value = index
emit('showTooltip', result, event, 'click')
Expand All @@ -178,6 +191,17 @@ const handleClearSelection = () => {
selectedResultIndex.value = null
}

watch(latestResult, () => {
// Update tooltip if a data point is selected
if (selectedResultIndex.value !== null) {
const result = displayResults.value[selectedResultIndex.value]
emit('showTooltip', result, null, 'click')
} else if (lastHoverIndex.value !== null) {
const result = displayResults.value[lastHoverIndex.value]
emit('showTooltip', result, null, 'hover')
}
})

onMounted(() => {
window.addEventListener('clear-data-point-selection', handleClearSelection)
})
Expand Down
2 changes: 1 addition & 1 deletion web/static/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/static/js/app.js

Large diffs are not rendered by default.

Loading