From 7dcd2117d3a2fca01af5bacd85ce6520d246294b Mon Sep 17 00:00:00 2001 From: ghaddaditw <40211818-ghaddaditw@users.noreply.replit.com> Date: Fri, 30 May 2025 17:59:27 +0000 Subject: [PATCH] Add new pages to access key features and fix page navigation Adds Tasks, Finances, Voice, AI, Analytics, and Settings pages with routing in App.tsx. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 10e60398-05df-4c0e-8698-578a1494e818 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/81470e0d-8ae8-4335-9301-cd9a69e670fa/29a30ac5-78e2-49a4-b73b-7a3f9967ce79.jpg --- client/src/App.tsx | 12 ++ client/src/pages/AIPage.tsx | 272 ++++++++++++++++++++++++ client/src/pages/AnalyticsPage.tsx | 320 +++++++++++++++++++++++++++++ client/src/pages/FinancesPage.tsx | 277 +++++++++++++++++++++++++ client/src/pages/SettingsPage.tsx | 251 ++++++++++++++++++++++ client/src/pages/TasksPage.tsx | 273 ++++++++++++++++++++++++ client/src/pages/VoicePage.tsx | 256 +++++++++++++++++++++++ 7 files changed, 1661 insertions(+) create mode 100644 client/src/pages/AIPage.tsx create mode 100644 client/src/pages/AnalyticsPage.tsx create mode 100644 client/src/pages/FinancesPage.tsx create mode 100644 client/src/pages/SettingsPage.tsx create mode 100644 client/src/pages/TasksPage.tsx create mode 100644 client/src/pages/VoicePage.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 8925e7d..e263502 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -12,6 +12,12 @@ import DashboardPage from "@/pages/DashboardPage"; import LoginPage from "@/pages/LoginPage"; import ProfessionalDirectory from "@/pages/ProfessionalDirectory"; import OnboardingPage from "@/pages/OnboardingPage"; +import TasksPage from "@/pages/TasksPage"; +import FinancesPage from "@/pages/FinancesPage"; +import VoicePage from "@/pages/VoicePage"; +import AIPage from "@/pages/AIPage"; +import AnalyticsPage from "@/pages/AnalyticsPage"; +import SettingsPage from "@/pages/SettingsPage"; import NotFound from "@/pages/not-found"; function Router() { @@ -22,6 +28,12 @@ function Router() { + + + + + + diff --git a/client/src/pages/AIPage.tsx b/client/src/pages/AIPage.tsx new file mode 100644 index 0000000..d45e120 --- /dev/null +++ b/client/src/pages/AIPage.tsx @@ -0,0 +1,272 @@ +import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { useToast } from "@/hooks/use-toast"; +import Sidebar from "@/components/layout/Sidebar"; +import { Brain, MessageSquare, Lightbulb, Laugh, Send } from "lucide-react"; +import { apiRequest } from "@/lib/queryClient"; + +export default function AIPage() { + const { toast } = useToast(); + const [message, setMessage] = useState(""); + const [chatHistory, setChatHistory] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const { data: dailyJoke } = useQuery({ + queryKey: ["/api/ai/daily-joke"], + }); + + const { data: status } = useQuery({ + queryKey: ["/api/ai/status"], + }); + + const { data: interactions = [] } = useQuery({ + queryKey: ["/api/ai/interactions"], + }); + + const handleSendMessage = async (e: React.FormEvent) => { + e.preventDefault(); + if (!message.trim()) return; + + const userMessage = { role: "user", content: message, timestamp: new Date() }; + setChatHistory(prev => [...prev, userMessage]); + setMessage(""); + setIsLoading(true); + + try { + const response = await apiRequest("/api/ai/chat", { + method: "POST", + body: JSON.stringify({ message }), + }); + + const aiMessage = { + role: "assistant", + content: response.content || "I'm here to help! How can I assist you today?", + timestamp: new Date() + }; + setChatHistory(prev => [...prev, aiMessage]); + } catch (error) { + toast({ + title: "Error", + description: "Failed to send message. Please try again.", + variant: "destructive", + }); + } finally { + setIsLoading(false); + } + }; + + const generatePersonalizedJoke = async () => { + try { + const response = await apiRequest("/api/ai/joke", { + method: "POST", + }); + toast({ + title: "Here's a joke for you!", + description: response.content || "Why don't tasks ever get lonely? Because they always have deadlines to meet!", + }); + } catch (error) { + toast({ + title: "Error", + description: "Failed to generate joke. Please try again.", + variant: "destructive", + }); + } + }; + + const generateInsight = async (type: string) => { + try { + const response = await apiRequest("/api/ai/insight", { + method: "POST", + body: JSON.stringify({ type }), + }); + toast({ + title: `${type.charAt(0).toUpperCase() + type.slice(1)} Insight`, + description: response.content || "Here's an insight based on your data!", + }); + } catch (error) { + toast({ + title: "Error", + description: "Failed to generate insight. Please try again.", + variant: "destructive", + }); + } + }; + + return ( +
+ +
+
+
+

AI Assistant

+

+ Chat with your AI assistant and get personalized insights +

+
+ +
+ {/* Chat Interface */} +
+ + + + + AI Chat + + + Have a conversation with your AI assistant + + + +
+ {chatHistory.length === 0 ? ( +
+ +

Start a conversation with your AI assistant!

+

Try asking about your tasks, finances, or request insights.

+
+ ) : ( + chatHistory.map((msg, index) => ( +
+
+

{msg.content}

+

+ {new Date(msg.timestamp).toLocaleTimeString()} +

+
+
+ )) + )} + {isLoading && ( +
+
+
+
+ AI is thinking... +
+
+
+ )} +
+
+ setMessage(e.target.value)} + placeholder="Type your message..." + disabled={isLoading} + className="flex-1" + /> + +
+
+
+
+ + {/* AI Features Sidebar */} +
+ {/* AI Status */} + + + + + AI Status + + + +
+
+ Model Status + + {status?.loaded ? "Ready" : "Loading"} + +
+
+ Interactions Today + + {interactions.length} + +
+
+
+
+ + {/* Quick Actions */} + + + Quick Actions + + Get instant AI-powered assistance + + + + + + + + + + + {/* Daily Joke */} + {dailyJoke && ( + + + + + Daily Joke + + + +

"{dailyJoke.joke}"

+
+
+ )} + + {/* Recent Interactions */} + {interactions.length > 0 && ( + + + Recent Interactions + + +
+ {interactions.slice(0, 5).map((interaction: any) => ( +
+
{interaction.type}
+
+ {new Date(interaction.createdAt).toLocaleDateString()} +
+
+ ))} +
+
+
+ )} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/pages/AnalyticsPage.tsx b/client/src/pages/AnalyticsPage.tsx new file mode 100644 index 0000000..246690a --- /dev/null +++ b/client/src/pages/AnalyticsPage.tsx @@ -0,0 +1,320 @@ +import { useQuery } from "@tanstack/react-query"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import Sidebar from "@/components/layout/Sidebar"; +import { BarChart3, TrendingUp, TrendingDown, Calendar, Target, DollarSign } from "lucide-react"; + +export default function AnalyticsPage() { + const { data: tasks = [], isLoading: tasksLoading } = useQuery({ + queryKey: ["/api/tasks"], + }); + + const { data: financialSummary = { income: 0, expenses: 0, net: 0 }, isLoading: financialLoading } = useQuery({ + queryKey: ["/api/financial/summary"], + }); + + const { data: records = [], isLoading: recordsLoading } = useQuery({ + queryKey: ["/api/financial/records"], + }); + + if (tasksLoading || financialLoading || recordsLoading) { + return ( +
+ +
+
+
+
+
+ {[1, 2, 3, 4].map((i) => ( +
+ ))} +
+
+
+
+ ); + } + + // Calculate task analytics + const completedTasks = tasks.filter((task: any) => task.status === 'completed').length; + const pendingTasks = tasks.filter((task: any) => task.status === 'pending').length; + const inProgressTasks = tasks.filter((task: any) => task.status === 'in_progress').length; + const totalTasks = tasks.length; + const completionRate = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0; + + // Calculate financial analytics + const thisMonth = new Date().getMonth(); + const thisYear = new Date().getFullYear(); + const monthlyRecords = records.filter((record: any) => { + const recordDate = new Date(record.createdAt); + return recordDate.getMonth() === thisMonth && recordDate.getFullYear() === thisYear; + }); + + const monthlyIncome = monthlyRecords + .filter((record: any) => record.type === 'income') + .reduce((sum: number, record: any) => sum + record.amount, 0); + + const monthlyExpenses = monthlyRecords + .filter((record: any) => record.type === 'expense') + .reduce((sum: number, record: any) => sum + record.amount, 0); + + return ( +
+ +
+
+
+

Analytics

+

+ Insights and performance metrics for your tasks and finances +

+
+ + {/* Task Analytics */} +
+

Task Performance

+
+ + + Total Tasks + + + +
{totalTasks}
+

All time

+
+
+ + + + Completed + + + +
{completedTasks}
+

+ {completionRate.toFixed(1)}% completion rate +

+
+
+ + + + In Progress + + + +
{inProgressTasks}
+

Active tasks

+
+
+ + + + Pending + + + +
{pendingTasks}
+

Awaiting action

+
+
+
+
+ + {/* Financial Analytics */} +
+

Financial Overview

+
+ + + Total Income + + + +
+ ${financialSummary.income.toFixed(2)} +
+

+ This month: ${monthlyIncome.toFixed(2)} +

+
+
+ + + + Total Expenses + + + +
+ ${financialSummary.expenses.toFixed(2)} +
+

+ This month: ${monthlyExpenses.toFixed(2)} +

+
+
+ + + + Net Balance + + + +
= 0 ? 'text-green-600' : 'text-red-600'}`}> + ${financialSummary.net.toFixed(2)} +
+

+ Monthly: ${(monthlyIncome - monthlyExpenses).toFixed(2)} +

+
+
+
+
+ + {/* Performance Insights */} +
+ + + + + Task Distribution + + + Breakdown of your task statuses + + + +
+
+
+
+ Completed +
+
+ {completedTasks} + {totalTasks > 0 ? ((completedTasks / totalTasks) * 100).toFixed(0) : 0}% +
+
+ +
+
+
+ In Progress +
+
+ {inProgressTasks} + {totalTasks > 0 ? ((inProgressTasks / totalTasks) * 100).toFixed(0) : 0}% +
+
+ +
+
+
+ Pending +
+
+ {pendingTasks} + {totalTasks > 0 ? ((pendingTasks / totalTasks) * 100).toFixed(0) : 0}% +
+
+
+
+
+ + + + Financial Health + + Your financial performance indicators + + + +
+
+ Savings Rate + 0 && (financialSummary.net / financialSummary.income) > 0.2 ? "default" : "secondary"}> + {financialSummary.income > 0 ? ((financialSummary.net / financialSummary.income) * 100).toFixed(1) : 0}% + +
+ +
+ Monthly Trends + = 0 ? "default" : "destructive"}> + {(monthlyIncome - monthlyExpenses) >= 0 ? "Positive" : "Negative"} + +
+ +
+ Transaction Count + + {records.length} total + +
+ +
+ This Month + + {monthlyRecords.length} transactions + +
+
+
+
+
+ + {/* Recommendations */} + + + Recommendations + + Suggestions to improve your productivity and financial health + + + +
+ {completionRate < 70 && ( +
+

+ Task Management: Your completion rate is {completionRate.toFixed(1)}%. + Consider breaking down large tasks into smaller, manageable pieces. +

+
+ )} + + {pendingTasks > inProgressTasks && pendingTasks > 0 && ( +
+

+ Productivity: You have {pendingTasks} pending tasks. + Start working on them to improve your productivity flow. +

+
+ )} + + {financialSummary.net < 0 && ( +
+

+ Financial Health: Your expenses exceed your income. + Review your spending patterns and consider creating a budget. +

+
+ )} + + {financialSummary.income > 0 && (financialSummary.net / financialSummary.income) > 0.3 && ( +
+

+ Great Job! You're saving over 30% of your income. + Consider investing your savings for long-term growth. +

+
+ )} +
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/pages/FinancesPage.tsx b/client/src/pages/FinancesPage.tsx new file mode 100644 index 0000000..978401a --- /dev/null +++ b/client/src/pages/FinancesPage.tsx @@ -0,0 +1,277 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Badge } from "@/components/ui/badge"; +import { useToast } from "@/hooks/use-toast"; +import Sidebar from "@/components/layout/Sidebar"; +import { Plus, DollarSign, TrendingUp, TrendingDown } from "lucide-react"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { apiRequest } from "@/lib/queryClient"; + +export default function FinancesPage() { + const { toast } = useToast(); + const queryClient = useQueryClient(); + const [open, setOpen] = useState(false); + const [newRecord, setNewRecord] = useState({ + type: "expense", + amount: "", + description: "", + category: "", + }); + + const { data: records = [], isLoading: recordsLoading } = useQuery({ + queryKey: ["/api/financial/records"], + }); + + const { data: summary = { income: 0, expenses: 0, net: 0 }, isLoading: summaryLoading } = useQuery({ + queryKey: ["/api/financial/summary"], + }); + + const createRecordMutation = useMutation({ + mutationFn: (record: typeof newRecord) => + apiRequest("/api/financial/records", { + method: "POST", + body: JSON.stringify({ + ...record, + amount: parseFloat(record.amount), + }), + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/financial/records"] }); + queryClient.invalidateQueries({ queryKey: ["/api/financial/summary"] }); + setOpen(false); + setNewRecord({ type: "expense", amount: "", description: "", category: "" }); + toast({ + title: "Record created", + description: "Your financial record has been successfully created.", + }); + }, + onError: () => { + toast({ + title: "Error", + description: "Failed to create record. Please try again.", + variant: "destructive", + }); + }, + }); + + const handleCreateRecord = (e: React.FormEvent) => { + e.preventDefault(); + if (!newRecord.amount || isNaN(parseFloat(newRecord.amount))) { + toast({ + title: "Invalid amount", + description: "Please enter a valid amount.", + variant: "destructive", + }); + return; + } + createRecordMutation.mutate(newRecord); + }; + + const categories = { + income: ["Salary", "Freelance", "Investment", "Business", "Other"], + expense: ["Food", "Transportation", "Housing", "Entertainment", "Healthcare", "Shopping", "Other"], + }; + + if (recordsLoading || summaryLoading) { + return ( +
+ +
+
+
+
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+
+
+ ); + } + + return ( +
+ +
+
+
+
+

Finances

+

+ Track your income, expenses, and financial goals +

+
+ + + + + + + Add Financial Record + + Add a new income or expense record. + + +
+
+ + +
+
+ + setNewRecord({ ...newRecord, amount: e.target.value })} + required + /> +
+
+ + +
+
+ + setNewRecord({ ...newRecord, description: e.target.value })} + /> +
+ +
+
+
+
+ + {/* Summary Cards */} +
+ + + Total Income + + + +
+ ${summary.income.toFixed(2)} +
+
+
+ + + + Total Expenses + + + +
+ ${summary.expenses.toFixed(2)} +
+
+
+ + + + Net Balance + + + +
= 0 ? 'text-green-600' : 'text-red-600'}`}> + ${summary.net.toFixed(2)} +
+
+
+
+ + {/* Records List */} + + + Recent Transactions + + Your latest financial transactions + + + + {records.length === 0 ? ( +
+ +

+ No records yet +

+

+ Start tracking your finances by adding your first record. +

+ +
+ ) : ( +
+ {records.map((record: any) => ( +
+
+
+ {record.type === 'income' ? : } +
+
+
{record.description || record.category}
+
+ {record.category} • {new Date(record.createdAt).toLocaleDateString()} +
+
+
+
+
+ {record.type === 'income' ? '+' : '-'}${record.amount.toFixed(2)} +
+ + {record.type} + +
+
+ ))} +
+ )} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/pages/SettingsPage.tsx b/client/src/pages/SettingsPage.tsx new file mode 100644 index 0000000..8845af0 --- /dev/null +++ b/client/src/pages/SettingsPage.tsx @@ -0,0 +1,251 @@ +import { useState } from "react"; +import { useAuth } from "@/context/AuthContext"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import { Separator } from "@/components/ui/separator"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useToast } from "@/hooks/use-toast"; +import Sidebar from "@/components/layout/Sidebar"; +import { User, Bell, Shield, Palette } from "lucide-react"; + +export default function SettingsPage() { + const { user, updateProfile } = useAuth(); + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [profile, setProfile] = useState({ + username: user?.username || "", + email: user?.email || "", + fullName: user?.fullName || "", + }); + const [notifications, setNotifications] = useState({ + emailNotifications: true, + pushNotifications: true, + taskReminders: true, + financialAlerts: true, + }); + + const handleProfileUpdate = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + try { + await updateProfile(profile); + toast({ + title: "Profile updated", + description: "Your profile has been successfully updated.", + }); + } catch (error) { + toast({ + title: "Error", + description: "Failed to update profile. Please try again.", + variant: "destructive", + }); + } finally { + setLoading(false); + } + }; + + return ( +
+ +
+
+
+

Settings

+

+ Manage your account settings and preferences +

+
+ + + + + + Profile + + + + Notifications + + + + Security + + + + Appearance + + + + + + + Profile Information + + Update your personal information and account details. + + + +
+
+
+ + setProfile({ ...profile, username: e.target.value })} + /> +
+
+ + setProfile({ ...profile, email: e.target.value })} + /> +
+
+
+ + setProfile({ ...profile, fullName: e.target.value })} + /> +
+ +
+
+
+
+ + + + + Notification Preferences + + Choose what notifications you want to receive. + + + +
+
+ +

+ Receive notifications via email +

+
+ + setNotifications({ ...notifications, emailNotifications: checked }) + } + /> +
+ +
+
+ +

+ Receive push notifications in your browser +

+
+ + setNotifications({ ...notifications, pushNotifications: checked }) + } + /> +
+ +
+
+ +

+ Get reminded about upcoming tasks and deadlines +

+
+ + setNotifications({ ...notifications, taskReminders: checked }) + } + /> +
+ +
+
+ +

+ Receive alerts about financial goals and budgets +

+
+ + setNotifications({ ...notifications, financialAlerts: checked }) + } + /> +
+
+
+
+ + + + + Security Settings + + Manage your account security and privacy settings. + + + +
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + Appearance Settings + + Customize the look and feel of your application. + + + +
+
+ +

+ Switch between light and dark themes +

+
+ +
+
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/pages/TasksPage.tsx b/client/src/pages/TasksPage.tsx new file mode 100644 index 0000000..d8f4bd6 --- /dev/null +++ b/client/src/pages/TasksPage.tsx @@ -0,0 +1,273 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Badge } from "@/components/ui/badge"; +import { useToast } from "@/hooks/use-toast"; +import Sidebar from "@/components/layout/Sidebar"; +import { Plus, Calendar, Clock, AlertCircle } from "lucide-react"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { apiRequest } from "@/lib/queryClient"; + +export default function TasksPage() { + const { toast } = useToast(); + const queryClient = useQueryClient(); + const [open, setOpen] = useState(false); + const [newTask, setNewTask] = useState({ + title: "", + description: "", + priority: "medium", + dueDate: "", + }); + + const { data: tasks = [], isLoading } = useQuery({ + queryKey: ["/api/tasks"], + }); + + const createTaskMutation = useMutation({ + mutationFn: (task: typeof newTask) => + apiRequest("/api/tasks", { + method: "POST", + body: JSON.stringify(task), + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/tasks"] }); + setOpen(false); + setNewTask({ title: "", description: "", priority: "medium", dueDate: "" }); + toast({ + title: "Task created", + description: "Your task has been successfully created.", + }); + }, + onError: () => { + toast({ + title: "Error", + description: "Failed to create task. Please try again.", + variant: "destructive", + }); + }, + }); + + const updateTaskMutation = useMutation({ + mutationFn: ({ id, status }: { id: number; status: string }) => + apiRequest(`/api/tasks/${id}`, { + method: "PATCH", + body: JSON.stringify({ status }), + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/tasks"] }); + toast({ + title: "Task updated", + description: "Task status has been updated.", + }); + }, + }); + + const handleCreateTask = (e: React.FormEvent) => { + e.preventDefault(); + createTaskMutation.mutate(newTask); + }; + + const getPriorityColor = (priority: string) => { + switch (priority) { + case "high": return "destructive"; + case "medium": return "default"; + case "low": return "secondary"; + default: return "default"; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case "completed": return "secondary"; + case "in_progress": return "default"; + case "pending": return "outline"; + default: return "outline"; + } + }; + + if (isLoading) { + return ( +
+ +
+
+
+
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+
+
+ ); + } + + return ( +
+ +
+
+
+
+

Tasks

+

+ Manage your tasks and track your progress +

+
+ + + + + + + Create New Task + + Add a new task to your todo list. + + +
+
+ + setNewTask({ ...newTask, title: e.target.value })} + required + /> +
+
+ +