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:
+74
-18
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
Reference in New Issue
Block a user