From b751f2380700a4d4d3d98f3ccd359cc6687c51a0 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Thu, 22 Oct 2026 16:59:02 +0530 Subject: [PATCH 1/2] fix: keep job history pending until its data is ready --- e2e/history-loading.spec.ts | 87 +++++++++++++++ package.json | 3 +- pnpm-lock.yaml | 201 ++--------------------------------- src/components/dashboard.tsx | 43 ++++++-- 4 files changed, 134 insertions(+), 200 deletions(-) create mode 100644 e2e/history-loading.spec.ts diff --git a/e2e/history-loading.spec.ts b/e2e/history-loading.spec.ts new file mode 100644 index 0000000..15816f0 --- /dev/null +++ b/e2e/history-loading.spec.ts @@ -0,0 +1,87 @@ +import { expect, test } from '@playwright/test'; + +for (const width of [390, 768, 1440]) { + for (const completeResume of [false, true]) { + test(`saved guest history is not shown as empty while auth resolves at ${width}px (complete resume: ${completeResume})`, async ({ + page, + }) => { + await page.setViewportSize({ width, height: 900 }); + await page.addInitScript((completeResume) => { + const state = window as unknown as Window & { historyLayoutShifts: number[] }; + state.historyLayoutShifts = []; + new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + const shift = entry as PerformanceEntry & { hadRecentInput: boolean; value: number }; + if (!shift.hadRecentInput) state.historyLayoutShifts.push(shift.value); + } + }).observe({ type: 'layout-shift', buffered: true }); + if (completeResume) + localStorage.setItem( + 'rt-resumes', + JSON.stringify([ + { + id: 'pending-history-resume', + name: 'Synthetic resume', + source: + '# Synthetic Applicant\n\n## Experience\n\n### Example\n\n- Built React screens.\n\n## Projects\n\n### Reader\n\n- Added search.\n\n## Education\n\nBSc Example University\n\n## Skills\n\nReact', + created_at: 1792660000, + updated_at: 1792660000, + }, + ]) + ); + localStorage.setItem( + 'rt-jobs', + JSON.stringify([ + { + id: 'pending-history-job', + resume_id: 'pending-history-resume', + company: 'Acceptance fixture', + role: 'Saved pending history job', + status: 'draft', + url: '', + jd_text: '', + created_at: 1792660000, + updated_at: 1792660000, + }, + ]) + ); + }, completeResume); + let releaseSession: () => void = () => {}; + const sessionGate = new Promise((resolve) => { + releaseSession = resolve; + }); + await page.route('**/api/auth/get-session**', async (route) => { + await sessionGate; + await route.fulfill({ json: null }); + }); + try { + await page.goto('/dashboard'); + const history = page.getByRole('region', { name: 'History', exact: true }); + await expect(page.getByRole('heading', { name: 'Jobs', exact: true })).toBeVisible(); + await expect(history.getByText('No job history yet', { exact: true })).toHaveCount(0); + await expect(history.getByText('0 jobs', { exact: true })).toHaveCount(0); + await expect(history.getByRole('group', { name: 'Filter history' })).toHaveCount(0); + await expect(history.getByRole('status', { name: 'Loading job history' })).toBeVisible(); + // Let viewport initialization settle before measuring the data transition. + await page.waitForTimeout(600); + releaseSession(); + await expect( + history.getByRole('button', { name: 'Saved pending history job', exact: true }) + ).toBeVisible(); + await expect(history.getByRole('status')).toHaveCount(0); + await expect(history.getByText('1 job', { exact: true })).toBeVisible(); + expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(width); + await page.waitForTimeout(600); + expect( + await page.evaluate(() => + ( + window as unknown as Window & { historyLayoutShifts: number[] } + ).historyLayoutShifts.reduce((sum, value) => sum + value, 0) + ) + ).toBeLessThanOrEqual(0.1); + } finally { + releaseSession(); + } + }); + } +} diff --git a/package.json b/package.json index 94e4d8c..60fe9bd 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "dompurify@<3.4.16": "3.4.16", "qs@<6.16.0": "6.16.0", "@ai-sdk/provider-utils@>=4.0.0 <4.0.33": "4.0.33", - "smol-toml@<1.9.0": "1.9.0" + "smol-toml@<1.9.0": "1.9.0", + "astro@7.2.8>vite": "8.2.4" } }, "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17e935b..5526299 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,7 @@ overrides: qs@<6.16.0: 6.16.0 '@ai-sdk/provider-utils@>=4.0.0 <4.0.33': 4.0.33 smol-toml@<1.9.0: 1.9.0 + astro@7.2.8>vite: 8.2.4 importers: @@ -2372,9 +2373,6 @@ packages: '@oxc-project/types@0.140.0': resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} - '@oxc-project/types@0.149.0': - resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} - '@oxc-project/types@0.153.0': resolution: {integrity: sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==} @@ -3487,72 +3485,36 @@ packages: cpu: [arm] os: [android] - '@rolldown/binding-android-arm-eabi@1.2.8': - resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - '@rolldown/binding-android-arm64@1.2.13': resolution: {integrity: sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.2.8': - resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - '@rolldown/binding-darwin-arm64@1.2.13': resolution: {integrity: sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.2.8': - resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - '@rolldown/binding-darwin-x64@1.2.13': resolution: {integrity: sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.8': - resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.13': resolution: {integrity: sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.2.8': - resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.13': resolution: {integrity: sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': - resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.13': resolution: {integrity: sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3560,13 +3522,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.2.8': - resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.2.13': resolution: {integrity: sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3574,13 +3529,6 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.2.8': - resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.2.13': resolution: {integrity: sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3588,13 +3536,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-ppc64-gnu@1.2.8': - resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.13': resolution: {integrity: sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3602,13 +3543,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.8': - resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.13': resolution: {integrity: sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3616,13 +3550,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.8': - resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.2.13': resolution: {integrity: sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3630,49 +3557,24 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-musl@1.2.8': - resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - '@rolldown/binding-openharmony-arm64@1.2.13': resolution: {integrity: sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.2.8': - resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - '@rolldown/binding-win32-arm64-msvc@1.2.13': resolution: {integrity: sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.2.8': - resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.13': resolution: {integrity: sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.8': - resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -6127,10 +6029,6 @@ packages: resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.28: - resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.29: resolution: {integrity: sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==} engines: {node: ^10 || ^12 || >=14} @@ -6333,11 +6231,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.2.8: - resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - rou3@0.9.2: resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} @@ -6867,8 +6760,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@8.2.2: - resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + vite@8.2.4: + resolution: {integrity: sha512-lfauXnrf2x0p+UoA+wXh0dgFWnUjCyG6Js95KBkgaCwUU9k0DAHI4W6Q0WCx+h+5g+Alce1y4Zi2QjjK0UvGBw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -9226,8 +9119,6 @@ snapshots: '@oxc-project/types@0.140.0': {} - '@oxc-project/types@0.149.0': {} - '@oxc-project/types@0.153.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': @@ -10510,93 +10401,48 @@ snapshots: '@rolldown/binding-android-arm-eabi@1.2.13': optional: true - '@rolldown/binding-android-arm-eabi@1.2.8': - optional: true - '@rolldown/binding-android-arm64@1.2.13': optional: true - '@rolldown/binding-android-arm64@1.2.8': - optional: true - '@rolldown/binding-darwin-arm64@1.2.13': optional: true - '@rolldown/binding-darwin-arm64@1.2.8': - optional: true - '@rolldown/binding-darwin-x64@1.2.13': optional: true - '@rolldown/binding-darwin-x64@1.2.8': - optional: true - '@rolldown/binding-freebsd-x64@1.2.13': optional: true - '@rolldown/binding-freebsd-x64@1.2.8': - optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.13': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': - optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.13': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.8': - optional: true - '@rolldown/binding-linux-arm64-musl@1.2.13': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.8': - optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.13': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.8': - optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.13': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.8': - optional: true - '@rolldown/binding-linux-x64-gnu@1.2.13': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.8': - optional: true - '@rolldown/binding-linux-x64-musl@1.2.13': optional: true - '@rolldown/binding-linux-x64-musl@1.2.8': - optional: true - '@rolldown/binding-openharmony-arm64@1.2.13': optional: true - '@rolldown/binding-openharmony-arm64@1.2.8': - optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.13': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.8': - optional: true - '@rolldown/binding-win32-x64-msvc@1.2.13': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.8': - optional: true - '@rolldown/pluginutils@1.0.1': {} '@saas-maker/app-health@https://github.com/sass-maker/app-health/releases/download/node-v0.4.0/saas-maker-app-health-0.4.0.tgz(express@5.2.1)': @@ -11355,8 +11201,8 @@ snapshots: ultrahtml: 1.7.0 unifont: 0.7.5 unstorage: 1.17.5(@vercel/functions@3.7.6(@aws-sdk/credential-provider-web-identity@3.972.67)(ws@8.21.0))(aws4fetch@1.0.20) - vite: 8.2.2(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.2.2(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0)) + vite: 8.2.4(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.4(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.5.4 @@ -13325,12 +13171,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.2 - postcss@8.5.28: - dependencies: - nanoid: 3.3.19 - picocolors: 1.1.1 - source-map-js: 1.2.2 - postcss@8.5.29: dependencies: nanoid: 3.3.19 @@ -13674,27 +13514,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.13 '@rolldown/binding-win32-x64-msvc': 1.2.13 - rolldown@1.2.8: - dependencies: - '@oxc-project/types': 0.149.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.8 - '@rolldown/binding-android-arm64': 1.2.8 - '@rolldown/binding-darwin-arm64': 1.2.8 - '@rolldown/binding-darwin-x64': 1.2.8 - '@rolldown/binding-freebsd-x64': 1.2.8 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 - '@rolldown/binding-linux-arm64-gnu': 1.2.8 - '@rolldown/binding-linux-arm64-musl': 1.2.8 - '@rolldown/binding-linux-ppc64-gnu': 1.2.8 - '@rolldown/binding-linux-s390x-gnu': 1.2.8 - '@rolldown/binding-linux-x64-gnu': 1.2.8 - '@rolldown/binding-linux-x64-musl': 1.2.8 - '@rolldown/binding-openharmony-arm64': 1.2.8 - '@rolldown/binding-win32-arm64-msvc': 1.2.8 - '@rolldown/binding-win32-x64-msvc': 1.2.8 - rou3@0.9.2: {} router@2.2.0: @@ -14267,12 +14086,12 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.2.2(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0): + vite@8.2.4(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 - postcss: 8.5.28 - rolldown: 1.2.8 + postcss: 8.5.29 + rolldown: 1.2.13 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.12.4 @@ -14312,9 +14131,9 @@ snapshots: terser: 5.16.9 yaml: 2.9.0 - vitefu@1.1.3(vite@8.2.2(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.4(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0)): optionalDependencies: - vite: 8.2.2(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0) + vite: 8.2.4(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0) vitest@4.1.11(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/coverage-v8@4.1.11)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.3.4(@types/node@20.19.33)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.16.9)(yaml@2.9.0)): dependencies: diff --git a/src/components/dashboard.tsx b/src/components/dashboard.tsx index 2939e15..5da179f 100644 --- a/src/components/dashboard.tsx +++ b/src/components/dashboard.tsx @@ -6,6 +6,7 @@ import { Fragment, useEffect, useMemo, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; +import { Skeleton } from '@/components/ui/skeleton'; import { Table, TableBody, @@ -73,6 +74,7 @@ import { } from '@/lib/local-storage'; import { jobDetailsOrNull } from '@/lib/job-details'; import { normalizeJobUrl } from '@/lib/job-discovery-alerts'; +import { cn } from '@/lib/utils'; import type { DiscoveredJob } from '@/lib/job-discovery-types'; import type { AchievementEvidence, @@ -225,6 +227,8 @@ export function Dashboard({ serverHistory = [], }: DashboardProps) { const { isGuest, isPending: authPending } = useAuth(); + const [localDataReady, setLocalDataReady] = useState(false); + const dataPending = authPending || (isGuest && !localDataReady); const [resumes, setResumes] = useState(serverResumes); const [jobs, setJobs] = useState(serverJobs.map(toDashboardJob)); const [history, setHistory] = useState(serverHistory); @@ -275,6 +279,7 @@ export function Dashboard({ setApplicationPackets(localListApplicationPackets()); setProfileAnswers(localListProfileAnswers()); setJobDiscoveryAlerts(localListJobDiscoveryAlerts()); + setLocalDataReady(true); /* eslint-enable react-hooks/set-state-in-effect */ } }, [authPending, isGuest]); @@ -728,7 +733,9 @@ export function Dashboard({ )} - {!resumes.some((r) => masterResumeReadiness(r.source).ready) && ( + {dataPending ? ( +