diff --git a/.env.example b/.env.example index 2f3a864..bd7a4d7 100644 --- a/.env.example +++ b/.env.example @@ -103,7 +103,12 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000 # When configured, "Continue with " buttons appear on the auth pages. # Google — Create credentials at https://console.cloud.google.com/apis/credentials -# Redirect URI: https://yourdomain.com/api/auth/callback/google +# Redirect URIs (add BOTH): +# https://yourdomain.com/api/auth/callback/google +# https://yourdomain.com/api/portal/auth/google/callback +# For local dev, also add: +# http://localhost:3000/api/auth/callback/google +# http://localhost:3000/api/portal/auth/google/callback # AUTH_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com # AUTH_GOOGLE_CLIENT_SECRET=GOCSPX-your-google-client-secret diff --git a/app/components/portal/InterviewCard.vue b/app/components/portal/InterviewCard.vue new file mode 100644 index 0000000..7d19846 --- /dev/null +++ b/app/components/portal/InterviewCard.vue @@ -0,0 +1,95 @@ + + + diff --git a/app/components/portal/PipelineProgress.vue b/app/components/portal/PipelineProgress.vue new file mode 100644 index 0000000..a239de0 --- /dev/null +++ b/app/components/portal/PipelineProgress.vue @@ -0,0 +1,152 @@ + + + diff --git a/app/components/portal/StatusBadge.vue b/app/components/portal/StatusBadge.vue new file mode 100644 index 0000000..0ee38ff --- /dev/null +++ b/app/components/portal/StatusBadge.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/components/portal/StatusTimeline.vue b/app/components/portal/StatusTimeline.vue new file mode 100644 index 0000000..99ef2ee --- /dev/null +++ b/app/components/portal/StatusTimeline.vue @@ -0,0 +1,111 @@ + + + diff --git a/app/layouts/portal.vue b/app/layouts/portal.vue new file mode 100644 index 0000000..15c5581 --- /dev/null +++ b/app/layouts/portal.vue @@ -0,0 +1,46 @@ + + + diff --git a/app/pages/jobs/[slug]/apply.vue b/app/pages/jobs/[slug]/apply.vue index 9050f13..8358087 100644 --- a/app/pages/jobs/[slug]/apply.vue +++ b/app/pages/jobs/[slug]/apply.vue @@ -163,6 +163,8 @@ async function handleSubmit() { const hasAnyFiles = Object.keys(fileUploads.value).length > 0 || !!resumeFile.value + let result: { success: boolean; portalToken?: string | null } | undefined + if (hasAnyFiles) { // Use FormData when files are present const formData = new FormData() @@ -201,13 +203,13 @@ async function handleSubmit() { if (utmTerm) formData.append('utmTerm', utmTerm) if (utmContent) formData.append('utmContent', utmContent) - await $fetch(`/api/public/jobs/${jobSlug}/apply`, { + result = await $fetch<{ success: boolean; portalToken?: string | null }>(`/api/public/jobs/${jobSlug}/apply`, { method: 'POST', body: formData, }) } else { // No files — use JSON as before - await $fetch(`/api/public/jobs/${jobSlug}/apply`, { + result = await $fetch<{ success: boolean; portalToken?: string | null }>(`/api/public/jobs/${jobSlug}/apply`, { method: 'POST', body: { firstName: form.value.firstName.trim(), @@ -228,7 +230,13 @@ async function handleSubmit() { } track('application_submitted', { slug: jobSlug }) - await navigateTo(`/jobs/${jobSlug}/confirmation`) + // Redirect directly to the portal dashboard if we have a token, + // otherwise fall back to the confirmation page + if (result?.portalToken) { + await navigateTo(`/portal/t/${result.portalToken}?fresh=1`) + } else { + await navigateTo({ path: `/jobs/${jobSlug}/confirmation` }) + } } catch (err: any) { const message = err.data?.statusMessage ?? 'Something went wrong. Please try again.' submitError.value = message diff --git a/app/pages/jobs/[slug]/confirmation.vue b/app/pages/jobs/[slug]/confirmation.vue index 329443a..7e67760 100644 --- a/app/pages/jobs/[slug]/confirmation.vue +++ b/app/pages/jobs/[slug]/confirmation.vue @@ -1,5 +1,5 @@