fix: independent try/catch per onboarding detection call
This commit is contained in:
165
src/stores/onboarding.ts
Normal file
165
src/stores/onboarding.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { useSettingsStore } from './settings'
|
||||
|
||||
export type ChecklistItemKey =
|
||||
| 'create_client'
|
||||
| 'create_project'
|
||||
| 'track_entry'
|
||||
| 'review_entries'
|
||||
| 'create_invoice'
|
||||
| 'explore_reports'
|
||||
|
||||
export interface ChecklistItem {
|
||||
key: ChecklistItemKey
|
||||
label: string
|
||||
description: string
|
||||
route: string
|
||||
tourId: string
|
||||
completed: boolean
|
||||
}
|
||||
|
||||
interface ChecklistState {
|
||||
dismissed: boolean
|
||||
items: Record<ChecklistItemKey, { completed: boolean }>
|
||||
}
|
||||
|
||||
const DEFAULT_STATE: ChecklistState = {
|
||||
dismissed: false,
|
||||
items: {
|
||||
create_client: { completed: false },
|
||||
create_project: { completed: false },
|
||||
track_entry: { completed: false },
|
||||
review_entries: { completed: false },
|
||||
create_invoice: { completed: false },
|
||||
explore_reports: { completed: false },
|
||||
},
|
||||
}
|
||||
|
||||
const ITEM_DEFINITIONS: Omit<ChecklistItem, 'completed'>[] = [
|
||||
{ key: 'create_client', label: 'Create your first client', description: 'Add a client to bill your work to', route: '/clients', tourId: 'clients' },
|
||||
{ key: 'create_project', label: 'Create a project', description: 'Organize your work under a project', route: '/projects', tourId: 'projects' },
|
||||
{ key: 'track_entry', label: 'Track your first time entry', description: 'Start the timer and log some work', route: '/timer', tourId: 'timer' },
|
||||
{ key: 'review_entries', label: 'Review your entries', description: 'See your tracked time in list or timeline view', route: '/entries', tourId: 'entries' },
|
||||
{ key: 'create_invoice', label: 'Create an invoice', description: 'Generate an invoice from tracked time', route: '/invoices', tourId: 'invoices' },
|
||||
{ key: 'explore_reports', label: 'Explore reports', description: 'View hours, profitability, and expense reports', route: '/reports', tourId: 'reports' },
|
||||
]
|
||||
|
||||
export const useOnboardingStore = defineStore('onboarding', () => {
|
||||
const state = ref<ChecklistState>({ ...DEFAULT_STATE })
|
||||
const loaded = ref(false)
|
||||
|
||||
const items = computed<ChecklistItem[]>(() =>
|
||||
ITEM_DEFINITIONS.map(def => ({
|
||||
...def,
|
||||
completed: state.value.items[def.key]?.completed || false,
|
||||
}))
|
||||
)
|
||||
|
||||
const completedCount = computed(() => items.value.filter(i => i.completed).length)
|
||||
const totalCount = computed(() => items.value.length)
|
||||
const allComplete = computed(() => completedCount.value >= totalCount.value)
|
||||
const isDismissed = computed(() => state.value.dismissed)
|
||||
const isVisible = computed(() => loaded.value && !state.value.dismissed && !allComplete.value)
|
||||
|
||||
async function load() {
|
||||
const settingsStore = useSettingsStore()
|
||||
const raw = settingsStore.settings.onboarding_checklist
|
||||
if (raw) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as ChecklistState
|
||||
state.value = { ...DEFAULT_STATE, ...parsed, items: { ...DEFAULT_STATE.items, ...parsed.items } }
|
||||
} catch {
|
||||
state.value = { ...DEFAULT_STATE }
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-detect data-based completions
|
||||
await detectCompletions()
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
async function detectCompletions() {
|
||||
try {
|
||||
const clients = await invoke<any[]>('get_clients')
|
||||
if (clients.length > 0) markComplete('create_client')
|
||||
} catch { /* individual failure - continue */ }
|
||||
|
||||
try {
|
||||
const projects = await invoke<any[]>('get_projects')
|
||||
if (projects.length > 0) markComplete('create_project')
|
||||
} catch { /* individual failure - continue */ }
|
||||
|
||||
try {
|
||||
const entries = await invoke<any[]>('get_time_entries', { startDate: null, endDate: null })
|
||||
if (entries.length > 0) markComplete('track_entry')
|
||||
} catch { /* individual failure - continue */ }
|
||||
|
||||
try {
|
||||
const invoices = await invoke<any[]>('get_invoices')
|
||||
if (invoices.length > 0) markComplete('create_invoice')
|
||||
} catch { /* individual failure - continue */ }
|
||||
}
|
||||
|
||||
function markComplete(key: ChecklistItemKey) {
|
||||
if (!state.value.items[key].completed) {
|
||||
state.value.items[key].completed = true
|
||||
persist()
|
||||
}
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
state.value.dismissed = true
|
||||
persist()
|
||||
}
|
||||
|
||||
function restore() {
|
||||
state.value.dismissed = false
|
||||
persist()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.value = {
|
||||
dismissed: false,
|
||||
items: {
|
||||
create_client: { completed: false },
|
||||
create_project: { completed: false },
|
||||
track_entry: { completed: false },
|
||||
review_entries: { completed: false },
|
||||
create_invoice: { completed: false },
|
||||
explore_reports: { completed: false },
|
||||
},
|
||||
}
|
||||
persist()
|
||||
}
|
||||
|
||||
async function persist() {
|
||||
const settingsStore = useSettingsStore()
|
||||
await settingsStore.updateSetting('onboarding_checklist', JSON.stringify(state.value))
|
||||
}
|
||||
|
||||
// Called from router guard when user visits specific pages
|
||||
function onRouteVisit(path: string) {
|
||||
if (path === '/entries') markComplete('review_entries')
|
||||
if (path === '/reports') markComplete('explore_reports')
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
loaded,
|
||||
items,
|
||||
completedCount,
|
||||
totalCount,
|
||||
allComplete,
|
||||
isDismissed,
|
||||
isVisible,
|
||||
load,
|
||||
detectCompletions,
|
||||
markComplete,
|
||||
dismiss,
|
||||
restore,
|
||||
reset,
|
||||
onRouteVisit,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user