Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
be8f623
feat: add interview validation schemas for creation, updating, and qu…
JoachimLK Mar 10, 2026
59bdb36
feat: add interviews dashboard page with filtering, editing, and dele…
JoachimLK Mar 10, 2026
7879e38
feat: add email template validation schemas and pre-made templates
JoachimLK Mar 10, 2026
616ada5
feat: add email template management system
JoachimLK Mar 10, 2026
771917f
feat: integrate email template selection for interview invitations
JoachimLK Mar 10, 2026
0eb29b0
feat: add interview scheduling functionality with sidebar integration
JoachimLK Mar 10, 2026
ef5cdbb
feat: enhance interview management with inline editing and rescheduli…
JoachimLK Mar 10, 2026
27f179e
feat: implement advanced filtering and sorting options for job applic…
JoachimLK Mar 10, 2026
6033d06
feat: refactor interview management with enhanced status transitions …
JoachimLK Mar 10, 2026
e05b877
feat: centralize system email templates in shared module for improved…
JoachimLK Mar 10, 2026
a60f489
feat(dashboard): enhance job management and pipeline tracking
JoachimLK Mar 11, 2026
a94e4b6
feat: add fullscreen toggle functionality to job detail view
JoachimLK Mar 11, 2026
78a3ae8
feat: add teleport target prop to modals for improved flexibility in …
JoachimLK Mar 11, 2026
2c01f77
feat: make candidate email addresses clickable for improved user inte…
JoachimLK Mar 11, 2026
9188d3b
feat: implement sortable candidate and application tables with improv…
JoachimLK Mar 11, 2026
ddda624
feat: improve date formatting helper to return local timezone date st…
JoachimLK Mar 11, 2026
22e6a0a
feat: add functionality to move applications directly to interview st…
JoachimLK Mar 11, 2026
57e692a
feat: add iCalendar generation for interview invitations
JoachimLK Mar 11, 2026
08f778a
feat(calendar): add Google Calendar integration with OAuth2 flow
JoachimLK Mar 11, 2026
be9ccbd
feat(google-calendar): update integration instructions and add enviro…
JoachimLK Mar 11, 2026
7f19a06
fix the drizzle schema and improved org switcher
JoachimLK Mar 11, 2026
bb5244a
feat(calendar): add Google Calendar sync status indicators in intervi…
JoachimLK Mar 11, 2026
c46d13d
feat: add script to backfill google_calendar_event_link for existing …
JoachimLK Mar 11, 2026
a935615
fix: correct promise chaining for Google Calendar event creation
JoachimLK Mar 11, 2026
6c942d0
feat(interview): add Google Calendar notification preferences and cus…
JoachimLK Mar 11, 2026
58810b1
feat(interview): enhance interview scheduling with Google Calendar in…
JoachimLK Mar 11, 2026
140d6ac
feat(calendar): update webhook renewal to require specific permission…
JoachimLK Mar 11, 2026
457af10
feat: enhance webhook handling with cron secret validation and improv…
JoachimLK Mar 11, 2026
728feb2
feat(dark mode): enhance checkbox and radio styles for dark mode rend…
JoachimLK Mar 11, 2026
b75b0e5
feat(dashboard): update job pipeline display logic to use application…
JoachimLK Mar 11, 2026
49976c3
feat(AppTopBar): remove unused transition classes for user menu
JoachimLK Mar 11, 2026
8c2e225
feat: add Greenhouse vs Open Source ATS comparison article and enhanc…
JoachimLK Mar 12, 2026
c46549e
fix: update G2 ranking link for Greenhouse in ATS comparison article
JoachimLK Mar 12, 2026
b9533fe
refactor: simplify refreshNuxtData calls in useInterview composable a…
JoachimLK Mar 12, 2026
de61770
Merge branch 'main' into feat/interview-scheduling
JoachimLK Mar 12, 2026
3968d01
chore: update package overrides and dependencies
JoachimLK Mar 12, 2026
665e059
fix: cast return type of getAuth function to Auth
JoachimLK Mar 12, 2026
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
244 changes: 0 additions & 244 deletions app/components/AppSidebar.vue

This file was deleted.

3 changes: 2 additions & 1 deletion app/components/AppTopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
Kanban, FileText, LogOut, Table2,
Sun, Moon, MessageSquarePlus, Settings,
ChevronDown, Menu, X, Users, ChevronLeft,
LayoutDashboard,
LayoutDashboard, Calendar,
} from 'lucide-vue-next'

const route = useRoute()
Expand Down Expand Up @@ -104,6 +104,7 @@ const mainNav = [
{ label: 'Jobs', to: '/dashboard', icon: Briefcase, exact: true },
{ label: 'Candidates', to: '/dashboard/candidates', icon: Users, exact: false },
{ label: 'Applications', to: '/dashboard/applications', icon: FileText, exact: false },
{ label: 'Interviews', to: '/dashboard/interviews', icon: Calendar, exact: false },
{ label: 'Settings', to: '/dashboard/settings', icon: Settings, exact: false },
]

Expand Down
41 changes: 34 additions & 7 deletions app/components/CandidateDetailSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@ function formatResponseValue(value: unknown): string {
}

const responsesCount = computed(() => application.value?.responses?.length ?? 0)

// ─────────────────────────────────────────────
// Interview scheduling
// ─────────────────────────────────────────────

const showScheduleSidebar = ref(false)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Dismiss the nested scheduler before closing the parent sidebar.

This sidebar is rendered outside the <aside> and is not tied to open, so the header close button and the existing Escape path can close the parent while InterviewScheduleSidebar stays mounted or reopens on the next open. It also is not reset on applicationId changes, which can preserve stale initial form state because app/components/InterviewScheduleSidebar.vue only seeds its form on mount (Line 56-62).

⚙️ Suggested fix
 function onKeydown(e: KeyboardEvent) {
   if (e.key === 'Escape') {
     if (showPreview.value) {
       closePreview()
     } else if (showDocDeleteConfirm.value) {
       showDocDeleteConfirm.value = null
+    } else if (showScheduleSidebar.value) {
+      showScheduleSidebar.value = false
     } else {
       emit('close')
     }
   }
 }
 
+watch(() => props.open, (isOpen) => {
+  if (!isOpen) showScheduleSidebar.value = false
+})
+
 // Reset state when switching to a different application
 watch(() => props.applicationId, () => {
   isEditingNotes.value = false
   activeTab.value = 'overview'
   uploadError.value = null
   showDocDeleteConfirm.value = null
+  showScheduleSidebar.value = false
   closePreview()
 })
 
 <InterviewScheduleSidebar
-  v-if="showScheduleSidebar && application"
+  v-if="open && showScheduleSidebar && application"
   :application-id="props.applicationId"
   :candidate-name="`${application.candidate.firstName} ${application.candidate.lastName}`"
   :job-title="application.job?.title ?? ''"

Also applies to: 345-362, 784-792

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/CandidateDetailSidebar.vue` at line 308, The nested
InterviewScheduleSidebar (controlled by showScheduleSidebar) can remain mounted
or reopen when the parent aside closes and doesn't reset on applicationId
changes; update the parent component so that whenever the parent is closed (the
same path that flips open/close or the header close action) it also sets
showScheduleSidebar = false and/or emits a dismiss to InterviewScheduleSidebar,
and add a watcher on applicationId to reset showScheduleSidebar (or re-key
InterviewScheduleSidebar) so the nested form is unmounted and reseeded on app
change; locate and modify the places using showScheduleSidebar, the parent close
handler, and any open toggle to ensure the nested sidebar is dismissed before or
as part of closing the parent.

</script>

<template>
Expand Down Expand Up @@ -336,13 +342,24 @@ const responsesCount = computed(() => application.value?.responses?.length ?? 0)
<div v-else class="min-w-0">
<h2 class="text-lg font-semibold text-surface-400">Loading…</h2>
</div>
<button
class="rounded-md p-1.5 text-surface-400 hover:text-surface-600 hover:bg-surface-100 dark:hover:text-surface-300 dark:hover:bg-surface-800 transition-colors shrink-0 ml-3"
title="Close (Esc)"
@click="emit('close')"
>
<X class="size-5" />
</button>
<div class="flex items-center gap-1 shrink-0 ml-3">
<button
v-if="application"
class="inline-flex items-center gap-1.5 rounded-lg border border-surface-300 dark:border-surface-700 px-2.5 py-1.5 text-sm font-medium text-surface-600 dark:text-surface-400 hover:border-brand-400 dark:hover:border-brand-600 hover:bg-brand-50 dark:hover:bg-brand-950/30 hover:text-brand-700 dark:hover:text-brand-300 transition-all cursor-pointer"
title="Schedule Interview"
@click="showScheduleSidebar = true"
>
<Calendar class="size-3.5" />
Schedule
</button>
<button
class="rounded-md p-1.5 text-surface-400 hover:text-surface-600 hover:bg-surface-100 dark:hover:text-surface-300 dark:hover:bg-surface-800 transition-colors"
title="Close (Esc)"
@click="emit('close')"
>
<X class="size-5" />
</button>
</div>
</div>

<!-- Tabs -->
Expand Down Expand Up @@ -764,6 +781,16 @@ const responsesCount = computed(() => application.value?.responses?.length ?? 0)
</aside>
</Transition>

<!-- Interview Schedule Sidebar -->
<InterviewScheduleSidebar
v-if="showScheduleSidebar && application"
:application-id="props.applicationId"
:candidate-name="`${application.candidate.firstName} ${application.candidate.lastName}`"
:job-title="application.job?.title ?? ''"
@close="showScheduleSidebar = false"
@scheduled="showScheduleSidebar = false"
/>



<!-- Document delete confirmation dialog -->
Expand Down
Loading
Loading