55d636b512
- New ToastProvider/useToast (self-built, no dep): success/error toasts replace
all five alert() calls, with server error messages surfaced
- New ConfirmProvider/useConfirm promise-based dialog replaces all six
window.confirm sites, showing what is being deleted; Escape/overlay cancels
- Dashboard + BreedingCalendar loaders use Promise.allSettled with a
danger banner + Retry instead of silently blanking on fetch failure
- Settings load failure now surfaces a toast instead of vanishing
- Fix BreedingCalendar females list: read paginated {data} shape (was .dogs,
so 'Start Heat Cycle' never listed any females)
- Nav links highlight on detail routes (/dogs/123, /pedigree/5)
- Catch-all 404 route with link back to Dashboard
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
42 lines
1.1 KiB
React
42 lines
1.1 KiB
React
import { createContext, useContext, useEffect, useState } from 'react'
|
|
import axios from 'axios'
|
|
|
|
const SettingsContext = createContext({})
|
|
|
|
export function SettingsProvider({ children }) {
|
|
const [settings, setSettings] = useState({
|
|
kennel_name: 'BREEDR',
|
|
kennel_tagline: '',
|
|
})
|
|
const [loading, setLoading] = useState(true)
|
|
// Surfaced by consumers inside the ToastProvider (this provider sits above it)
|
|
const [loadError, setLoadError] = useState(false)
|
|
|
|
useEffect(() => {
|
|
axios.get('/api/settings')
|
|
.then(res => {
|
|
setSettings(prev => ({ ...prev, ...res.data }))
|
|
})
|
|
.catch((err) => {
|
|
console.error('Failed to load settings:', err)
|
|
setLoadError(true)
|
|
})
|
|
.finally(() => setLoading(false))
|
|
}, [])
|
|
|
|
const saveSettings = async (updates) => {
|
|
await axios.put('/api/settings', updates)
|
|
setSettings(prev => ({ ...prev, ...updates }))
|
|
}
|
|
|
|
return (
|
|
<SettingsContext.Provider value={{ settings, saveSettings, loading, loadError }}>
|
|
{children}
|
|
</SettingsContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useSettings() {
|
|
return useContext(SettingsContext)
|
|
}
|