diff --git a/app/explore/experts/components/ConsultantCard.tsx b/app/explore/experts/components/ConsultantCard.tsx new file mode 100644 index 0000000..2cef303 --- /dev/null +++ b/app/explore/experts/components/ConsultantCard.tsx @@ -0,0 +1,222 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Domain, SubDomain, Tag } from "@prisma/client"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; +import { TConsultantProfile } from "@/types/consultant"; + +interface ConsultantCardProps { + consultant: TConsultantProfile; + metadata: { + domains: Domain[]; + subdomains: SubDomain[]; + tags: Tag[]; + } | null; +} + +function StarIcon(props: React.SVGProps) { + return ( + + + + ); +} + +export function ConsultantCard({ consultant, metadata }: ConsultantCardProps) { + const router = useRouter(); + + return ( +
+
router.push(`/explore/experts/${consultant.id}`)} + > + {`Portrait +
+
+

{consultant.user.name}

+ {consultant.user.email && ( + + @{consultant.user.email.split("@")[0]} + + )} +
+
+

{consultant.description}

+
+ + Experience: {consultant.experience} + +
+
+ + Specialization: {consultant.specialization} + +
+
+ + Qualifications: {consultant.qualifications} + +
+
+ Domain: + + {consultant.domain.name} + + Subdomains: + {consultant.subDomains.map((sd) => ( + + {sd.name} + + ))} +
+
+ Tags: + {consultant.tags.map((t) => ( + + {t.name} + + ))} +
+
+ + + {consultant.rating.toFixed(1)} ({consultant.reviews?.length || 0}{" "} + reviews) + +
+
+
+
+
+
+ {consultant.subscriptionPlans && + consultant.subscriptionPlans.length > 0 ? ( + + + {consultant.subscriptionPlans + .slice() + .sort((a, b) => a.durationInMonths - b.durationInMonths) + .map((plan) => ( + + {(() => { + switch (plan.durationInMonths) { + case 1: + return "1 Month"; + case 3: + return "3 Months"; + case 6: + return "6 Months"; + case 12: + return "12 Months"; + default: + return `${plan.durationInMonths} Months`; + } + })()} + + ))} + + {consultant.subscriptionPlans + .slice() + .sort((a, b) => a.durationInMonths - b.durationInMonths) + .map((plan) => ( + + + +
+
+ ${plan.price / 100} +
+
+ {(() => { + switch (plan.durationInMonths) { + case 1: + return "1 month"; + case 3: + return "3 months"; + case 6: + return "6 months"; + case 12: + return "12 months"; + default: + return `${plan.durationInMonths} months`; + } + })()} +
+
+
+
Calls per week
+
{plan.callsPerWeek}
+
+
+
Email support
+
{plan.emailSupport}
+
+
+
Video meetings
+
+ {plan.videoMeetings} per month +
+
+
+
+
+ ))} +
+ ) : ( +
+

+ No subscription plans available at the moment. +

+
+ )} +
+
+ + + +
+
+
+ ); +} diff --git a/app/explore/experts/components/FeaturedExperts.tsx b/app/explore/experts/components/FeaturedExperts.tsx new file mode 100644 index 0000000..505d9ff --- /dev/null +++ b/app/explore/experts/components/FeaturedExperts.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { Card, CardHeader, CardContent } from "@/components/ui/card"; +import Link from "next/link"; + +export function FeaturedExperts() { + return ( +
+
+
+
+
+ Featured Experts +
+

+ Top Consultants +

+

+ Discover the best of the best. Our top consultants are ready to help + you with your business needs. +

+
+
+
+ + +

John Doe

+
+ +

+ Get help with your business strategy from a top consultant. +

+
+
+ + +

Eliot

+
+ +

+ Get help with your product design from a top consultant. +

+
+
+ + +

Macmillan

+
+ +

+ Get help with your marketing strategy from a top consultant. +

+
+
+
+
+ + View All Experts + +
+
+
+ ); +} diff --git a/app/explore/experts/components/FiltersSection.tsx b/app/explore/experts/components/FiltersSection.tsx new file mode 100644 index 0000000..1dedd8a --- /dev/null +++ b/app/explore/experts/components/FiltersSection.tsx @@ -0,0 +1,256 @@ +"use client"; + +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Domain, SubDomain, Tag } from "@prisma/client"; +import { useState } from "react"; + +interface FiltersSectionProps { + metadata: { + domains: Domain[]; + subdomains: SubDomain[]; + tags: Tag[]; + } | null; + selectedDomain: string | null; + setSelectedDomain: (value: string | null) => void; + selectedSubdomain: string | null; + setSelectedSubdomain: (value: string | null) => void; + selectedTags: string[]; + setSelectedTags: (tags: string[]) => void; + experienceYears: number; + setExperienceYears: (years: number) => void; + pricing: number; + setPricing: (price: number) => void; +} + +function XIcon(props: React.SVGProps) { + return ( + + + + + ); +} + +export function FiltersSection({ + metadata, + selectedDomain, + setSelectedDomain, + selectedSubdomain, + setSelectedSubdomain, + selectedTags, + setSelectedTags, + experienceYears, + setExperienceYears, + pricing, + setPricing, +}: FiltersSectionProps) { + const [searchTerm, setSearchTerm] = useState(""); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + const handleTagSelect = (tag: string) => { + if (!selectedTags.includes(tag)) { + setSelectedTags([...selectedTags, tag]); + } + setIsDropdownOpen(false); + setSearchTerm(""); + }; + + const handleTagRemove = (tag: string) => { + setSelectedTags(selectedTags.filter((t) => t !== tag)); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value); + setIsDropdownOpen(true); + }; + + const filteredTags = + metadata?.tags.filter( + (tag) => + tag.name.toLowerCase().includes(searchTerm.toLowerCase()) && + !selectedTags.includes(tag.name) + ) || []; + + return ( +
+
+
+ + +
+
+ + +
+
+
+
+ +
+ setIsDropdownOpen(true)} + /> + {isDropdownOpen && ( +
+
    + {filteredTags.map((tag) => ( +
  • handleTagSelect(tag.name)} + > + {tag.name} +
  • + ))} +
+
+ )} +
+
+ {selectedTags.map((tag) => ( + + {tag} + + + ))} +
+
+
+
+
+ + setExperienceYears(Number(e.target.value))} + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" + /> +
+ 0 + 15 + 30+ +
+
+ {experienceYears === 30 ? "30+" : experienceYears} years +
+
+
+
+
+ + setPricing(Number(e.target.value))} + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" + /> +
+ $0 + $500 + $1000+ +
+
+ ${pricing === 1000 ? "1000+" : pricing} +
+
+
+
+ ); +} diff --git a/app/explore/experts/components/SearchBar.tsx b/app/explore/experts/components/SearchBar.tsx new file mode 100644 index 0000000..fc90405 --- /dev/null +++ b/app/explore/experts/components/SearchBar.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { Input } from "@/components/ui/input"; + +interface SearchBarProps { + onSearch: (value: string) => void; +} + +export function SearchBar({ onSearch }: SearchBarProps) { + return ( +
+ onSearch(e.target.value)} + /> +
+ ); +} diff --git a/app/explore/experts/components/Testimonials.tsx b/app/explore/experts/components/Testimonials.tsx new file mode 100644 index 0000000..d1624b7 --- /dev/null +++ b/app/explore/experts/components/Testimonials.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { Avatar } from "@/components/ui/avatar"; + +export function Testimonials() { + return ( +
+
+
+

+ + What Our Customers Say + +

+

+ Hear from our valued customers about their experience with our experts. +

+
+
+ +

+ “The expert I consulted with was incredibly knowledgeable and + helped me solve my business challenges quickly.“ +

+

+ - Satisfied Client +

+
+
+
+ ); +} diff --git a/app/explore/experts/page.tsx b/app/explore/experts/page.tsx index d18e3ba..59ec665 100644 --- a/app/explore/experts/page.tsx +++ b/app/explore/experts/page.tsx @@ -1,29 +1,13 @@ "use client"; -import { Avatar } from "@/components/ui/avatar"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { - Domain, - SubDomain, - Tag, - ConsultationPlan, - SubscriptionPlan, - WebinarPlan, - ClassPlan, -} from "@prisma/client"; -import Image from "next/image"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; + +import { useState, useEffect } from "react"; +import { Domain, SubDomain, Tag } from "@prisma/client"; +import { TConsultantProfile } from "@/types/consultant"; +import { FiltersSection } from "./components/FiltersSection"; +import { ConsultantCard } from "./components/ConsultantCard"; +import { FeaturedExperts } from "./components/FeaturedExperts"; +import { Testimonials } from "./components/Testimonials"; +import { SearchBar } from "./components/SearchBar"; interface MetaData { domains: Domain[]; @@ -31,46 +15,16 @@ interface MetaData { tags: Tag[]; } -interface Consultant { - id: string; - description: string; - qualifications: string; - specialization: string; - experience: string; - rating: number; - domainId: string; - scheduleType: string; - userId: string; - reviews: any[]; - slotsOfAvailabilityWeekly: any[]; - slotsOfAvailabilityCustom: any[]; - consultationPlans: ConsultationPlan[]; - subscriptionPlans: SubscriptionPlan[]; - webinarPlans: WebinarPlan[]; - classPlans: ClassPlan[]; - user: { - id: string; - name: string; - email: string; - image: string; - }; -} - function FindExperts() { const [metadata, setMetadata] = useState(null); - const [consultants, setConsultants] = useState([]); + const [consultants, setConsultants] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedTags, setSelectedTags] = useState([]); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [experienceYears, setExperienceYears] = useState(0); const [pricing, setPricing] = useState(0); const [selectedDomain, setSelectedDomain] = useState(null); - const [selectedSubdomain, setSelectedSubdomain] = useState( - null, - ); - - const router = useRouter(); + const [selectedSubdomain, setSelectedSubdomain] = useState(null); useEffect(() => { async function fetchData() { @@ -94,30 +48,6 @@ function FindExperts() { fetchData(); }, []); - const handleTagSelect = (tag: string) => { - if (!selectedTags.includes(tag)) { - setSelectedTags([...selectedTags, tag]); - } - setIsDropdownOpen(false); - setSearchTerm(""); - }; - - const handleTagRemove = (tag: string) => { - setSelectedTags(selectedTags.filter((t) => t !== tag)); - }; - - const handleInputChange = (e: React.ChangeEvent) => { - setSearchTerm(e.target.value); - setIsDropdownOpen(true); - }; - - const filteredTags = - metadata?.tags.filter( - (tag) => - tag.name.toLowerCase().includes(searchTerm.toLowerCase()) && - !selectedTags.includes(tag.name), - ) || []; - if (isLoading) { return (
@@ -133,410 +63,44 @@ function FindExperts() { Find an Expert

- Search for experts in various fields. Enter keywords to find experts - in specific areas. + Search for experts in various fields. Enter keywords to find experts in + specific areas.

-
-
-
- - -
-
- - -
-
-
-
- -
- setIsDropdownOpen(true)} - /> - {isDropdownOpen && ( -
-
    - {filteredTags.map((tag) => ( -
  • handleTagSelect(tag.name)} - > - {tag.name} -
  • - ))} -
-
- )} -
-
- {selectedTags.map((tag) => ( - - {tag} - - - ))} -
-
-
-
-
- - setExperienceYears(Number(e.target.value))} - className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" - /> -
- 0 - 15 - 30+ -
-
- {experienceYears === 30 ? "30+" : experienceYears} years -
-
-
-
-
- - setPricing(Number(e.target.value))} - className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" - /> -
- $0 - $500 - $1000+ -
-
- ${pricing === 1000 ? "1000+" : pricing} -
-
-
-
-
- -
+ + + + +
{metadata?.domains.map((domain) => { - const domainConsultants = - consultants?.filter( - (consultant) => consultant.domainId === domain.id, - ) ?? []; + const domainConsultants = consultants?.filter( + (consultant) => consultant.domainId === domain.id, + ) ?? []; return (

{domain.name}

{domainConsultants.length > 0 ? ( - domainConsultants.map( - (consultant) => ( - console.log(consultant), - ( -
-
- router.push(`/explore/experts/${consultant.id}`) - } - > - {`Portrait -
-
-

- {consultant.user.name} -

- - @{consultant.user.email.split("@")[0]} - -
-
-

- {consultant.description} -

-
- - Experience: {consultant.experience} - -
-
- - Specialization: {consultant.specialization} - -
-
- - Qualifications: {consultant.qualifications} - -
-
- - Domain: - - - { - metadata?.domains.find( - (d) => d.id === consultant.domainId, - )?.name - } - - - Subdomains: - - {metadata?.subdomains - .filter( - (sd) => sd.domainId === consultant.domainId, - ) - .map((sd) => ( - - {sd.name} - - ))} -
-
- - Tags: - - {metadata?.tags - .filter( - (t) => t.domainId === consultant.domainId, - ) - .map((t) => ( - - {t.name} - - ))} -
-
- - - {consultant.rating.toFixed(1)} ( - {consultant.reviews?.length || 0} reviews) - -
-
-
-
-
-
- {consultant.subscriptionPlans && - consultant.subscriptionPlans.length > 0 ? ( - - - {Array.from( - consultant.subscriptionPlans || [], - ) - .sort( - (a, b) => - a.durationInMonths - b.durationInMonths, - ) - .map((plan: SubscriptionPlan) => ( - - {(() => { - switch (plan.durationInMonths) { - case 1: - return "1 Month"; - case 3: - return "3 Months"; - case 6: - return "6 Months"; - case 12: - return "12 Months"; - default: - return `${plan.durationInMonths} Months`; - } - })()} - - ))} - - {Array.from(consultant.subscriptionPlans || []) - .sort( - (a, b) => - a.durationInMonths - b.durationInMonths, - ) - .map((plan: SubscriptionPlan) => ( - - - -
-
- ${plan.price / 100} -
-
- {(() => { - switch (plan.durationInMonths) { - case 1: - return "1 month"; - case 3: - return "3 months"; - case 6: - return "6 months"; - case 12: - return "12 months"; - default: - return `${plan.durationInMonths} months`; - } - })()} -
-
-
-
Calls per week
-
- {plan.callsPerWeek} -
-
-
-
Email support
-
- {plan.emailSupport} -
-
-
-
Video meetings
-
- {plan.videoMeetings} per month -
-
-
-
-
- ))} -
- ) : ( -
-

- No subscription plans available at the moment. -

-
- )} -
-
- - - -
-
-
- ) - ), - ) + domainConsultants.map((consultant) => ( + + )) ) : (

No consultants available in this domain at the moment. @@ -553,130 +117,9 @@ function FindExperts() { export default function ExploreExperts() { return ( <> -

-
-
-
-
- Featured Experts -
-

- Top Consultants -

-

- Discover the best of the best. Our top consultants are ready to - help you with your business needs. -

-
-
-
- - -

John Doe

-
- -

- Get help with your business strategy from a top consultant. -

-
-
- - -

Eliot

-
- -

- Get help with your product design from a top consultant. -

-
-
- - -

Macmillan

-
- -

- Get help with your marketing strategy from a top consultant. -

-
-
-
-
- - View All Experts - -
-
-
+ - -
-
-
-

- - What Our Customers Say - -

-

- Hear from our valued customers about their experience with our - experts. -

-
-
- -

- “The expert I consulted with was incredibly knowledgeable - and helped me solve my business challenges quickly.“ -

-

- - Satisfied Client -

-
-
-
+ ); } - -function XIcon(props: Readonly>) { - return ( - - - - - ); -} - -function StarIcon(props: Readonly>) { - return ( - - - - ); -}