@burdenoff/microfe-technospam 2026.1031.1 → 2026.1031.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/technospam/components/StatusBadge.js +1 -1
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- package/dist/technospam/components/SubmissionViews.js +1 -1
- package/dist/technospam/components/SubmissionViews.js.map +1 -1
- package/dist/technospam/components/lesson/InteractiveWorkspace.js +1 -1
- package/dist/technospam/components/lesson/InteractiveWorkspace.js.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +194 -186
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +5 -2
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsPortfolioPagePage.js +1 -0
- package/dist/technospam/pages/ApplicationsPortfolioPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +2 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +167 -162
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +2 -2
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +77 -77
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesLessonPlayerPagePage.js +2 -2
- package/dist/technospam/pages/CoursesLessonPlayerPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +3 -3
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +71 -71
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +6 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +1 -0
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +97 -98
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/package.json +7 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipMentorsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipMentorsPagePage.tsx"],"sourcesContent":["import { createElement, useMemo, useState } from 'react';\nimport { Award, MessageSquare, Search, Sparkles, Star, UserPlus, Users, X } from 'lucide-react';\nimport { Badge, Button, GlassCard, Input, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n PageLayout,\n StatCard,\n coverGradient,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useBecomeMentor,\n useMentorsQuery,\n usePageLimit,\n type TecMentor,\n} from '../api/technospamApi';\nimport { formatMoneyFromMinorUnits } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\n/** Circular gradient medallion standing in for a mentor avatar, with a live\n * \"accepting\" presence dot — mirrors the directory mockup within the dark theme. */\nfunction MentorMedallion({ mentor, size = 'md' }: { mentor: TecMentor; size?: 'md' | 'lg' }) {\n const box = size === 'lg' ? 'h-20 w-20' : 'h-16 w-16';\n return (\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n 'flex items-center justify-center rounded-full bg-gradient-to-br text-white shadow-md ring-1 ring-white/15',\n box,\n coverGradient(mentor.headline)\n )}\n >\n {createElement(topicIcon(mentor.expertise.join(' ') || mentor.headline), {\n size: size === 'lg' ? 32 : 26,\n strokeWidth: 1.7,\n })}\n </span>\n {mentor.isAcceptingMentees ? (\n <span className=\"absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full border-2 border-bg-surface bg-status-success-text\" />\n ) : null}\n </div>\n );\n}\n\nfunction MentorCard({ mentor, onOpen }: { mentor: TecMentor; onOpen: () => void }) {\n const tr = useTr();\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col gap-4 p-5 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <div className=\"flex items-start gap-4\">\n <MentorMedallion mentor={mentor} />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {mentor.headline}\n </h3>\n {mentor.role ? (\n <p className=\"mt-0.5 truncate text-xs font-medium text-text-secondary\">{mentor.role}</p>\n ) : null}\n {mentor.isAcceptingMentees ? (\n <Badge\n variant=\"outline\"\n className=\"mt-1.5 inline-flex items-center gap-1.5 border-status-success-border bg-status-success-bg-subtle text-status-success-text\"\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-status-success-text\" />{' '}\n {tr('technospam.common.acceptingMentees', 'Accepting mentees')}\n </Badge>\n ) : (\n <Badge\n variant=\"outline\"\n className=\"mt-1.5 border-border-seam bg-bg-sunken text-text-secondary\"\n >\n {tr('technospam.common.notAcceptingMentees', 'Not accepting')}\n </Badge>\n )}\n </div>\n </div>\n\n {mentor.bio ? <p className=\"line-clamp-2 text-sm text-text-secondary\">{mentor.bio}</p> : null}\n\n {mentor.expertise.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {mentor.expertise.slice(0, 4).map((skill) => (\n <Badge key={skill} variant=\"secondary\" className=\"text-[11px]\">\n {skill}\n </Badge>\n ))}\n {mentor.expertise.length > 4 ? (\n <span className=\"self-center text-xs text-text-muted\">\n +{mentor.expertise.length - 4}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"mt-auto grid grid-cols-3 divide-x divide-border-seam rounded-xl border border-border-seam bg-bg-sunken text-center\">\n <div className=\"px-2 py-2.5\">\n <div className=\"flex items-center justify-center gap-1 text-sm font-semibold text-text-primary\">\n <Star size={13} className=\"text-accent-teal\" />\n {mentor.ratingAvg > 0 ? mentor.ratingAvg.toFixed(1) : 'New'}\n </div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.rating', 'rating')}\n </div>\n </div>\n <div className=\"px-2 py-2.5\">\n <div className=\"text-sm font-semibold text-text-primary\">{mentor.sessionsCount}</div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.sessions', 'sessions')}\n </div>\n </div>\n <div className=\"px-2 py-2.5\">\n <div className=\"text-sm font-semibold text-text-primary\">\n {formatMoneyFromMinorUnits(mentor.hourlyRate, mentor.currency)}\n </div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.perHour', '/ hr')}\n </div>\n </div>\n </div>\n\n <Button\n className=\"w-full\"\n onClick={(e) => {\n e.stopPropagation();\n onOpen();\n }}\n >\n {tr('technospam.common.viewProfile', 'View profile')}\n </Button>\n </GlassCard>\n );\n}\n\nfunction BecomeMentorForm({\n onClose,\n onDone,\n workspaceId,\n}: {\n onClose: () => void;\n onDone: () => void;\n workspaceId?: string;\n}) {\n const [headline, setHeadline] = useState('');\n const [role, setRole] = useState('');\n const [bio, setBio] = useState('');\n const [expertise, setExpertise] = useState('');\n const [hourlyRate, setHourlyRate] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [becomeMentor, { loading }] = useBecomeMentor();\n const { emit } = useTechnoSpamAnalytics();\n\n const canSubmit = headline.trim().length > 0 && !loading;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n const input: {\n headline: string;\n role?: string;\n bio?: string;\n expertise?: string[];\n hourlyRate?: number;\n currency?: string;\n } = { headline: headline.trim() };\n if (role.trim()) input.role = role.trim();\n if (bio.trim()) input.bio = bio.trim();\n const exp = expertise\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n if (exp.length) input.expertise = exp;\n // The form collects a dollars-and-cents rate (e.g. \"50\" or \"49.99\") but\n // TecMentor.hourlyRate is stored in the smallest currency unit (cents)\n // so BOFF-4433's fee math never loses precision to floating point --\n // convert here rather than storing the raw dollar figure as if it were\n // cents (which would under-charge/under-pay by 100x).\n const rateDollars = Number.parseFloat(hourlyRate);\n if (!Number.isNaN(rateDollars) && rateDollars >= 0) {\n input.hourlyRate = Math.round(rateDollars * 100);\n }\n if (currency.trim()) input.currency = currency.trim().toUpperCase();\n\n emit('technospam.mentor.become_mentor_clicked', { workspaceId });\n const res = await becomeMentor({ variables: { input } });\n if (res.data?.tecBecomeMentor) {\n emit('technospam.mentor.became_mentor', {\n workspaceId,\n entityId: res.data.tecBecomeMentor.id,\n entityType: 'TEC_MENTOR',\n });\n notify.success('Mentor profile created', 'Your profile is pending review.');\n onDone();\n }\n };\n\n return (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-accent-primary to-accent-indigo text-white shadow-sm\">\n <UserPlus size={20} />\n </span>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Become a mentor</h2>\n <p className=\"text-sm text-text-secondary\">\n Share your expertise and start mentoring builders in the community.\n </p>\n </div>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={onClose} aria-label=\"Close\">\n <X size={16} />\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Headline <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n placeholder=\"e.g. Staff Engineer · Distributed systems & career growth\"\n value={headline}\n onChange={(e) => setHeadline(e.target.value)}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Role & company\n </label>\n <Input\n placeholder=\"e.g. Staff Engineer @ Acme\"\n value={role}\n onChange={(e) => setRole(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n Shown as a subtitle under your headline. Optional.\n </p>\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Bio</label>\n <Textarea\n rows={3}\n placeholder=\"Tell mentees about your experience and how you can help.\"\n value={bio}\n onChange={(e) => setBio(e.target.value)}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Expertise</label>\n <Input\n placeholder=\"React, System design, Career coaching\"\n value={expertise}\n onChange={(e) => setExpertise(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">Comma-separated topics.</p>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Hourly rate\n </label>\n <Input\n type=\"number\"\n min={0}\n step={0.01}\n placeholder=\"0.00\"\n value={hourlyRate}\n onChange={(e) => setHourlyRate(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n Dollars per hour (e.g. 50 or 49.99). Leave 0 for free.\n </p>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Currency</label>\n <Input\n placeholder=\"USD\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n <UserPlus size={16} /> {loading ? 'Submitting…' : 'Submit profile'}\n </Button>\n <Button variant=\"outline\" onClick={onClose} disabled={loading}>\n Cancel\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nexport function MentorshipMentorsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [search, setSearch] = useState('');\n const [showForm, setShowForm] = useState(false);\n\n const { limit, more } = usePageLimit();\n const { data, loading, error, refetch } = useMentorsQuery({\n status: 'ACTIVE',\n pagination: pageArg(limit),\n });\n\n const mentors = data?.tecListMentors?.items ?? [];\n const hasMore = data?.tecListMentors?.pageInfo?.hasMore ?? false;\n const userId = currentUser?.id;\n const alreadyMentor = Boolean(userId && mentors.some((m) => m.userId === userId));\n\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return mentors;\n return mentors.filter(\n (m) =>\n m.headline.toLowerCase().includes(q) ||\n m.expertise.some((skill) => skill.toLowerCase().includes(q))\n );\n }, [mentors, search]);\n\n const accepting = mentors.filter((m) => m.isAcceptingMentees).length;\n const totalSessions = mentors.reduce((s, m) => s + m.sessionsCount, 0);\n const rated = mentors.filter((m) => m.ratingAvg > 0);\n const avgRating = rated.length\n ? (rated.reduce((s, m) => s + m.ratingAvg, 0) / rated.length).toFixed(1)\n : '—';\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.findAMentorTitle', 'Find a Mentor')}\n description={tr(\n 'technospam.mentorship.description',\n 'Connect with experienced builders for 1:1 guidance, code review, and career coaching.'\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/sessions-page')}>\n <MessageSquare size={16} /> {tr('technospam.common.mySessions', 'My sessions')}\n </Button>\n }\n >\n {alreadyMentor ? (\n <GlassCard className=\"mb-6 flex items-center gap-3 p-4\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-status-success-bg-subtle text-status-success-text\">\n <Award size={20} />\n </span>\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">You're a mentor</div>\n <div className=\"text-xs text-text-secondary\">\n Your mentor profile is live. Mentees can request sessions with you.\n </div>\n </div>\n </GlassCard>\n ) : showForm ? (\n <BecomeMentorForm\n onClose={() => setShowForm(false)}\n onDone={() => {\n setShowForm(false);\n void refetch();\n }}\n workspaceId={workspaceId}\n />\n ) : null}\n\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statMentors', 'Mentors')}\n value={mentors.length}\n icon={Users}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAcceptingMentees', 'Accepting mentees')}\n value={accepting}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAvgRating', 'Avg rating')}\n value={avgRating}\n icon={Star}\n tone=\"amber\"\n />\n <StatCard\n label={tr('technospam.mentorship.statTotalSessions', 'Total sessions')}\n value={totalSessions}\n icon={MessageSquare}\n tone=\"purple\"\n />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative w-full sm:w-80\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n placeholder={tr(\n 'technospam.mentorship.searchPlaceholder',\n 'Search by headline or expertise…'\n )}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n {!alreadyMentor && !showForm ? (\n <Button onClick={() => setShowForm(true)}>\n <UserPlus size={16} /> {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n ) : null}\n </div>\n\n {loading && !mentors.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadMentors', \"Couldn't load mentors\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Users size={26} />}\n title={\n search\n ? tr('technospam.mentorship.emptySearchTitle', 'No matching mentors')\n : tr('technospam.mentorship.emptyDefaultTitle', 'No mentors yet')\n }\n description={\n search\n ? tr('technospam.mentorship.emptySearchDesc', 'Try a different search term.')\n : tr(\n 'technospam.mentorship.emptyDefaultDesc',\n 'Active mentors will appear here. TechnoSpam is pre-launch — be one of the first to sign up.'\n )\n }\n action={\n !search && !alreadyMentor ? (\n <Button onClick={() => setShowForm(true)}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((mentor) => (\n <MentorCard\n key={mentor.id}\n mentor={mentor}\n onOpen={() => {\n emit('technospam.mentor.viewed', {\n workspaceId,\n entityId: mentor.id,\n entityType: 'TEC_MENTOR',\n });\n navigate(`/mentorship/mentor-profile-page?id=${mentor.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MentorshipMentorsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipMentorsPagePage.tsx"],"sourcesContent":["import { createElement, useMemo, useState } from 'react';\nimport { Award, MessageSquare, Search, Sparkles, Star, UserPlus, Users, X } from 'lucide-react';\nimport { Badge, Button, GlassCard, Input, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n PageLayout,\n StatCard,\n coverGradient,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useBecomeMentor,\n useMentorsQuery,\n usePageLimit,\n type TecMentor,\n} from '../api/technospamApi';\nimport { formatMoneyFromMinorUnits } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\n/** Circular gradient medallion standing in for a mentor avatar, with a live\n * \"accepting\" presence dot — mirrors the directory mockup within the dark theme. */\nfunction MentorMedallion({ mentor, size = 'md' }: { mentor: TecMentor; size?: 'md' | 'lg' }) {\n const box = size === 'lg' ? 'h-20 w-20' : 'h-16 w-16';\n return (\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n 'flex items-center justify-center rounded-full bg-gradient-to-br text-white shadow-md ring-1 ring-white/15',\n box,\n coverGradient(mentor.headline)\n )}\n >\n {createElement(topicIcon(mentor.expertise.join(' ') || mentor.headline), {\n size: size === 'lg' ? 32 : 26,\n strokeWidth: 1.7,\n })}\n </span>\n {mentor.isAcceptingMentees ? (\n <span className=\"absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full border-2 border-bg-surface bg-status-success-text\" />\n ) : null}\n </div>\n );\n}\n\nfunction MentorCard({ mentor, onOpen }: { mentor: TecMentor; onOpen: () => void }) {\n const tr = useTr();\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col gap-4 p-5 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <div className=\"flex items-start gap-4\">\n <MentorMedallion mentor={mentor} />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {mentor.headline}\n </h3>\n {mentor.role ? (\n <p className=\"mt-0.5 truncate text-xs font-medium text-text-secondary\">{mentor.role}</p>\n ) : null}\n {mentor.isAcceptingMentees ? (\n <Badge\n variant=\"outline\"\n className=\"mt-1.5 inline-flex items-center gap-1.5 border-status-success-border bg-status-success-bg-subtle text-status-success-text\"\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-status-success-text\" />{' '}\n {tr('technospam.common.acceptingMentees', 'Accepting mentees')}\n </Badge>\n ) : (\n <Badge\n variant=\"outline\"\n className=\"mt-1.5 border-border-seam bg-bg-sunken text-text-secondary\"\n >\n {tr('technospam.common.notAcceptingMentees', 'Not accepting')}\n </Badge>\n )}\n </div>\n </div>\n\n {mentor.bio ? <p className=\"line-clamp-2 text-sm text-text-secondary\">{mentor.bio}</p> : null}\n\n {mentor.expertise.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {mentor.expertise.slice(0, 4).map((skill) => (\n <Badge key={skill} variant=\"secondary\" className=\"text-[11px]\">\n {skill}\n </Badge>\n ))}\n {mentor.expertise.length > 4 ? (\n <span className=\"self-center text-xs text-text-muted\">\n +{mentor.expertise.length - 4}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"mt-auto grid grid-cols-3 divide-x divide-border-seam rounded-xl border border-border-seam bg-bg-sunken text-center\">\n <div className=\"px-2 py-2.5\">\n <div className=\"flex items-center justify-center gap-1 text-sm font-semibold text-text-primary\">\n <Star size={13} className=\"text-accent-teal\" />\n {mentor.ratingAvg > 0 ? mentor.ratingAvg.toFixed(1) : 'New'}\n </div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.rating', 'rating')}\n </div>\n </div>\n <div className=\"px-2 py-2.5\">\n <div className=\"text-sm font-semibold text-text-primary\">{mentor.sessionsCount}</div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.sessions', 'sessions')}\n </div>\n </div>\n <div className=\"px-2 py-2.5\">\n <div className=\"text-sm font-semibold text-text-primary\">\n {formatMoneyFromMinorUnits(mentor.hourlyRate, mentor.currency)}\n </div>\n <div className=\"text-[11px] text-text-muted\">\n {tr('technospam.common.perHour', '/ hr')}\n </div>\n </div>\n </div>\n\n <Button\n className=\"w-full\"\n onClick={(e) => {\n e.stopPropagation();\n onOpen();\n }}\n >\n {tr('technospam.common.viewProfile', 'View profile')}\n </Button>\n </GlassCard>\n );\n}\n\nfunction BecomeMentorForm({\n onClose,\n onDone,\n workspaceId,\n}: {\n onClose: () => void;\n onDone: () => void;\n workspaceId?: string;\n}) {\n const [headline, setHeadline] = useState('');\n const [role, setRole] = useState('');\n const [bio, setBio] = useState('');\n const [expertise, setExpertise] = useState('');\n const [hourlyRate, setHourlyRate] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [becomeMentor, { loading }] = useBecomeMentor();\n const { emit } = useTechnoSpamAnalytics();\n\n const canSubmit = headline.trim().length > 0 && !loading;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n const input: {\n headline: string;\n role?: string;\n bio?: string;\n expertise?: string[];\n hourlyRate?: number;\n currency?: string;\n } = { headline: headline.trim() };\n if (role.trim()) input.role = role.trim();\n if (bio.trim()) input.bio = bio.trim();\n const exp = expertise\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n if (exp.length) input.expertise = exp;\n // The form collects a dollars-and-cents rate (e.g. \"50\" or \"49.99\") but\n // TecMentor.hourlyRate is stored in the smallest currency unit (cents)\n // so BOFF-4433's fee math never loses precision to floating point --\n // convert here rather than storing the raw dollar figure as if it were\n // cents (which would under-charge/under-pay by 100x).\n const rateDollars = Number.parseFloat(hourlyRate);\n if (!Number.isNaN(rateDollars) && rateDollars >= 0) {\n input.hourlyRate = Math.round(rateDollars * 100);\n }\n if (currency.trim()) input.currency = currency.trim().toUpperCase();\n\n emit('technospam.mentor.become_mentor_clicked', { workspaceId });\n const res = await becomeMentor({ variables: { input } });\n if (res.data?.tecBecomeMentor) {\n emit('technospam.mentor.became_mentor', {\n workspaceId,\n entityId: res.data.tecBecomeMentor.id,\n entityType: 'TEC_MENTOR',\n });\n notify.success('Mentor profile created', 'Your profile is pending review.');\n onDone();\n }\n };\n\n return (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-accent-primary to-accent-indigo text-white shadow-sm\">\n <UserPlus size={20} />\n </span>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Become a mentor</h2>\n <p className=\"text-sm text-text-secondary\">\n Share your expertise and start mentoring builders in the community.\n </p>\n </div>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={onClose} aria-label=\"Close\">\n <X size={16} />\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Headline <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n placeholder=\"e.g. Staff Engineer · Distributed systems & career growth\"\n value={headline}\n onChange={(e) => setHeadline(e.target.value)}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Role & company\n </label>\n <Input\n placeholder=\"e.g. Staff Engineer @ Acme\"\n value={role}\n onChange={(e) => setRole(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n Shown as a subtitle under your headline. Optional.\n </p>\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Bio</label>\n <Textarea\n rows={3}\n placeholder=\"Tell mentees about your experience and how you can help.\"\n value={bio}\n onChange={(e) => setBio(e.target.value)}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Expertise</label>\n <Input\n placeholder=\"React, System design, Career coaching\"\n value={expertise}\n onChange={(e) => setExpertise(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">Comma-separated topics.</p>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Hourly rate\n </label>\n <Input\n type=\"number\"\n min={0}\n step={0.01}\n placeholder=\"0.00\"\n value={hourlyRate}\n onChange={(e) => setHourlyRate(e.target.value)}\n />\n <p className=\"mt-1 text-xs text-text-muted\">\n Dollars per hour (e.g. 50 or 49.99). Leave 0 for free.\n </p>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Currency</label>\n <Input\n placeholder=\"USD\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n <UserPlus size={16} /> {loading ? 'Submitting…' : 'Submit profile'}\n </Button>\n <Button variant=\"outline\" onClick={onClose} disabled={loading}>\n Cancel\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nexport function MentorshipMentorsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [search, setSearch] = useState('');\n const [showForm, setShowForm] = useState(false);\n\n const { limit, more } = usePageLimit();\n const { data, loading, error, refetch } = useMentorsQuery({\n status: 'ACTIVE',\n pagination: pageArg(limit),\n });\n\n const mentors = data?.tecListMentors?.items ?? [];\n const hasMore = data?.tecListMentors?.pageInfo?.hasMore ?? false;\n const userId = currentUser?.id;\n const alreadyMentor = Boolean(userId && mentors.some((m) => m.userId === userId));\n\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return mentors;\n return mentors.filter(\n (m) =>\n m.headline.toLowerCase().includes(q) ||\n m.expertise.some((skill) => skill.toLowerCase().includes(q))\n );\n }, [mentors, search]);\n\n const accepting = mentors.filter((m) => m.isAcceptingMentees).length;\n const totalSessions = mentors.reduce((s, m) => s + m.sessionsCount, 0);\n const rated = mentors.filter((m) => m.ratingAvg > 0);\n const avgRating = rated.length\n ? (rated.reduce((s, m) => s + m.ratingAvg, 0) / rated.length).toFixed(1)\n : '—';\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.findAMentorTitle', 'Find a Mentor')}\n description={tr(\n 'technospam.mentorship.description',\n 'Connect with experienced builders for 1:1 guidance, code review, and career coaching.'\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/sessions-page')}>\n <MessageSquare size={16} /> {tr('technospam.common.mySessions', 'My sessions')}\n </Button>\n }\n >\n {alreadyMentor ? (\n <GlassCard className=\"mb-6 flex items-center gap-3 p-4\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-status-success-bg-subtle text-status-success-text\">\n <Award size={20} />\n </span>\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">You're a mentor</div>\n <div className=\"text-xs text-text-secondary\">\n Your mentor profile is live. Mentees can request sessions with you.\n </div>\n </div>\n </GlassCard>\n ) : showForm ? (\n <BecomeMentorForm\n onClose={() => setShowForm(false)}\n onDone={() => {\n setShowForm(false);\n void refetch();\n }}\n workspaceId={workspaceId}\n />\n ) : null}\n\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statMentors', 'Mentors')}\n value={mentors.length}\n icon={Users}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAcceptingMentees', 'Accepting mentees')}\n value={accepting}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAvgRating', 'Avg rating')}\n value={avgRating}\n icon={Star}\n tone=\"amber\"\n />\n <StatCard\n label={tr('technospam.mentorship.statTotalSessions', 'Total sessions')}\n value={totalSessions}\n icon={MessageSquare}\n tone=\"purple\"\n />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative w-full sm:w-80\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n aria-label={tr('technospam.mentorship.searchLabel', 'Search mentors')}\n placeholder={tr(\n 'technospam.mentorship.searchPlaceholder',\n 'Search by headline or expertise…'\n )}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n {!alreadyMentor && !showForm ? (\n <Button onClick={() => setShowForm(true)}>\n <UserPlus size={16} /> {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n ) : null}\n </div>\n\n {loading && !mentors.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadMentors', \"Couldn't load mentors\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Users size={26} />}\n title={\n search\n ? tr('technospam.mentorship.emptySearchTitle', 'No matching mentors')\n : tr('technospam.mentorship.emptyDefaultTitle', 'No mentors yet')\n }\n description={\n search\n ? tr('technospam.mentorship.emptySearchDesc', 'Try a different search term.')\n : tr(\n 'technospam.mentorship.emptyDefaultDesc',\n 'Active mentors will appear here. TechnoSpam is pre-launch — be one of the first to sign up.'\n )\n }\n action={\n !search && !alreadyMentor ? (\n <Button onClick={() => setShowForm(true)}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((mentor) => (\n <MentorCard\n key={mentor.id}\n mentor={mentor}\n onOpen={() => {\n emit('technospam.mentor.viewed', {\n workspaceId,\n entityId: mentor.id,\n entityType: 'TEC_MENTOR',\n });\n navigate(`/mentorship/mentor-profile-page?id=${mentor.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipSessionsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MentorshipSessionsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"names":[],"mappings":"AAySA,wBAAgB,0BAA0B,gCAmPzC"}
|
|
@@ -15,11 +15,11 @@ import { focusableCardProps as b, useFocusedRecordParam as x } from "../hooks/us
|
|
|
15
15
|
import { useState as S } from "react";
|
|
16
16
|
import { CalendarClock as C, Check as w, CheckCircle2 as T, Clock as E, MessageSquare as D, Users as O, Video as k, X as A } from "lucide-react";
|
|
17
17
|
import { jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
18
|
-
import { Button as N, GlassCard as P, Input as F, Tabs as I,
|
|
18
|
+
import { Button as N, GlassCard as P, Input as F, Tabs as I, TabsContent as L, TabsList as R, TabsTrigger as z } from "@burdenoff/fe-libs/ui";
|
|
19
19
|
//#region src/technospam/pages/MentorshipSessionsPagePage.tsx
|
|
20
|
-
var
|
|
21
|
-
function
|
|
22
|
-
let p = () => f(e.id), m = () => f(d ? null : e.id), h =
|
|
20
|
+
var B = new Set(["REQUESTED", "CONFIRMED"]);
|
|
21
|
+
function V({ session: e, showMentor: t, isMentor: n, onCancel: r, onConfirm: a, onComplete: o, busy: u, focused: d, onFocusChange: f }) {
|
|
22
|
+
let p = () => f(e.id), m = () => f(d ? null : e.id), h = B.has(e.status), [g, _] = S("none"), [v, y] = S(""), [x, D] = S("5"), [O, I] = S(""), L = async () => {
|
|
23
23
|
await a(e.id, v.trim()), _("none"), y("");
|
|
24
24
|
}, R = async () => {
|
|
25
25
|
await o(e.id, Math.min(5, Math.max(1, Number(x) || 5)), O.trim()), _("none");
|
|
@@ -170,7 +170,7 @@ function B({ session: e, showMentor: t, isMentor: n, onCancel: r, onConfirm: a,
|
|
|
170
170
|
]
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
|
-
function
|
|
173
|
+
function H({ loading: e, error: t, sessions: n, showMentor: r, emptyTitle: i, emptyDescription: a, emptyAction: o, isMentor: s, onCancel: c, onConfirm: l, onComplete: p, busyId: m, focusedId: h, onFocusChange: g }) {
|
|
174
174
|
return e && n.length === 0 ? /* @__PURE__ */ j(f, {
|
|
175
175
|
count: 3,
|
|
176
176
|
height: "h-24"
|
|
@@ -184,7 +184,7 @@ function V({ loading: e, error: t, sessions: n, showMentor: r, emptyTitle: i, em
|
|
|
184
184
|
action: o
|
|
185
185
|
}) : /* @__PURE__ */ j("div", {
|
|
186
186
|
className: "space-y-3",
|
|
187
|
-
children: [...n].sort((e, t) => new Date(t.scheduledAt).getTime() - new Date(e.scheduledAt).getTime()).map((e) => /* @__PURE__ */ j(
|
|
187
|
+
children: [...n].sort((e, t) => new Date(t.scheduledAt).getTime() - new Date(e.scheduledAt).getTime()).map((e) => /* @__PURE__ */ j(V, {
|
|
188
188
|
session: e,
|
|
189
189
|
showMentor: r,
|
|
190
190
|
isMentor: s,
|
|
@@ -197,15 +197,15 @@ function V({ loading: e, error: t, sessions: n, showMentor: r, emptyTitle: i, em
|
|
|
197
197
|
}, e.id))
|
|
198
198
|
});
|
|
199
199
|
}
|
|
200
|
-
function
|
|
201
|
-
let i = t(), { currentUser: s, workspaceId: c } = e(), { emit: l } = n(), d = r(), [b, w] = S("mentee"), [E, k] = S(null), [A, P] = x(y.session), F = s?.id,
|
|
200
|
+
function U() {
|
|
201
|
+
let i = t(), { currentUser: s, workspaceId: c } = e(), { emit: l } = n(), d = r(), [b, w] = S("mentee"), [E, k] = S(null), [A, P] = x(y.session), F = s?.id, B = v(F ? { menteeId: F } : {}), V = _({}), U = V.data?.tecListMentors?.items.find((e) => e.userId === s?.id), W = U?.id, G = v(W ? { mentorId: W } : {}), [K] = m(), [q] = g(), [ee] = h(), J = F ? B.data?.tecListSessions?.items ?? [] : [], Y = W ? G.data?.tecListSessions?.items ?? [] : [], X = [...J, ...Y], te = X.filter((e) => e.status === "REQUESTED" || e.status === "CONFIRMED").length, ne = X.filter((e) => e.status === "COMPLETED").length, Z = async (e) => {
|
|
202
202
|
k(e), l("technospam.session.cancel_clicked", {
|
|
203
203
|
workspaceId: c,
|
|
204
204
|
entityId: e,
|
|
205
205
|
entityType: "TEC_MENTOR_SESSION"
|
|
206
206
|
});
|
|
207
207
|
try {
|
|
208
|
-
(await
|
|
208
|
+
(await K({ variables: { id: e } })).data?.tecCancelSession && (l("technospam.session.cancelled", {
|
|
209
209
|
workspaceId: c,
|
|
210
210
|
entityId: e,
|
|
211
211
|
entityType: "TEC_MENTOR_SESSION"
|
|
@@ -216,10 +216,10 @@ function H() {
|
|
|
216
216
|
}, Q = async (e, t) => {
|
|
217
217
|
k(e);
|
|
218
218
|
try {
|
|
219
|
-
(await
|
|
219
|
+
(await q({ variables: {
|
|
220
220
|
id: e,
|
|
221
221
|
meetingUrl: t || void 0
|
|
222
|
-
} })).data?.tecConfirmSession && (p.success("Session confirmed", "The mentee has been notified."), await
|
|
222
|
+
} })).data?.tecConfirmSession && (p.success("Session confirmed", "The mentee has been notified."), await G.refetch());
|
|
223
223
|
} finally {
|
|
224
224
|
k(null);
|
|
225
225
|
}
|
|
@@ -230,7 +230,7 @@ function H() {
|
|
|
230
230
|
entityType: "TEC_MENTOR_SESSION"
|
|
231
231
|
});
|
|
232
232
|
try {
|
|
233
|
-
(await
|
|
233
|
+
(await ee({ variables: {
|
|
234
234
|
id: e,
|
|
235
235
|
rating: t,
|
|
236
236
|
feedback: n || void 0
|
|
@@ -238,7 +238,7 @@ function H() {
|
|
|
238
238
|
workspaceId: c,
|
|
239
239
|
entityId: e,
|
|
240
240
|
entityType: "TEC_MENTOR_SESSION"
|
|
241
|
-
}), p.success("Session completed", "Nice work — the session is wrapped up."), await
|
|
241
|
+
}), p.success("Session completed", "Nice work — the session is wrapped up."), await G.refetch());
|
|
242
242
|
} finally {
|
|
243
243
|
k(null);
|
|
244
244
|
}
|
|
@@ -256,93 +256,92 @@ function H() {
|
|
|
256
256
|
d("technospam.common.findMentor", "Find a mentor")
|
|
257
257
|
]
|
|
258
258
|
}),
|
|
259
|
-
children: [
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
259
|
+
children: [/* @__PURE__ */ M("div", {
|
|
260
|
+
className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
261
|
+
children: [
|
|
262
|
+
/* @__PURE__ */ j(o, {
|
|
263
|
+
label: d("technospam.mentorship.statAsMentee", "As mentee"),
|
|
264
|
+
value: J.length,
|
|
265
|
+
icon: D,
|
|
266
|
+
tone: "blue"
|
|
267
|
+
}),
|
|
268
|
+
/* @__PURE__ */ j(o, {
|
|
269
|
+
label: d("technospam.mentorship.statAsMentor", "As mentor"),
|
|
270
|
+
value: W ? Y.length : 0,
|
|
271
|
+
icon: O,
|
|
272
|
+
tone: "purple"
|
|
273
|
+
}),
|
|
274
|
+
/* @__PURE__ */ j(o, {
|
|
275
|
+
label: d("technospam.mentorship.statUpcoming", "Upcoming"),
|
|
276
|
+
value: te,
|
|
277
|
+
icon: C,
|
|
278
|
+
tone: "teal"
|
|
279
|
+
}),
|
|
280
|
+
/* @__PURE__ */ j(o, {
|
|
281
|
+
label: d("technospam.mentorship.statCompleted", "Completed"),
|
|
282
|
+
value: ne,
|
|
283
|
+
icon: T,
|
|
284
|
+
tone: "green"
|
|
285
|
+
})
|
|
286
|
+
]
|
|
287
|
+
}), /* @__PURE__ */ M(I, {
|
|
288
|
+
value: b,
|
|
289
|
+
onValueChange: (e) => w(e),
|
|
290
|
+
className: "gap-4",
|
|
291
|
+
children: [/* @__PURE__ */ M(R, { children: [/* @__PURE__ */ j(z, {
|
|
292
|
+
value: "mentee",
|
|
293
|
+
children: d("technospam.mentorship.tabAsMentee", "As mentee")
|
|
294
|
+
}), /* @__PURE__ */ j(z, {
|
|
295
|
+
value: "mentor",
|
|
296
|
+
children: d("technospam.mentorship.tabAsMentor", "As mentor")
|
|
297
|
+
})] }), /* @__PURE__ */ j(L, {
|
|
298
|
+
value: b,
|
|
299
|
+
children: b === "mentee" ? /* @__PURE__ */ j(H, {
|
|
300
|
+
loading: B.loading,
|
|
301
|
+
error: B.error,
|
|
302
|
+
sessions: J,
|
|
303
|
+
showMentor: !0,
|
|
304
|
+
emptyTitle: d("technospam.mentorship.noSessionsBookedTitle", "No sessions booked yet"),
|
|
305
|
+
emptyDescription: d("technospam.mentorship.noSessionsBookedDesc", "Find a mentor and request a session to get personalized guidance."),
|
|
306
|
+
emptyAction: /* @__PURE__ */ j(N, {
|
|
307
|
+
onClick: () => i("/mentorship/mentors-page"),
|
|
308
|
+
children: d("technospam.common.findMentor", "Find a mentor")
|
|
280
309
|
}),
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
310
|
+
isMentor: !1,
|
|
311
|
+
onCancel: Z,
|
|
312
|
+
onConfirm: Q,
|
|
313
|
+
onComplete: $,
|
|
314
|
+
busyId: E,
|
|
315
|
+
focusedId: A,
|
|
316
|
+
onFocusChange: P
|
|
317
|
+
}) : V.loading && !U ? /* @__PURE__ */ j(f, {
|
|
318
|
+
count: 3,
|
|
319
|
+
height: "h-24"
|
|
320
|
+
}) : W ? /* @__PURE__ */ j(H, {
|
|
321
|
+
loading: G.loading,
|
|
322
|
+
error: G.error,
|
|
323
|
+
sessions: Y,
|
|
324
|
+
showMentor: !1,
|
|
325
|
+
emptyTitle: d("technospam.mentorship.noSessionsToHostTitle", "No sessions to host yet"),
|
|
326
|
+
emptyDescription: d("technospam.mentorship.noSessionsToHostDesc", "When mentees request time with you, their sessions will show up here."),
|
|
327
|
+
isMentor: !0,
|
|
328
|
+
onCancel: Z,
|
|
329
|
+
onConfirm: Q,
|
|
330
|
+
onComplete: $,
|
|
331
|
+
busyId: E,
|
|
332
|
+
focusedId: A,
|
|
333
|
+
onFocusChange: P
|
|
334
|
+
}) : /* @__PURE__ */ j(u, {
|
|
335
|
+
icon: /* @__PURE__ */ j(O, { size: 26 }),
|
|
336
|
+
title: d("technospam.mentorship.notAMentorTitle", "You're not a mentor yet"),
|
|
337
|
+
description: d("technospam.mentorship.notAMentorDesc", "Create a mentor profile to start hosting sessions and helping others build."),
|
|
338
|
+
action: /* @__PURE__ */ j(N, {
|
|
339
|
+
onClick: () => i("/mentorship/mentors-page"),
|
|
340
|
+
children: d("technospam.common.becomeAMentor", "Become a mentor")
|
|
286
341
|
})
|
|
287
|
-
]
|
|
288
|
-
}),
|
|
289
|
-
/* @__PURE__ */ j(I, {
|
|
290
|
-
value: b,
|
|
291
|
-
onValueChange: (e) => w(e),
|
|
292
|
-
className: "mb-4",
|
|
293
|
-
children: /* @__PURE__ */ M(L, { children: [/* @__PURE__ */ j(R, {
|
|
294
|
-
value: "mentee",
|
|
295
|
-
children: d("technospam.mentorship.tabAsMentee", "As mentee")
|
|
296
|
-
}), /* @__PURE__ */ j(R, {
|
|
297
|
-
value: "mentor",
|
|
298
|
-
children: d("technospam.mentorship.tabAsMentor", "As mentor")
|
|
299
|
-
})] })
|
|
300
|
-
}),
|
|
301
|
-
b === "mentee" ? /* @__PURE__ */ j(V, {
|
|
302
|
-
loading: z.loading,
|
|
303
|
-
error: z.error,
|
|
304
|
-
sessions: J,
|
|
305
|
-
showMentor: !0,
|
|
306
|
-
emptyTitle: d("technospam.mentorship.noSessionsBookedTitle", "No sessions booked yet"),
|
|
307
|
-
emptyDescription: d("technospam.mentorship.noSessionsBookedDesc", "Find a mentor and request a session to get personalized guidance."),
|
|
308
|
-
emptyAction: /* @__PURE__ */ j(N, {
|
|
309
|
-
onClick: () => i("/mentorship/mentors-page"),
|
|
310
|
-
children: d("technospam.common.findMentor", "Find a mentor")
|
|
311
|
-
}),
|
|
312
|
-
isMentor: !1,
|
|
313
|
-
onCancel: Z,
|
|
314
|
-
onConfirm: Q,
|
|
315
|
-
onComplete: $,
|
|
316
|
-
busyId: E,
|
|
317
|
-
focusedId: A,
|
|
318
|
-
onFocusChange: P
|
|
319
|
-
}) : B.loading && !H ? /* @__PURE__ */ j(f, {
|
|
320
|
-
count: 3,
|
|
321
|
-
height: "h-24"
|
|
322
|
-
}) : U ? /* @__PURE__ */ j(V, {
|
|
323
|
-
loading: W.loading,
|
|
324
|
-
error: W.error,
|
|
325
|
-
sessions: Y,
|
|
326
|
-
showMentor: !1,
|
|
327
|
-
emptyTitle: d("technospam.mentorship.noSessionsToHostTitle", "No sessions to host yet"),
|
|
328
|
-
emptyDescription: d("technospam.mentorship.noSessionsToHostDesc", "When mentees request time with you, their sessions will show up here."),
|
|
329
|
-
isMentor: !0,
|
|
330
|
-
onCancel: Z,
|
|
331
|
-
onConfirm: Q,
|
|
332
|
-
onComplete: $,
|
|
333
|
-
busyId: E,
|
|
334
|
-
focusedId: A,
|
|
335
|
-
onFocusChange: P
|
|
336
|
-
}) : /* @__PURE__ */ j(u, {
|
|
337
|
-
icon: /* @__PURE__ */ j(O, { size: 26 }),
|
|
338
|
-
title: d("technospam.mentorship.notAMentorTitle", "You're not a mentor yet"),
|
|
339
|
-
description: d("technospam.mentorship.notAMentorDesc", "Create a mentor profile to start hosting sessions and helping others build."),
|
|
340
|
-
action: /* @__PURE__ */ j(N, {
|
|
341
|
-
onClick: () => i("/mentorship/mentors-page"),
|
|
342
|
-
children: d("technospam.common.becomeAMentor", "Become a mentor")
|
|
343
342
|
})
|
|
344
|
-
})
|
|
345
|
-
]
|
|
343
|
+
})]
|
|
344
|
+
})]
|
|
346
345
|
}) : /* @__PURE__ */ j(a, {
|
|
347
346
|
hero: !0,
|
|
348
347
|
title: d("technospam.mentorship.sessionsTitle", "My Sessions"),
|
|
@@ -355,6 +354,6 @@ function H() {
|
|
|
355
354
|
});
|
|
356
355
|
}
|
|
357
356
|
//#endregion
|
|
358
|
-
export {
|
|
357
|
+
export { U as MentorshipSessionsPagePage };
|
|
359
358
|
|
|
360
359
|
//# sourceMappingURL=MentorshipSessionsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipSessionsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n CalendarClock,\n Check,\n CheckCircle2,\n Clock,\n MessageSquare,\n Users,\n Video,\n X,\n} from 'lucide-react';\nimport { Button, GlassCard, Input, Tabs, TabsList, TabsTrigger } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCancelSession,\n useCompleteSession,\n useConfirmSession,\n useMentorsQuery,\n useSessionsQuery,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\nimport { cn } from '../utils/cn';\nimport { useTr } from '../hooks/useTr';\n\ntype Perspective = 'mentee' | 'mentor';\n\nconst CANCELLABLE = new Set(['REQUESTED', 'CONFIRMED']);\n\nfunction SessionRow({\n session,\n showMentor,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busy,\n focused,\n onFocusChange,\n}: {\n session: TecMentorSession;\n showMentor: boolean;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busy: boolean;\n /** True when this is the session the page (and the assistant) is focused on. */\n focused: boolean;\n onFocusChange: (id: string | null) => void;\n}) {\n // Acting on a session focuses it, so \"cancel this session\" names this one.\n const focus = () => onFocusChange(session.id);\n const toggleFocus = () => onFocusChange(focused ? null : session.id);\n const canCancel = CANCELLABLE.has(session.status);\n const [mode, setMode] = useState<'none' | 'confirm' | 'complete'>('none');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [rating, setRating] = useState('5');\n const [feedback, setFeedback] = useState('');\n\n const submitConfirm = async () => {\n await onConfirm(session.id, meetingUrl.trim());\n setMode('none');\n setMeetingUrl('');\n };\n const submitComplete = async () => {\n await onComplete(session.id, Math.min(5, Math.max(1, Number(rating) || 5)), feedback.trim());\n setMode('none');\n };\n\n return (\n <GlassCard\n className={cn(\n 'flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, toggleFocus)}\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-accent-primary to-accent-purple text-white shadow-sm\">\n <Video size={18} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n aria-pressed={focused}\n onClick={toggleFocus}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {session.topic}\n </button>\n <StatusBadge status={session.status} />\n {session.rating ? (\n <span className=\"text-xs text-text-muted\">★ {session.rating}/5</span>\n ) : null}\n </div>\n {showMentor && session.mentor?.headline ? (\n <div className=\"mt-0.5 truncate text-xs text-text-secondary\">\n with {session.mentor.headline}\n </div>\n ) : null}\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock size={13} /> {formatDateTime(session.scheduledAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(session.durationMinutes)}\n </span>\n </div>\n {session.agenda ? (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">{session.agenda}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n {session.meetingUrl ? (\n <Button\n size=\"sm\"\n onClick={() => window.open(session.meetingUrl ?? '', '_blank', 'noopener,noreferrer')}\n >\n <Video size={15} /> Join\n </Button>\n ) : null}\n {isMentor && session.status === 'REQUESTED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'confirm' ? 'none' : 'confirm');\n }}\n >\n <Check size={15} /> Confirm\n </Button>\n ) : null}\n {isMentor && session.status === 'CONFIRMED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'complete' ? 'none' : 'complete');\n }}\n >\n <CheckCircle2 size={15} /> Mark complete\n </Button>\n ) : null}\n {canCancel ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n focus();\n onCancel(session.id);\n }}\n disabled={busy}\n >\n <X size={15} /> Cancel\n </Button>\n ) : null}\n </div>\n </div>\n\n {mode === 'confirm' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center\">\n <Input\n className=\"flex-1\"\n placeholder=\"Meeting link (e.g. https://meet.…)\"\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n />\n <Button size=\"sm\" onClick={submitConfirm} disabled={busy}>\n Confirm session\n </Button>\n </div>\n ) : null}\n {mode === 'complete' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-text-secondary\">Rating (1–5)</label>\n <Input\n type=\"number\"\n min={1}\n max={5}\n className=\"w-20\"\n value={rating}\n onChange={(e) => setRating(e.target.value)}\n />\n </div>\n <Input\n placeholder=\"Feedback for the mentee (optional)\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n />\n <div>\n <Button size=\"sm\" onClick={submitComplete} disabled={busy}>\n Complete session\n </Button>\n </div>\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nfunction SessionsList({\n loading,\n error,\n sessions,\n showMentor,\n emptyTitle,\n emptyDescription,\n emptyAction,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busyId,\n focusedId,\n onFocusChange,\n}: {\n loading: boolean;\n error?: { message?: string };\n sessions: TecMentorSession[];\n showMentor: boolean;\n emptyTitle: string;\n emptyDescription: string;\n emptyAction?: React.ReactNode;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busyId: string | null;\n focusedId: string | null;\n onFocusChange: (id: string | null) => void;\n}) {\n if (loading && sessions.length === 0) return <LoadingRows count={3} height=\"h-24\" />;\n if (error) return <ErrorView title=\"Couldn't load sessions\" error={error} />;\n if (sessions.length === 0) {\n return (\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={emptyTitle}\n description={emptyDescription}\n action={emptyAction}\n />\n );\n }\n const sorted = [...sessions].sort(\n (a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime()\n );\n return (\n <div className=\"space-y-3\">\n {sorted.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n showMentor={showMentor}\n isMentor={isMentor}\n onCancel={onCancel}\n onConfirm={onConfirm}\n onComplete={onComplete}\n busy={busyId === session.id}\n focused={focusedId === session.id}\n onFocusChange={onFocusChange}\n />\n ))}\n </div>\n );\n}\n\nexport function MentorshipSessionsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [tab, setTab] = useState<Perspective>('mentee');\n const [cancellingId, setCancellingId] = useState<string | null>(null);\n // The focused session lives in `?sessionId=` so the assistant's page context\n // names it (\"cancel this session\") and a reload keeps it focused.\n const [focusedSessionId, setFocusedSessionId] = useFocusedRecordParam(FOCUS_PARAMS.session);\n\n const menteeId = currentUser?.id;\n\n // As mentee: sessions the current user booked.\n const menteeQ = useSessionsQuery(menteeId ? { menteeId } : {});\n\n // Resolve whether the current user is a mentor, to load their mentor sessions.\n const mentorsQ = useMentorsQuery({});\n const myMentor = mentorsQ.data?.tecListMentors?.items.find((m) => m.userId === currentUser?.id);\n const mentorId = myMentor?.id;\n const mentorQ = useSessionsQuery(mentorId ? { mentorId } : {});\n\n const [cancelSession] = useCancelSession();\n const [confirmSession] = useConfirmSession();\n const [completeSession] = useCompleteSession();\n\n const menteeSessions = menteeId ? (menteeQ.data?.tecListSessions?.items ?? []) : [];\n const mentorSessions = mentorId ? (mentorQ.data?.tecListSessions?.items ?? []) : [];\n const allSessions = [...menteeSessions, ...mentorSessions];\n const upcomingCount = allSessions.filter(\n (s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED'\n ).length;\n const completedCount = allSessions.filter((s) => s.status === 'COMPLETED').length;\n\n const handleCancel = async (id: string) => {\n setCancellingId(id);\n emit('technospam.session.cancel_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await cancelSession({ variables: { id } });\n if (res.data?.tecCancelSession) {\n emit('technospam.session.cancelled', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session cancelled', 'The session has been cancelled.');\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleConfirm = async (id: string, meetingUrl: string) => {\n setCancellingId(id);\n try {\n const res = await confirmSession({\n variables: { id, meetingUrl: meetingUrl || undefined },\n });\n if (res.data?.tecConfirmSession) {\n notify.success('Session confirmed', 'The mentee has been notified.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleComplete = async (id: string, rating: number, feedback: string) => {\n setCancellingId(id);\n emit('technospam.session.complete_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await completeSession({\n variables: { id, rating, feedback: feedback || undefined },\n });\n if (res.data?.tecCompleteSession) {\n emit('technospam.session.completed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session completed', 'Nice work — the session is wrapped up.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n if (!currentUser?.id) {\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n 'Track your mentorship sessions.'\n )}\n >\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={tr('technospam.mentorship.signInTitle', 'Sign in to view your sessions')}\n description={tr(\n 'technospam.mentorship.signInDesc',\n \"Your booked and hosted mentorship sessions will appear here once you're signed in.\"\n )}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n \"Manage the mentorship sessions you've booked and the ones you host.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n <Users size={16} /> {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statAsMentee', 'As mentee')}\n value={menteeSessions.length}\n icon={MessageSquare}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAsMentor', 'As mentor')}\n value={mentorId ? mentorSessions.length : 0}\n icon={Users}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.mentorship.statUpcoming', 'Upcoming')}\n value={upcomingCount}\n icon={CalendarClock}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.mentorship.statCompleted', 'Completed')}\n value={completedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n </div>\n\n <Tabs value={tab} onValueChange={(v) => setTab(v as Perspective)} className=\"mb-4\">\n <TabsList>\n <TabsTrigger value=\"mentee\">\n {tr('technospam.mentorship.tabAsMentee', 'As mentee')}\n </TabsTrigger>\n <TabsTrigger value=\"mentor\">\n {tr('technospam.mentorship.tabAsMentor', 'As mentor')}\n </TabsTrigger>\n </TabsList>\n </Tabs>\n\n {tab === 'mentee' ? (\n <SessionsList\n loading={menteeQ.loading}\n error={menteeQ.error}\n sessions={menteeSessions}\n showMentor\n emptyTitle={tr('technospam.mentorship.noSessionsBookedTitle', 'No sessions booked yet')}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsBookedDesc',\n 'Find a mentor and request a session to get personalized guidance.'\n )}\n emptyAction={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n isMentor={false}\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n ) : mentorsQ.loading && !myMentor ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : !mentorId ? (\n <EmptyView\n icon={<Users size={26} />}\n title={tr('technospam.mentorship.notAMentorTitle', \"You're not a mentor yet\")}\n description={tr(\n 'technospam.mentorship.notAMentorDesc',\n 'Create a mentor profile to start hosting sessions and helping others build.'\n )}\n action={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n }\n />\n ) : (\n <SessionsList\n loading={mentorQ.loading}\n error={mentorQ.error}\n sessions={mentorSessions}\n showMentor={false}\n emptyTitle={tr('technospam.mentorship.noSessionsToHostTitle', 'No sessions to host yet')}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsToHostDesc',\n 'When mentees request time with you, their sessions will show up here.'\n )}\n isMentor\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MentorshipSessionsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n CalendarClock,\n Check,\n CheckCircle2,\n Clock,\n MessageSquare,\n Users,\n Video,\n X,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCancelSession,\n useCompleteSession,\n useConfirmSession,\n useMentorsQuery,\n useSessionsQuery,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\nimport { cn } from '../utils/cn';\nimport { useTr } from '../hooks/useTr';\n\ntype Perspective = 'mentee' | 'mentor';\n\nconst CANCELLABLE = new Set(['REQUESTED', 'CONFIRMED']);\n\nfunction SessionRow({\n session,\n showMentor,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busy,\n focused,\n onFocusChange,\n}: {\n session: TecMentorSession;\n showMentor: boolean;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busy: boolean;\n /** True when this is the session the page (and the assistant) is focused on. */\n focused: boolean;\n onFocusChange: (id: string | null) => void;\n}) {\n // Acting on a session focuses it, so \"cancel this session\" names this one.\n const focus = () => onFocusChange(session.id);\n const toggleFocus = () => onFocusChange(focused ? null : session.id);\n const canCancel = CANCELLABLE.has(session.status);\n const [mode, setMode] = useState<'none' | 'confirm' | 'complete'>('none');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [rating, setRating] = useState('5');\n const [feedback, setFeedback] = useState('');\n\n const submitConfirm = async () => {\n await onConfirm(session.id, meetingUrl.trim());\n setMode('none');\n setMeetingUrl('');\n };\n const submitComplete = async () => {\n await onComplete(session.id, Math.min(5, Math.max(1, Number(rating) || 5)), feedback.trim());\n setMode('none');\n };\n\n return (\n <GlassCard\n className={cn(\n 'flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, toggleFocus)}\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-accent-primary to-accent-purple text-white shadow-sm\">\n <Video size={18} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n aria-pressed={focused}\n onClick={toggleFocus}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {session.topic}\n </button>\n <StatusBadge status={session.status} />\n {session.rating ? (\n <span className=\"text-xs text-text-muted\">★ {session.rating}/5</span>\n ) : null}\n </div>\n {showMentor && session.mentor?.headline ? (\n <div className=\"mt-0.5 truncate text-xs text-text-secondary\">\n with {session.mentor.headline}\n </div>\n ) : null}\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock size={13} /> {formatDateTime(session.scheduledAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(session.durationMinutes)}\n </span>\n </div>\n {session.agenda ? (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">{session.agenda}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n {session.meetingUrl ? (\n <Button\n size=\"sm\"\n onClick={() => window.open(session.meetingUrl ?? '', '_blank', 'noopener,noreferrer')}\n >\n <Video size={15} /> Join\n </Button>\n ) : null}\n {isMentor && session.status === 'REQUESTED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'confirm' ? 'none' : 'confirm');\n }}\n >\n <Check size={15} /> Confirm\n </Button>\n ) : null}\n {isMentor && session.status === 'CONFIRMED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'complete' ? 'none' : 'complete');\n }}\n >\n <CheckCircle2 size={15} /> Mark complete\n </Button>\n ) : null}\n {canCancel ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n focus();\n onCancel(session.id);\n }}\n disabled={busy}\n >\n <X size={15} /> Cancel\n </Button>\n ) : null}\n </div>\n </div>\n\n {mode === 'confirm' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center\">\n <Input\n className=\"flex-1\"\n placeholder=\"Meeting link (e.g. https://meet.…)\"\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n />\n <Button size=\"sm\" onClick={submitConfirm} disabled={busy}>\n Confirm session\n </Button>\n </div>\n ) : null}\n {mode === 'complete' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-text-secondary\">Rating (1–5)</label>\n <Input\n type=\"number\"\n min={1}\n max={5}\n className=\"w-20\"\n value={rating}\n onChange={(e) => setRating(e.target.value)}\n />\n </div>\n <Input\n placeholder=\"Feedback for the mentee (optional)\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n />\n <div>\n <Button size=\"sm\" onClick={submitComplete} disabled={busy}>\n Complete session\n </Button>\n </div>\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nfunction SessionsList({\n loading,\n error,\n sessions,\n showMentor,\n emptyTitle,\n emptyDescription,\n emptyAction,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busyId,\n focusedId,\n onFocusChange,\n}: {\n loading: boolean;\n error?: { message?: string };\n sessions: TecMentorSession[];\n showMentor: boolean;\n emptyTitle: string;\n emptyDescription: string;\n emptyAction?: React.ReactNode;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busyId: string | null;\n focusedId: string | null;\n onFocusChange: (id: string | null) => void;\n}) {\n if (loading && sessions.length === 0) return <LoadingRows count={3} height=\"h-24\" />;\n if (error) return <ErrorView title=\"Couldn't load sessions\" error={error} />;\n if (sessions.length === 0) {\n return (\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={emptyTitle}\n description={emptyDescription}\n action={emptyAction}\n />\n );\n }\n const sorted = [...sessions].sort(\n (a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime()\n );\n return (\n <div className=\"space-y-3\">\n {sorted.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n showMentor={showMentor}\n isMentor={isMentor}\n onCancel={onCancel}\n onConfirm={onConfirm}\n onComplete={onComplete}\n busy={busyId === session.id}\n focused={focusedId === session.id}\n onFocusChange={onFocusChange}\n />\n ))}\n </div>\n );\n}\n\nexport function MentorshipSessionsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [tab, setTab] = useState<Perspective>('mentee');\n const [cancellingId, setCancellingId] = useState<string | null>(null);\n // The focused session lives in `?sessionId=` so the assistant's page context\n // names it (\"cancel this session\") and a reload keeps it focused.\n const [focusedSessionId, setFocusedSessionId] = useFocusedRecordParam(FOCUS_PARAMS.session);\n\n const menteeId = currentUser?.id;\n\n // As mentee: sessions the current user booked.\n const menteeQ = useSessionsQuery(menteeId ? { menteeId } : {});\n\n // Resolve whether the current user is a mentor, to load their mentor sessions.\n const mentorsQ = useMentorsQuery({});\n const myMentor = mentorsQ.data?.tecListMentors?.items.find((m) => m.userId === currentUser?.id);\n const mentorId = myMentor?.id;\n const mentorQ = useSessionsQuery(mentorId ? { mentorId } : {});\n\n const [cancelSession] = useCancelSession();\n const [confirmSession] = useConfirmSession();\n const [completeSession] = useCompleteSession();\n\n const menteeSessions = menteeId ? (menteeQ.data?.tecListSessions?.items ?? []) : [];\n const mentorSessions = mentorId ? (mentorQ.data?.tecListSessions?.items ?? []) : [];\n const allSessions = [...menteeSessions, ...mentorSessions];\n const upcomingCount = allSessions.filter(\n (s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED'\n ).length;\n const completedCount = allSessions.filter((s) => s.status === 'COMPLETED').length;\n\n const handleCancel = async (id: string) => {\n setCancellingId(id);\n emit('technospam.session.cancel_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await cancelSession({ variables: { id } });\n if (res.data?.tecCancelSession) {\n emit('technospam.session.cancelled', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session cancelled', 'The session has been cancelled.');\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleConfirm = async (id: string, meetingUrl: string) => {\n setCancellingId(id);\n try {\n const res = await confirmSession({\n variables: { id, meetingUrl: meetingUrl || undefined },\n });\n if (res.data?.tecConfirmSession) {\n notify.success('Session confirmed', 'The mentee has been notified.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleComplete = async (id: string, rating: number, feedback: string) => {\n setCancellingId(id);\n emit('technospam.session.complete_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await completeSession({\n variables: { id, rating, feedback: feedback || undefined },\n });\n if (res.data?.tecCompleteSession) {\n emit('technospam.session.completed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session completed', 'Nice work — the session is wrapped up.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n if (!currentUser?.id) {\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n 'Track your mentorship sessions.'\n )}\n >\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={tr('technospam.mentorship.signInTitle', 'Sign in to view your sessions')}\n description={tr(\n 'technospam.mentorship.signInDesc',\n \"Your booked and hosted mentorship sessions will appear here once you're signed in.\"\n )}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n \"Manage the mentorship sessions you've booked and the ones you host.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n <Users size={16} /> {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statAsMentee', 'As mentee')}\n value={menteeSessions.length}\n icon={MessageSquare}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAsMentor', 'As mentor')}\n value={mentorId ? mentorSessions.length : 0}\n icon={Users}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.mentorship.statUpcoming', 'Upcoming')}\n value={upcomingCount}\n icon={CalendarClock}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.mentorship.statCompleted', 'Completed')}\n value={completedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs value={tab} onValueChange={(v) => setTab(v as Perspective)} className=\"gap-4\">\n <TabsList>\n <TabsTrigger value=\"mentee\">\n {tr('technospam.mentorship.tabAsMentee', 'As mentee')}\n </TabsTrigger>\n <TabsTrigger value=\"mentor\">\n {tr('technospam.mentorship.tabAsMentor', 'As mentor')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value={tab}>\n {tab === 'mentee' ? (\n <SessionsList\n loading={menteeQ.loading}\n error={menteeQ.error}\n sessions={menteeSessions}\n showMentor\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsBookedTitle',\n 'No sessions booked yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsBookedDesc',\n 'Find a mentor and request a session to get personalized guidance.'\n )}\n emptyAction={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n isMentor={false}\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n ) : mentorsQ.loading && !myMentor ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : !mentorId ? (\n <EmptyView\n icon={<Users size={26} />}\n title={tr('technospam.mentorship.notAMentorTitle', \"You're not a mentor yet\")}\n description={tr(\n 'technospam.mentorship.notAMentorDesc',\n 'Create a mentor profile to start hosting sessions and helping others build.'\n )}\n action={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n }\n />\n ) : (\n <SessionsList\n loading={mentorQ.loading}\n error={mentorQ.error}\n sessions={mentorSessions}\n showMentor={false}\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsToHostTitle',\n 'No sessions to host yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsToHostDesc',\n 'When mentees request time with you, their sessions will show up here.'\n )}\n isMentor\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n )}\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-technospam",
|
|
3
|
-
"version": "2026.1031.
|
|
3
|
+
"version": "2026.1031.2",
|
|
4
4
|
"description": "Burdenoff TechnoSpam Microfrontend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"framer-motion": "^12.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
+
"@axe-core/playwright": "4.11.0",
|
|
68
69
|
"@eslint/js": "^9.39.2",
|
|
69
70
|
"@graphql-codegen/add": "^5.0.0",
|
|
70
71
|
"@graphql-codegen/cli": "^5.0.0",
|
|
@@ -80,17 +81,17 @@
|
|
|
80
81
|
"@storybook/test": "8.6.18",
|
|
81
82
|
"@storybook/test-runner": "0.24.4",
|
|
82
83
|
"@tailwindcss/vite": "^4.3.2",
|
|
84
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
85
|
+
"@testing-library/react": "^16.1.0",
|
|
83
86
|
"@types/react": "^19.0.2",
|
|
84
87
|
"@types/react-dom": "^19.0.2",
|
|
85
88
|
"@vitejs/plugin-react": "^4.3.4",
|
|
86
|
-
"@testing-library/jest-dom": "^6.6.3",
|
|
87
|
-
"@testing-library/react": "^16.1.0",
|
|
88
89
|
"autoprefixer": "^10.4.20",
|
|
89
|
-
"jsdom": "^25.0.1",
|
|
90
90
|
"eslint": "^9.18.0",
|
|
91
91
|
"eslint-plugin-react": "^7.37.5",
|
|
92
92
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
93
93
|
"eslint-plugin-react-refresh": "^0.4.26",
|
|
94
|
+
"jsdom": "^25.0.1",
|
|
94
95
|
"lefthook": "^2.1.1",
|
|
95
96
|
"postcss": "^8.4.49",
|
|
96
97
|
"prettier": "^3.4.2",
|
|
@@ -100,8 +101,8 @@
|
|
|
100
101
|
"typescript": "^5.9.0",
|
|
101
102
|
"typescript-eslint": "^8.54.0",
|
|
102
103
|
"vite": "8.0.8",
|
|
103
|
-
"
|
|
104
|
-
"
|
|
104
|
+
"vite-plugin-dts": "3.9.1",
|
|
105
|
+
"vitest": "^3.0.0"
|
|
105
106
|
},
|
|
106
107
|
"peerDependencies": {
|
|
107
108
|
"@apollo/client": "^4.0.11",
|