From 3cd5e47b0ed2a105b755bbb553213e235255375c Mon Sep 17 00:00:00 2001 From: Samarth Prashar Date: Fri, 24 Oct 2025 20:20:47 +0530 Subject: [PATCH 01/13] Made CRM working --- src/App.tsx | 4 +- src/components/Navbar.tsx | 200 +++++++++++++++++------------- src/components/ProtectedRoute.tsx | 30 +++++ src/pages/ArchitectDashboard.tsx | 148 ++++++++++++++++++++++ 4 files changed, 296 insertions(+), 86 deletions(-) create mode 100644 src/components/ProtectedRoute.tsx create mode 100644 src/pages/ArchitectDashboard.tsx diff --git a/src/App.tsx b/src/App.tsx index 42e24b97..154ab5c1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,7 +25,8 @@ import Architects from "./pages/categories/Architects"; import Contractors from "./pages/categories/Contractors"; import CategoriesLayout from "./pages/categories/CategoriesLayout"; import ArchitectDetail from "./pages/categories/ArchitectDetail"; - +import ProtectedRoute from "./components/ProtectedRoute"; +import ArchitectDashboard from "./pages/ArchitectDashboard.tsx"; // Import i18n configuration import './i18n'; @@ -44,6 +45,7 @@ const AppRoutes = () => { } /> } /> } /> + } /> }> } /> } /> diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 26b67922..afe97249 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Menu, X, User, Search } from 'lucide-react'; +import { Menu, X, User, Search, LayoutDashboard } from 'lucide-react'; // ⚡ added LayoutDashboard icon import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Link, NavLink } from 'react-router-dom'; @@ -111,6 +111,19 @@ const Navbar: React.FC = () => {
+ + {/* ⚡ Architect Dashboard button added here */} + {user && ( + + )} + {user ? ( ) : ( @@ -142,98 +155,115 @@ const Navbar: React.FC = () => {
{/* Mobile Menu */} - {isOpen && ( -
-
-
- - { - if (e.key === 'Enter') { - handleSearchClick(); - setIsOpen(false); - } - }} - /> -
+{isOpen && ( +
+
+
+ + { + if (e.key === 'Enter') { + handleSearchClick(); + setIsOpen(false); + } + }} + /> +
- setIsOpen(false)}>Home + setIsOpen(false)}>Home - {/* Services main link (redirect only) */} - setIsOpen(false)}>Services + {/* Services main link (redirect only) */} + setIsOpen(false)}>Services - {/* New "All Services" mobile button */} - - - {isMobileServicesOpen && ( -
- setIsOpen(false)}>Worker - {/* setIsOpen(false)}>Home Owners */} - setIsOpen(false)}>Architects/Designers - setIsOpen(false)}>Contractors - setIsOpen(false)}>Developers - setIsOpen(false)}>Material Suppliers -
- )} + {/* New "All Services" mobile button */} + - setIsOpen(false)}>Blog - setIsOpen(false)}>About - setIsOpen(false)}>Contact - -
- - {/* Profile Button added above sign out */} - {user && ( - - )} - - {user ? ( - - ) : ( - <> - - - - )} -
-
-
+ {isMobileServicesOpen && ( +
+ setIsOpen(false)}>Worker + {/* setIsOpen(false)}>Home Owners */} + setIsOpen(false)}>Architects/Designers + setIsOpen(false)}>Contractors + setIsOpen(false)}>Developers + setIsOpen(false)}>Material Suppliers +
+ )} + + setIsOpen(false)}>Blog + setIsOpen(false)}>About + setIsOpen(false)}>Contact + +
+ + {/* Profile Button added above sign out */} + {user && ( + + )} + + {/* 🔹 Architect Dashboard Button (added, do not remove) */} + {user && ( + + )} + + {user ? ( + + ) : ( + <> + + + )}
+
+
+)} + + ); }; export default Navbar; + + diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx new file mode 100644 index 00000000..45e5be85 --- /dev/null +++ b/src/components/ProtectedRoute.tsx @@ -0,0 +1,30 @@ +import React, { ReactNode } from "react"; +import { Navigate } from "react-router-dom"; +import { useAuth } from "@/contexts/AuthContext"; + +interface ProtectedRouteProps { + children: ReactNode; + requiredRole?: string; +} + +const ProtectedRoute: React.FC = ({ children, requiredRole }) => { + const { user, userRole, loading } = useAuth(); + + if (loading) { + return

Checking authentication...

; + } + + // If user is not logged in + if (!user) { + return ; + } + + // If user role doesn't match required role + if (requiredRole && userRole !== requiredRole) { + return ; + } + + return <>{children}; +}; + +export default ProtectedRoute; diff --git a/src/pages/ArchitectDashboard.tsx b/src/pages/ArchitectDashboard.tsx new file mode 100644 index 00000000..1cf2db26 --- /dev/null +++ b/src/pages/ArchitectDashboard.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useState } from "react"; +import { supabase } from "@/integrations/supabase/client"; +import { useAuth } from "@/contexts/AuthContext"; +import { useNavigate } from "react-router-dom"; + +// Define a TypeScript interface for the project +interface Project { + id: string | number; + arcID: string | number; + project_Name: string; + client_name: string; + price: number; + status: "active" | "completed" | string; + created_at: string; +} + +export default function ArchitectDashboard() { + const { user, userRole, loading: authLoading } = useAuth(); + const navigate = useNavigate(); + + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (authLoading) return; + + if (!user) { + navigate("/auth/login"); + return; + } + + if (userRole !== "architect") { + navigate("/"); + return; + } + + const fetchProjects = async () => { + setLoading(true); + + // ✅ Explicitly type the response to avoid deep type inference + const { + data: architectData, + error: architectError, + } = await supabase + .from("architects") + .select("id") + .eq("user_id", user.id) + .single(); + + if (architectError || !architectData) { + console.error("Error fetching architect:", architectError); + setProjects([]); + setLoading(false); + return; + } + + const architectId = architectData.id; + + // ✅ Explicit type annotation again to avoid inference recursion + const { + data: projectsData, + error: projectsError, + }: { data: Project[] | null; error: any } = await supabase + .from("ArchitectRequest") + .select("*") + .eq("arcID", architectId) + .order("created_at", { ascending: false }); + + if (projectsError) { + console.error("Error fetching projects:", projectsError); + setProjects([]); + } else { + setProjects(projectsData || []); + } + setLoading(false); + }; + + fetchProjects(); + }, [user, userRole, authLoading, navigate]); + + if (authLoading || loading) { + return

Loading dashboard...

; + } + + return ( +
+

🏗️ Architect CRM Dashboard

+ + {/* Summary cards */} +
+
+

Total Projects

+

{projects.length}

+
+
+

Active Projects

+

+ {projects.filter((p) => p.status === "active").length} +

+
+
+

Completed Projects

+

+ {projects.filter((p) => p.status === "completed").length} +

+
+
+ + {/* Project Table */} +
+

Your Projects

+ {projects.length > 0 ? ( + + + + + + + + + + + + {projects.map((p) => ( + + + + + + + + ))} + +
NameClientPriceStatusCreated
{p.project_Name}{p.client_name}₹{p.price} + {p.status.charAt(0).toUpperCase() + p.status.slice(1)} + + {new Date(p.created_at).toLocaleDateString()} +
+ ) : ( +

No projects found.

+ )} +
+
+ ); +} From f5ed6c0133e83acd9543d458767846689be7c886 Mon Sep 17 00:00:00 2001 From: Samarth Prashar Date: Wed, 29 Oct 2025 01:03:54 +0530 Subject: [PATCH 02/13] Made a proper Ui for the Architect page --- src/pages/categories/Architects.tsx | 152 ++++++++++++++++++---------- 1 file changed, 96 insertions(+), 56 deletions(-) diff --git a/src/pages/categories/Architects.tsx b/src/pages/categories/Architects.tsx index 770a6f4e..26bc359f 100644 --- a/src/pages/categories/Architects.tsx +++ b/src/pages/categories/Architects.tsx @@ -4,7 +4,7 @@ import { useAuth } from "@/contexts/AuthContext"; import { useNavigate } from "react-router-dom"; import Navbar from "@/components/Navbar"; import Footer from "@/components/Footer"; -import { Loader2 } from "lucide-react"; +import { Loader2, CheckCircle2, Smile } from "lucide-react"; const ArchitectsPage = () => { const { user } = useAuth(); @@ -21,7 +21,6 @@ const ArchitectsPage = () => { }); const [message, setMessage] = useState(""); - // Fetch architects const fetchArchitects = async () => { setLoading(true); const { data, error } = await supabase.from("architects").select("*"); @@ -34,12 +33,12 @@ const ArchitectsPage = () => { fetchArchitects(); }, []); - // Handle form input - const handleChange = (e: React.ChangeEvent) => { + const handleChange = ( + e: React.ChangeEvent + ) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; - // Register architect const handleRegister = async (e: React.FormEvent) => { e.preventDefault(); if (!user) { @@ -56,17 +55,15 @@ const ArchitectsPage = () => { setLoading(true); setMessage(""); - const { error } = await supabase - .from("architects") - .insert([ - { - name, - specialization, - description, - image_url, - user_id: user.id, // must match your Supabase table column exactly - }, - ]); + const { error } = await supabase.from("architects").insert([ + { + name, + specialization, + description, + image_url, + user_id: user.id, + }, + ]); setLoading(false); @@ -75,13 +72,17 @@ const ArchitectsPage = () => { setMessage(`Failed to register: ${error.message}`); } else { setMessage("Successfully registered as architect!"); - setFormData({ name: "", specialization: "", description: "", image_url: "" }); + setFormData({ + name: "", + specialization: "", + description: "", + image_url: "", + }); setShowForm(false); fetchArchitects(); } }; - // Filtered architects const filteredArchitects = architects.filter( (arch) => arch.name?.toLowerCase().includes(search.toLowerCase()) || @@ -92,35 +93,83 @@ const ArchitectsPage = () => {
-
-
-

- Find Top Architects for Your Project + {/* Hero Section (Professional Look) */} +
+ {/* Left content */} +
+

+ Hire Skilled Architect
in{" "} + Minutes

+

+ Connect with expert architects to design your dream spaces — from modern homes to smart offices, all with precision and creativity +

+ +
+ setSearch(e.target.value)} + /> + +
+ +
+ Popular Services: + Aashiana Architect + Aviral Design Studio +
{user && ( )}
-
- Architectural planning + {/* Right Video */} +
+
+ +
+ + {/* Floating Popups */} +
+ +
+

Verified Experts

+

Background Checked

+
+
+ +
+ +
+

Happy Customers

+
+
- {/* Registration Form */} + {/* Architect Registration Form */} {showForm && (
-

+

Register as an Architect

@@ -131,7 +180,7 @@ const ArchitectsPage = () => { placeholder="Full Name" value={formData.name} onChange={handleChange} - className="p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-purple-600 focus:outline-none" + className="p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-600 focus:outline-none" required /> { placeholder="Specialization (e.g. Interior Design)" value={formData.specialization} onChange={handleChange} - className="p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-purple-600 focus:outline-none" + className="p-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-600 focus:outline-none" required />