Enhance user experience with protected routes and AI chat improvements

Adds protected routes with loading skeletons, fixes AI chat data handling, and handles task array checks.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 6bac2659-689a-442d-ae16-f45f8d1450c7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9777c70b-fc38-4831-8d6b-78dfffe041b0/b010d0e6-5a54-4e9e-b559-909112e9dc8a.jpg
This commit is contained in:
ghaddaditw
2025-06-08 16:16:36 +00:00
parent 30c0103b06
commit 3eaff970ce
3 changed files with 81 additions and 27 deletions
+74 -18
View File
@@ -1,10 +1,10 @@
import { Switch, Route } from "wouter"; import { Switch, Route, Redirect } from "wouter";
import { queryClient } from "./lib/queryClient"; import { queryClient } from "./lib/queryClient";
import { QueryClientProvider } from "@tanstack/react-query"; import { QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster"; import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip"; import { TooltipProvider } from "@/components/ui/tooltip";
import { FloatingVoiceControl } from "@/components/voice/FloatingVoiceControl"; import { FloatingVoiceControl } from "@/components/voice/FloatingVoiceControl";
import { AuthProvider } from "@/context/AuthContext"; import { AuthProvider, useAuth } from "@/context/AuthContext";
import { ThemeProvider } from "@/context/ThemeContext"; import { ThemeProvider } from "@/context/ThemeContext";
import { AdaptiveThemeProvider } from "@/context/AdaptiveThemeContext"; import { AdaptiveThemeProvider } from "@/context/AdaptiveThemeContext";
import { VoiceProvider } from "@/context/VoiceProvider"; import { VoiceProvider } from "@/context/VoiceProvider";
@@ -12,6 +12,7 @@ import { NotificationProvider } from "@/context/NotificationProvider";
import { TourManagerProvider } from "@/components/onboarding/TourManager"; import { TourManagerProvider } from "@/components/onboarding/TourManager";
import { MoodDetector } from "@/components/MoodDetector"; import { MoodDetector } from "@/components/MoodDetector";
import { RTLProvider } from "@/components/RTLProvider"; import { RTLProvider } from "@/components/RTLProvider";
import { Skeleton } from "@/components/ui/skeleton";
import LandingPage from "@/pages/LandingPage"; import LandingPage from "@/pages/LandingPage";
import DashboardPage from "@/pages/DashboardPage"; import DashboardPage from "@/pages/DashboardPage";
import LoginPage from "@/pages/LoginPage"; import LoginPage from "@/pages/LoginPage";
@@ -31,26 +32,81 @@ import ChatPage from "@/pages/ChatPage";
import ComfortHubPage from "@/pages/ComfortHubPage"; import ComfortHubPage from "@/pages/ComfortHubPage";
import NotFound from "@/pages/not-found"; import NotFound from "@/pages/not-found";
// Protected Route wrapper
function ProtectedRoute({ component: Component }: { component: React.ComponentType }) {
const { user, isLoading } = useAuth();
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="space-y-4 w-full max-w-md p-6">
<Skeleton className="h-8 w-3/4" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" />
</div>
</div>
);
}
if (!user) {
return <Redirect to="/login" />;
}
// Check if user needs onboarding
if (!user.onboardingComplete) {
return <Redirect to="/onboarding" />;
}
return <Component />;
}
// Public route that redirects authenticated users
function PublicRoute({ component: Component }: { component: React.ComponentType }) {
const { user, isLoading } = useAuth();
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="space-y-4 w-full max-w-md p-6">
<Skeleton className="h-8 w-3/4" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" />
</div>
</div>
);
}
if (user) {
if (!user.onboardingComplete) {
return <Redirect to="/onboarding" />;
}
return <Redirect to="/dashboard" />;
}
return <Component />;
}
function Router() { function Router() {
return ( return (
<Switch> <Switch>
<Route path="/" component={LandingPage} /> <Route path="/" component={() => <PublicRoute component={LandingPage} />} />
<Route path="/login" component={LoginPage} /> <Route path="/login" component={() => <PublicRoute component={LoginPage} />} />
<Route path="/register" component={LoginPage} /> <Route path="/register" component={() => <PublicRoute component={LoginPage} />} />
<Route path="/onboarding" component={OnboardingPage} /> <Route path="/onboarding" component={OnboardingPage} />
<Route path="/dashboard" component={DashboardPage} /> <Route path="/dashboard" component={() => <ProtectedRoute component={DashboardPage} />} />
<Route path="/tasks" component={TasksPage} /> <Route path="/tasks" component={() => <ProtectedRoute component={TasksPage} />} />
<Route path="/finances" component={FinancesPage} /> <Route path="/finances" component={() => <ProtectedRoute component={FinancesPage} />} />
<Route path="/voice" component={VoicePage} /> <Route path="/voice" component={() => <ProtectedRoute component={VoicePage} />} />
<Route path="/ai" component={AIPage} /> <Route path="/ai" component={() => <ProtectedRoute component={AIPage} />} />
<Route path="/analytics" component={AnalyticsPage} /> <Route path="/analytics" component={() => <ProtectedRoute component={AnalyticsPage} />} />
<Route path="/monitoring" component={SystemMonitoringPage} /> <Route path="/monitoring" component={() => <ProtectedRoute component={SystemMonitoringPage} />} />
<Route path="/focus" component={FocusPage} /> <Route path="/focus" component={() => <ProtectedRoute component={FocusPage} />} />
<Route path="/themes" component={ThemePage} /> <Route path="/themes" component={() => <ProtectedRoute component={ThemePage} />} />
<Route path="/chat" component={ChatPage} /> <Route path="/chat" component={() => <ProtectedRoute component={ChatPage} />} />
<Route path="/comfort" component={ComfortHubPage} /> <Route path="/comfort" component={() => <ProtectedRoute component={ComfortHubPage} />} />
<Route path="/settings" component={SettingsPage} /> <Route path="/settings" component={() => <ProtectedRoute component={SettingsPage} />} />
<Route path="/professionals" component={ProfessionalDirectory} /> <Route path="/professionals" component={() => <ProtectedRoute component={ProfessionalDirectory} />} />
<Route path="/benchmark" component={() => <ProtectedRoute component={BenchmarkPage} />} />
<Route component={NotFound} /> <Route component={NotFound} />
</Switch> </Switch>
); );
+3 -5
View File
@@ -38,14 +38,12 @@ export default function AIPage() {
setIsLoading(true); setIsLoading(true);
try { try {
const response = await apiRequest("/api/ai/chat", { const response = await apiRequest("POST", "/api/ai/chat", { message });
method: "POST", const data = await response.json();
body: JSON.stringify({ message }),
});
const aiMessage = { const aiMessage = {
role: "assistant", role: "assistant",
content: response.content || "I'm here to help! How can I assist you today?", content: data.content || "I'm here to help! How can I assist you today?",
timestamp: new Date() timestamp: new Date()
}; };
setChatHistory(prev => [...prev, aiMessage]); setChatHistory(prev => [...prev, aiMessage]);
+4 -4
View File
@@ -48,7 +48,7 @@ export default function TasksPage() {
// Filter and sort tasks // Filter and sort tasks
const filteredAndSortedTasks = useMemo(() => { const filteredAndSortedTasks = useMemo(() => {
if (!tasks) return []; if (!Array.isArray(tasks)) return [];
const filtered = tasks.filter((task: any) => { const filtered = tasks.filter((task: any) => {
const matchesSearch = task.title.toLowerCase().includes(searchQuery.toLowerCase()) || const matchesSearch = task.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -68,9 +68,9 @@ export default function TasksPage() {
bValue = b.title.toLowerCase(); bValue = b.title.toLowerCase();
break; break;
case "priority": case "priority":
const priorityOrder = { high: 3, medium: 2, low: 1 }; const priorityOrder: Record<string, number> = { high: 3, medium: 2, low: 1 };
aValue = priorityOrder[a.priority] || 0; aValue = priorityOrder[a.priority as string] || 0;
bValue = priorityOrder[b.priority] || 0; bValue = priorityOrder[b.priority as string] || 0;
break; break;
case "dueDate": case "dueDate":
aValue = a.dueDate ? new Date(a.dueDate) : new Date('9999-12-31'); aValue = a.dueDate ? new Date(a.dueDate) : new Date('9999-12-31');