fix: aria-label binding, dead import, guardAndLoad network error handling
- Fix 1: Add missing `:` binding prefix to aria-label on score badge (was emitting literal backtick template string to DOM) - Fix 2: Remove unused `watch` import from InterviewPrepView.vue - Fix 3: guardAndLoad now checks interviewsStore.error after fetchAll; shows pageError banner instead of silently redirecting to /interviews on network failure; job is now a ref set explicitly in the guard - Fix 4: Remove unconditional research-badge from InterviewCard.vue (added in this branch; card has no access to prep store so badge always showed regardless of whether research exists)
This commit is contained in:
parent
1cee73e233
commit
8479f79701
2 changed files with 18 additions and 24 deletions
|
|
@ -176,7 +176,6 @@ const columnColor = computed(() => {
|
|||
<div v-if="interviewDateLabel" class="date-chip">
|
||||
{{ dateChipIcon }} {{ interviewDateLabel }}
|
||||
</div>
|
||||
<div class="research-badge research-badge--done">🔬 Research ready</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<button class="card-action" @click.stop="emit('move', job.id)">Move to… ›</button>
|
||||
|
|
@ -331,23 +330,6 @@ const columnColor = computed(() => {
|
|||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.research-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
border-radius: 99px;
|
||||
padding: 2px 8px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
align-self: flex-start;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.research-badge--done {
|
||||
background: color-mix(in srgb, var(--status-phone) 12%, var(--color-surface-raised));
|
||||
color: var(--status-phone);
|
||||
border: 1px solid color-mix(in srgb, var(--status-phone) 30%, var(--color-surface-raised));
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { usePrepStore } from '../stores/prep'
|
||||
import { useInterviewsStore } from '../stores/interviews'
|
||||
import type { PipelineJob } from '../stores/interviews'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
|
@ -22,10 +23,7 @@ const jobId = computed<number | null>(() => {
|
|||
// ── Current job (from interviews store) ───────────────────────────────────────
|
||||
const PREP_VALID_STATUSES = ['phone_screen', 'interviewing', 'offer'] as const
|
||||
|
||||
const job = computed(() => {
|
||||
if (jobId.value === null) return null
|
||||
return interviewsStore.jobs.find(j => j.id === jobId.value) ?? null
|
||||
})
|
||||
const job = ref<PipelineJob | null>(null)
|
||||
|
||||
// ── Tabs ──────────────────────────────────────────────────────────────────────
|
||||
type TabId = 'jd' | 'email' | 'letter'
|
||||
|
|
@ -35,6 +33,9 @@ const activeTab = ref<TabId>('jd')
|
|||
const notesKey = computed(() => `cf-prep-notes-${jobId.value ?? 'none'}`)
|
||||
const callNotes = useStorage(notesKey, '')
|
||||
|
||||
// ── Page-level error (e.g. network failure during guard) ──────────────────────
|
||||
const pageError = ref<string | null>(null)
|
||||
|
||||
// ── Routing / guard ───────────────────────────────────────────────────────────
|
||||
async function guardAndLoad() {
|
||||
if (jobId.value === null) {
|
||||
|
|
@ -45,6 +46,11 @@ async function guardAndLoad() {
|
|||
// Ensure the interviews store is populated
|
||||
if (interviewsStore.jobs.length === 0) {
|
||||
await interviewsStore.fetchAll()
|
||||
if (interviewsStore.error) {
|
||||
// Store fetch failed — don't redirect, show error
|
||||
pageError.value = 'Failed to load job data. Please try again.'
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const found = interviewsStore.jobs.find(j => j.id === jobId.value)
|
||||
|
|
@ -53,6 +59,7 @@ async function guardAndLoad() {
|
|||
return
|
||||
}
|
||||
|
||||
job.value = found
|
||||
await prepStore.fetchFor(jobId.value)
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +368,7 @@ async function onGenerate() {
|
|||
<span
|
||||
class="score-badge"
|
||||
:class="matchScoreBadge(matchScore).cls"
|
||||
aria-label="`Match score: ${matchScore ?? 'unknown'}%`"
|
||||
:aria-label="`Match score: ${matchScore ?? 'unknown'}%`"
|
||||
>
|
||||
{{ matchScoreBadge(matchScore).icon }}
|
||||
</span>
|
||||
|
|
@ -446,6 +453,11 @@ async function onGenerate() {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Network/load error — don't redirect, show message -->
|
||||
<div v-else-if="pageError" class="error-banner" role="alert">
|
||||
{{ pageError }}
|
||||
</div>
|
||||
|
||||
<!-- Fallback while redirecting -->
|
||||
<div v-else class="prep-loading" aria-live="polite">
|
||||
Redirecting…
|
||||
|
|
|
|||
Loading…
Reference in a new issue