diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 89576ca..8fd5a76 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -37,7 +37,7 @@ export const router = createRouter({ { path: 'developer', component: () => import('../views/settings/DeveloperView.vue') }, ], }, - // AI profile wizard — standalone full-page chat + // AI profile wizard — post-setup settings entry point (correctly blocked by wizard gate during onboarding) { path: '/wizard/ai-profile', component: () => import('../views/wizard/WizardAIView.vue') }, // Onboarding wizard — full-page layout, no AppNav { diff --git a/web/src/stores/appConfig.ts b/web/src/stores/appConfig.ts index 0ae85bb..542625b 100644 --- a/web/src/stores/appConfig.ts +++ b/web/src/stores/appConfig.ts @@ -2,7 +2,7 @@ import { ref } from 'vue' import { defineStore } from 'pinia' import { useApiFetch } from '../composables/useApi' -export type Tier = 'free' | 'paid' | 'premium' | 'ultra' +export type Tier = 'free' | 'paid' | 'premium' export type InferenceProfile = 'remote' | 'cpu' | 'single-gpu' | 'dual-gpu' export const useAppConfigStore = defineStore('appConfig', () => { diff --git a/web/src/stores/wizard/__tests__/aiInterview.test.ts b/web/src/stores/wizard/__tests__/aiInterview.test.ts index ae582f4..70278f9 100644 --- a/web/src/stores/wizard/__tests__/aiInterview.test.ts +++ b/web/src/stores/wizard/__tests__/aiInterview.test.ts @@ -157,6 +157,44 @@ describe('useAiInterviewStore', () => { expect(store.error).toBe('Failed to save profile. Please try again.') }) + // ── skip() ───────────────────────────────────────────────────────────────── + + it('skip() sends the skip signal to the backend', async () => { + mockFetch.mockResolvedValue({ + data: { reply: 'No problem, moving on.', extracted_fields: {}, complete: false }, + error: null, + }) + + const store = useAiInterviewStore() + await store.skip() + + expect(mockFetch).toHaveBeenCalledWith( + '/api/wizard/ai/interview', + expect.objectContaining({ method: 'POST' }), + ) + const body = JSON.parse((mockFetch.mock.calls[0][1] as { body: string }).body) + expect(body.history[0]).toEqual({ role: 'user', content: 'skip' }) + }) + + // ── keepChatting() ───────────────────────────────────────────────────────── + + it('keepChatting() clears the complete flag without resetting messages', async () => { + mockFetch.mockResolvedValue({ + data: { reply: 'All done!', extracted_fields: { name: 'Alice' }, complete: true }, + error: null, + }) + + const store = useAiInterviewStore() + await store.send('done') + expect(store.complete).toBe(true) + + store.keepChatting() + + expect(store.complete).toBe(false) + expect(store.messages.length).toBeGreaterThan(0) + expect(store.fields).toEqual({ name: 'Alice' }) + }) + // ── startOver() ──────────────────────────────────────────────────────────── it('startOver() resets all state and clears localStorage', async () => { diff --git a/web/src/stores/wizard/aiInterview.ts b/web/src/stores/wizard/aiInterview.ts index a66fa59..220c226 100644 --- a/web/src/stores/wizard/aiInterview.ts +++ b/web/src/stores/wizard/aiInterview.ts @@ -2,7 +2,8 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { useApiFetch } from '../../composables/useApi' -const LS_KEY = 'peregrine:wizard-draft' +const LS_KEY = 'peregrine:wizard-draft' +const SKIP_SIGNAL = 'skip' export interface ChatMessage { role: 'user' | 'assistant' @@ -40,6 +41,7 @@ export const useAiInterviewStore = defineStore('aiInterview', () => { if (loading.value) return if (userText !== '') { messages.value = [...messages.value, { role: 'user', content: userText }] + _persist() } loading.value = true error.value = null @@ -78,6 +80,14 @@ export const useAiInterviewStore = defineStore('aiInterview', () => { return true } + function skip() { + return send(SKIP_SIGNAL) + } + + function keepChatting() { + complete.value = false + } + function startOver() { messages.value = [] fields.value = {} @@ -86,5 +96,5 @@ export const useAiInterviewStore = defineStore('aiInterview', () => { localStorage.removeItem(LS_KEY) } - return { messages, fields, complete, loading, saving, error, restore, send, finalize, startOver } + return { messages, fields, complete, loading, saving, error, restore, send, skip, finalize, keepChatting, startOver } }) diff --git a/web/src/views/settings/MyProfileView.vue b/web/src/views/settings/MyProfileView.vue index eb238b9..e15727b 100644 --- a/web/src/views/settings/MyProfileView.vue +++ b/web/src/views/settings/MyProfileView.vue @@ -5,6 +5,26 @@

Your identity and preferences used for cover letters, research, and interview prep.

+ +
+
+ +
+

Set up your profile with AI

+

+ + +

+
+
+ + Start AI setup + + + Upgrade + +
+
Loading profile…