Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ jobs:
name: Start Playwright frontend
env:
BACKGROUND_SERVICE_NAME: Playwright frontend
BACKGROUND_RUN_COMMAND: CAPTCHA_KEY='' bun run serve:worktree
BACKGROUND_RUN_COMMAND: bun run supabase:with-env -- bun scripts/playwright-frontend-preview.ts
BACKGROUND_WAIT_ON: |
http-get://localhost:5173
BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-frontend.log
Expand Down
30 changes: 30 additions & 0 deletions scripts/playwright-frontend-preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { spawnSync } from 'node:child_process'

const supabaseUrl = process.env.SUPABASE_URL
if (!supabaseUrl) {
console.error('SUPABASE_URL is required for Playwright frontend preview')
process.exit(1)
}

const normalizedSupabaseHost = supabaseUrl.replace(/^https?:\/\//, '').replace(/\/+$/, '')
const apiDomain = `${normalizedSupabaseHost}/functions/v1`

const env = {
...process.env,
API_DOMAIN: apiDomain,
CAPTCHA_KEY: '',
ENV: 'local',
SUPA_ANON: process.env.SUPABASE_ANON_KEY || '',
SUPA_URL: supabaseUrl,
}

const build = spawnSync('bun', ['run', 'build'], { env, stdio: 'inherit' })
if ((build.status ?? 1) !== 0)
process.exit(build.status ?? 1)

const preview = spawnSync('bunx', ['vite', 'preview', '--host', '127.0.0.1', '--port', '5173'], {
env,
stdio: 'inherit',
})

process.exit(preview.status ?? 1)
32 changes: 24 additions & 8 deletions src/components/dashboard/UpdateStatsChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,27 @@ const cycleEnd = computed(() => {

const DAY_IN_MS = 1000 * 60 * 60 * 24

// Map action display names back to action filter keys for navigation
const actionToFilterKey: Record<string, string> = {
requested: 'get',
install: 'set',
fail: 'set_fail',
const UPDATE_FAILURE_ACTIONS = [
'set_fail',
'update_fail',
'download_fail',
'windows_path_fail',
'canonical_path_fail',
'directory_path_fail',
'unzip_fail',
'low_mem_fail',
'download_manifest_file_fail',
'download_manifest_checksum_fail',
'download_manifest_brotli_fail',
'decrypt_fail',
'checksum_fail',
] as const

// Map chart buckets back to the log actions they aggregate.
const actionToLogActions: Record<string, readonly string[]> = {
requested: ['get'],
install: ['set'],
fail: UPDATE_FAILURE_ACTIONS,
}

// Click handler for tooltip items - navigates to logs page filtered by action type and date
Expand All @@ -81,11 +97,11 @@ const tooltipClickHandler = computed<TooltipClickHandler | undefined>(() => {
onAppClick: (actionKey: string, clickContext?: { date: Date, dataIndex: number }) => {
// Navigate to logs page with action filter and date range
// The actionKey here is the internal key like 'requested', 'install', 'fail'
const filterAction = actionToFilterKey[actionKey] || actionKey
const filterActions = actionToLogActions[actionKey] ?? [actionKey]
const params = new URLSearchParams()
params.set('action', filterAction)
filterActions.forEach(action => params.append('action', action))

// Add date range if provided (start of day to end of day)
// Add date range if provided (selected day)
if (clickContext?.date) {
const startOfDay = dayjs(clickContext.date).startOf('day')
const endOfDay = dayjs(clickContext.date).endOf('day')
Expand Down
12 changes: 7 additions & 5 deletions src/components/tables/LogTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,16 @@ function formatAction(elem: Element): string {

// Initialize action filters from URL query parameter
function initializeActionFilters(): void {
const actionParam = route.query.action
if (actionParam && typeof actionParam === 'string') {
// Find the filter key for this action
const filterKey = actionToFilter[actionParam]
const actionParams = [route.query.action]
.flat()
.filter((action): action is string => typeof action === 'string')

actionParams.forEach((action) => {
const filterKey = actionToFilter[action]
if (filterKey && actionFilters.value[filterKey] !== undefined) {
actionFilters.value[filterKey] = true
}
}
})
}

// Compute active actions based on filters
Expand Down
Loading