diff --git a/frontend/src/pages/MyAnalyticsPage.tsx b/frontend/src/pages/MyAnalyticsPage.tsx
index e5aa3564..47cfa700 100644
--- a/frontend/src/pages/MyAnalyticsPage.tsx
+++ b/frontend/src/pages/MyAnalyticsPage.tsx
@@ -11,6 +11,7 @@ import {
ResponsiveContainer,
} from 'recharts'
import { Spinner } from '@/components/common/Spinner'
+import { EmptyState } from '@/components/common/EmptyState'
import { analyticsApi } from '@/api'
import { usePermissions } from '@/hooks/usePermissions'
import type { PersonalAnalyticsResponse, AnalyticsPeriod } from '@/types'
@@ -54,8 +55,11 @@ export default function MyAnalyticsPage() {
if (!data) {
return (
-
-
Failed to load analytics data.
+
+
)
}
diff --git a/frontend/src/pages/TeamAnalyticsPage.tsx b/frontend/src/pages/TeamAnalyticsPage.tsx
index 950533aa..533d4809 100644
--- a/frontend/src/pages/TeamAnalyticsPage.tsx
+++ b/frontend/src/pages/TeamAnalyticsPage.tsx
@@ -11,8 +11,10 @@ import {
ResponsiveContainer,
} from 'recharts'
import { Spinner } from '@/components/common/Spinner'
+import { EmptyState } from '@/components/common/EmptyState'
import { analyticsApi } from '@/api'
import { usePermissions } from '@/hooks/usePermissions'
+import { toast } from '@/lib/toast'
import type { TeamAnalyticsResponse, AnalyticsPeriod } from '@/types'
const CHART_COLORS = {
@@ -46,6 +48,12 @@ export default function TeamAnalyticsPage() {
.finally(() => setLoading(false))
}, [period, isAccountOwner, isSuperAdmin])
+ useEffect(() => {
+ if (!isAccountOwner && !isSuperAdmin) {
+ toast.info('Viewing your personal analytics', { id: 'analytics-redirect' })
+ }
+ }, [isAccountOwner, isSuperAdmin])
+
if (!isAccountOwner && !isSuperAdmin) {
return
}
@@ -60,8 +68,11 @@ export default function TeamAnalyticsPage() {
if (!data) {
return (
-
-
Failed to load analytics data.
+
+
)
}
diff --git a/frontend/src/pages/TreeLibraryPage.tsx b/frontend/src/pages/TreeLibraryPage.tsx
index 59c9d90a..0132cbfe 100644
--- a/frontend/src/pages/TreeLibraryPage.tsx
+++ b/frontend/src/pages/TreeLibraryPage.tsx
@@ -21,6 +21,8 @@ import { usePinnedFlowsStore } from '@/store/pinnedFlowsStore'
import { useCachedQuota } from '@/hooks/useCachedQuota'
import { AIFlowBuilderModal } from '@/components/ai-builder/AIFlowBuilderModal'
import { CreateFlowDropdown } from '@/components/common/CreateFlowDropdown'
+import { Spinner } from '@/components/common/Spinner'
+import { EmptyState } from '@/components/common/EmptyState'
import { toast } from '@/lib/toast'
export function TreeLibraryPage() {
@@ -465,13 +467,17 @@ export function TreeLibraryPage() {
{/* Loading State */}
{isLoading ? (
) : trees.length === 0 ? (
-
- No flows found.{' '}
- {(searchQuery || hasActiveFilters) && 'Try adjusting your filters.'}
-
+
) : (
<>
{treeLibraryView === 'grid' && (
diff --git a/frontend/src/pages/TreeNavigationPage.tsx b/frontend/src/pages/TreeNavigationPage.tsx
index 848ae3af..20f6e30f 100644
--- a/frontend/src/pages/TreeNavigationPage.tsx
+++ b/frontend/src/pages/TreeNavigationPage.tsx
@@ -795,7 +795,7 @@ export function TreeNavigationPage() {
{index < 9 && (
selectingOption === option.id ? (
-
+
) : (
diff --git a/frontend/src/pages/account/TargetListsPage.tsx b/frontend/src/pages/account/TargetListsPage.tsx
index a0cf025a..17ced4e6 100644
--- a/frontend/src/pages/account/TargetListsPage.tsx
+++ b/frontend/src/pages/account/TargetListsPage.tsx
@@ -4,6 +4,10 @@ import { targetListsApi } from '@/api'
import type { TargetList, TargetListCreate, TargetEntry } from '@/types'
import { toast } from '@/lib/toast'
import { ConfirmDialog } from '@/components/common/ConfirmDialog'
+import { Modal } from '@/components/common/Modal'
+import { Spinner } from '@/components/common/Spinner'
+import { EmptyState } from '@/components/common/EmptyState'
+import { PageHeader } from '@/components/common/PageHeader'
export default function TargetListsPage() {
const [lists, setLists] = useState([])
@@ -103,34 +107,31 @@ export default function TargetListsPage() {
return (
-
-
-
Target Lists
-
- Saved server lists for maintenance flow batch launching
-
-
-
openEditor()}
- className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-4 py-2 text-[0.875rem] font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90"
- >
-
- New List
-
-
+
openEditor()}
+ className="flex items-center gap-1.5 rounded-lg bg-gradient-brand px-4 py-2 text-[0.875rem] font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90"
+ >
+
+ New List
+
+ )}
+ />
{isLoading ? (
) : lists.length === 0 ? (
-
-
-
No target lists yet
-
- Create lists of servers to reuse across maintenance runs
-
-
+ }
+ title="No target lists yet"
+ description="Create lists of servers to reuse across maintenance runs."
+ />
) : (
{lists.map(list => (
@@ -172,68 +173,68 @@ export default function TargetListsPage() {
)}
{/* Editor Modal */}
- {showEditor && (
-
-
-
- {editingList ? 'Edit Target List' : 'New Target List'}
-
-
-
-
- Name
-
- setEditorName(e.target.value)}
- className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
- placeholder="e.g. RDS Farm A"
- />
-
-
-
- Description (optional)
-
- setEditorDescription(e.target.value)}
- className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
- placeholder="e.g. Production RDS servers"
- />
-
-
-
- Targets — one per line (add notes after #)
-
-
-
-
- setShowEditor(false)}
- className="rounded-lg border border-border px-4 py-2 text-[0.875rem] text-muted-foreground hover:bg-accent hover:text-foreground"
- >
- Cancel
-
-
- {isSaving ? 'Saving\u2026' : 'Save'}
-
-
+
setShowEditor(false)}
+ title={editingList ? 'Edit Target List' : 'New Target List'}
+ size="md"
+ footer={(
+
+ setShowEditor(false)}
+ className="rounded-lg border border-border px-4 py-2 text-[0.875rem] text-muted-foreground hover:bg-accent hover:text-foreground"
+ >
+ Cancel
+
+
+ {isSaving ? 'Saving\u2026' : 'Save'}
+
+
+ )}
+ >
+
+
+
+ Name
+
+ setEditorName(e.target.value)}
+ className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
+ placeholder="e.g. RDS Farm A"
+ />
+
+
+
+ Description (optional)
+
+ setEditorDescription(e.target.value)}
+ className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
+ placeholder="e.g. Production RDS servers"
+ />
+
+
+
+ Targets — one per line (add notes after #)
+
+
- )}
+
{deleteTarget && (
-
-
-
Team Categories
-
Manage tree categories for your team
-
-
setCreateOpen(true)} className={cn('flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium', 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90')}>
-
- Create Category
-
-
+
+
setCreateOpen(true)} className={cn('flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium', 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90')}>
+
+ Create Category
+
+ )}
+ />
{loading ? (
diff --git a/frontend/src/pages/admin/UserDetailPage.tsx b/frontend/src/pages/admin/UserDetailPage.tsx
index 48ac2cfc..69f6c575 100644
--- a/frontend/src/pages/admin/UserDetailPage.tsx
+++ b/frontend/src/pages/admin/UserDetailPage.tsx
@@ -3,6 +3,8 @@ import { useParams, useNavigate } from 'react-router-dom'
import { ArrowLeft, Shield, Crown, UserCheck, UserX, Clock, Ticket, KeyRound, Copy, Check, Archive, ArchiveRestore, Trash2 } from 'lucide-react'
import { StatusBadge } from '@/components/admin'
import { Modal } from '@/components/common/Modal'
+import { Spinner } from '@/components/common/Spinner'
+import { EmptyState } from '@/components/common/EmptyState'
import { adminApi } from '@/api/admin'
import { toast } from '@/lib/toast'
import { cn } from '@/lib/utils'
@@ -177,14 +179,25 @@ export function UserDetailPage() {
if (loading) {
return (
)
}
if (!user) {
return (
-
User not found
+
navigate('/admin/users')}
+ className="rounded-md border border-border px-4 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
+ >
+ Back to Users
+
+ )}
+ />
)
}
@@ -202,7 +215,7 @@ export function UserDetailPage() {
-
+
{user.full_name || user.email}
{user.email}