feat(survey): show job picker when navigating to /survey with no id

This commit is contained in:
pyr0ball 2026-03-21 00:49:55 -07:00
parent 80999b9e7b
commit 3bfce5e6ef

View file

@ -13,11 +13,7 @@ const VALID_STAGES = ['survey', 'phone_screen', 'interviewing', 'offer']
const rawId = route.params.id const rawId = route.params.id
const jobId = rawId ? parseInt(String(rawId), 10) : NaN const jobId = rawId ? parseInt(String(rawId), 10) : NaN
const pickerMode = !rawId || isNaN(jobId)
// Redirect if no valid id
if (!jobId || isNaN(jobId)) {
router.replace('/interviews')
}
// UI state // UI state
let saveSuccessTimer: ReturnType<typeof setTimeout> | null = null let saveSuccessTimer: ReturnType<typeof setTimeout> | null = null
@ -35,11 +31,21 @@ const job = computed(() =>
interviewsStore.jobs.find(j => j.id === jobId) ?? null interviewsStore.jobs.find(j => j.id === jobId) ?? null
) )
// Jobs eligible for survey (used in picker mode)
const pickerJobs = computed(() =>
interviewsStore.jobs.filter(j => VALID_STAGES.includes(j.status))
)
const stageLabel: Record<string, string> = {
survey: 'Survey', phone_screen: 'Phone Screen',
interviewing: 'Interviewing', offer: 'Offer',
}
onMounted(async () => { onMounted(async () => {
if (!jobId || isNaN(jobId)) return
if (interviewsStore.jobs.length === 0) { if (interviewsStore.jobs.length === 0) {
await interviewsStore.fetchAll() await interviewsStore.fetchAll()
} }
if (pickerMode) return
if (!job.value || !VALID_STAGES.includes(job.value.status)) { if (!job.value || !VALID_STAGES.includes(job.value.status)) {
router.replace('/interviews') router.replace('/interviews')
return return
@ -126,12 +132,6 @@ async function saveToJob() {
} }
} }
// Stage label helper
const stageLabel: Record<string, string> = {
survey: 'Survey', phone_screen: 'Phone Screen',
interviewing: 'Interviewing', offer: 'Offer',
}
// History accordion // History accordion
const historyOpen = ref(false) const historyOpen = ref(false)
function formatDate(iso: string | null): string { function formatDate(iso: string | null): string {
@ -149,6 +149,32 @@ function toggleHistoryEntry(id: number) {
<template> <template>
<div class="survey-layout"> <div class="survey-layout">
<!-- Job picker (no id in route) -->
<div v-if="pickerMode" class="survey-content picker-mode">
<h2 class="picker-heading">Survey Assistant</h2>
<p class="picker-sub">Select a job to open the survey assistant.</p>
<div v-if="pickerJobs.length === 0" class="picker-empty">
No jobs in an active interview stage. Move a job to Survey, Phone Screen, Interviewing, or Offer first.
</div>
<ul v-else class="picker-list" role="list">
<li
v-for="j in pickerJobs"
:key="j.id"
class="picker-item"
@click="router.push('/survey/' + j.id)"
>
<div class="picker-item__main">
<span class="picker-item__company">{{ j.company }}</span>
<span class="picker-item__title">{{ j.title }}</span>
</div>
<span class="stage-badge">{{ stageLabel[j.status] ?? j.status }}</span>
</li>
</ul>
</div>
<!-- Survey assistant (id present) -->
<template v-else>
<!-- Sticky context bar --> <!-- Sticky context bar -->
<div class="context-bar" v-if="job"> <div class="context-bar" v-if="job">
<span class="context-company">{{ job.company }}</span> <span class="context-company">{{ job.company }}</span>
@ -305,6 +331,8 @@ function toggleHistoryEntry(id: number) {
</div> </div>
</details> </details>
</div> </div>
</template><!-- end v-else (id present) -->
</div> </div>
</template> </template>
@ -725,4 +753,82 @@ function toggleHistoryEntry(id: number) {
@keyframes spin { @keyframes spin {
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
/* ── Picker mode ── */
.picker-mode {
padding-top: var(--space-8, 2rem);
}
.picker-heading {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-text, #1a202c);
margin: 0 0 var(--space-1) 0;
}
.picker-sub {
font-size: 0.875rem;
color: var(--color-text-muted, #718096);
margin: 0 0 var(--space-4) 0;
}
.picker-empty {
font-size: 0.875rem;
color: var(--color-text-muted, #718096);
padding: var(--space-4);
border: 1px dashed var(--color-border, #e2e8f0);
border-radius: var(--radius-md, 8px);
text-align: center;
}
.picker-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.picker-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
background: var(--color-surface, #fff);
border: 1px solid var(--color-border, #e2e8f0);
border-radius: var(--radius-md, 8px);
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.picker-item:hover {
border-color: var(--color-accent, #3182ce);
background: var(--color-accent-subtle, #ebf4ff);
}
.picker-item__main {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.picker-item__company {
font-weight: 600;
font-size: 0.9rem;
color: var(--color-text, #1a202c);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.picker-item__title {
font-size: 0.8rem;
color: var(--color-text-muted, #718096);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style> </style>