fix: broken functionality - auth errors, toast logic, role update, routing, step library
- Extract backend error detail in auth store login/register - Fix inverted 4xx toast logic and add 429 rate limit handling - Send account_role field to match backend schema in role update - Use type-aware routing for Repeat Last Session button - Add step library placeholder page and route, remove dot badge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ export const accountsApi = {
|
||||
async updateMemberRole(userId: string, role: string): Promise<AccountMember> {
|
||||
const response = await apiClient.patch<AccountMember>(
|
||||
`/accounts/me/members/${userId}/role`,
|
||||
{ role }
|
||||
{ account_role: role }
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -32,13 +32,15 @@ function handleGlobalError(error: AxiosError) {
|
||||
return
|
||||
}
|
||||
|
||||
// Client errors (4xx)
|
||||
// Rate limit
|
||||
if (status === 429) {
|
||||
toast.error(data?.detail || 'Too many requests — please try again shortly')
|
||||
return
|
||||
}
|
||||
|
||||
// Client errors (4xx) — show backend detail if present
|
||||
if (status >= 400 && status < 500) {
|
||||
const message = data?.detail || 'Invalid request'
|
||||
// Only show generic messages - pages handle specific errors
|
||||
if (!data?.detail) {
|
||||
toast.error(message)
|
||||
}
|
||||
toast.error(data?.detail || 'Invalid request')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ export function Sidebar() {
|
||||
<NavItem href="/my-trees" icon={PenLine} label="Flow Editor" />
|
||||
<NavItem href="/sessions" icon={Clock} label="Sessions" badge={activeSessionCount || undefined} />
|
||||
<NavItem href="/shares" icon={FileText} label="Exports" />
|
||||
<NavItem href="/step-library" icon={Bookmark} label="Step Library" badge="dot" />
|
||||
<NavItem href="/step-library" icon={Bookmark} label="Step Library" />
|
||||
<NavItem href="/analytics" icon={BarChart3} label="Analytics" />
|
||||
</div>
|
||||
|
||||
|
||||
25
frontend/src/pages/StepLibraryPage.tsx
Normal file
25
frontend/src/pages/StepLibraryPage.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Bookmark } from 'lucide-react'
|
||||
|
||||
export default function StepLibraryPage() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<span title="Step Library"><Bookmark className="h-8 w-8 text-muted-foreground" /></span>
|
||||
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">Step Library</h1>
|
||||
</div>
|
||||
<p className="mt-2 text-muted-foreground">Reusable steps for your flows — coming soon.</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center justify-center py-16 text-center">
|
||||
<div className="rounded-full bg-primary/10 p-4 mb-4">
|
||||
<Bookmark className="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold text-foreground mb-2">Coming Soon</h2>
|
||||
<p className="max-w-md text-sm text-muted-foreground">
|
||||
The Step Library will let you create, share, and reuse common troubleshooting steps across all your flows.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export function TreeLibraryPage() {
|
||||
const lastSessionData = (() => {
|
||||
const raw = safeGetItem('last-session')
|
||||
if (!raw) return null
|
||||
try { return JSON.parse(raw) as { tree_id: string; tree_name: string; client_name: string; ticket_number: string } }
|
||||
try { return JSON.parse(raw) as { tree_id: string; tree_name: string; client_name: string; ticket_number: string; tree_type?: string } }
|
||||
catch { return null }
|
||||
})()
|
||||
|
||||
@@ -450,7 +450,7 @@ export function TreeLibraryPage() {
|
||||
{lastSessionData && (
|
||||
<div className="mb-6">
|
||||
<button
|
||||
onClick={() => navigate(`/trees/${lastSessionData.tree_id}/navigate`, {
|
||||
onClick={() => navigate(getSessionResumePath(lastSessionData.tree_id, lastSessionData.tree_type), {
|
||||
state: { prefillClientName: lastSessionData.client_name, prefillTicketNumber: lastSessionData.ticket_number },
|
||||
})}
|
||||
className={cn(
|
||||
|
||||
@@ -31,6 +31,7 @@ const MySharesPage = lazy(() => import('@/pages/MySharesPage'))
|
||||
const TeamAnalyticsPage = lazy(() => import('@/pages/TeamAnalyticsPage'))
|
||||
const MyAnalyticsPage = lazy(() => import('@/pages/MyAnalyticsPage'))
|
||||
const FeedbackPage = lazy(() => import('@/pages/FeedbackPage'))
|
||||
const StepLibraryPage = lazy(() => import('@/pages/StepLibraryPage'))
|
||||
const AccountSettingsPage = lazy(() => import('@/pages/AccountSettingsPage'))
|
||||
// Admin pages
|
||||
const AdminLayout = lazy(() => import('@/components/admin/AdminLayout'))
|
||||
@@ -235,6 +236,14 @@ export const router = createBrowserRouter([
|
||||
</Suspense>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'step-library',
|
||||
element: (
|
||||
<Suspense fallback={<PageLoader />}>
|
||||
<StepLibraryPage />
|
||||
</Suspense>
|
||||
),
|
||||
},
|
||||
// Admin routes
|
||||
{
|
||||
path: 'admin',
|
||||
|
||||
@@ -48,7 +48,8 @@ export const useAuthStore = create<AuthState>()(
|
||||
// Fetch user info
|
||||
await get().fetchUser()
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : 'Login failed'
|
||||
const axiosErr = error as { response?: { data?: { detail?: string } } }
|
||||
const message = axiosErr.response?.data?.detail || (error instanceof Error ? error.message : 'Login failed')
|
||||
set({ error: message, isLoading: false })
|
||||
throw error
|
||||
}
|
||||
@@ -61,7 +62,8 @@ export const useAuthStore = create<AuthState>()(
|
||||
// After registration, log the user in
|
||||
await get().login({ email: data.email, password: data.password })
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : 'Registration failed'
|
||||
const axiosErr = error as { response?: { data?: { detail?: string } } }
|
||||
const message = axiosErr.response?.data?.detail || (error instanceof Error ? error.message : 'Registration failed')
|
||||
set({ error: message, isLoading: false })
|
||||
throw error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user