fix: surface errors and polish UX across Step Library and Batch pages (#92)
- StepLibraryPage: replace silent console.error with toast.error on edit/save failures; replace manual setTimeout toast with toast.success helper - BatchStatusPage: add error state with retry button for failed session loads instead of showing ambiguous "No sessions found" - StepDetailModal: guard clipboard copy against browser denial (no false "Copied!" state on permission error); fix dead "See all reviews" button with inline expand/collapse toggle - StepLibraryBrowser: add "Try again" retry button to error state; retry increments a counter that re-triggers both load effects Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #92.
This commit is contained in:
@@ -21,6 +21,7 @@ export default function BatchStatusPage() {
|
||||
const [sessions, setSessions] = useState<Session[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||
const [loadError, setLoadError] = useState<string | null>(null)
|
||||
const [batchDate, setBatchDate] = useState<Date | null>(null)
|
||||
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
@@ -30,9 +31,12 @@ export default function BatchStatusPage() {
|
||||
try {
|
||||
const data = await sessionsApi.list({ batch_id: batchId, size: 100 })
|
||||
setSessions(Array.isArray(data) ? data : [])
|
||||
setLoadError(null)
|
||||
if (data.length > 0 && data[0].started_at) {
|
||||
setBatchDate(new Date(data[0].started_at))
|
||||
}
|
||||
} catch {
|
||||
setLoadError('Failed to load batch sessions')
|
||||
} finally {
|
||||
if (showRefreshing) setIsRefreshing(false)
|
||||
}
|
||||
@@ -48,6 +52,8 @@ export default function BatchStatusPage() {
|
||||
loadSessions(),
|
||||
])
|
||||
setTree(treeData)
|
||||
} catch {
|
||||
setLoadError('Failed to load batch data')
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
@@ -178,7 +184,17 @@ export default function BatchStatusPage() {
|
||||
|
||||
{/* Target cards */}
|
||||
<div className="space-y-2">
|
||||
{sessions.length === 0 ? (
|
||||
{loadError ? (
|
||||
<div className="rounded-lg border border-red-400/20 bg-red-400/10 p-4 text-center">
|
||||
<p className="text-sm text-red-400 mb-3">{loadError}</p>
|
||||
<button
|
||||
onClick={() => loadSessions(true)}
|
||||
className="rounded-md border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
) : sessions.length === 0 ? (
|
||||
<p className="text-center text-[0.875rem] text-muted-foreground py-8">
|
||||
No sessions found for this batch.
|
||||
</p>
|
||||
@@ -207,3 +223,4 @@ export default function BatchStatusPage() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usePermissions } from '@/hooks/usePermissions'
|
||||
import { stepsApi } from '@/api/steps'
|
||||
import { StepLibraryBrowser } from '@/components/step-library/StepLibraryBrowser'
|
||||
import { StepFormModal } from '@/components/step-library/StepFormModal'
|
||||
import { toast } from '@/lib/toast'
|
||||
import type { Step, StepListItem } from '@/types/step'
|
||||
|
||||
export default function StepLibraryPage() {
|
||||
@@ -20,9 +21,6 @@ export default function StepLibraryPage() {
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null)
|
||||
|
||||
// Toast for "Save to My Library"
|
||||
const [saveToast, setSaveToast] = useState<string | null>(null)
|
||||
|
||||
// Increment to trigger StepLibraryBrowser reload
|
||||
const [refreshKey, setRefreshKey] = useState(0)
|
||||
const refresh = () => setRefreshKey(k => k + 1)
|
||||
@@ -32,8 +30,8 @@ export default function StepLibraryPage() {
|
||||
try {
|
||||
const full = await stepsApi.get(step.id)
|
||||
setEditingStep(full)
|
||||
} catch (err) {
|
||||
console.error('Failed to load step for edit:', err)
|
||||
} catch {
|
||||
toast.error('Failed to load step for editing')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +66,10 @@ export default function StepLibraryPage() {
|
||||
category_id: full.category_id,
|
||||
tags: full.tags,
|
||||
})
|
||||
setSaveToast(`"${full.title}" saved to My Steps`)
|
||||
setTimeout(() => setSaveToast(null), 3000)
|
||||
toast.success(`"${full.title}" saved to My Steps`)
|
||||
refresh()
|
||||
} catch (err) {
|
||||
console.error('Failed to save step:', err)
|
||||
} catch {
|
||||
toast.error('Failed to save step to your library')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,12 +165,6 @@ export default function StepLibraryPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Save Toast */}
|
||||
{saveToast && (
|
||||
<div className="fixed bottom-6 left-1/2 z-50 -translate-x-1/2 rounded-lg border border-border bg-card px-4 py-2 text-sm text-foreground shadow-lg">
|
||||
{saveToast}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user