Compare commits
10
Commits
3bb6f85db9
...
31b54a1ecc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b54a1ecc | ||
|
|
83ffdf860a | ||
|
|
8d26a47b46 | ||
|
|
bbc3862e49 | ||
|
|
5a8e72c176 | ||
|
|
3eaff970ce | ||
|
|
30c0103b06 | ||
|
|
92a4bf4643 | ||
|
|
871ae2c733 | ||
|
|
d7f612559a |
@@ -0,0 +1,2 @@
|
|||||||
|
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
|
||||||
|
NODE_ENV=development
|
||||||
@@ -8,7 +8,5 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
<!-- This is a replit script which adds a banner on the top of the page when opened in development mode outside the replit environment -->
|
|
||||||
<script type="text/javascript" src="https://replit.com/public/js/replit-dev-banner.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+26
-77
@@ -1,85 +1,34 @@
|
|||||||
import { Switch, Route } from "wouter";
|
import { useState, useEffect } from "react";
|
||||||
import { queryClient } from "./lib/queryClient";
|
import LandingPage from "./pages/LandingPage";
|
||||||
import { QueryClientProvider } from "@tanstack/react-query";
|
import { AuthProvider } from "./context/AuthContext";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { ThemeProvider } from "./context/ThemeContext";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { RTLProvider } from "./components/RTLProvider";
|
||||||
import { FloatingVoiceControl } from "@/components/voice/FloatingVoiceControl";
|
// Import i18n configuration
|
||||||
import { AuthProvider } from "@/context/AuthContext";
|
import "./i18n/config";
|
||||||
import { ThemeProvider } from "@/context/ThemeContext";
|
|
||||||
import { AdaptiveThemeProvider } from "@/context/AdaptiveThemeContext";
|
|
||||||
import { VoiceProvider } from "@/context/VoiceProvider";
|
|
||||||
import { NotificationProvider } from "@/context/NotificationProvider";
|
|
||||||
import { TourManagerProvider } from "@/components/onboarding/TourManager";
|
|
||||||
import { MoodDetector } from "@/components/MoodDetector";
|
|
||||||
import { RTLProvider } from "@/components/RTLProvider";
|
|
||||||
import LandingPage from "@/pages/LandingPage";
|
|
||||||
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 SystemMonitoringPage from "@/pages/SystemMonitoringPage";
|
|
||||||
import SettingsPage from "@/pages/SettingsPage";
|
|
||||||
import FocusPage from "@/pages/FocusPage";
|
|
||||||
import ThemePage from "@/pages/ThemePage";
|
|
||||||
import BenchmarkPage from "@/pages/BenchmarkPage";
|
|
||||||
import ChatPage from "@/pages/ChatPage";
|
|
||||||
import ComfortHubPage from "@/pages/ComfortHubPage";
|
|
||||||
import NotFound from "@/pages/not-found";
|
|
||||||
|
|
||||||
function Router() {
|
|
||||||
return (
|
|
||||||
<Switch>
|
|
||||||
<Route path="/" component={LandingPage} />
|
|
||||||
<Route path="/login" component={LoginPage} />
|
|
||||||
<Route path="/register" component={LoginPage} />
|
|
||||||
<Route path="/onboarding" component={OnboardingPage} />
|
|
||||||
<Route path="/dashboard" component={DashboardPage} />
|
|
||||||
<Route path="/tasks" component={TasksPage} />
|
|
||||||
<Route path="/finances" component={FinancesPage} />
|
|
||||||
<Route path="/voice" component={VoicePage} />
|
|
||||||
<Route path="/ai" component={AIPage} />
|
|
||||||
<Route path="/analytics" component={AnalyticsPage} />
|
|
||||||
<Route path="/monitoring" component={SystemMonitoringPage} />
|
|
||||||
<Route path="/focus" component={FocusPage} />
|
|
||||||
<Route path="/themes" component={ThemePage} />
|
|
||||||
<Route path="/chat" component={ChatPage} />
|
|
||||||
<Route path="/comfort" component={ComfortHubPage} />
|
|
||||||
<Route path="/settings" component={SettingsPage} />
|
|
||||||
<Route path="/professionals" component={ProfessionalDirectory} />
|
|
||||||
<Route component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
console.log("🎯 App component is rendering...");
|
||||||
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("🎯 App component useEffect running...");
|
||||||
|
setIsLoaded(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<ThemeProvider>
|
||||||
<RTLProvider>
|
<RTLProvider>
|
||||||
<ThemeProvider>
|
<AuthProvider>
|
||||||
<AuthProvider>
|
{isLoaded ? (
|
||||||
<AdaptiveThemeProvider>
|
<div className="app-container">
|
||||||
<NotificationProvider>
|
<LandingPage />
|
||||||
<VoiceProvider>
|
</div>
|
||||||
<TourManagerProvider>
|
) : (
|
||||||
<TooltipProvider>
|
<div className="loading">Loading...</div>
|
||||||
<Toaster />
|
)}
|
||||||
<Router />
|
</AuthProvider>
|
||||||
<FloatingVoiceControl />
|
|
||||||
<MoodDetector />
|
|
||||||
</TooltipProvider>
|
|
||||||
</TourManagerProvider>
|
|
||||||
</VoiceProvider>
|
|
||||||
</NotificationProvider>
|
|
||||||
</AdaptiveThemeProvider>
|
|
||||||
</AuthProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</RTLProvider>
|
</RTLProvider>
|
||||||
</QueryClientProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function Header() {
|
export function Header() {
|
||||||
const { user, logout } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
const { theme, toggleTheme } = useTheme();
|
const { theme, toggleTheme } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -160,3 +160,5 @@ export default function Header() {
|
|||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
@@ -126,7 +126,7 @@ const adminNavigation = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Sidebar({ className }: SidebarProps) {
|
function Sidebar({ className }: SidebarProps) {
|
||||||
const [location] = useLocation();
|
const [location] = useLocation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -233,3 +233,5 @@ export default function Sidebar({ className }: SidebarProps) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Sidebar;
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
interface SkeletonProps {
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Skeleton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("animate-pulse rounded-md bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card skeleton for general content
|
||||||
|
function CardSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Skeleton className="h-4 w-[250px]" />
|
||||||
|
<Skeleton className="h-4 w-[200px]" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
<Skeleton className="h-4 w-[180px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task item skeleton
|
||||||
|
function TaskSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("flex items-center space-x-4 p-4 border-b", className)}>
|
||||||
|
<Skeleton className="h-4 w-4 rounded" />
|
||||||
|
<div className="space-y-2 flex-1">
|
||||||
|
<Skeleton className="h-4 w-[200px]" />
|
||||||
|
<Skeleton className="h-3 w-[150px]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<Skeleton className="h-6 w-16 rounded-full" />
|
||||||
|
<Skeleton className="h-6 w-12 rounded-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Financial record skeleton
|
||||||
|
function FinancialSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("flex items-center justify-between p-4 border-b", className)}>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<Skeleton className="h-10 w-10 rounded-full" />
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Skeleton className="h-4 w-[120px]" />
|
||||||
|
<Skeleton className="h-3 w-[80px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right space-y-1">
|
||||||
|
<Skeleton className="h-4 w-[60px] ml-auto" />
|
||||||
|
<Skeleton className="h-3 w-[40px] ml-auto" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chat message skeleton
|
||||||
|
function MessageSkeleton({ className, isOwn = false }: SkeletonProps & { isOwn?: boolean }) {
|
||||||
|
return (
|
||||||
|
<div className={cn("flex gap-3 mb-4", isOwn && "flex-row-reverse", className)}>
|
||||||
|
{!isOwn && <Skeleton className="h-8 w-8 rounded-full" />}
|
||||||
|
<div className={cn("max-w-[70%] space-y-2", isOwn && "items-end")}>
|
||||||
|
<div className={cn(
|
||||||
|
"rounded-lg p-3 space-y-1",
|
||||||
|
isOwn ? "bg-primary" : "bg-muted"
|
||||||
|
)}>
|
||||||
|
<Skeleton className={cn("h-3", isOwn ? "bg-primary-foreground/20" : "bg-foreground/20", "w-[150px]")} />
|
||||||
|
<Skeleton className={cn("h-3", isOwn ? "bg-primary-foreground/20" : "bg-foreground/20", "w-[100px]")} />
|
||||||
|
</div>
|
||||||
|
<Skeleton className="h-2 w-[60px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analytics chart skeleton
|
||||||
|
function ChartSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("space-y-4", className)}>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Skeleton className="h-6 w-[150px]" />
|
||||||
|
<Skeleton className="h-4 w-[80px]" />
|
||||||
|
</div>
|
||||||
|
<div className="h-[300px] flex items-end justify-between space-x-2">
|
||||||
|
{Array.from({ length: 7 }).map((_, i) => (
|
||||||
|
<Skeleton
|
||||||
|
key={i}
|
||||||
|
className="w-full"
|
||||||
|
style={{ height: `${Math.random() * 60 + 40}%` }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
{Array.from({ length: 7 }).map((_, i) => (
|
||||||
|
<Skeleton key={i} className="h-3 w-8" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table skeleton
|
||||||
|
function TableSkeleton({ rows = 5, columns = 4, className }: SkeletonProps & { rows?: number; columns?: number }) {
|
||||||
|
return (
|
||||||
|
<div className={cn("space-y-3", className)}>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex space-x-4">
|
||||||
|
{Array.from({ length: columns }).map((_, i) => (
|
||||||
|
<Skeleton key={i} className="h-4 flex-1" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{/* Rows */}
|
||||||
|
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||||
|
<div key={rowIndex} className="flex space-x-4">
|
||||||
|
{Array.from({ length: columns }).map((_, colIndex) => (
|
||||||
|
<Skeleton key={colIndex} className="h-4 flex-1" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profile skeleton
|
||||||
|
function ProfileSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("flex items-center space-x-4", className)}>
|
||||||
|
<Skeleton className="h-16 w-16 rounded-full" />
|
||||||
|
<div className="space-y-2 flex-1">
|
||||||
|
<Skeleton className="h-5 w-[180px]" />
|
||||||
|
<Skeleton className="h-4 w-[120px]" />
|
||||||
|
<Skeleton className="h-3 w-[100px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List skeleton
|
||||||
|
function ListSkeleton({ items = 5, className }: SkeletonProps & { items?: number }) {
|
||||||
|
return (
|
||||||
|
<div className={cn("space-y-3", className)}>
|
||||||
|
{Array.from({ length: items }).map((_, i) => (
|
||||||
|
<div key={i} className="flex items-center space-x-3">
|
||||||
|
<Skeleton className="h-6 w-6 rounded" />
|
||||||
|
<div className="space-y-1 flex-1">
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
<Skeleton className="h-3 w-3/4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page skeleton for full page loading
|
||||||
|
function PageSkeleton({ className }: SkeletonProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("space-y-6 p-6", className)}>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Skeleton className="h-8 w-[200px]" />
|
||||||
|
<Skeleton className="h-4 w-[300px]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content grid */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
|
<CardSkeleton key={i} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Skeleton,
|
||||||
|
CardSkeleton,
|
||||||
|
TaskSkeleton,
|
||||||
|
FinancialSkeleton,
|
||||||
|
MessageSkeleton,
|
||||||
|
ChartSkeleton,
|
||||||
|
TableSkeleton,
|
||||||
|
ProfileSkeleton,
|
||||||
|
ListSkeleton,
|
||||||
|
PageSkeleton
|
||||||
|
}
|
||||||
@@ -20,6 +20,14 @@ interface VoiceContextType {
|
|||||||
|
|
||||||
const VoiceContext = createContext<VoiceContextType | undefined>(undefined);
|
const VoiceContext = createContext<VoiceContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export function useVoiceContext() {
|
||||||
|
const context = useContext(VoiceContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useVoiceContext must be used within a VoiceProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
export function VoiceProvider({ children }: { children: React.ReactNode }) {
|
export function VoiceProvider({ children }: { children: React.ReactNode }) {
|
||||||
const { isListening, startListening, stopListening, isSupported } = useVoice();
|
const { isListening, startListening, stopListening, isSupported } = useVoice();
|
||||||
const [language, setLanguage] = useState<'en' | 'ar'>('en');
|
const [language, setLanguage] = useState<'en' | 'ar'>('en');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback, useRef } from "react";
|
import { useState, useCallback, useRef } from "react";
|
||||||
import { useVoiceContext } from "@/context/VoiceProvider";
|
// Removed circular dependency import
|
||||||
|
// import { useVoiceContext } from "@/context/VoiceProvider";
|
||||||
|
|
||||||
export function useVoice() {
|
export function useVoice() {
|
||||||
const [isListening, setIsListening] = useState(false);
|
const [isListening, setIsListening] = useState(false);
|
||||||
|
|||||||
@@ -39,6 +39,25 @@
|
|||||||
"logout": "تسجيل الخروج",
|
"logout": "تسجيل الخروج",
|
||||||
"profile": "الملف الشخصي"
|
"profile": "الملف الشخصي"
|
||||||
},
|
},
|
||||||
|
"auth": {
|
||||||
|
"login": "تسجيل الدخول",
|
||||||
|
"register": "إنشاء حساب",
|
||||||
|
"signup": "إنشاء حساب",
|
||||||
|
"email": "البريد الإلكتروني",
|
||||||
|
"password": "كلمة المرور",
|
||||||
|
"confirmPassword": "تأكيد كلمة المرور",
|
||||||
|
"username": "اسم المستخدم",
|
||||||
|
"firstName": "الاسم الأول",
|
||||||
|
"lastName": "الاسم الأخير",
|
||||||
|
"forgotPassword": "نسيت كلمة المرور؟",
|
||||||
|
"createAccount": "إنشاء حساب جديد",
|
||||||
|
"alreadyHaveAccount": "لديك حساب بالفعل؟",
|
||||||
|
"dontHaveAccount": "ليس لديك حساب؟",
|
||||||
|
"loginSuccess": "تم تسجيل الدخول بنجاح",
|
||||||
|
"registerSuccess": "تم إنشاء الحساب بنجاح",
|
||||||
|
"invalidCredentials": "بيانات الدخول غير صحيحة",
|
||||||
|
"accountExists": "الحساب موجود بالفعل"
|
||||||
|
},
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"title": "المهام",
|
"title": "المهام",
|
||||||
"createTask": "إنشاء مهمة",
|
"createTask": "إنشاء مهمة",
|
||||||
@@ -319,7 +338,10 @@
|
|||||||
"browserNative": "متصفح أصلي",
|
"browserNative": "متصفح أصلي",
|
||||||
"azureSpeech": "Azure Speech",
|
"azureSpeech": "Azure Speech",
|
||||||
"active": "نشط",
|
"active": "نشط",
|
||||||
"requestsThisMonth": "طلب هذا الشهر"
|
"requestsThisMonth": "طلب هذا الشهر",
|
||||||
|
"claude3Active": "Claude 3 Sonnet - نشط",
|
||||||
|
"apiUsageValue": "2,341 طلب هذا الشهر",
|
||||||
|
"saveAiSettings": "حفظ إعدادات الذكاء الاصطناعي"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"login": "تسجيل الدخول",
|
"login": "تسجيل الدخول",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
--secondary-foreground: 222.2 84% 4.9%;
|
--secondary-foreground: 222.2 84% 4.9%;
|
||||||
--muted: 210 40% 96%;
|
--muted: 210 40% 96%;
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
--accent: 210 40% 96%;
|
--accent: 210 40% 96%;
|
||||||
--accent-foreground: 222.2 84% 4.9%;
|
--accent-foreground: 222.2 84% 4.9%;
|
||||||
--destructive: 0 84.2% 60.2%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
|||||||
+21
-6
@@ -2,10 +2,25 @@ import { StrictMode } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import "./i18n/config";
|
// i18n config is now imported in App.tsx
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
console.log("🚀 Main.tsx is loading...");
|
||||||
<StrictMode>
|
|
||||||
<App />
|
const rootElement = document.getElementById("root");
|
||||||
</StrictMode>
|
console.log("📍 Root element:", rootElement);
|
||||||
);
|
|
||||||
|
if (rootElement) {
|
||||||
|
console.log("✅ Root element found, creating React root...");
|
||||||
|
const root = createRoot(rootElement);
|
||||||
|
console.log("✅ React root created, rendering App...");
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ App rendered!");
|
||||||
|
} else {
|
||||||
|
console.error("❌ Root element not found!");
|
||||||
|
}
|
||||||
|
|||||||
+22
-22
@@ -18,14 +18,17 @@ export default function AIPage() {
|
|||||||
|
|
||||||
const { data: dailyJoke } = useQuery({
|
const { data: dailyJoke } = useQuery({
|
||||||
queryKey: ["/api/ai/daily-joke"],
|
queryKey: ["/api/ai/daily-joke"],
|
||||||
|
select: (data: any) => data || { joke: "Why don't AI assistants ever get tired? Because they run on endless loops!" }
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: status } = useQuery({
|
const { data: status } = useQuery({
|
||||||
queryKey: ["/api/ai/status"],
|
queryKey: ["/api/ai/status"],
|
||||||
|
select: (data: any) => data || { loaded: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: interactions = [] } = useQuery({
|
const { data: interactions = [] } = useQuery({
|
||||||
queryKey: ["/api/ai/interactions"],
|
queryKey: ["/api/ai/interactions"],
|
||||||
|
select: (data: any) => Array.isArray(data) ? data : []
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSendMessage = async (e: React.FormEvent) => {
|
const handleSendMessage = async (e: React.FormEvent) => {
|
||||||
@@ -38,14 +41,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]);
|
||||||
@@ -62,12 +63,11 @@ export default function AIPage() {
|
|||||||
|
|
||||||
const generatePersonalizedJoke = async () => {
|
const generatePersonalizedJoke = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiRequest("/api/ai/joke", {
|
const response = await apiRequest("POST", "/api/ai/generate-joke", {});
|
||||||
method: "POST",
|
const data = await response.json();
|
||||||
});
|
|
||||||
toast({
|
toast({
|
||||||
title: "Here's a joke for you!",
|
title: "Here's a joke for you!",
|
||||||
description: response.content || "Why don't tasks ever get lonely? Because they always have deadlines to meet!",
|
description: data.content || "Why don't tasks ever get lonely? Because they always have deadlines to meet!",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
@@ -80,13 +80,13 @@ export default function AIPage() {
|
|||||||
|
|
||||||
const generateInsight = async (type: string) => {
|
const generateInsight = async (type: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await apiRequest("/api/ai/insight", {
|
const response = await apiRequest("POST", "/api/ai/chat", {
|
||||||
method: "POST",
|
message: `Generate an insight about my ${type} data`
|
||||||
body: JSON.stringify({ type }),
|
|
||||||
});
|
});
|
||||||
|
const data = await response.json();
|
||||||
toast({
|
toast({
|
||||||
title: `${type.charAt(0).toUpperCase() + type.slice(1)} Insight`,
|
title: `${type.charAt(0).toUpperCase() + type.slice(1)} Insight`,
|
||||||
description: response.content || "Here's an insight based on your data!",
|
description: data.content || "Here's an insight based on your data!",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
@@ -185,14 +185,14 @@ export default function AIPage() {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm">Model Status</span>
|
<span className="text-sm">Model Status</span>
|
||||||
<Badge variant={status?.loaded ? "default" : "secondary"}>
|
<Badge variant={(status as any)?.loaded ? "default" : "secondary"}>
|
||||||
{status?.loaded ? "Ready" : "Loading"}
|
{(status as any)?.loaded ? "Ready" : "Loading"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm">Interactions Today</span>
|
<span className="text-sm">Interactions Today</span>
|
||||||
<Badge variant="outline">
|
<Badge variant="outline">
|
||||||
{interactions.length}
|
{Array.isArray(interactions) ? interactions.length : 0}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -237,24 +237,24 @@ export default function AIPage() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className="text-sm italic">"{dailyJoke.joke}"</p>
|
<p className="text-sm italic">"{(dailyJoke as any)?.joke || 'Why don\'t AI assistants ever get tired? Because they run on endless loops!'}"</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Recent Interactions */}
|
{/* Recent Interactions */}
|
||||||
{interactions.length > 0 && (
|
{Array.isArray(interactions) && interactions.length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Recent Interactions</CardTitle>
|
<CardTitle>Recent Interactions</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{interactions.slice(0, 5).map((interaction: any) => (
|
{(interactions as any[]).slice(0, 5).map((interaction: any, index: number) => (
|
||||||
<div key={interaction.id} className="text-xs p-2 bg-gray-50 dark:bg-gray-800 rounded">
|
<div key={interaction.id || index} className="text-xs p-2 bg-gray-50 dark:bg-gray-800 rounded">
|
||||||
<div className="font-medium">{interaction.type}</div>
|
<div className="font-medium">{interaction.type || 'AI Interaction'}</div>
|
||||||
<div className="text-gray-600 dark:text-gray-300">
|
<div className="text-gray-600 dark:text-gray-300">
|
||||||
{new Date(interaction.createdAt).toLocaleDateString()}
|
{interaction.createdAt ? new Date(interaction.createdAt).toLocaleDateString() : 'Today'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { PerformanceBenchmark } from '@/components/PerformanceBenchmark';
|
import { PerformanceBenchmark } from '@/components/PerformanceBenchmark';
|
||||||
import { Header } from '@/components/layout/Header';
|
import { Header } from '@/components/layout/Header';
|
||||||
import { Sidebar } from '@/components/layout/Sidebar';
|
import Sidebar from '@/components/layout/Sidebar';
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { useAuth } from '@/hooks/useAuth';
|
|||||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||||
import { Badge } from '@/components/ui/badge';
|
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { PageHeader } from '@/components/ui/page-header';
|
import { PageHeader } from '@/components/ui/page-header';
|
||||||
|
import { MessageSkeleton, ListSkeleton } from '@/components/ui/loading-skeleton';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import Sidebar from '@/components/layout/Sidebar';
|
import Sidebar from '@/components/layout/Sidebar';
|
||||||
import {
|
import {
|
||||||
@@ -214,13 +214,34 @@ export default function ChatPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
||||||
<Sidebar className="w-64 border-r" />
|
<Sidebar className="w-64 border-r" />
|
||||||
<div className="flex-1 overflow-auto p-6">
|
<div className="flex-1 flex">
|
||||||
<div className="animate-pulse space-y-4">
|
{/* Chat List Skeleton */}
|
||||||
<div className="h-8 bg-gray-200 dark:bg-gray-700 rounded w-1/4"></div>
|
<div className="w-80 border-r border-border bg-background">
|
||||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
|
<div className="p-4 border-b">
|
||||||
<div className="grid grid-cols-3 gap-4 h-96">
|
<PageHeader title="Messages" description="Your conversations" />
|
||||||
<div className="bg-gray-200 dark:bg-gray-700 rounded"></div>
|
</div>
|
||||||
<div className="col-span-2 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
<div className="p-4 space-y-3">
|
||||||
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
|
<ListSkeleton key={i} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Chat Content Skeleton */}
|
||||||
|
<div className="flex-1 flex flex-col">
|
||||||
|
<div className="p-4 border-b">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="w-10 h-10 bg-gray-200 dark:bg-gray-700 rounded-full animate-pulse"></div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-32 animate-pulse"></div>
|
||||||
|
<div className="h-3 bg-gray-200 dark:bg-gray-700 rounded w-24 animate-pulse"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-4 space-y-4">
|
||||||
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
|
<MessageSkeleton key={i} isOwn={i % 3 === 0} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Header from "@/components/layout/Header";
|
|
||||||
import Sidebar from "@/components/layout/Sidebar";
|
import Sidebar from "@/components/layout/Sidebar";
|
||||||
import TaskList from "@/components/tasks/TaskList";
|
import TaskList from "@/components/tasks/TaskList";
|
||||||
import FinancialOverview from "@/components/financial/FinancialOverview";
|
import FinancialOverview from "@/components/financial/FinancialOverview";
|
||||||
import VoiceRecorder from "@/components/voice/VoiceRecorder";
|
import VoiceRecorder from "@/components/voice/VoiceRecorder";
|
||||||
import VoiceIndicator from "@/components/voice/VoiceIndicator";
|
import VoiceIndicator from "@/components/voice/VoiceIndicator";
|
||||||
import DailyJoke from "@/components/ai/DailyJoke";
|
import DailyJoke from "@/components/ai/DailyJoke";
|
||||||
import { CommandPalette } from "@/components/CommandPalette";
|
|
||||||
import { useVoiceContext } from "@/context/VoiceProvider";
|
import { useVoiceContext } from "@/context/VoiceProvider";
|
||||||
import { useNotifications } from "@/context/NotificationProvider";
|
import { useNotifications } from "@/context/NotificationProvider";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
Mic,
|
Mic,
|
||||||
Brain,
|
Brain,
|
||||||
Activity,
|
Activity,
|
||||||
TrendingUp,
|
|
||||||
Zap,
|
Zap,
|
||||||
HelpCircle
|
HelpCircle
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@@ -29,9 +28,8 @@ import { useTourManager, getMainTourSteps } from "@/components/onboarding/TourMa
|
|||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
const { user, isLoading } = useAuth();
|
const { user, isLoading } = useAuth();
|
||||||
const [, setLocation] = useLocation();
|
const [, setLocation] = useLocation();
|
||||||
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
|
useVoiceContext();
|
||||||
const { isListening, speak } = useVoiceContext();
|
useNotifications();
|
||||||
const { unreadCount } = useNotifications();
|
|
||||||
const { startTour } = useTourManager();
|
const { startTour } = useTourManager();
|
||||||
|
|
||||||
const handleStartTour = () => {
|
const handleStartTour = () => {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { PageHeader } from '@/components/ui/page-header';
|
import { PageHeader } from '@/components/ui/page-header';
|
||||||
|
import { FinancialSkeleton, CardSkeleton, ChartSkeleton } from '@/components/ui/loading-skeleton';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { apiRequest } from '@/lib/queryClient';
|
|
||||||
import Sidebar from '@/components/layout/Sidebar';
|
import Sidebar from '@/components/layout/Sidebar';
|
||||||
import { Plus, TrendingUp, TrendingDown, DollarSign } from 'lucide-react';
|
import { Plus, TrendingUp, TrendingDown, DollarSign } from 'lucide-react';
|
||||||
|
|
||||||
@@ -99,14 +99,43 @@ export default function FinancesPage() {
|
|||||||
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
||||||
<Sidebar className="w-64 border-r" />
|
<Sidebar className="w-64 border-r" />
|
||||||
<div className="flex-1 overflow-auto p-6">
|
<div className="flex-1 overflow-auto p-6">
|
||||||
<div className="animate-pulse space-y-4">
|
<div className="space-y-6">
|
||||||
<div className="h-8 bg-gray-200 dark:bg-gray-700 rounded w-1/4"></div>
|
<PageHeader
|
||||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
|
title="Financial Overview"
|
||||||
<div className="grid grid-cols-3 gap-4">
|
description="Track your income and expenses"
|
||||||
{[...Array(3)].map((_, i) => (
|
/>
|
||||||
<div key={i} className="h-24 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
|
||||||
|
{/* Summary cards skeleton */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
|
<CardSkeleton key={i} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Chart skeleton */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Spending Overview</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ChartSkeleton />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Transactions</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
|
<FinancialSkeleton key={i} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import Sidebar from "@/components/layout/Sidebar";
|
|||||||
import FocusMode from "@/components/focus/FocusMode";
|
import FocusMode from "@/components/focus/FocusMode";
|
||||||
|
|
||||||
export default function FocusPage() {
|
export default function FocusPage() {
|
||||||
const { t } = useTranslation();
|
useTranslation();
|
||||||
const [isFocusModeOpen, setIsFocusModeOpen] = useState(false);
|
const [isFocusModeOpen, setIsFocusModeOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { useVoice } from "@/hooks/useVoice";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import DailyJoke from "@/components/ai/DailyJoke";
|
import DailyJoke from "@/components/ai/DailyJoke";
|
||||||
import VoiceIndicator from "@/components/voice/VoiceIndicator";
|
import VoiceIndicator from "@/components/voice/VoiceIndicator";
|
||||||
import Header from "@/components/layout/Header";
|
import Header from "@/components/layout/Header";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
|
||||||
import {
|
import {
|
||||||
Mic,
|
Mic,
|
||||||
Volume2,
|
|
||||||
Brain,
|
Brain,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
Calendar,
|
Calendar,
|
||||||
@@ -17,26 +15,35 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
export default function LandingPage() {
|
export default function LandingPage() {
|
||||||
|
console.log("🚀 LandingPage is rendering...");
|
||||||
const [, setLocation] = useLocation();
|
const [, setLocation] = useLocation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isListening, startListening, stopListening, isSupported } = useVoice();
|
// Temporarily removed voice hooks that might be causing issues
|
||||||
|
// const { isListening, startListening, stopListening, isSupported } = useVoice();
|
||||||
|
|
||||||
|
// Log auth state for debugging
|
||||||
|
console.log("👤 User authentication state:", user);
|
||||||
|
|
||||||
// Redirect to dashboard if already authenticated
|
// Redirect to dashboard if already authenticated
|
||||||
if (user) {
|
if (user) {
|
||||||
|
console.log("👤 User authenticated, redirecting to dashboard...");
|
||||||
setLocation("/dashboard");
|
setLocation("/dashboard");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGetStarted = () => {
|
const handleGetStarted = () => {
|
||||||
|
console.log("🔘 Get Started button clicked");
|
||||||
setLocation("/login");
|
setLocation("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVoiceDemo = () => {
|
const handleVoiceDemo = () => {
|
||||||
if (isListening) {
|
console.log("🔘 Voice Demo button clicked");
|
||||||
stopListening();
|
// Voice functionality temporarily disabled for debugging
|
||||||
} else {
|
// if (isListening) {
|
||||||
startListening();
|
// stopListening();
|
||||||
}
|
// } else {
|
||||||
|
// startListening();
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -78,26 +85,15 @@ export default function LandingPage() {
|
|||||||
<ArrowRight className="ml-2 w-5 h-5" />
|
<ArrowRight className="ml-2 w-5 h-5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{isSupported && (
|
<Button
|
||||||
<Button
|
size="lg"
|
||||||
size="lg"
|
variant="outline"
|
||||||
variant="outline"
|
onClick={handleVoiceDemo}
|
||||||
onClick={handleVoiceDemo}
|
className="border-2 border-blue-300 dark:border-blue-700 text-blue-700 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20 px-8 py-3 text-lg font-semibold rounded-xl transition-all"
|
||||||
className="border-2 border-blue-300 dark:border-blue-700 text-blue-700 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20 px-8 py-3 text-lg font-semibold rounded-xl transition-all"
|
>
|
||||||
>
|
<Mic className="w-5 h-5 mr-2" />
|
||||||
{isListening ? (
|
Try Voice Demo
|
||||||
<>
|
</Button>
|
||||||
<Volume2 className="w-5 h-5 mr-2 animate-pulse" />
|
|
||||||
Listening...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Mic className="w-5 h-5 mr-2" />
|
|
||||||
Try Voice Demo
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Features Grid */}
|
{/* Features Grid */}
|
||||||
@@ -143,15 +139,9 @@ export default function LandingPage() {
|
|||||||
<p className="text-slate-600 dark:text-slate-300 max-w-2xl mx-auto">
|
<p className="text-slate-600 dark:text-slate-300 max-w-2xl mx-auto">
|
||||||
Control your productivity suite with natural voice commands. Create tasks, check finances, and navigate the app hands-free.
|
Control your productivity suite with natural voice commands. Create tasks, check finances, and navigate the app hands-free.
|
||||||
</p>
|
</p>
|
||||||
{isSupported ? (
|
<p className="text-sm text-green-600 dark:text-green-400 font-medium">
|
||||||
<p className="text-sm text-green-600 dark:text-green-400 font-medium">
|
✓ Voice control supported on your device
|
||||||
✓ Voice control supported on your device
|
</p>
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-sm text-orange-600 dark:text-orange-400">
|
|
||||||
Voice control requires a modern browser with microphone access
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -344,18 +344,18 @@ export default function SettingsPage() {
|
|||||||
<div className="pt-4 border-t">
|
<div className="pt-4 border-t">
|
||||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">Current Model Status</p>
|
<p className="font-medium">{t('settings.currentModelStatus')}</p>
|
||||||
<p className="text-green-600 dark:text-green-400">Claude 3 Sonnet - Active</p>
|
<p className="text-green-600 dark:text-green-400">{t('settings.claude3Active')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">API Usage</p>
|
<p className="font-medium">{t('settings.apiUsage')}</p>
|
||||||
<p className="text-gray-600 dark:text-gray-400">2,341 requests this month</p>
|
<p className="text-gray-600 dark:text-gray-400">{t('settings.apiUsageValue')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button className="w-full">
|
<Button className="w-full">
|
||||||
Save AI Model Settings
|
{t('settings.saveAiSettings')}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
@@ -9,6 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { PageHeader } from '@/components/ui/page-header';
|
import { PageHeader } from '@/components/ui/page-header';
|
||||||
|
import { TaskSkeleton, CardSkeleton } from '@/components/ui/loading-skeleton';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { VoiceShortcuts } from '@/components/voice/VoiceShortcuts';
|
import { VoiceShortcuts } from '@/components/voice/VoiceShortcuts';
|
||||||
import Sidebar from '@/components/layout/Sidebar';
|
import Sidebar from '@/components/layout/Sidebar';
|
||||||
@@ -21,8 +22,8 @@ export default function TasksPage() {
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [statusFilter, setStatusFilter] = useState('all');
|
const [statusFilter, setStatusFilter] = useState('all');
|
||||||
const [priorityFilter, setPriorityFilter] = useState('all');
|
const [priorityFilter, setPriorityFilter] = useState('all');
|
||||||
const [sortBy, setSortBy] = useState('createdAt');
|
const [sortBy] = useState('createdAt');
|
||||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
|
const [sortOrder] = useState<'asc' | 'desc'>('desc');
|
||||||
|
|
||||||
const [newTask, setNewTask] = useState({
|
const [newTask, setNewTask] = useState({
|
||||||
title: '',
|
title: '',
|
||||||
@@ -33,18 +34,21 @@ export default function TasksPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fetch tasks
|
// Fetch tasks
|
||||||
const { data: tasks, isLoading } = useQuery({
|
const { data: tasksData, isLoading } = useQuery({
|
||||||
queryKey: ['/api/tasks'],
|
queryKey: ['/api/tasks'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const response = await fetch('/api/tasks');
|
const response = await fetch('/api/tasks');
|
||||||
if (!response.ok) throw new Error('Failed to fetch tasks');
|
if (!response.ok) throw new Error('Failed to fetch tasks');
|
||||||
return response.json();
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tasks = tasksData?.tasks || [];
|
||||||
|
|
||||||
// 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()) ||
|
||||||
@@ -64,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');
|
||||||
@@ -170,12 +174,19 @@ export default function TasksPage() {
|
|||||||
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
<div className="flex h-screen bg-gray-50 dark:bg-gray-900">
|
||||||
<Sidebar className="w-64 border-r" />
|
<Sidebar className="w-64 border-r" />
|
||||||
<div className="flex-1 overflow-auto p-6">
|
<div className="flex-1 overflow-auto p-6">
|
||||||
<div className="animate-pulse space-y-4">
|
<div className="space-y-6">
|
||||||
<div className="h-8 bg-gray-200 dark:bg-gray-700 rounded w-1/4"></div>
|
<PageHeader
|
||||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
|
title="Tasks"
|
||||||
|
description="Manage your tasks and projects efficiently"
|
||||||
|
/>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
|
<CardSkeleton key={i} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{[...Array(5)].map((_, i) => (
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
<div key={i} className="h-20 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
<TaskSkeleton key={i} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,20 +6,22 @@ import { Badge } from "@/components/ui/badge";
|
|||||||
import { PageHeader } from "@/components/ui/page-header";
|
import { PageHeader } from "@/components/ui/page-header";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import Sidebar from "@/components/layout/Sidebar";
|
import Sidebar from "@/components/layout/Sidebar";
|
||||||
import { Mic, MicOff, Volume2, Settings, PlayCircle, StopCircle } from "lucide-react";
|
import { Mic, MicOff, Volume2, Settings, PlayCircle } from "lucide-react";
|
||||||
import { useVoice } from "@/hooks/useVoice";
|
import { useVoice } from "@/hooks/useVoice";
|
||||||
|
|
||||||
export default function VoicePage() {
|
export default function VoicePage() {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [isListening, setIsListening] = useState(false);
|
const [isListening, setIsListening] = useState(false);
|
||||||
const { isEnabled, isSupported, startListening, stopListening } = useVoice();
|
const { isSupported, startListening, stopListening } = useVoice();
|
||||||
|
|
||||||
const { data: commands = [], isLoading } = useQuery({
|
const { data: commands = [], isLoading } = useQuery({
|
||||||
queryKey: ["/api/voice/commands"],
|
queryKey: ["/api/voice/commands"],
|
||||||
|
select: (data: any) => Array.isArray(data) ? data : []
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: status } = useQuery({
|
useQuery({
|
||||||
queryKey: ["/api/voice/status"],
|
queryKey: ["/api/voice/status"],
|
||||||
|
select: (data: any) => data || { enabled: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleToggleListening = () => {
|
const handleToggleListening = () => {
|
||||||
@@ -188,7 +190,7 @@ export default function VoicePage() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Recent Voice Commands */}
|
{/* Recent Voice Commands */}
|
||||||
{!isLoading && commands.length > 0 && (
|
{!isLoading && Array.isArray(commands) && commands.length > 0 && (
|
||||||
<Card className="mt-6">
|
<Card className="mt-6">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Recent Commands</CardTitle>
|
<CardTitle>Recent Commands</CardTitle>
|
||||||
@@ -198,7 +200,7 @@ export default function VoicePage() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{commands.slice(0, 10).map((command: any) => (
|
{(commands as any[]).slice(0, 10).map((command: any) => (
|
||||||
<div key={command.id} className="flex items-center justify-between p-3 border rounded-lg">
|
<div key={command.id} className="flex items-center justify-between p-3 border rounded-lg">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">{command.transcription}</div>
|
<div className="font-medium">{command.transcription}</div>
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -2,4 +2,3 @@
|
|||||||
# https://curl.se/docs/http-cookies.html
|
# https://curl.se/docs/http-cookies.html
|
||||||
# This file was generated by libcurl! Edit at your own risk.
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||
#HttpOnly_localhost FALSE / FALSE 1749461849 connect.sid s%3A7zNq68aZliAxFVLCx2Je90dRY7pfLT09.S88988v3rhMbUHGcYdlwP%2BnTeYkWW4UYUggKYWfcgco
|
|
||||||
|
|||||||
Generated
+1562
-1625
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,7 @@
|
|||||||
"compression": "^1.8.0",
|
"compression": "^1.8.0",
|
||||||
"connect-pg-simple": "^10.0.0",
|
"connect-pg-simple": "^10.0.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-orm": "^0.39.1",
|
"drizzle-orm": "^0.39.1",
|
||||||
"drizzle-zod": "^0.7.0",
|
"drizzle-zod": "^0.7.0",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
import express, { type Request, Response, NextFunction } from "express";
|
import express, { type Request, Response, NextFunction } from "express";
|
||||||
import { registerRoutes } from "./routes";
|
import { registerRoutes } from "./routes";
|
||||||
import { taskScheduler } from "./services/taskScheduler";
|
import { taskScheduler } from "./services/taskScheduler";
|
||||||
import { setupVite, serveStatic, log } from "./vite";
|
import { setupVite, serveStatic, log } from "./vite";
|
||||||
|
import session from 'express-session';
|
||||||
|
import passport from 'passport';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.set('trust proxy', true);
|
app.set('trust proxy', true);
|
||||||
@@ -23,6 +26,22 @@ app.use((req, res, next) => {
|
|||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.urlencoded({ extended: false }));
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
|
||||||
|
// Session middleware
|
||||||
|
app.use(session({
|
||||||
|
secret: process.env.SESSION_SECRET || 'default_secret',
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false,
|
||||||
|
cookie: {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: app.get('env') === 'production',
|
||||||
|
maxAge: 1000 * 60 * 60 * 24, // 1 day
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Passport middleware
|
||||||
|
app.use(passport.initialize());
|
||||||
|
app.use(passport.session());
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const path = req.path;
|
const path = req.path;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const securityMiddleware = helmet({
|
|||||||
defaultSrc: ["'self'"],
|
defaultSrc: ["'self'"],
|
||||||
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
||||||
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
||||||
scriptSrc: ["'self'", "'unsafe-inline'", "https://replit.com"],
|
scriptSrc: ["'self'", "'unsafe-inline'"],
|
||||||
imgSrc: ["'self'", "data:", "https:"],
|
imgSrc: ["'self'", "data:", "https:"],
|
||||||
connectSrc: ["'self'", "ws:", "wss:"]
|
connectSrc: ["'self'", "ws:", "wss:"]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
cookie: {
|
cookie: {
|
||||||
secure: false, // Set to true in production with HTTPS
|
secure: false, // Set to true in production with HTTPS
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days for development
|
||||||
sameSite: 'lax' // Allow cross-origin requests for development
|
sameSite: 'lax' // Allow cross-origin requests for development
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
+15
-3
@@ -1,11 +1,14 @@
|
|||||||
import express, { type Express } from "express";
|
import express, { type Express } from "express";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
import { createServer as createViteServer, createLogger } from "vite";
|
import { createServer as createViteServer, createLogger } from "vite";
|
||||||
import { type Server } from "http";
|
import { type Server } from "http";
|
||||||
import viteConfig from "../vite.config";
|
import viteConfig from "../vite.config";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const viteLogger = createLogger();
|
const viteLogger = createLogger();
|
||||||
|
|
||||||
export function log(message: string, source = "express") {
|
export function log(message: string, source = "express") {
|
||||||
@@ -20,10 +23,12 @@ export function log(message: string, source = "express") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function setupVite(app: Express, server: Server) {
|
export async function setupVite(app: Express, server: Server) {
|
||||||
|
log("🔧 Setting up Vite development server...");
|
||||||
|
|
||||||
const serverOptions = {
|
const serverOptions = {
|
||||||
middlewareMode: true,
|
middlewareMode: true,
|
||||||
hmr: { server },
|
hmr: { server },
|
||||||
allowedHosts: true,
|
allowedHosts: true as true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const vite = await createViteServer({
|
const vite = await createViteServer({
|
||||||
@@ -40,27 +45,34 @@ export async function setupVite(app: Express, server: Server) {
|
|||||||
appType: "custom",
|
appType: "custom",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
log("✅ Vite server created, adding middleware...");
|
||||||
app.use(vite.middlewares);
|
app.use(vite.middlewares);
|
||||||
|
log("✅ Vite middleware added");
|
||||||
app.use("*", async (req, res, next) => {
|
app.use("*", async (req, res, next) => {
|
||||||
const url = req.originalUrl;
|
const url = req.originalUrl;
|
||||||
|
log(`🌐 Handling request for: ${url}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const clientTemplate = path.resolve(
|
const clientTemplate = path.resolve(
|
||||||
import.meta.dirname,
|
__dirname,
|
||||||
"..",
|
"..",
|
||||||
"client",
|
"client",
|
||||||
"index.html",
|
"index.html",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log(`📄 Reading template from: ${clientTemplate}`);
|
||||||
// always reload the index.html file from disk incase it changes
|
// always reload the index.html file from disk incase it changes
|
||||||
let template = await fs.promises.readFile(clientTemplate, "utf-8");
|
let template = await fs.promises.readFile(clientTemplate, "utf-8");
|
||||||
template = template.replace(
|
template = template.replace(
|
||||||
`src="/src/main.tsx"`,
|
`src="/src/main.tsx"`,
|
||||||
`src="/src/main.tsx?v=${nanoid()}"`,
|
`src="/src/main.tsx?v=${nanoid()}"`,
|
||||||
);
|
);
|
||||||
|
log(`🔄 Transforming HTML with Vite...`);
|
||||||
const page = await vite.transformIndexHtml(url, template);
|
const page = await vite.transformIndexHtml(url, template);
|
||||||
|
log(`✅ Sending transformed HTML`);
|
||||||
res.status(200).set({ "Content-Type": "text/html" }).end(page);
|
res.status(200).set({ "Content-Type": "text/html" }).end(page);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
log(`❌ Error processing request: ${e}`);
|
||||||
vite.ssrFixStacktrace(e as Error);
|
vite.ssrFixStacktrace(e as Error);
|
||||||
next(e);
|
next(e);
|
||||||
}
|
}
|
||||||
@@ -68,7 +80,7 @@ export async function setupVite(app: Express, server: Server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function serveStatic(app: Express) {
|
export function serveStatic(app: Express) {
|
||||||
const distPath = path.resolve(import.meta.dirname, "public");
|
const distPath = path.resolve(__dirname, "public");
|
||||||
|
|
||||||
if (!fs.existsSync(distPath)) {
|
if (!fs.existsSync(distPath)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
+10
-2
@@ -2,7 +2,12 @@ import type { Config } from "tailwindcss";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
content: ["./client/index.html", "./client/src/**/*.{js,jsx,ts,tsx}"],
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./client/index.html",
|
||||||
|
"./client/src/**/*.{js,ts,jsx,tsx}"
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
@@ -10,9 +15,13 @@ export default {
|
|||||||
md: "calc(var(--radius) - 2px)",
|
md: "calc(var(--radius) - 2px)",
|
||||||
sm: "calc(var(--radius) - 4px)",
|
sm: "calc(var(--radius) - 4px)",
|
||||||
},
|
},
|
||||||
|
borderColor: {
|
||||||
|
DEFAULT: "hsl(var(--border))",
|
||||||
|
},
|
||||||
colors: {
|
colors: {
|
||||||
background: "hsl(var(--background))",
|
background: "hsl(var(--background))",
|
||||||
foreground: "hsl(var(--foreground))",
|
foreground: "hsl(var(--foreground))",
|
||||||
|
border: "hsl(var(--border))",
|
||||||
card: {
|
card: {
|
||||||
DEFAULT: "hsl(var(--card))",
|
DEFAULT: "hsl(var(--card))",
|
||||||
foreground: "hsl(var(--card-foreground))",
|
foreground: "hsl(var(--card-foreground))",
|
||||||
@@ -41,7 +50,6 @@ export default {
|
|||||||
DEFAULT: "hsl(var(--destructive))",
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
foreground: "hsl(var(--destructive-foreground))",
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
},
|
},
|
||||||
border: "hsl(var(--border))",
|
|
||||||
input: "hsl(var(--input))",
|
input: "hsl(var(--input))",
|
||||||
ring: "hsl(var(--ring))",
|
ring: "hsl(var(--ring))",
|
||||||
chart: {
|
chart: {
|
||||||
|
|||||||
+8
-15
@@ -1,31 +1,24 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
runtimeErrorOverlay(),
|
|
||||||
...(process.env.NODE_ENV !== "production" &&
|
|
||||||
process.env.REPL_ID !== undefined
|
|
||||||
? [
|
|
||||||
await import("@replit/vite-plugin-cartographer").then((m) =>
|
|
||||||
m.cartographer(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(import.meta.dirname, "client", "src"),
|
"@": path.resolve(__dirname, "client", "src"),
|
||||||
"@shared": path.resolve(import.meta.dirname, "shared"),
|
"@shared": path.resolve(__dirname, "shared"),
|
||||||
"@assets": path.resolve(import.meta.dirname, "attached_assets"),
|
"@assets": path.resolve(__dirname, "attached_assets"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
root: path.resolve(import.meta.dirname, "client"),
|
root: path.resolve(__dirname, "client"),
|
||||||
build: {
|
build: {
|
||||||
outDir: path.resolve(import.meta.dirname, "dist/public"),
|
outDir: path.resolve(__dirname, "dist/public"),
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user