diff --git a/attached_assets/Screenshot 2025-05-30 at 8.37.45 PM.png b/attached_assets/Screenshot 2025-05-30 at 8.37.45 PM.png
new file mode 100644
index 0000000..8d4e643
Binary files /dev/null and b/attached_assets/Screenshot 2025-05-30 at 8.37.45 PM.png differ
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 56e4d16..8925e7d 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -11,7 +11,7 @@ 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 OnboardingPage from "@/pages/OnboardingPage";
import NotFound from "@/pages/not-found";
function Router() {
@@ -20,6 +20,7 @@ function Router() {
+
diff --git a/client/src/pages/LoginPage.tsx b/client/src/pages/LoginPage.tsx
index 9bbaff7..f3c6b5d 100644
--- a/client/src/pages/LoginPage.tsx
+++ b/client/src/pages/LoginPage.tsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
import { Link, useLocation } from "wouter";
import { useAuth } from "@/hooks/useAuth";
import { useTheme } from "@/context/ThemeContext";
@@ -35,10 +35,11 @@ export default function LoginPage() {
const [error, setError] = useState("");
// Redirect if already authenticated
- if (user) {
- setLocation("/dashboard");
- return null;
- }
+ useEffect(() => {
+ if (user) {
+ setLocation("/dashboard");
+ }
+ }, [user, setLocation]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
diff --git a/client/src/pages/OnboardingPage.tsx b/client/src/pages/OnboardingPage.tsx
new file mode 100644
index 0000000..f22142f
--- /dev/null
+++ b/client/src/pages/OnboardingPage.tsx
@@ -0,0 +1,349 @@
+import { useState } from "react";
+import { useAuth } from "@/hooks/useAuth";
+import { useLocation } from "wouter";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Textarea } from "@/components/ui/textarea";
+import { Badge } from "@/components/ui/badge";
+import { Separator } from "@/components/ui/separator";
+import { Switch } from "@/components/ui/switch";
+import { Progress } from "@/components/ui/progress";
+import {
+ CheckCircle,
+ ArrowRight,
+ User,
+ Briefcase,
+ Calendar,
+ Clock,
+ MapPin,
+ Phone,
+ Users
+} from "lucide-react";
+
+export default function OnboardingPage() {
+ const { user, updateProfile } = useAuth();
+ const [, setLocation] = useLocation();
+ const [currentStep, setCurrentStep] = useState(1);
+ const [formData, setFormData] = useState({
+ // Professional profile fields
+ businessName: "",
+ bio: "",
+ specializations: "",
+ address: "",
+ phoneNumber: "",
+ allowAppointmentBooking: false,
+
+ // Availability settings
+ workingHours: {
+ monday: { enabled: true, start: "09:00", end: "17:00" },
+ tuesday: { enabled: true, start: "09:00", end: "17:00" },
+ wednesday: { enabled: true, start: "09:00", end: "17:00" },
+ thursday: { enabled: true, start: "09:00", end: "17:00" },
+ friday: { enabled: true, start: "09:00", end: "17:00" },
+ saturday: { enabled: false, start: "09:00", end: "17:00" },
+ sunday: { enabled: false, start: "09:00", end: "17:00" },
+ }
+ });
+
+ const totalSteps = user?.role === "pro" ? 3 : 2;
+ const progress = (currentStep / totalSteps) * 100;
+
+ const handleInputChange = (field: string, value: any) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleNext = () => {
+ if (currentStep < totalSteps) {
+ setCurrentStep(currentStep + 1);
+ }
+ };
+
+ const handleBack = () => {
+ if (currentStep > 1) {
+ setCurrentStep(currentStep - 1);
+ }
+ };
+
+ const handleComplete = async () => {
+ try {
+ await updateProfile({
+ onboardingComplete: true,
+ businessName: formData.businessName || null,
+ bio: formData.bio || null,
+ specializations: formData.specializations ? formData.specializations.split(',').map(s => s.trim()).join(',') : null,
+ address: formData.address || null,
+ phoneNumber: formData.phoneNumber || null,
+ allowAppointmentBooking: formData.allowAppointmentBooking,
+ });
+ setLocation("/dashboard");
+ } catch (error) {
+ console.error("Failed to complete onboarding:", error);
+ }
+ };
+
+ const renderStep1 = () => (
+
+
+
+
+
+
Welcome to TaskFin AI Suite!
+
+ Let's set up your account to get the most out of your experience.
+
+
+
+
+
+ Account Overview
+ Your account details and features
+
+
+
+ Account Type
+
+ {user?.role}
+
+
+
+
+
+
Available Features:
+
+
+
+ Task Management
+
+
+
+ Financial Tracking
+
+
+
+ Voice Commands (Arabic/English)
+
+
+
+ AI Insights
+
+ {user?.role === "pro" && (
+ <>
+
+
+ Professional Profiles
+
+
+
+ Appointment Booking
+
+ >
+ )}
+
+
+
+
+
+ );
+
+ const renderStep2 = () => (
+
+
+
+
+
+
Professional Profile
+
+ Set up your professional information for better networking and discovery.
+
+
+
+
+
+ Business Information
+ Help others find and connect with you
+
+
+
+
+ handleInputChange("businessName", e.target.value)}
+ />
+
+
+
+
+
+
+
+
+ handleInputChange("specializations", e.target.value)}
+ />
+
+
+
+
+ {user?.role === "pro" && (
+
+
+
+
+ Let clients book appointments with you online
+
+
+
handleInputChange("allowAppointmentBooking", checked)}
+ />
+
+ )}
+
+
+
+ );
+
+ const renderStep3 = () => (
+
+
+
+
+
+
Availability Settings
+
+ Set your working hours for appointment booking.
+
+
+
+
+
+ Working Hours
+ Configure when clients can book appointments
+
+
+ {Object.entries(formData.workingHours).map(([day, settings]) => (
+
+ ))}
+
+
+
+ );
+
+ return (
+
+
+
+ {/* Progress */}
+
+
+ Step {currentStep} of {totalSteps}
+ {Math.round(progress)}% complete
+
+
+
+
+ {/* Content */}
+ {currentStep === 1 && renderStep1()}
+ {currentStep === 2 && renderStep2()}
+ {currentStep === 3 && user?.role === "pro" && renderStep3()}
+
+ {/* Navigation */}
+
+
+
+ {currentStep < totalSteps ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/pages/ProfessionalDirectory.tsx b/client/src/pages/ProfessionalDirectory.tsx
index 7f37416..3de8ed1 100644
--- a/client/src/pages/ProfessionalDirectory.tsx
+++ b/client/src/pages/ProfessionalDirectory.tsx
@@ -34,12 +34,50 @@ export default function ProfessionalDirectory() {
enabled: true,
});
+ // Mock data for now since the API endpoint doesn't exist yet
+ const mockProfessionals: Professional[] = [
+ {
+ id: 1,
+ firstName: "Dr. Ahmed",
+ lastName: "Al-Rashid",
+ businessName: "Muscat Medical Center",
+ professionalType: "doctor",
+ bio: "Experienced cardiologist with 15+ years of practice in Oman",
+ specializations: ["Cardiology", "Internal Medicine", "Preventive Care"],
+ address: "Al Khuwair, Muscat, Oman",
+ phoneNumber: "+968 2234 5678",
+ email: "ahmed.rashid@medical.om",
+ profileImageUrl: "",
+ averageRating: 4.8,
+ totalReviews: 127,
+ allowAppointmentBooking: true,
+ },
+ {
+ id: 2,
+ firstName: "Fatima",
+ lastName: "Al-Zahra",
+ businessName: "Legal Consultancy Oman",
+ professionalType: "lawyer",
+ bio: "Corporate law specialist focusing on business formation and compliance",
+ specializations: ["Corporate Law", "Business Formation", "Contract Law"],
+ address: "Ruwi, Muscat, Oman",
+ phoneNumber: "+968 2445 6789",
+ email: "fatima.zahra@legal.om",
+ profileImageUrl: "",
+ averageRating: 4.9,
+ totalReviews: 89,
+ allowAppointmentBooking: true,
+ },
+ ];
+
+ const displayProfessionals = Array.isArray(professionals) && professionals.length > 0 ? professionals as Professional[] : mockProfessionals;
+
const professionalTypes = [
"doctor", "dentist", "therapist", "consultant", "lawyer",
"accountant", "coach", "tutor", "other"
];
- const filteredProfessionals = professionals.filter((prof: Professional) =>
+ const filteredProfessionals = displayProfessionals.filter((prof: Professional) =>
prof.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
prof.lastName.toLowerCase().includes(searchTerm.toLowerCase()) ||
prof.businessName?.toLowerCase().includes(searchTerm.toLowerCase()) ||