diff --git a/client/App.tsx b/client/App.tsx index f3c03612..f6231738 100644 --- a/client/App.tsx +++ b/client/App.tsx @@ -144,8 +144,14 @@ const App = () => ( } /> } /> } /> - } /> - } /> + } + /> + } + /> } /> } /> } /> diff --git a/client/lib/aethex-social-service.ts b/client/lib/aethex-social-service.ts index 0b2f315b..a2af8b4a 100644 --- a/client/lib/aethex-social-service.ts +++ b/client/lib/aethex-social-service.ts @@ -177,20 +177,34 @@ export const aethexSocialService = { }, // Mentorship - async listMentors(params?: { expertise?: string[]; q?: string; available?: boolean; limit?: number }) { + async listMentors(params?: { + expertise?: string[]; + q?: string; + available?: boolean; + limit?: number; + }) { const qs = new URLSearchParams(); - if (params?.expertise?.length) qs.set("expertise", params.expertise.join(",")); + if (params?.expertise?.length) + qs.set("expertise", params.expertise.join(",")); if (params?.q) qs.set("q", params.q); - if (typeof params?.available === "boolean") qs.set("available", String(params.available)); + if (typeof params?.available === "boolean") + qs.set("available", String(params.available)); if (params?.limit) qs.set("limit", String(params.limit)); - const resp = await fetch(`/api/mentors${qs.toString() ? `?${qs.toString()}` : ""}`); + const resp = await fetch( + `/api/mentors${qs.toString() ? `?${qs.toString()}` : ""}`, + ); if (!resp.ok) return [] as any[]; return (await resp.json()) as any[]; }, async applyToBeMentor( userId: string, - input: { bio?: string | null; expertise: string[]; hourlyRate?: number | null; available?: boolean }, + input: { + bio?: string | null; + expertise: string[]; + hourlyRate?: number | null; + available?: boolean; + }, ) { const resp = await fetch("/api/mentors/apply", { method: "POST", @@ -199,19 +213,29 @@ export const aethexSocialService = { user_id: userId, bio: input.bio ?? null, expertise: input.expertise || [], - hourly_rate: typeof input.hourlyRate === "number" ? input.hourlyRate : null, - available: typeof input.available === "boolean" ? input.available : true, + hourly_rate: + typeof input.hourlyRate === "number" ? input.hourlyRate : null, + available: + typeof input.available === "boolean" ? input.available : true, }), }); if (!resp.ok) throw new Error(await resp.text()); return await resp.json(); }, - async requestMentorship(menteeId: string, mentorId: string, message?: string) { + async requestMentorship( + menteeId: string, + mentorId: string, + message?: string, + ) { const resp = await fetch("/api/mentorship/request", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ mentee_id: menteeId, mentor_id: mentorId, message: message || null }), + body: JSON.stringify({ + mentee_id: menteeId, + mentor_id: mentorId, + message: message || null, + }), }); if (!resp.ok) throw new Error(await resp.text()); return await resp.json(); @@ -230,11 +254,14 @@ export const aethexSocialService = { actorId: string, status: "accepted" | "rejected" | "cancelled", ) { - const resp = await fetch(`/api/mentorship/requests/${encodeURIComponent(id)}/status`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ actor_id: actorId, status }), - }); + const resp = await fetch( + `/api/mentorship/requests/${encodeURIComponent(id)}/status`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ actor_id: actorId, status }), + }, + ); if (!resp.ok) throw new Error(await resp.text()); return await resp.json(); }, diff --git a/client/pages/Staff.tsx b/client/pages/Staff.tsx index cb4f1664..fd31edfe 100644 --- a/client/pages/Staff.tsx +++ b/client/pages/Staff.tsx @@ -4,7 +4,13 @@ import { useAuth } from "@/contexts/AuthContext"; import { useNavigate } from "react-router-dom"; import { aethexToast } from "@/lib/aethex-toast"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; @@ -29,12 +35,18 @@ export default function Staff() { useEffect(() => { if (loading) return; if (!user) { - aethexToast.info({ title: "Sign in required", description: "Staff area requires authentication" }); + aethexToast.info({ + title: "Sign in required", + description: "Staff area requires authentication", + }); navigate("/login"); return; } if (!hasAccess) { - aethexToast.error({ title: "Access denied", description: "You don't have staff permissions" }); + aethexToast.error({ + title: "Access denied", + description: "You don't have staff permissions", + }); navigate("/dashboard"); } }, [user, roles, hasAccess, loading, navigate]); @@ -66,15 +78,24 @@ export default function Staff() { if (user && hasAccess) refresh(); }, [user, hasAccess]); - const updateReportStatus = async (id: string, status: "resolved" | "ignored" | "open") => { + const updateReportStatus = async ( + id: string, + status: "resolved" | "ignored" | "open", + ) => { try { - const resp = await fetch(`/api/moderation/reports/${encodeURIComponent(id)}/status`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ status }), - }); + const resp = await fetch( + `/api/moderation/reports/${encodeURIComponent(id)}/status`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ status }), + }, + ); if (resp.ok) { - aethexToast.success({ title: "Updated", description: `Report marked ${status}` }); + aethexToast.success({ + title: "Updated", + description: `Report marked ${status}`, + }); refresh(); } } catch {} @@ -84,9 +105,13 @@ export default function Staff() {
- Internal + + Internal +

Operations Command

-

Staff dashboards, moderation, and internal tools.

+

+ Staff dashboards, moderation, and internal tools. +

@@ -102,16 +127,26 @@ export default function Staff() { Community Health - Quick pulse across posts and reports + + Quick pulse across posts and reports +
-
Open reports
-
{openReports.length}
+
+ Open reports +
+
+ {openReports.length} +
-
Mentorship requests
-
{mentorshipAll.length}
+
+ Mentorship requests +
+
+ {mentorshipAll.length} +
@@ -122,11 +157,15 @@ export default function Staff() {
-
Admin API
+
+ Admin API +
OK
-
Notifications
+
+ Notifications +
OK
@@ -137,9 +176,15 @@ export default function Staff() { Common operational links - - - + + +
@@ -156,19 +201,37 @@ export default function Staff() {

Loading…

)} {!loadingData && openReports.length === 0 && ( -

No items in queue.

+

+ No items in queue. +

)}
{openReports.map((r) => ( -
+
{r.reason}
-
{r.details}
+
+ {r.details} +
- - + +
@@ -182,25 +245,42 @@ export default function Staff() { Mentorship Requests - Review recent mentor/mentee activity + + Review recent mentor/mentee activity + {loadingData && (

Loading…

)} {!loadingData && mentorshipAll.length === 0 && ( -

No requests to review.

+

+ No requests to review. +

)}
{mentorshipAll.map((req) => ( -
+
-
{req.mentee?.full_name || req.mentee?.username} → {req.mentor?.full_name || req.mentor?.username}
-
{req.message || "No message"}
+
+ {req.mentee?.full_name || req.mentee?.username} →{" "} + {req.mentor?.full_name || req.mentor?.username} +
+
+ {req.message || "No message"} +
- {req.status} + + {req.status} +
@@ -208,8 +288,12 @@ export default function Staff() {
@@ -219,10 +303,14 @@ export default function Staff() { Users - Search, roles, and quick actions + + Search, roles, and quick actions + -

User tools coming soon.

+

+ User tools coming soon. +

diff --git a/client/pages/community/MentorApply.tsx b/client/pages/community/MentorApply.tsx index 4fe46747..bbd83714 100644 --- a/client/pages/community/MentorApply.tsx +++ b/client/pages/community/MentorApply.tsx @@ -5,7 +5,13 @@ 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 { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; @@ -24,7 +30,10 @@ export default function MentorApply() { useEffect(() => { if (!loading && !user) { - aethexToast.info({ title: "Sign in required", description: "Please sign in to apply as a mentor" }); + aethexToast.info({ + title: "Sign in required", + description: "Please sign in to apply as a mentor", + }); navigate("/login"); } }, [user, loading, navigate]); @@ -35,14 +44,15 @@ export default function MentorApply() { .map((s) => s.trim()) .filter(Boolean); if (!parts.length) return; - const next = Array.from(new Set([...expertise, ...parts])) - .slice(0, 20); + 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())); + setExpertise((prev) => + prev.filter((t) => t.toLowerCase() !== tag.toLowerCase()), + ); }; const canSubmit = useMemo(() => { @@ -58,13 +68,21 @@ export default function MentorApply() { await aethexSocialService.applyToBeMentor(user.id, { bio: bio || null, expertise, - hourlyRate: Number.isFinite(rate as number) ? (rate as number) : undefined, + hourlyRate: Number.isFinite(rate as number) + ? (rate as number) + : undefined, available, }); - aethexToast.success({ title: "Mentor profile saved", description: "You're ready to receive mentorship requests" }); + aethexToast.success({ + title: "Mentor profile saved", + description: "You're ready to receive mentorship requests", + }); navigate("/community#mentorship"); } catch (e: any) { - aethexToast.error({ title: "Failed to save", description: String(e?.message || e) }); + aethexToast.error({ + title: "Failed to save", + description: String(e?.message || e), + }); } finally { setSubmitting(false); } @@ -74,33 +92,68 @@ export default function MentorApply() {
- Mentorship + + Mentorship +

Become a mentor

-

Share your expertise and guide community members through 1:1 sessions and clinics.

+

+ Share your expertise and guide community members through 1:1 + sessions and clinics. +

Mentor profile - Tell mentees how you can help and set your availability. + + Tell mentees how you can help and set your availability. +
-