Skip to content

Commit b01b792

Browse files
committed
feat: add job status badge and enhance job actions in dashboard
1 parent 68dd20d commit b01b792

3 files changed

Lines changed: 99 additions & 122 deletions

File tree

app/components/AppTopBar.vue

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ const activeJobTitle = computed(() => {
6666
return found?.title ?? 'Job'
6767
})
6868
69+
const activeJobStatus = computed(() => {
70+
if (!activeJobId.value) return null
71+
const found = sidebarJobs.value.find((j: any) => j.id === activeJobId.value)
72+
return (found as any)?.status ?? null
73+
})
74+
75+
const jobStatusBadgeClasses: Record<string, string> = {
76+
draft: 'bg-surface-50 text-surface-600 ring-surface-200 dark:bg-surface-800/60 dark:text-surface-400 dark:ring-surface-700',
77+
open: 'bg-success-50 text-success-700 ring-success-200 dark:bg-success-950/60 dark:text-success-400 dark:ring-success-800',
78+
closed: 'bg-warning-50 text-warning-700 ring-warning-200 dark:bg-warning-950/60 dark:text-warning-400 dark:ring-warning-800',
79+
archived: 'bg-surface-50 text-surface-400 ring-surface-200 dark:bg-surface-800/60 dark:text-surface-500 dark:ring-surface-700',
80+
}
81+
6982
const { data: feedbackConfig } = useFetch('/api/feedback/config', {
7083
key: 'feedback-config',
7184
headers: useRequestHeaders(['cookie']),
@@ -303,6 +316,13 @@ onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
303316
<span class="text-sm font-semibold text-surface-900 dark:text-surface-100 truncate max-w-48">
304317
{{ activeJobTitle }}
305318
</span>
319+
<span
320+
v-if="activeJobStatus"
321+
class="inline-flex shrink-0 items-center rounded-md px-1.5 py-0.5 text-[10px] font-semibold capitalize ring-1 ring-inset"
322+
:class="jobStatusBadgeClasses[activeJobStatus] ?? 'bg-surface-50 text-surface-600 ring-surface-200'"
323+
>
324+
{{ activeJobStatus }}
325+
</span>
306326
</div>
307327

308328
<nav class="flex items-center gap-0.5 ml-2">
@@ -319,6 +339,10 @@ onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
319339
{{ tab.label }}
320340
</NuxtLink>
321341
</nav>
342+
343+
<div class="ml-auto flex items-center gap-2">
344+
<div id="job-sub-nav-actions" />
345+
</div>
322346
</div>
323347
</div>
324348
</Transition>

app/pages/dashboard/jobs/[id]/candidates.vue

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { Table2, Users, SlidersHorizontal, X, Check, ChevronsUpDown, ChevronUp, ChevronDown, UserRound } from 'lucide-vue-next'
2+
import { Users, SlidersHorizontal, X, Check, ChevronsUpDown, ChevronUp, ChevronDown, UserRound } from 'lucide-vue-next'
33
44
definePageMeta({
55
layout: 'dashboard',
@@ -248,21 +248,6 @@ const isLoading = computed(() => jobFetchStatus.value === 'pending' || appFetchS
248248
</div>
249249

250250
<template v-else-if="jobData">
251-
<!-- Header -->
252-
<div class="flex items-center gap-3.5 mb-6">
253-
<div class="flex size-9 items-center justify-center rounded-xl bg-brand-50 ring-1 ring-brand-100 dark:bg-brand-950/60 dark:ring-brand-900/40">
254-
<Table2 class="size-4.5 text-brand-600 dark:text-brand-400" />
255-
</div>
256-
<div>
257-
<h1 class="text-lg font-semibold tracking-tight text-surface-900 dark:text-surface-50">
258-
{{ jobData.title }}
259-
</h1>
260-
<p class="mt-0.5 text-[13px] text-surface-500 dark:text-surface-400">
261-
{{ total }} candidate{{ total === 1 ? '' : 's' }} applied
262-
</p>
263-
</div>
264-
</div>
265-
266251
<!-- Toolbar -->
267252
<div class="flex items-center gap-3 mb-4">
268253
<!-- Column / filter picker -->

app/pages/dashboard/jobs/[id]/index.vue

Lines changed: 74 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import {
33
ArrowLeft, ArrowRight, Briefcase, Clock, Hash, UserRound, Mail, MessageSquare,
4-
Kanban, FileText, Paperclip, Download, Eye, Phone, Search, ExternalLink,
4+
FileText, Paperclip, Download, Eye, Phone, Search, ExternalLink,
55
UserPlus, Pencil, Trash2, MoreHorizontal, Globe, ChevronDown,
66
} from 'lucide-vue-next'
77
import { z } from 'zod'
@@ -577,126 +577,94 @@ const isLoading = computed(() => {
577577
</div>
578578

579579
<template v-else-if="jobData">
580-
<!-- ═══════════════════════════════════════ -->
581-
<!-- TOP HEADER with job info + quick actions -->
582-
<!-- ═══════════════════════════════════════ -->
583-
<div class="shrink-0 border-b border-surface-200/80 bg-gradient-to-r from-white via-white to-surface-50/50 px-6 py-4 dark:border-surface-800/60 dark:from-surface-900 dark:via-surface-900 dark:to-surface-950/50">
584-
<div class="flex items-center justify-between gap-4">
585-
<div class="flex items-center gap-3.5 min-w-0">
586-
<div class="flex size-9 items-center justify-center rounded-xl bg-brand-50 ring-1 ring-brand-100 dark:bg-brand-950/60 dark:ring-brand-900/40">
587-
<Kanban class="size-4.5 text-brand-600 dark:text-brand-400" />
588-
</div>
589-
<div class="min-w-0">
590-
<div class="flex items-center gap-2.5">
591-
<h1 class="text-lg font-semibold tracking-tight text-surface-900 dark:text-surface-50 truncate">
592-
{{ jobData.title }}
593-
</h1>
594-
<span
595-
class="inline-flex shrink-0 items-center rounded-md px-2 py-0.5 text-[11px] font-semibold capitalize ring-1 ring-inset"
596-
:class="{
597-
'bg-surface-50 text-surface-600 ring-surface-200 dark:bg-surface-800/60 dark:text-surface-400 dark:ring-surface-700': jobData.status === 'draft',
598-
'bg-success-50 text-success-700 ring-success-200 dark:bg-success-950/60 dark:text-success-400 dark:ring-success-800': jobData.status === 'open',
599-
'bg-warning-50 text-warning-700 ring-warning-200 dark:bg-warning-950/60 dark:text-warning-400 dark:ring-warning-800': jobData.status === 'closed',
600-
'bg-surface-50 text-surface-400 ring-surface-200 dark:bg-surface-800/60 dark:text-surface-500 dark:ring-surface-700': jobData.status === 'archived',
601-
}"
602-
>
603-
{{ jobData.status }}
604-
</span>
605-
</div>
606-
<p class="mt-0.5 text-[13px] text-surface-500 dark:text-surface-400">
607-
{{ applications.length }} application{{ applications.length === 1 ? '' : 's' }}
608-
</p>
609-
</div>
610-
</div>
580+
<!-- Quick actions teleported to sub-nav bar -->
581+
<Teleport to="#job-sub-nav-actions">
582+
<div class="flex items-center gap-2">
583+
<!-- Add Candidate -->
584+
<button
585+
class="hidden sm:inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-surface-200 dark:border-surface-700/80 px-2.5 py-1 text-[11px] font-medium text-surface-600 dark:text-surface-300 hover:bg-white hover:border-surface-300 dark:hover:bg-surface-800 dark:hover:border-surface-600 transition-all duration-150"
586+
@click="showApplyModal = true"
587+
>
588+
<UserPlus class="size-3" />
589+
Add Candidate
590+
</button>
611591

612-
<!-- Quick actions -->
613-
<div class="flex items-center gap-2 shrink-0">
614-
<!-- Add Candidate -->
615-
<button
616-
class="hidden sm:inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-surface-200 dark:border-surface-700/80 px-3 py-1.5 text-xs font-medium text-surface-600 dark:text-surface-300 hover:bg-surface-50 hover:border-surface-300 dark:hover:bg-surface-800 dark:hover:border-surface-600 transition-all duration-150 shadow-sm"
617-
@click="showApplyModal = true"
618-
>
619-
<UserPlus class="size-3.5" />
620-
Add Candidate
621-
</button>
592+
<!-- Primary job action (e.g., Publish) -->
593+
<button
594+
v-if="primaryJobTransition"
595+
:disabled="isJobTransitioning"
596+
class="inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-semibold transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed"
597+
:class="jobTransitionClasses[primaryJobTransition] ?? 'border border-surface-300 text-surface-600 hover:bg-surface-50'"
598+
@click="handleJobTransition(primaryJobTransition)"
599+
>
600+
{{ jobTransitionLabels[primaryJobTransition] ?? primaryJobTransition }}
601+
</button>
622602

623-
<!-- Primary job action (e.g., Publish) -->
603+
<!-- More menu -->
604+
<div ref="moreMenuRef" class="relative">
624605
<button
625-
v-if="primaryJobTransition"
626-
:disabled="isJobTransitioning"
627-
class="inline-flex cursor-pointer items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-xs font-semibold transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
628-
:class="jobTransitionClasses[primaryJobTransition] ?? 'border border-surface-300 text-surface-600 hover:bg-surface-50'"
629-
@click="handleJobTransition(primaryJobTransition)"
606+
class="inline-flex cursor-pointer items-center justify-center rounded-md border border-surface-200 dark:border-surface-700/80 p-1 text-surface-500 hover:bg-white hover:text-surface-700 dark:hover:bg-surface-800 dark:hover:text-surface-300 transition-all duration-150"
607+
@click="showMoreMenu = !showMoreMenu"
630608
>
631-
{{ jobTransitionLabels[primaryJobTransition] ?? primaryJobTransition }}
609+
<MoreHorizontal class="size-3.5" />
632610
</button>
633611

634-
<!-- More menu -->
635-
<div ref="moreMenuRef" class="relative">
636-
<button
637-
class="inline-flex cursor-pointer items-center justify-center rounded-lg border border-surface-200 dark:border-surface-700/80 p-1.5 text-surface-500 hover:bg-surface-50 hover:text-surface-700 dark:hover:bg-surface-800 dark:hover:text-surface-300 transition-all duration-150 shadow-sm"
638-
@click="showMoreMenu = !showMoreMenu"
639-
>
640-
<MoreHorizontal class="size-4" />
641-
</button>
642-
643-
<Transition
644-
enter-active-class="transition duration-150 ease-out"
645-
enter-from-class="opacity-0 scale-95 -translate-y-1"
646-
enter-to-class="opacity-100 scale-100 translate-y-0"
647-
leave-active-class="transition duration-100 ease-in"
648-
leave-from-class="opacity-100 scale-100 translate-y-0"
649-
leave-to-class="opacity-0 scale-95 -translate-y-1"
612+
<Transition
613+
enter-active-class="transition duration-150 ease-out"
614+
enter-from-class="opacity-0 scale-95 -translate-y-1"
615+
enter-to-class="opacity-100 scale-100 translate-y-0"
616+
leave-active-class="transition duration-100 ease-in"
617+
leave-from-class="opacity-100 scale-100 translate-y-0"
618+
leave-to-class="opacity-0 scale-95 -translate-y-1"
619+
>
620+
<div
621+
v-if="showMoreMenu"
622+
class="absolute right-0 top-full mt-1.5 z-50 w-52 rounded-xl border border-surface-200 dark:border-surface-700/80 bg-white dark:bg-surface-900 shadow-xl shadow-surface-900/5 dark:shadow-black/20 py-1.5 origin-top-right"
650623
>
651-
<div
652-
v-if="showMoreMenu"
653-
class="absolute right-0 top-full mt-1.5 z-50 w-52 rounded-xl border border-surface-200 dark:border-surface-700/80 bg-white dark:bg-surface-900 shadow-xl shadow-surface-900/5 dark:shadow-black/20 py-1.5 origin-top-right"
624+
<button
625+
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors"
626+
@click="startEdit"
654627
>
655-
<button
656-
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors"
657-
@click="startEdit"
658-
>
659-
<Pencil class="size-3.5 text-surface-400" />
660-
Edit Job
661-
</button>
662-
<button
663-
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors sm:hidden"
664-
@click="showApplyModal = true; showMoreMenu = false"
665-
>
666-
<UserPlus class="size-3.5 text-surface-400" />
667-
Add Candidate
668-
</button>
669-
<template v-if="secondaryJobTransitions.length > 0">
670-
<div class="border-t border-surface-100 dark:border-surface-800 my-1.5 mx-2" />
671-
<button
672-
v-for="t in secondaryJobTransitions"
673-
:key="t"
674-
:disabled="isJobTransitioning"
675-
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors disabled:opacity-50"
676-
@click="handleJobTransition(t); showMoreMenu = false"
677-
>
678-
{{ jobTransitionLabels[t] ?? t }}
679-
</button>
680-
</template>
628+
<Pencil class="size-3.5 text-surface-400" />
629+
Edit Job
630+
</button>
631+
<button
632+
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors sm:hidden"
633+
@click="showApplyModal = true; showMoreMenu = false"
634+
>
635+
<UserPlus class="size-3.5 text-surface-400" />
636+
Add Candidate
637+
</button>
638+
<template v-if="secondaryJobTransitions.length > 0">
681639
<div class="border-t border-surface-100 dark:border-surface-800 my-1.5 mx-2" />
682640
<button
683-
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-950/60 transition-colors"
684-
@click="showDeleteConfirm = true; showMoreMenu = false"
641+
v-for="t in secondaryJobTransitions"
642+
:key="t"
643+
:disabled="isJobTransitioning"
644+
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-surface-700 dark:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800/80 transition-colors disabled:opacity-50"
645+
@click="handleJobTransition(t); showMoreMenu = false"
685646
>
686-
<Trash2 class="size-3.5" />
687-
Delete Job
647+
{{ jobTransitionLabels[t] ?? t }}
688648
</button>
689-
</div>
690-
</Transition>
691-
</div>
649+
</template>
650+
<div class="border-t border-surface-100 dark:border-surface-800 my-1.5 mx-2" />
651+
<button
652+
class="flex w-full cursor-pointer items-center gap-2.5 px-3.5 py-2 text-sm text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-950/60 transition-colors"
653+
@click="showDeleteConfirm = true; showMoreMenu = false"
654+
>
655+
<Trash2 class="size-3.5" />
656+
Delete Job
657+
</button>
658+
</div>
659+
</Transition>
660+
</div>
692661

693-
<div class="hidden sm:flex items-center gap-1 rounded-lg bg-surface-100/80 px-2.5 py-1 text-[11px] font-medium text-surface-400 dark:bg-surface-800/60 dark:text-surface-500">
694-
<span class="font-mono text-[10px]">↑↓</span>
695-
<span>navigate</span>
696-
</div>
662+
<div class="hidden sm:flex items-center gap-1 rounded-md bg-surface-100/80 px-2 py-0.5 text-[10px] font-medium text-surface-400 dark:bg-surface-800/60 dark:text-surface-500">
663+
<span class="font-mono text-[10px]">↑↓</span>
664+
<span>navigate</span>
697665
</div>
698666
</div>
699-
</div>
667+
</Teleport>
700668

701669
<!-- ═══════════════════════════════════════ -->
702670
<!-- PIPELINE STATUS TABS -->

0 commit comments

Comments
 (0)