From 864d4f361bfdab1b9fb10dd461333d39ca0e13b4 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 18 Oct 2025 18:45:14 +0000 Subject: [PATCH] Create Request Mentorship page cgen-b1244f553b5a46c59e60cbff19e9a929 --- client/pages/community/MentorshipRequest.tsx | 197 +++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 client/pages/community/MentorshipRequest.tsx diff --git a/client/pages/community/MentorshipRequest.tsx b/client/pages/community/MentorshipRequest.tsx new file mode 100644 index 00000000..a15a3a84 --- /dev/null +++ b/client/pages/community/MentorshipRequest.tsx @@ -0,0 +1,197 @@ +import React, { useEffect, useMemo, useState } from "react"; +import Layout from "@/components/Layout"; +import { useAuth } from "@/contexts/AuthContext"; +import { aethexToast } from "@/lib/aethex-toast"; +import { aethexSocialService } from "@/lib/aethex-social-service"; +import { Badge } from "@/components/ui/badge"; +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 { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { useNavigate } from "react-router-dom"; + +interface MentorRow { + user_id: string; + bio: string | null; + expertise: string[] | null; + available: boolean; + hourly_rate: number | null; + user_profiles?: { id: string; full_name: string | null; username: string | null; avatar_url: string | null; bio: string | null } | null; +} + +export default function MentorshipRequest() { + const { user, loading } = useAuth(); + const navigate = useNavigate(); + const [mentors, setMentors] = useState([]); + const [query, setQuery] = useState(""); + const [expertiseInput, setExpertiseInput] = useState(""); + const [expertise, setExpertise] = useState([]); + const [loadingMentors, setLoadingMentors] = useState(true); + + const [dialogOpen, setDialogOpen] = useState(false); + const [selectedMentor, setSelectedMentor] = useState(null); + const [message, setMessage] = useState(""); + const [submitting, setSubmitting] = useState(false); + + useEffect(() => { + if (!loading && !user) { + aethexToast.info({ title: "Sign in required", description: "Please sign in to request mentorship" }); + navigate("/login"); + } + }, [user, loading, navigate]); + + const loadMentors = async () => { + setLoadingMentors(true); + try { + const rows = await aethexSocialService.listMentors({ q: query || undefined, expertise: expertise.length ? expertise : undefined, available: true, limit: 30 }); + setMentors(rows as MentorRow[]); + } catch (e: any) { + aethexToast.error({ title: "Failed to load mentors", description: String(e?.message || e) }); + } finally { + setLoadingMentors(false); + } + }; + + useEffect(() => { + loadMentors(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const addExpertise = () => { + const parts = expertiseInput + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + if (!parts.length) return; + const next = Array.from(new Set([...expertise, ...parts])) + .slice(0, 20); + setExpertise(next); + setExpertiseInput(""); + }; + + const removeExpertise = (tag: string) => { + setExpertise((prev) => prev.filter((t) => t.toLowerCase() !== tag.toLowerCase())); + }; + + const onOpenRequest = (m: MentorRow) => { + setSelectedMentor(m); + setMessage(""); + setDialogOpen(true); + }; + + const onSubmitRequest = async () => { + if (!user?.id || !selectedMentor) return; + try { + setSubmitting(true); + await aethexSocialService.requestMentorship(user.id, selectedMentor.user_id, message || undefined); + aethexToast.success({ title: "Request sent", description: "The mentor has been notified" }); + setDialogOpen(false); + } catch (e: any) { + aethexToast.error({ title: "Failed to send", description: String(e?.message || e) }); + } finally { + setSubmitting(false); + } + }; + + const filtersActive = useMemo(() => query.trim().length > 0 || expertise.length > 0, [query, expertise]); + + return ( + +
+
+ Mentorship +

Request mentorship

+

Find mentors by skill and send a short request. You’ll be notified when they respond.

+
+ + + + Filters + Refine mentors by topic or keyword. + + +
+
+ +
+ setQuery(e.target.value)} placeholder="Name, username, bio" /> + +
+
+
+ +
+ setExpertiseInput(e.target.value)} placeholder="Add tags, e.g. Unreal, AI, Networking" onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addExpertise(); } }} /> + +
+ {expertise.length > 0 && ( +
+ {expertise.map((tag) => ( + removeExpertise(tag)}> + {tag} + + ))} +
+ )} +
+
+ {filtersActive && ( +
+ + +
+ )} +
+
+ +
+ {loadingMentors && ( + Loading mentors... + )} + {!loadingMentors && mentors.length === 0 && ( + No mentors found. Try adjusting filters. + )} + {!loadingMentors && mentors.map((m) => ( + + + + {m.user_profiles?.full_name || m.user_profiles?.username || "Mentor"} + + + {(m.expertise || []).slice(0, 5).join(", ")} + + + + {m.bio &&

{m.bio}

} + {typeof m.hourly_rate === "number" && ( +

Rate: ${m.hourly_rate}/hr

+ )} +
+ +
+
+
+ ))} +
+ + + + + Request mentorship + +
+ +