diff --git a/frontend/src/components/common/ThemeToggle.tsx b/frontend/src/components/common/ThemeToggle.tsx deleted file mode 100644 index 4ecd2f50..00000000 --- a/frontend/src/components/common/ThemeToggle.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Sun, Moon, Monitor } from 'lucide-react' -import { useThemeStore } from '@/store/themeStore' -import { cn } from '@/lib/utils' - -export function ThemeToggle() { - const { theme, setTheme } = useThemeStore() - - const options = [ - { value: 'light' as const, icon: Sun, label: 'Light' }, - { value: 'dark' as const, icon: Moon, label: 'Dark' }, - { value: 'system' as const, icon: Monitor, label: 'System' }, - ] - - return ( -
- {options.map(({ value, icon: Icon, label }) => ( - - ))} -
- ) -} - -export default ThemeToggle diff --git a/frontend/src/components/common/UpgradePrompt.tsx b/frontend/src/components/common/UpgradePrompt.tsx deleted file mode 100644 index 76ac8a54..00000000 --- a/frontend/src/components/common/UpgradePrompt.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { cn } from '@/lib/utils' -import { useSubscription } from '@/hooks/useSubscription' - -interface UpgradePromptProps { - feature: string // e.g., "create more trees", "start more sessions" - className?: string -} - -export function UpgradePrompt({ feature, className }: UpgradePromptProps) { - const { plan } = useSubscription() - - return ( -
-

Plan Limit Reached

-

- Your {plan} plan doesn't allow you to {feature}. Upgrade your plan to continue. -

- -
- ) -} diff --git a/frontend/src/components/layout/AppLayout-original.tsx b/frontend/src/components/layout/AppLayout-original.tsx deleted file mode 100644 index 3b28b8aa..00000000 --- a/frontend/src/components/layout/AppLayout-original.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { useState, useEffect, useCallback } from 'react' -import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom' -import { useAuthStore } from '@/store/authStore' -import { usePermissions } from '@/hooks/usePermissions' -import { ThemeToggle } from '@/components/common/ThemeToggle' -import { BrandLogo } from '@/components/common/BrandLogo' -import { BrandWordmark } from '@/components/common/BrandWordmark' -import { Menu, X } from 'lucide-react' -import { cn } from '@/lib/utils' - -export function AppLayout() { - const location = useLocation() - const navigate = useNavigate() - const { user, logout } = useAuthStore() - const { effectiveRole, isSuperAdmin } = usePermissions() - const [mobileMenuOpen, setMobileMenuOpen] = useState(false) - - const handleLogout = async () => { - setMobileMenuOpen(false) - await logout() - navigate('/login') - } - - // Close mobile menu on route change - key-based reset - const [prevPath, setPrevPath] = useState(location.pathname) - if (prevPath !== location.pathname) { - setPrevPath(location.pathname) - if (mobileMenuOpen) setMobileMenuOpen(false) - } - - // Close on Escape - const handleKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'Escape') setMobileMenuOpen(false) - }, []) - - useEffect(() => { - if (mobileMenuOpen) { - document.addEventListener('keydown', handleKeyDown) - document.body.style.overflow = 'hidden' - } else { - document.body.style.overflow = '' - } - return () => { - document.removeEventListener('keydown', handleKeyDown) - document.body.style.overflow = '' - } - }, [mobileMenuOpen, handleKeyDown]) - - const navItems = [ - { path: '/', label: 'Home' }, - { path: '/trees', label: 'Trees' }, - { path: '/my-trees', label: 'My Trees' }, - { path: '/sessions', label: 'Sessions' }, - { path: '/account', label: 'Account' }, - { path: '/settings', label: 'Settings' }, - ...(isSuperAdmin ? [{ path: '/admin', label: 'Admin Panel' }] : []), - ] - - return ( -
- {/* Header */} -
-
-
- {/* Mobile hamburger */} - - - - - - - -
- -
- - {user?.name || user?.email} - - {effectiveRole && effectiveRole !== 'engineer' && ( - - {effectiveRole === 'super_admin' ? 'Super Admin' : - effectiveRole === 'owner' ? 'Owner' : - 'Viewer'} - - )} - - -
-
-
- - {/* Mobile Nav Drawer */} - {mobileMenuOpen && ( -
- {/* Backdrop */} -
setMobileMenuOpen(false)} - aria-hidden="true" - /> - - {/* Drawer */} - -
- )} - - {/* Main Content */} -
- -
-
- ) -} - -export default AppLayout diff --git a/frontend/src/components/layout/AppLayout.tsx b/frontend/src/components/layout/AppLayout.tsx index 7f7abcbb..fcf28542 100644 --- a/frontend/src/components/layout/AppLayout.tsx +++ b/frontend/src/components/layout/AppLayout.tsx @@ -50,7 +50,6 @@ export function AppLayout() { { path: '/my-trees', label: 'My Trees' }, { path: '/sessions', label: 'Sessions' }, { path: '/account', label: 'Account' }, - { path: '/settings', label: 'Settings' }, ...(isSuperAdmin ? [{ path: '/admin', label: 'Admin Panel' }] : []), ] diff --git a/frontend/src/pages/AccountSettingsPage.tsx b/frontend/src/pages/AccountSettingsPage.tsx index 81d5d58b..8b276ead 100644 --- a/frontend/src/pages/AccountSettingsPage.tsx +++ b/frontend/src/pages/AccountSettingsPage.tsx @@ -1,16 +1,20 @@ import { useEffect, useState } from 'react' -import { Building2, Users, Mail, Crown, Loader2, AlertCircle, Check, X } from 'lucide-react' +import { Link } from 'react-router-dom' +import { Building2, Users, Mail, Crown, Loader2, AlertCircle, Check, X, Settings, FolderTree } from 'lucide-react' import { accountsApi } from '@/api' import type { Account, AccountMember, AccountInvite } from '@/types' import { cn } from '@/lib/utils' import { usePermissions } from '@/hooks/usePermissions' import { useSubscription } from '@/hooks/useSubscription' import { useAuthStore } from '@/store/authStore' +import { useUserPreferencesStore } from '@/store/userPreferencesStore' import { CheckoutButton } from '@/components/subscription/CheckoutButton' +import { toast } from '@/lib/toast' export function AccountSettingsPage() { const { isAccountOwner } = usePermissions() const { plan, limits, usage } = useSubscription() + const { defaultExportFormat, setDefaultExportFormat } = useUserPreferencesStore() const subscription = useAuthStore((s) => s.subscription) const [account, setAccount] = useState(null) @@ -442,6 +446,60 @@ export function AccountSettingsPage() { )}
)} + + {/* Team Categories Link (owners only) */} + {isAccountOwner && ( + +
+ +
+

Team Categories

+

Manage tree categories for your team

+
+
+ + + )} + + {/* Preferences Section */} +
+
+ +

Preferences

+
+ +
+ +

+ This format will be pre-selected when exporting sessions +

+ +
+
) diff --git a/frontend/src/pages/QuickStartPage-Enhanced.tsx b/frontend/src/pages/QuickStartPage-Enhanced.tsx deleted file mode 100644 index 39bd0fca..00000000 --- a/frontend/src/pages/QuickStartPage-Enhanced.tsx +++ /dev/null @@ -1,315 +0,0 @@ -import { useState, useEffect, useRef } from 'react' -import { useNavigate, Link } from 'react-router-dom' -import { Search, Clock, ArrowRight, Play, Loader2, TrendingUp, Sparkles, Zap } from 'lucide-react' -import { treesApi } from '@/api/trees' -import { sessionsApi } from '@/api/sessions' -import type { TreeListItem } from '@/types' -import type { Session } from '@/types/session' -import { cn } from '@/lib/utils' - -function timeAgo(dateStr: string): string { - const now = Date.now() - const then = new Date(dateStr).getTime() - const diffMs = now - then - const minutes = Math.floor(diffMs / 60000) - if (minutes < 1) return 'just now' - if (minutes < 60) return `${minutes}m ago` - const hours = Math.floor(minutes / 60) - if (hours < 24) return `${hours}h ago` - const days = Math.floor(hours / 24) - return `${days}d ago` -} - -export function QuickStartPage() { - const navigate = useNavigate() - const [query, setQuery] = useState('') - const [searchResults, setSearchResults] = useState([]) - const [isSearching, setIsSearching] = useState(false) - const [showResults, setShowResults] = useState(false) - const [activeSessions, setActiveSessions] = useState([]) - const [recentTrees, setRecentTrees] = useState<{ tree_id: string; name: string; lastUsed: string }[]>([]) - const [isLoading, setIsLoading] = useState(true) - const searchRef = useRef(null) - const debounceRef = useRef | null>(null) - - // Load sessions on mount - useEffect(() => { - async function loadData() { - try { - const [active, recent] = await Promise.all([ - sessionsApi.list({ completed: false, size: 5 }), - sessionsApi.list({ size: 10 }), - ]) - setActiveSessions(active.slice(0, 3)) - - // Deduplicate recent sessions by tree_id, max 5 - const seen = new Set() - const deduped: { tree_id: string; name: string; lastUsed: string }[] = [] - for (const s of recent) { - if (!seen.has(s.tree_id) && deduped.length < 5) { - seen.add(s.tree_id) - deduped.push({ - tree_id: s.tree_id, - name: s.tree_snapshot?.name || 'Unnamed Tree', - lastUsed: s.started_at, - }) - } - } - setRecentTrees(deduped) - } catch (err) { - console.error('Failed to load sessions:', err) - } finally { - setIsLoading(false) - } - } - loadData() - }, []) - - // Debounced search - useEffect(() => { - if (debounceRef.current) clearTimeout(debounceRef.current) - - if (query.length < 2) { - setSearchResults([]) - setShowResults(false) - setIsSearching(false) - return - } - - setIsSearching(true) - setShowResults(true) - debounceRef.current = setTimeout(async () => { - try { - const results = await treesApi.search(query, 8) - setSearchResults(results) - } catch (err) { - console.error('Search failed:', err) - setSearchResults([]) - } finally { - setIsSearching(false) - } - }, 300) - - return () => { - if (debounceRef.current) clearTimeout(debounceRef.current) - } - }, [query]) - - // Close dropdown on outside click - useEffect(() => { - function handleClick(e: MouseEvent) { - if (searchRef.current && !searchRef.current.contains(e.target as Node)) { - setShowResults(false) - } - } - document.addEventListener('mousedown', handleClick) - return () => document.removeEventListener('mousedown', handleClick) - }, []) - - return ( -
- {/* Animated background grid */} -
- -
- {/* Hero Section */} -
- {/* Badge */} -
-
- - AI-Powered Troubleshooting -
-
- - {/* Title */} -

- What are you troubleshooting? -

- -

- Search our library of proven decision trees or continue where you left off -

- - {/* Enhanced Search Bar */} -
-
- {/* Glow effect */} -
- -
- - setQuery(e.target.value)} - onFocus={() => query.length >= 2 && setShowResults(true)} - placeholder="Paste ticket subject or search for a tree..." - className={cn( - 'w-full rounded-xl border border-slate-700/50 bg-slate-900/90 backdrop-blur-xl py-5 pl-14 pr-5 text-lg', - 'text-white placeholder:text-slate-500', - 'focus:outline-none focus:ring-2 focus:ring-violet-500/50 focus:border-violet-500/50', - 'transition-all duration-300' - )} - /> - {query && ( - - )} -
-
- - {/* Enhanced Search Results Dropdown */} - {showResults && ( -
- {isSearching ? ( -
- -
- ) : searchResults.length === 0 ? ( -
-
No results found
-
Try a different search term
-
- ) : ( -
    - {searchResults.map((tree, idx) => ( -
  • - -
  • - ))} -
- )} -
- )} -
-
- - {/* Continue Session Section */} - {activeSessions.length > 0 && ( -
-
-
-
-

- Continue Session -

-
-
-
- -
- {activeSessions.map((session, idx) => ( - - ))} -
-
- )} - - {/* Recent Trees Section */} - {!isLoading && recentTrees.length > 0 && ( -
-
-
- -

- Recent Trees -

-
-
-
- -
- {recentTrees.map((tree, idx) => ( - - ))} -
-
- )} - - {/* Footer CTA */} -
- - Browse All Trees - - -
-
-
- ) -} - -export default QuickStartPage diff --git a/frontend/src/pages/QuickStartPage.tsx b/frontend/src/pages/QuickStartPage.tsx index d9f7da1a..6a59983b 100644 --- a/frontend/src/pages/QuickStartPage.tsx +++ b/frontend/src/pages/QuickStartPage.tsx @@ -112,7 +112,7 @@ export function QuickStartPage() {
{/* Badge */}
- + DECISION TREE PLATFORM
@@ -132,7 +132,7 @@ export function QuickStartPage() {
- + - {query.length >= 2 && ( - + {isSearching && ( + )}
@@ -202,7 +197,7 @@ export function QuickStartPage() {
- +
@@ -258,7 +253,7 @@ export function QuickStartPage() {
)}
- +
@@ -292,7 +287,7 @@ export function QuickStartPage() { >
- +
diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx deleted file mode 100644 index effbc6c4..00000000 --- a/frontend/src/pages/SettingsPage.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Settings } from 'lucide-react' -import { useUserPreferencesStore } from '@/store/userPreferencesStore' -import { cn } from '@/lib/utils' -import { toast } from '@/lib/toast' - -export function SettingsPage() { - const { defaultExportFormat, setDefaultExportFormat } = useUserPreferencesStore() - - const handleExportFormatChange = (format: 'markdown' | 'text' | 'html') => { - setDefaultExportFormat(format) - toast.success('Preferences saved successfully') - } - - return ( -
-
-
- -

Settings

-
-

- Manage your application preferences -

-
- -
- {/* Export Preferences Section */} -
-

Export Preferences

-

- Configure default settings for session exports -

- -
- -

- This format will be pre-selected when exporting sessions -

- -
-
- - {/* About Section */} -
-

About

-

- ResolutionFlow - Decision Tree Platform -

-

- Transform troubleshooting into guided workflows -

-
-
-
- ) -} - -export default SettingsPage diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index f877604e..59ac587a 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -6,6 +6,5 @@ export { default as TreeNavigationPage } from './TreeNavigationPage' export { default as TreeEditorPage } from './TreeEditorPage' export { default as SessionHistoryPage } from './SessionHistoryPage' export { default as SessionDetailPage } from './SessionDetailPage' -export { default as SettingsPage } from './SettingsPage' export { default as AccountSettingsPage } from './AccountSettingsPage' export { default as AdminCategoriesPage } from './AdminCategoriesPage' diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 1685fba2..fe0ae2b0 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,4 +1,4 @@ -import { createBrowserRouter, Navigate } from 'react-router-dom' +import { createBrowserRouter } from 'react-router-dom' import { lazy, Suspense } from 'react' import { AppLayout, ProtectedRoute } from '@/components/layout' import { RouteError } from '@/components/common/RouteError' @@ -16,7 +16,6 @@ const TreeNavigationPage = lazy(() => import('@/pages/TreeNavigationPage')) const TreeEditorPage = lazy(() => import('@/pages/TreeEditorPage')) const SessionHistoryPage = lazy(() => import('@/pages/SessionHistoryPage')) const SessionDetailPage = lazy(() => import('@/pages/SessionDetailPage')) -const SettingsPage = lazy(() => import('@/pages/SettingsPage')) const AccountSettingsPage = lazy(() => import('@/pages/AccountSettingsPage')) // Admin pages const AdminLayout = lazy(() => import('@/components/admin/AdminLayout')) @@ -117,22 +116,6 @@ export const router = createBrowserRouter([ ), }, - { - path: 'settings', - element: ( - }> - - - ), - }, - { - path: 'account-settings', - element: ( - }> - - - ), - }, // Admin routes { path: 'admin', @@ -215,21 +198,25 @@ export const router = createBrowserRouter([ path: 'account', element: ( }> - - - + ), children: [ { index: true, - element: , + element: ( + }> + + + ), }, { path: 'categories', element: ( }> - + + + ), }, diff --git a/frontend/src/store/themeStore.ts b/frontend/src/store/themeStore.ts deleted file mode 100644 index 383314b0..00000000 --- a/frontend/src/store/themeStore.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { create } from 'zustand' -import { persist } from 'zustand/middleware' - -type Theme = 'light' | 'dark' | 'system' - -interface ThemeState { - theme: Theme - resolvedTheme: 'light' | 'dark' - setTheme: (theme: Theme) => void -} - -const getSystemTheme = (): 'light' | 'dark' => { - if (typeof window === 'undefined') return 'light' - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' -} - -const applyTheme = (theme: Theme): 'light' | 'dark' => { - const resolved = theme === 'system' ? getSystemTheme() : theme - const root = document.documentElement - - if (resolved === 'dark') { - root.classList.add('dark') - } else { - root.classList.remove('dark') - } - - return resolved -} - -export const useThemeStore = create()( - persist( - (set) => ({ - theme: 'system', - resolvedTheme: getSystemTheme(), - - setTheme: (theme: Theme) => { - const resolvedTheme = applyTheme(theme) - set({ theme, resolvedTheme }) - }, - }), - { - name: 'theme-storage', - onRehydrateStorage: () => (state) => { - // Apply theme on initial load after rehydration - if (state) { - applyTheme(state.theme) - } - }, - } - ) -) - -export default useThemeStore