pounce/frontend/src/lib/store.ts
Yves Gugger fde66af049
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Watchlist layout fix + TLD detail + Sniper/Yield/Listing redesign + deploy script
2025-12-13 15:39:51 +01:00

207 lines
4.8 KiB
TypeScript

/**
* Global state management with Zustand
*/
import { create } from 'zustand'
import { api } from './api'
interface User {
id: number
email: string
name: string | null
is_admin?: boolean
is_verified?: boolean
}
interface Domain {
id: number
name: string
status: string
is_available: boolean
registrar: string | null
expiration_date: string | null
notify_on_available: boolean
created_at: string
last_checked: string | null
}
interface Subscription {
tier: string
tier_name?: string
domain_limit: number
domains_used: number
portfolio_limit?: number
check_frequency?: string
history_days?: number
features?: {
email_alerts: boolean
priority_alerts: boolean
full_whois: boolean
expiration_tracking: boolean
domain_valuation: boolean
market_insights: boolean
api_access: boolean
webhooks: boolean
bulk_tools: boolean
seo_metrics: boolean
}
}
interface AppState {
// Auth
user: User | null
isAuthenticated: boolean
isLoading: boolean
// Domains
domains: Domain[]
domainsTotal: number
domainsPage: number
// Subscription
subscription: Subscription | null
// Actions
login: (email: string, password: string) => Promise<void>
register: (email: string, password: string, name?: string) => Promise<void>
logout: () => void
checkAuth: () => Promise<void>
fetchDomains: (page?: number) => Promise<void>
addDomain: (name: string) => Promise<void>
deleteDomain: (id: number) => Promise<void>
refreshDomain: (id: number) => Promise<void>
updateDomain: (id: number, updates: Partial<Domain>) => void
fetchSubscription: () => Promise<void>
}
export const useStore = create<AppState>((set, get) => ({
// Initial state
user: null,
isAuthenticated: false,
isLoading: true,
domains: [],
domainsTotal: 0,
domainsPage: 1,
subscription: null,
// Auth actions
login: async (email, password) => {
await api.login(email, password)
const user = await api.getMe()
set({ user, isAuthenticated: true, isLoading: false })
// Fetch user data (only once after login)
await Promise.all([
get().fetchDomains(),
get().fetchSubscription()
])
},
register: async (email, password, name) => {
await api.register(email, password, name)
// Note: No auto-login after registration
// User should verify email first (verification email is sent)
// They can then log in manually via the login page
},
logout: () => {
api.logout()
set({
user: null,
isAuthenticated: false,
domains: [],
subscription: null,
})
},
checkAuth: async () => {
// Skip if already authenticated and have data (prevents redundant fetches)
const state = get()
if (state.isAuthenticated && state.user && state.subscription) {
set({ isLoading: false })
return
}
set({ isLoading: true })
try {
// Cookie-based auth: if cookie is present and valid, /auth/me succeeds.
const user = await api.getMe()
set({ user, isAuthenticated: true })
// Fetch in parallel for speed
await Promise.all([
get().fetchDomains(),
get().fetchSubscription()
])
} catch {
set({ user: null, isAuthenticated: false })
} finally {
set({ isLoading: false })
}
},
// Domain actions
fetchDomains: async (page = 1) => {
try {
const response = await api.getDomains(page)
set({
domains: response.domains,
domainsTotal: response.total,
domainsPage: response.page,
})
} catch (error) {
console.error('Failed to fetch domains:', error)
}
},
addDomain: async (name) => {
await api.addDomain(name)
await get().fetchDomains(get().domainsPage)
await get().fetchSubscription()
},
deleteDomain: async (id) => {
await api.deleteDomain(id)
await get().fetchDomains(get().domainsPage)
await get().fetchSubscription()
},
refreshDomain: async (id) => {
const updated = await api.refreshDomain(id)
const domains = get().domains.map((d) =>
d.id === id ? { ...d, ...updated } : d
)
set({ domains })
},
updateDomain: (id, updates) => {
const domains = get().domains.map((d) =>
d.id === id ? { ...d, ...updates } : d
)
set({ domains })
},
// Subscription actions
fetchSubscription: async () => {
try {
const sub = await api.getSubscription()
set({
subscription: {
tier: sub.tier,
tier_name: sub.tier_name,
domain_limit: sub.domain_limit,
domains_used: sub.domains_used,
portfolio_limit: sub.portfolio_limit,
check_frequency: sub.check_frequency,
history_days: sub.history_days,
features: sub.features,
},
})
} catch {
// User might not have subscription
}
},
}))