@burdenoff/microfe-technospam 2026.1102.1 → 2026.1102.3
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/generated/wspace-types.d.ts +7468 -141
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/node_modules/@codemirror/autocomplete/dist/index.js +527 -0
- package/dist/node_modules/@codemirror/autocomplete/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/commands/dist/index.js +1108 -0
- package/dist/node_modules/@codemirror/commands/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-go/dist/index.js +206 -0
- package/dist/node_modules/@codemirror/lang-go/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-javascript/dist/index.js +321 -0
- package/dist/node_modules/@codemirror/lang-javascript/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-python/dist/index.js +265 -0
- package/dist/node_modules/@codemirror/lang-python/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/language/dist/index.js +957 -0
- package/dist/node_modules/@codemirror/language/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/ruby.js +164 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/ruby.js.map +1 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/shell.js +109 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/shell.js.map +1 -0
- package/dist/node_modules/@codemirror/state/dist/index.js +1739 -0
- package/dist/node_modules/@codemirror/state/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/streamparser/dist/index.js +70 -0
- package/dist/node_modules/@codemirror/streamparser/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/view/dist/index.js +5427 -0
- package/dist/node_modules/@codemirror/view/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/common/dist/index.js +896 -0
- package/dist/node_modules/@lezer/common/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/go/dist/index.js +215 -0
- package/dist/node_modules/@lezer/go/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/highlight/dist/index.js +395 -0
- package/dist/node_modules/@lezer/highlight/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/javascript/dist/index.js +437 -0
- package/dist/node_modules/@lezer/javascript/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/lr/dist/index.js +842 -0
- package/dist/node_modules/@lezer/lr/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/python/dist/index.js +352 -0
- package/dist/node_modules/@lezer/python/dist/index.js.map +1 -0
- package/dist/node_modules/@marijn/find-cluster-break/src/index.js +66 -0
- package/dist/node_modules/@marijn/find-cluster-break/src/index.js.map +1 -0
- package/dist/node_modules/style-mod/src/style-mod.js +74 -0
- package/dist/node_modules/style-mod/src/style-mod.js.map +1 -0
- package/dist/node_modules/w3c-keyname/index.js +90 -0
- package/dist/node_modules/w3c-keyname/index.js.map +1 -0
- package/dist/stories/fixtures/technospamFixtures.d.ts.map +1 -1
- package/dist/stories/journeys/Problems.stories.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoot.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoot.js +187 -178
- package/dist/technospam/TechnoSpamRoot.js.map +1 -1
- package/dist/technospam/api/technospamApi.d.ts +39 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +184 -150
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/MediaCover.js +2 -2
- package/dist/technospam/components/MediaCover.js.map +1 -1
- package/dist/technospam/components/MobileSectionTabs.d.ts +9 -0
- package/dist/technospam/components/MobileSectionTabs.d.ts.map +1 -0
- package/dist/technospam/components/MobileSectionTabs.js +43 -0
- package/dist/technospam/components/MobileSectionTabs.js.map +1 -0
- package/dist/technospam/components/PageLayout.d.ts +7 -1
- package/dist/technospam/components/PageLayout.d.ts.map +1 -1
- package/dist/technospam/components/PageLayout.js +40 -28
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/SubmissionViews.d.ts +6 -2
- package/dist/technospam/components/SubmissionViews.d.ts.map +1 -1
- package/dist/technospam/components/SubmissionViews.js +6 -5
- package/dist/technospam/components/SubmissionViews.js.map +1 -1
- package/dist/technospam/components/TechnoSpamErrorBoundary.d.ts +6 -0
- package/dist/technospam/components/TechnoSpamErrorBoundary.d.ts.map +1 -1
- package/dist/technospam/components/TechnoSpamErrorBoundary.js +2 -2
- package/dist/technospam/components/TechnoSpamErrorBoundary.js.map +1 -1
- package/dist/technospam/components/editor/CodeEditor.d.ts +26 -0
- package/dist/technospam/components/editor/CodeEditor.d.ts.map +1 -0
- package/dist/technospam/components/editor/CodeEditor.js +102 -0
- package/dist/technospam/components/editor/CodeEditor.js.map +1 -0
- package/dist/technospam/components/editor/RunOutputPanel.d.ts +14 -0
- package/dist/technospam/components/editor/RunOutputPanel.d.ts.map +1 -0
- package/dist/technospam/components/editor/RunOutputPanel.js +127 -0
- package/dist/technospam/components/editor/RunOutputPanel.js.map +1 -0
- package/dist/technospam/components/editor/editorLanguages.d.ts +11 -0
- package/dist/technospam/components/editor/editorLanguages.d.ts.map +1 -0
- package/dist/technospam/components/editor/editorLanguages.js +30 -0
- package/dist/technospam/components/editor/editorLanguages.js.map +1 -0
- package/dist/technospam/components/editor/editorTheme.d.ts +11 -0
- package/dist/technospam/components/editor/editorTheme.d.ts.map +1 -0
- package/dist/technospam/components/editor/editorTheme.js +120 -0
- package/dist/technospam/components/editor/editorTheme.js.map +1 -0
- package/dist/technospam/components/lesson/SaveCourseOffline.js +6 -6
- package/dist/technospam/hooks/usePhoneLayout.d.ts +18 -0
- package/dist/technospam/hooks/usePhoneLayout.d.ts.map +1 -0
- package/dist/technospam/hooks/usePhoneLayout.js +14 -0
- package/dist/technospam/hooks/usePhoneLayout.js.map +1 -0
- package/dist/technospam/hooks/useSolutionRunner.d.ts +35 -0
- package/dist/technospam/hooks/useSolutionRunner.d.ts.map +1 -0
- package/dist/technospam/hooks/useSolutionRunner.js +66 -0
- package/dist/technospam/hooks/useSolutionRunner.js.map +1 -0
- package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts +1 -1
- package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts.map +1 -1
- package/dist/technospam/hooks/useTechnoSpamAnalytics.js.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +416 -263
- 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 +89 -87
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +3 -3
- 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 +117 -81
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +21 -17
- package/dist/technospam/pages/DashboardDashboardPagePage.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/MentorshipMentorProfilePagePage.js +4 -4
- 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 +6 -4
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/dist/technospam/types/index.d.ts +7 -0
- package/dist/technospam/types/index.d.ts.map +1 -1
- package/dist/technospam/utils/mobileTabs.d.ts +45 -0
- package/dist/technospam/utils/mobileTabs.d.ts.map +1 -0
- package/dist/technospam/utils/mobileTabs.js +70 -0
- package/dist/technospam/utils/mobileTabs.js.map +1 -0
- package/dist/test/matchMedia.d.ts +6 -0
- package/dist/test/matchMedia.d.ts.map +1 -0
- package/package.json +16 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/technospam/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowRight,\n Award,\n BookOpen,\n Calendar,\n CalendarClock,\n CheckCircle2,\n Clock,\n Code2,\n GraduationCap,\n PlayCircle,\n Trophy,\n Users,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCoursesQuery,\n useHackathonsQuery,\n useMyCertificatesQuery,\n useMyEnrollmentsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecCourse,\n type TecEnrollment,\n type TecHackathon,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { useTr } from '../hooks/useTr';\nimport { resumePath } from '../utils/lessonPath';\n\n/* --------------------------------------------------------------- helpers */\n\nfunction SectionHeader({\n title,\n actionLabel,\n onAction,\n}: {\n title: string;\n actionLabel?: string;\n onAction?: () => void;\n}) {\n return (\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n {actionLabel && onAction ? (\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={onAction}>\n {actionLabel} <ArrowRight size={13} />\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction ProgressBar({ pct }: { pct: number }) {\n return (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${Math.max(0, Math.min(100, pct))}%` }}\n />\n </div>\n );\n}\n\nfunction ContinueLearningCard({\n e,\n resumeLessonId,\n onOpen,\n}: {\n e: TecEnrollment;\n /** The lesson this card resumes into, once known (BOFF-4107). */\n resumeLessonId?: string | null;\n onOpen: () => void;\n}) {\n const tr = useTr();\n const title = e.course?.title ?? tr('technospam.common.courseFallback', 'Course');\n const seed = `${e.course?.topic ?? ''} ${title}`;\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n data-resume-lesson={resumeLessonId ?? undefined}\n className=\"group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={seed} icon={topicIcon(e.course?.topic ?? title)} height=\"h-20\">\n {e.course?.level ? <StatusBadge status={e.course.level} kind=\"level\" /> : <span />}\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-white/15 text-white backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <PlayCircle size={16} />\n </span>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-2.5 p-3.5\">\n <div className=\"flex items-start gap-2\">\n <h3 className=\"line-clamp-2 flex-1 text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {title}\n </h3>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-auto flex items-center gap-2.5\">\n <div className=\"flex-1\">\n <ProgressBar pct={e.progressPct} />\n </div>\n <span className=\"shrink-0 text-xs font-semibold text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n </div>\n </button>\n );\n}\n\nfunction RecommendedCourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n height=\"h-20\"\n >\n <StatusBadge status={course.level} kind=\"level\" />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-1.5 p-3.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{course.summary}</p>\n ) : null}\n {course.topic ? (\n <span className=\"mt-auto truncate pt-0.5 text-xs text-text-muted\">{course.topic}</span>\n ) : null}\n </div>\n </button>\n );\n}\n\nfunction SessionRow({ s }: { s: TecMentorSession }) {\n return (\n <div className=\"-mx-2 flex items-start gap-3 rounded-lg border-b border-border-seam px-2 py-3 transition-colors last:border-0 hover:bg-bg-sunken\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <CalendarClock size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{s.topic}</span>\n <StatusBadge status={s.status} />\n </div>\n {s.mentor?.headline ? (\n <div className=\"truncate text-xs text-text-muted\">{s.mentor.headline}</div>\n ) : null}\n <div className=\"mt-0.5 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock size={12} className=\"text-text-muted\" />\n {formatDateTime(s.scheduledAt)}\n </div>\n </div>\n </div>\n );\n}\n\nfunction HackathonBanner({ h, onOpen }: { h: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group block w-full overflow-hidden rounded-xl border border-border-seam text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={`${h.theme ?? ''} ${h.title}`} icon={Trophy} height=\"h-24\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-white/80\">\n <Trophy size={12} /> {tr('technospam.common.openHackathon', 'Open hackathon')}\n </div>\n <h3 className=\"mt-0.5 truncate text-sm font-semibold text-white drop-shadow-sm\">\n {h.title}\n </h3>\n </div>\n {h.prizePool > 0 ? (\n <span className=\"shrink-0 rounded-full bg-white/20 px-2.5 py-1 text-xs font-semibold text-white backdrop-blur\">\n {formatMoney(h.prizePool, h.currency)}\n </span>\n ) : null}\n </MediaCover>\n <div className=\"flex items-center justify-between gap-2 bg-bg-surface px-3.5 py-2.5\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Calendar size={12} />\n {formatDate(h.startAt)} – {formatDate(h.endAt)}\n </span>\n <span className=\"flex items-center gap-1 text-xs font-medium text-accent-primary opacity-0 transition-opacity group-hover:opacity-100\">\n {tr('technospam.common.register', 'Register')} <ArrowRight size={12} />\n </span>\n </div>\n </button>\n );\n}\n\n/* ------------------------------------------------------------------ page */\n\nexport function DashboardDashboardPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const tr = useTr();\n const learnerId = currentUser?.id;\n\n // BOFF-10481: the learner's own enrollments (each with its next lesson, for\n // the BOFF-4107 Continue Learning deep link) and certificates come from the\n // canonical, self-scoped Buildmyiq fields, not the cross-learner list-all\n // reads a plain learner is not granted.\n const enrollmentsQ = useMyEnrollmentsQuery();\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useMyCertificatesQuery();\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.enrollments;\n const submissions = submissionsQ.data?.tecListSubmissions?.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions?.items ?? [];\n const certificates = certificatesQ.certificates;\n const courses = coursesQ.data?.tecListCourses?.items ?? [];\n const hackathons = hackathonsQ.data?.tecListHackathons?.items ?? [];\n\n const greetingName =\n currentUser?.firstName || currentUser?.name || currentUser?.email?.split('@')[0] || 'there';\n\n const inProgress = useMemo(\n () => enrollments.filter((e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'),\n [enrollments]\n );\n\n const continueLearning = useMemo(\n () =>\n [...inProgress]\n .sort(\n (a, b) =>\n new Date(b.lastActiveAt ?? b.startedAt).getTime() -\n new Date(a.lastActiveAt ?? a.startedAt).getTime()\n )\n .slice(0, 3),\n [inProgress]\n );\n\n const problemsSolved = useMemo(\n () => new Set(submissions.filter((s) => s.verdict === 'ACCEPTED').map((s) => s.problemId)).size,\n [submissions]\n );\n\n // Stable \"now\" captured once on mount (state initializers may call impure fns).\n const [now] = useState(() => Date.now());\n const upcomingSessions = useMemo(() => {\n return [...sessions]\n .filter(\n (s) =>\n (s.status === 'CONFIRMED' || s.status === 'REQUESTED') &&\n new Date(s.scheduledAt).getTime() > now\n )\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime());\n }, [sessions, now]);\n\n const recommended = useMemo(() => {\n const enrolledIds = new Set(enrollments.map((e) => e.courseId));\n return courses.filter((c) => !enrolledIds.has(c.id)).slice(0, 4);\n }, [courses, enrollments]);\n\n const openHackathons = hackathons.slice(0, 4);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.dashboard.title', 'Welcome back, {{name}}', { name: greetingName })}\n description={tr(\n 'technospam.dashboard.description',\n \"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.dashboard.myLearning', 'My Learning')}\n </Button>\n }\n >\n {/* Top metrics */}\n <div className=\"mb-5 grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatCard\n label={tr('technospam.dashboard.coursesInProgress', 'Courses in progress')}\n value={inProgress.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.dashboard.problemsSolved', 'Problems solved')}\n value={problemsSolved}\n icon={Code2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.dashboard.upcomingSessions', 'Upcoming sessions')}\n value={upcomingSessions.length}\n icon={CalendarClock}\n tone=\"amber\"\n />\n <StatCard\n label={tr('technospam.dashboard.certificates', 'Certificates')}\n value={certificates.length}\n icon={Award}\n tone=\"purple\"\n />\n </div>\n\n {/* Quick actions */}\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/courses/courses-page')}\n >\n <BookOpen size={18} />\n <span className=\"text-xs\">{tr('technospam.common.browseCourses', 'Browse courses')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/applications/problems-page')}\n >\n <Code2 size={18} />\n <span className=\"text-xs\">\n {tr('technospam.common.practiceProblems', 'Practice problems')}\n </span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/mentorship/mentors-page')}\n >\n <Users size={18} />\n <span className=\"text-xs\">{tr('technospam.common.findMentor', 'Find a mentor')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/hackathons/hackathons-page')}\n >\n <Trophy size={18} />\n <span className=\"text-xs\">{tr('technospam.common.hackathons', 'Hackathons')}</span>\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* Continue learning */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.continueLearning', 'Continue learning')}\n actionLabel={tr('technospam.dashboard.myLearning', 'My Learning')}\n onAction={() => navigate('/courses/my-learning-page')}\n />\n {enrollmentsQ.loading && !enrollments.length ? (\n <LoadingRows count={2} height=\"h-16\" />\n ) : enrollmentsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load your courses\")}\n error={enrollmentsQ.error}\n />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title={tr(\n 'technospam.dashboard.noCoursesInProgressTitle',\n 'No courses in progress'\n )}\n description={tr(\n 'technospam.dashboard.noCoursesInProgressDesc',\n 'Enroll in a course to start building skill by doing — your active courses show up here.'\n )}\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>\n {tr('technospam.common.browseCourses', 'Browse courses')}\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {continueLearning.map((e) => (\n <ContinueLearningCard\n key={e.id}\n e={e}\n resumeLessonId={e.nextLessonId}\n onOpen={() => navigate(resumePath(e.courseId, e.nextLessonId))}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.recommendedForYou', 'Recommended for you')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/courses/courses-page')}\n />\n {coursesQ.loading && !courses.length ? (\n <LoadingRows count={2} height=\"h-20\" />\n ) : coursesQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={coursesQ.error}\n />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title={tr(\n 'technospam.dashboard.noRecommendationsTitle',\n 'No courses to recommend yet'\n )}\n description={tr(\n 'technospam.dashboard.noRecommendationsDesc',\n 'New courses are on the way. TechnoSpam is pre-launch — check back soon.'\n )}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {recommended.map((c) => (\n <RecommendedCourseCard\n key={c.id}\n course={c}\n onOpen={() => navigate(`/courses/course-details-page?id=${c.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Side column */}\n <div className=\"space-y-6\">\n {/* Upcoming sessions */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.upcomingSessionsTitle', 'Upcoming sessions')}\n actionLabel={tr('technospam.common.allSessions', 'All sessions')}\n onAction={() => navigate('/mentorship/sessions-page')}\n />\n {sessionsQ.loading && !sessions.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : sessionsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadSessions', \"Couldn't load sessions\")}\n error={sessionsQ.error}\n />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title={tr('technospam.dashboard.noSessionsBookedTitle', 'No sessions booked')}\n description={tr(\n 'technospam.dashboard.noSessionsBookedDesc',\n 'Book time with a mentor to get unblocked — your upcoming sessions appear here.'\n )}\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n />\n ) : (\n <div>\n {upcomingSessions.slice(0, 5).map((s) => (\n <SessionRow key={s.id} s={s} />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Open hackathons */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.openHackathons', 'Open hackathons')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/hackathons/hackathons-page')}\n />\n {hackathonsQ.loading && !hackathons.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : hackathonsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={hackathonsQ.error}\n />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title={tr('technospam.dashboard.noOpenHackathonsTitle', 'No open hackathons')}\n description={tr(\n 'technospam.dashboard.noOpenHackathonsDesc',\n 'When registration opens for a hackathon, it will show up here to join.'\n )}\n />\n ) : (\n <div className=\"space-y-3\">\n {openHackathons.map((h) => (\n <HackathonBanner\n key={h.id}\n h={h}\n onOpen={() => navigate(`/hackathons/hackathon-details-page?id=${h.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Certificates summary */}\n {certificates.length > 0 ? (\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.common.latestCertificate', 'Latest certificate')}\n />\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-accent-primary/12 text-accent-teal\">\n <CheckCircle2 size={20} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {certificates[0]?.course?.title ??\n tr('technospam.common.courseFallback', 'Course')}\n </div>\n <div className=\"text-xs text-text-muted\">\n {tr('technospam.common.score', 'Score')} {certificates[0]?.score} ·{' '}\n {tr('technospam.common.issued', 'Issued')}{' '}\n {formatDate(certificates[0]?.issuedAt)}\n </div>\n </div>\n </div>\n </GlassCard>\n ) : null}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/technospam/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowRight,\n Award,\n BookOpen,\n Calendar,\n CalendarClock,\n CheckCircle2,\n Clock,\n Code2,\n GraduationCap,\n PlayCircle,\n Trophy,\n Users,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCoursesQuery,\n useHackathonsQuery,\n useMyCertificatesQuery,\n useMyEnrollmentsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecCourse,\n type TecEnrollment,\n type TecHackathon,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { useTr } from '../hooks/useTr';\nimport { resumePath } from '../utils/lessonPath';\n\n/* --------------------------------------------------------------- helpers */\n\nfunction SectionHeader({\n title,\n actionLabel,\n onAction,\n}: {\n title: string;\n actionLabel?: string;\n onAction?: () => void;\n}) {\n return (\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n {actionLabel && onAction ? (\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={onAction}>\n {actionLabel} <ArrowRight size={13} />\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction ProgressBar({ pct }: { pct: number }) {\n return (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${Math.max(0, Math.min(100, pct))}%` }}\n />\n </div>\n );\n}\n\nfunction ContinueLearningCard({\n e,\n resumeLessonId,\n onOpen,\n}: {\n e: TecEnrollment;\n /** The lesson this card resumes into, once known (BOFF-4107). */\n resumeLessonId?: string | null;\n onOpen: () => void;\n}) {\n const tr = useTr();\n const title = e.course?.title ?? tr('technospam.common.courseFallback', 'Course');\n const seed = `${e.course?.topic ?? ''} ${title}`;\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n data-resume-lesson={resumeLessonId ?? undefined}\n className=\"group flex w-[72%] shrink-0 snap-start flex-col overflow-hidden rounded-xl sm:w-auto sm:shrink border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={seed} icon={topicIcon(e.course?.topic ?? title)} height=\"h-20\">\n {e.course?.level ? <StatusBadge status={e.course.level} kind=\"level\" /> : <span />}\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-action-primary-text/15 text-action-primary-text backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <PlayCircle size={16} />\n </span>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-2.5 p-3.5\">\n <div className=\"flex items-start gap-2\">\n <h3 className=\"line-clamp-2 flex-1 text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {title}\n </h3>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-auto flex items-center gap-2.5\">\n <div className=\"flex-1\">\n <ProgressBar pct={e.progressPct} />\n </div>\n <span className=\"shrink-0 text-xs font-semibold text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n </div>\n </button>\n );\n}\n\nfunction RecommendedCourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n height=\"h-20\"\n >\n <StatusBadge status={course.level} kind=\"level\" />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-1.5 p-3.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{course.summary}</p>\n ) : null}\n {course.topic ? (\n <span className=\"mt-auto truncate pt-0.5 text-xs text-text-muted\">{course.topic}</span>\n ) : null}\n </div>\n </button>\n );\n}\n\nfunction SessionRow({ s }: { s: TecMentorSession }) {\n return (\n <div className=\"-mx-2 flex items-start gap-3 rounded-lg border-b border-border-seam px-2 py-3 transition-colors last:border-0 hover:bg-bg-sunken\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <CalendarClock size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{s.topic}</span>\n <StatusBadge status={s.status} />\n </div>\n {s.mentor?.headline ? (\n <div className=\"truncate text-xs text-text-muted\">{s.mentor.headline}</div>\n ) : null}\n <div className=\"mt-0.5 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock size={12} className=\"text-text-muted\" />\n {formatDateTime(s.scheduledAt)}\n </div>\n </div>\n </div>\n );\n}\n\nfunction HackathonBanner({ h, onOpen }: { h: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group block w-full overflow-hidden rounded-xl border border-border-seam text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={`${h.theme ?? ''} ${h.title}`} icon={Trophy} height=\"h-24\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-action-primary-text/80\">\n <Trophy size={12} /> {tr('technospam.common.openHackathon', 'Open hackathon')}\n </div>\n <h3 className=\"mt-0.5 truncate text-sm font-semibold text-action-primary-text drop-shadow-sm\">\n {h.title}\n </h3>\n </div>\n {h.prizePool > 0 ? (\n <span className=\"shrink-0 rounded-full bg-action-primary-text/20 px-2.5 py-1 text-xs font-semibold text-action-primary-text backdrop-blur\">\n {formatMoney(h.prizePool, h.currency)}\n </span>\n ) : null}\n </MediaCover>\n <div className=\"flex items-center justify-between gap-2 bg-bg-surface px-3.5 py-2.5\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Calendar size={12} />\n {formatDate(h.startAt)} – {formatDate(h.endAt)}\n </span>\n <span className=\"flex items-center gap-1 text-xs font-medium text-accent-primary opacity-0 transition-opacity group-hover:opacity-100\">\n {tr('technospam.common.register', 'Register')} <ArrowRight size={12} />\n </span>\n </div>\n </button>\n );\n}\n\n/* ------------------------------------------------------------------ page */\n\nexport function DashboardDashboardPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const tr = useTr();\n const learnerId = currentUser?.id;\n\n // BOFF-10481: the learner's own enrollments (each with its next lesson, for\n // the BOFF-4107 Continue Learning deep link) and certificates come from the\n // canonical, self-scoped Buildmyiq fields, not the cross-learner list-all\n // reads a plain learner is not granted.\n const enrollmentsQ = useMyEnrollmentsQuery();\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useMyCertificatesQuery();\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.enrollments;\n const submissions = submissionsQ.data?.tecListSubmissions?.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions?.items ?? [];\n const certificates = certificatesQ.certificates;\n const courses = coursesQ.data?.tecListCourses?.items ?? [];\n const hackathons = hackathonsQ.data?.tecListHackathons?.items ?? [];\n\n const greetingName =\n currentUser?.firstName || currentUser?.name || currentUser?.email?.split('@')[0] || 'there';\n\n const inProgress = useMemo(\n () => enrollments.filter((e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'),\n [enrollments]\n );\n\n const continueLearning = useMemo(\n () =>\n [...inProgress]\n .sort(\n (a, b) =>\n new Date(b.lastActiveAt ?? b.startedAt).getTime() -\n new Date(a.lastActiveAt ?? a.startedAt).getTime()\n )\n .slice(0, 3),\n [inProgress]\n );\n\n const problemsSolved = useMemo(\n () => new Set(submissions.filter((s) => s.verdict === 'ACCEPTED').map((s) => s.problemId)).size,\n [submissions]\n );\n\n // Stable \"now\" captured once on mount (state initializers may call impure fns).\n const [now] = useState(() => Date.now());\n const upcomingSessions = useMemo(() => {\n return [...sessions]\n .filter(\n (s) =>\n (s.status === 'CONFIRMED' || s.status === 'REQUESTED') &&\n new Date(s.scheduledAt).getTime() > now\n )\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime());\n }, [sessions, now]);\n\n const recommended = useMemo(() => {\n const enrolledIds = new Set(enrollments.map((e) => e.courseId));\n return courses.filter((c) => !enrolledIds.has(c.id)).slice(0, 4);\n }, [courses, enrollments]);\n\n const openHackathons = hackathons.slice(0, 4);\n\n return (\n <PageLayout\n hero\n mobileSection\n title={tr('technospam.dashboard.title', 'Welcome back, {{name}}', { name: greetingName })}\n description={tr(\n 'technospam.dashboard.description',\n \"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.dashboard.myLearning', 'My Learning')}\n </Button>\n }\n >\n {/* Top metrics */}\n <div className=\"mb-5 grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatCard\n label={tr('technospam.dashboard.coursesInProgress', 'Courses in progress')}\n value={inProgress.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.dashboard.problemsSolved', 'Problems solved')}\n value={problemsSolved}\n icon={Code2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.dashboard.upcomingSessions', 'Upcoming sessions')}\n value={upcomingSessions.length}\n icon={CalendarClock}\n tone=\"amber\"\n className=\"hidden sm:flex\"\n />\n <StatCard\n label={tr('technospam.dashboard.certificates', 'Certificates')}\n value={certificates.length}\n icon={Award}\n tone=\"purple\"\n className=\"hidden sm:flex\"\n />\n </div>\n\n {/* Quick actions — phones get the section tabs (bottom bar in the app,\n in-page switcher in the browser) and the hackathon card instead. */}\n <div className=\"mb-6 hidden grid-cols-2 gap-3 sm:grid sm:grid-cols-4\">\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/courses/courses-page')}\n >\n <BookOpen size={18} />\n <span className=\"text-xs\">{tr('technospam.common.browseCourses', 'Browse courses')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/applications/problems-page')}\n >\n <Code2 size={18} />\n <span className=\"text-xs\">\n {tr('technospam.common.practiceProblems', 'Practice problems')}\n </span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/mentorship/mentors-page')}\n >\n <Users size={18} />\n <span className=\"text-xs\">{tr('technospam.common.findMentor', 'Find a mentor')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/hackathons/hackathons-page')}\n >\n <Trophy size={18} />\n <span className=\"text-xs\">{tr('technospam.common.hackathons', 'Hackathons')}</span>\n </Button>\n </div>\n\n {/* Phones stack the cards in the mock's order (continue learning, the\n upcoming session and open hackathons, then recommendations): the two\n columns become `display: contents` below lg so `order` can interleave\n their cards; from lg up they are ordinary block columns again. */}\n <div className=\"flex flex-col gap-6 lg:grid lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"contents lg:col-span-2 lg:block lg:space-y-6\">\n {/* Continue learning */}\n <GlassCard className=\"order-1 p-4 sm:p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.continueLearning', 'Continue learning')}\n actionLabel={tr('technospam.dashboard.myLearning', 'My Learning')}\n onAction={() => navigate('/courses/my-learning-page')}\n />\n {enrollmentsQ.loading && !enrollments.length ? (\n <LoadingRows count={2} height=\"h-16\" />\n ) : enrollmentsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load your courses\")}\n error={enrollmentsQ.error}\n />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title={tr(\n 'technospam.dashboard.noCoursesInProgressTitle',\n 'No courses in progress'\n )}\n description={tr(\n 'technospam.dashboard.noCoursesInProgressDesc',\n 'Enroll in a course to start building skill by doing — your active courses show up here.'\n )}\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>\n {tr('technospam.common.browseCourses', 'Browse courses')}\n </Button>\n }\n />\n ) : (\n <div\n data-testid=\"continue-learning-list\"\n className=\"-mx-1 flex snap-x snap-mandatory gap-3 overflow-x-auto px-1 pb-1 sm:mx-0 sm:grid sm:snap-none sm:grid-cols-2 sm:gap-4 sm:overflow-visible sm:px-0 sm:pb-0 lg:grid-cols-3\"\n >\n {continueLearning.map((e) => (\n <ContinueLearningCard\n key={e.id}\n e={e}\n resumeLessonId={e.nextLessonId}\n onOpen={() => navigate(resumePath(e.courseId, e.nextLessonId))}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"order-3 p-4 sm:p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.recommendedForYou', 'Recommended for you')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/courses/courses-page')}\n />\n {coursesQ.loading && !courses.length ? (\n <LoadingRows count={2} height=\"h-20\" />\n ) : coursesQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={coursesQ.error}\n />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title={tr(\n 'technospam.dashboard.noRecommendationsTitle',\n 'No courses to recommend yet'\n )}\n description={tr(\n 'technospam.dashboard.noRecommendationsDesc',\n 'New courses are on the way. TechnoSpam is pre-launch — check back soon.'\n )}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {recommended.map((c) => (\n <RecommendedCourseCard\n key={c.id}\n course={c}\n onOpen={() => navigate(`/courses/course-details-page?id=${c.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Side column */}\n <div className=\"contents lg:block lg:space-y-6\">\n {/* Upcoming sessions */}\n <GlassCard className=\"order-2 p-4 sm:p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.upcomingSessionsTitle', 'Upcoming sessions')}\n actionLabel={tr('technospam.common.allSessions', 'All sessions')}\n onAction={() => navigate('/mentorship/sessions-page')}\n />\n {sessionsQ.loading && !sessions.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : sessionsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadSessions', \"Couldn't load sessions\")}\n error={sessionsQ.error}\n />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title={tr('technospam.dashboard.noSessionsBookedTitle', 'No sessions booked')}\n description={tr(\n 'technospam.dashboard.noSessionsBookedDesc',\n 'Book time with a mentor to get unblocked — your upcoming sessions appear here.'\n )}\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n />\n ) : (\n <div>\n {upcomingSessions.slice(0, 5).map((s) => (\n <SessionRow key={s.id} s={s} />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Open hackathons */}\n <GlassCard className=\"order-2 p-4 sm:p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.openHackathons', 'Open hackathons')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/hackathons/hackathons-page')}\n />\n {hackathonsQ.loading && !hackathons.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : hackathonsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={hackathonsQ.error}\n />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title={tr('technospam.dashboard.noOpenHackathonsTitle', 'No open hackathons')}\n description={tr(\n 'technospam.dashboard.noOpenHackathonsDesc',\n 'When registration opens for a hackathon, it will show up here to join.'\n )}\n />\n ) : (\n <div className=\"space-y-3\">\n {openHackathons.map((h) => (\n <HackathonBanner\n key={h.id}\n h={h}\n onOpen={() => navigate(`/hackathons/hackathon-details-page?id=${h.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Certificates summary */}\n {certificates.length > 0 ? (\n <GlassCard className=\"order-4 p-4 sm:p-5\">\n <SectionHeader\n title={tr('technospam.common.latestCertificate', 'Latest certificate')}\n />\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-accent-primary/12 text-accent-teal\">\n <CheckCircle2 size={20} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {certificates[0]?.course?.title ??\n tr('technospam.common.courseFallback', 'Course')}\n </div>\n <div className=\"text-xs text-text-muted\">\n {tr('technospam.common.score', 'Score')} {certificates[0]?.score} ·{' '}\n {tr('technospam.common.issued', 'Issued')}{' '}\n {formatDate(certificates[0]?.issuedAt)}\n </div>\n </div>\n </div>\n </GlassCard>\n ) : null}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ import { Badge as w, Button as T, GlassCard as E, Input as D, Select as ue, Sele
|
|
|
18
18
|
//#region src/technospam/pages/HackathonsHackathonDetailsPagePage.tsx
|
|
19
19
|
function k({ icon: e, label: t }) {
|
|
20
20
|
return /* @__PURE__ */ C("span", {
|
|
21
|
-
className: "inline-flex items-center gap-1.5 rounded-lg bg-
|
|
21
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-text/15 px-3 py-1.5 text-sm font-medium text-action-primary-text ring-1 ring-action-primary-text/25 backdrop-blur-sm",
|
|
22
22
|
children: [
|
|
23
23
|
/* @__PURE__ */ S(e, { size: 15 }),
|
|
24
24
|
" ",
|
|
@@ -37,7 +37,7 @@ function ge({ team: e, isMine: n, focused: r, onToggle: a }) {
|
|
|
37
37
|
children: [/* @__PURE__ */ C("div", {
|
|
38
38
|
className: "flex min-w-0 items-start gap-3",
|
|
39
39
|
children: [/* @__PURE__ */ S("span", {
|
|
40
|
-
className: t("flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-
|
|
40
|
+
className: t("flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-action-primary-text shadow-sm ring-1 ring-action-primary-text/10 transition-transform duration-200 group-hover:scale-105", i(e.name)),
|
|
41
41
|
children: S(o ? y : m, { size: 19 })
|
|
42
42
|
}), /* @__PURE__ */ C("div", {
|
|
43
43
|
className: "min-w-0",
|
|
@@ -139,7 +139,7 @@ function A() {
|
|
|
139
139
|
className: "flex flex-wrap items-center gap-2",
|
|
140
140
|
children: [/* @__PURE__ */ S(s, { status: Y.status }), Y.tags.map((e) => /* @__PURE__ */ S(w, {
|
|
141
141
|
variant: "secondary",
|
|
142
|
-
className: "border-
|
|
142
|
+
className: "border-action-primary-text/20 bg-action-primary-text/15 text-[11px] text-action-primary-text",
|
|
143
143
|
children: e
|
|
144
144
|
}, e))]
|
|
145
145
|
}), /* @__PURE__ */ C("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HackathonsHackathonDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/HackathonsHackathonDetailsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Award,\n CalendarClock,\n CalendarDays,\n Code2,\n ExternalLink,\n Flag,\n Github,\n Send,\n Trophy,\n UserPlus,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n MediaCover,\n PageLayout,\n StatusBadge,\n coverGradient,\n} from '../components';\nimport { cn } from '../utils/cn';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useHackathonQuery,\n useRegisterTeam,\n useSubmitProject,\n useTeamsQuery,\n type TecHackathonTeam,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\n\n/** Translucent glass chip used for stats overlaid on the vivid MediaCover hero. */\nfunction HeroChip({ icon: Icon, label }: { icon: LucideIcon; label: string }) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg bg-white/15 px-3 py-1.5 text-sm font-medium text-white ring-1 ring-white/25 backdrop-blur-sm\">\n <Icon size={15} /> {label}\n </span>\n );\n}\n\nfunction TeamCard({\n team,\n isMine,\n focused,\n onToggle,\n}: {\n team: TecHackathonTeam;\n isMine: boolean;\n /** The focused team (`?teamId=`) is the one the assistant acts on. */\n focused: boolean;\n onToggle: () => void;\n}) {\n const isWinner = team.status === 'WINNER';\n return (\n <GlassCard\n className={cn(\n 'group flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, onToggle)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <span\n className={cn(\n 'flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-white shadow-sm ring-1 ring-white/10 transition-transform duration-200 group-hover:scale-105',\n coverGradient(team.name)\n )}\n >\n {isWinner ? <Trophy size={19} /> : <Code2 size={19} />}\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={onToggle}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {team.name}\n </button>\n {isMine ? (\n <Badge variant=\"secondary\" className=\"text-[11px]\">\n Your team\n </Badge>\n ) : null}\n </div>\n {team.projectName ? (\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">{team.projectName}</p>\n ) : (\n <p className=\"mt-0.5 text-sm text-text-muted\">No project submitted yet</p>\n )}\n </div>\n </div>\n <StatusBadge status={team.status} />\n </div>\n\n {team.pitch ? <p className=\"line-clamp-3 text-sm text-text-secondary\">{team.pitch}</p> : null}\n\n <div className=\"mt-auto flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <Users size={13} /> {team.memberIds.length} member{team.memberIds.length === 1 ? '' : 's'}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Flag size={13} /> Score {team.score}\n </span>\n </div>\n\n {team.repoUrl || team.projectUrl ? (\n <div className=\"flex flex-wrap items-center gap-3 border-t border-border-seam pt-3\">\n {team.repoUrl ? (\n <a\n href={team.repoUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline\"\n >\n <Github size={13} /> Repository\n </a>\n ) : null}\n {team.projectUrl ? (\n <a\n href={team.projectUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline\"\n >\n <ExternalLink size={13} /> Live project\n </a>\n ) : null}\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nexport function HackathonsHackathonDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { currentUser } = useTechnoSpam();\n // The focused team lives in `?teamId=` so the assistant's page context names it.\n const [focusedTeamId, setFocusedTeamId] = useFocusedRecordParam(FOCUS_PARAMS.team);\n\n const { data, loading, error, refetch } = useHackathonQuery(id);\n const {\n data: teamsData,\n loading: teamsLoading,\n error: teamsError,\n refetch: refetchTeams,\n } = useTeamsQuery(id);\n\n const [registerTeam, { loading: registering }] = useRegisterTeam();\n const [submitProject, { loading: submitting }] = useSubmitProject();\n\n // register-team form state\n const [teamName, setTeamName] = useState('');\n const [memberIdsRaw, setMemberIdsRaw] = useState('');\n\n // submit-project form state\n const [projectTeamId, setProjectTeamId] = useState('');\n const [projectName, setProjectName] = useState('');\n const [projectUrl, setProjectUrl] = useState('');\n const [repoUrl, setRepoUrl] = useState('');\n const [pitch, setPitch] = useState('');\n\n const hackathon = data?.tecHackathon ?? null;\n const teams = teamsData?.tecListTeams?.items ?? [];\n const isRegistrationOpen = hackathon?.status === 'REGISTRATION_OPEN';\n const captainedTeams = currentUser?.id ? teams.filter((t) => t.captainId === currentUser.id) : [];\n const activeProjectTeamId = projectTeamId || captainedTeams[0]?.id || '';\n\n const handleRegister = async () => {\n if (!id || !teamName.trim()) return;\n const memberIds = memberIdsRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n const res = await registerTeam({\n variables: { input: { hackathonId: id, name: teamName.trim(), memberIds } },\n });\n if (res.data?.tecRegisterTeam) {\n notify.success('Team registered', `\"${teamName.trim()}\" is in the running.`);\n setTeamName('');\n setMemberIdsRaw('');\n await Promise.all([refetchTeams(), refetch()]);\n }\n };\n\n const handleSubmitProject = async () => {\n if (!activeProjectTeamId || !projectName.trim()) return;\n const res = await submitProject({\n variables: {\n input: {\n teamId: activeProjectTeamId,\n projectName: projectName.trim(),\n projectUrl: projectUrl.trim() || undefined,\n repoUrl: repoUrl.trim() || undefined,\n pitch: pitch.trim() || undefined,\n },\n },\n });\n if (res.data?.tecSubmitProject) {\n notify.success('Project submitted', `\"${projectName.trim()}\" is submitted for judging.`);\n setProjectName('');\n setProjectUrl('');\n setRepoUrl('');\n setPitch('');\n await Promise.all([refetchTeams(), refetch()]);\n }\n };\n\n if (loading && !hackathon) {\n return (\n <PageLayout title=\"Hackathon\" backPath=\"/hackathons/hackathons-page\">\n <Skeleton className=\"mb-4 h-32 w-full rounded-xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !hackathon) {\n return (\n <PageLayout title=\"Hackathon\" backPath=\"/hackathons/hackathons-page\">\n {error ? (\n <ErrorView title=\"Couldn't load hackathon\" error={error} />\n ) : (\n <EmptyView\n title=\"Hackathon not found\"\n description=\"This hackathon may no longer be listed.\"\n />\n )}\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n backPath=\"/hackathons/hackathons-page\"\n title={hackathon.title}\n description={hackathon.theme ?? undefined}\n >\n <MediaCover\n height=\"h-40\"\n seed={hackathon.theme ?? hackathon.id}\n icon={Trophy}\n className=\"mb-6 rounded-xl\"\n >\n <div className=\"flex w-full flex-col gap-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={hackathon.status} />\n {hackathon.tags.map((t) => (\n <Badge\n key={t}\n variant=\"secondary\"\n className=\"border-white/20 bg-white/15 text-[11px] text-white\"\n >\n {t}\n </Badge>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <HeroChip\n icon={Award}\n label={`${formatMoney(hackathon.prizePool, hackathon.currency)} prize pool`}\n />\n <HeroChip\n icon={CalendarDays}\n label={`${formatDate(hackathon.startAt)} – ${formatDate(hackathon.endAt)}`}\n />\n <HeroChip icon={Users} label={`Team of ${hackathon.maxTeamSize}`} />\n <HeroChip\n icon={Trophy}\n label={`${teams.length} team${teams.length === 1 ? '' : 's'} registered`}\n />\n </div>\n </div>\n </MediaCover>\n\n {hackathon.description ? (\n <GlassCard className=\"mb-6 p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">About</h2>\n <p className=\"whitespace-pre-line text-sm text-text-secondary\">{hackathon.description}</p>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"mb-6 p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Schedule</h2>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarDays size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Starts</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDate(hackathon.startAt)}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarDays size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Ends</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDate(hackathon.endAt)}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarClock size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Submission deadline</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDateTime(hackathon.submissionDeadline)}\n </div>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {isRegistrationOpen ? (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-3 flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-accent-primary/15 text-accent-primary ring-1 ring-accent-primary/20\">\n <UserPlus size={18} />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">Register a team</h2>\n </div>\n <p className=\"mb-4 text-sm text-text-secondary\">\n You'll be the team captain. Add teammates by their user id (optional) — teams can\n be up to {hackathon.maxTeamSize} members.\n </p>\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-secondary\">\n Team name\n </label>\n <Input\n placeholder=\"e.g. Byte Builders\"\n value={teamName}\n onChange={(e) => setTeamName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Teammate user ids{' '}\n <span className=\"text-text-muted\">(optional, comma-separated)</span>\n </label>\n <Input\n placeholder=\"user-id-1, user-id-2\"\n value={memberIdsRaw}\n onChange={(e) => setMemberIdsRaw(e.target.value)}\n />\n </div>\n </div>\n <div className=\"mt-4 flex justify-end\">\n <Button onClick={handleRegister} disabled={registering || !teamName.trim()}>\n <UserPlus size={16} /> {registering ? 'Registering…' : 'Register team'}\n </Button>\n </div>\n </GlassCard>\n ) : null}\n\n {captainedTeams.length > 0 ? (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-3 flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <Send size={18} />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">Submit your project</h2>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {captainedTeams.length > 1 ? (\n <div className=\"sm:col-span-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">Team</label>\n <Select value={activeProjectTeamId} onValueChange={setProjectTeamId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a team\" />\n </SelectTrigger>\n <SelectContent>\n {captainedTeams.map((t) => (\n <SelectItem key={t.id} value={t.id}>\n {t.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : (\n <div className=\"sm:col-span-2 text-sm text-text-secondary\">\n Submitting for{' '}\n <span className=\"font-medium text-text-primary\">{captainedTeams[0]?.name}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Project name\n </label>\n <Input\n placeholder=\"e.g. RealtimeCanvas\"\n value={projectName}\n onChange={(e) => setProjectName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Live project URL <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Input\n placeholder=\"https://…\"\n value={projectUrl}\n onChange={(e) => setProjectUrl(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Repository URL <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Input\n placeholder=\"https://github.com/…\"\n value={repoUrl}\n onChange={(e) => setRepoUrl(e.target.value)}\n />\n </div>\n <div className=\"sm:col-span-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Pitch <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Textarea\n rows={3}\n placeholder=\"What did you build, and why does it matter?\"\n value={pitch}\n onChange={(e) => setPitch(e.target.value)}\n />\n </div>\n </div>\n <div className=\"mt-4 flex justify-end\">\n <Button\n onClick={handleSubmitProject}\n disabled={submitting || !activeProjectTeamId || !projectName.trim()}\n >\n <Send size={16} /> {submitting ? 'Submitting…' : 'Submit project'}\n </Button>\n </div>\n </GlassCard>\n ) : null}\n\n <div>\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">\n Teams {teams.length ? <span className=\"text-text-muted\">({teams.length})</span> : null}\n </h2>\n {teamsLoading && !teams.length ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <Skeleton className=\"h-40 w-full rounded-xl\" />\n <Skeleton className=\"h-40 w-full rounded-xl\" />\n </div>\n ) : teamsError ? (\n <ErrorView title=\"Couldn't load teams\" error={teamsError} />\n ) : teams.length === 0 ? (\n <EmptyView\n icon={<Users size={26} />}\n title=\"No teams registered yet\"\n description={\n isRegistrationOpen\n ? 'Be the first to register a team above.'\n : 'Teams will appear here once registration opens.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {teams.map((team) => (\n <TeamCard\n key={team.id}\n team={team}\n isMine={!!currentUser?.id && team.captainId === currentUser.id}\n focused={focusedTeamId === team.id}\n onToggle={() => setFocusedTeamId(focusedTeamId === team.id ? null : team.id)}\n />\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HackathonsHackathonDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/HackathonsHackathonDetailsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Award,\n CalendarClock,\n CalendarDays,\n Code2,\n ExternalLink,\n Flag,\n Github,\n Send,\n Trophy,\n UserPlus,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n MediaCover,\n PageLayout,\n StatusBadge,\n coverGradient,\n} from '../components';\nimport { cn } from '../utils/cn';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useHackathonQuery,\n useRegisterTeam,\n useSubmitProject,\n useTeamsQuery,\n type TecHackathonTeam,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\n\n/** Translucent glass chip used for stats overlaid on the vivid MediaCover hero. */\nfunction HeroChip({ icon: Icon, label }: { icon: LucideIcon; label: string }) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-text/15 px-3 py-1.5 text-sm font-medium text-action-primary-text ring-1 ring-action-primary-text/25 backdrop-blur-sm\">\n <Icon size={15} /> {label}\n </span>\n );\n}\n\nfunction TeamCard({\n team,\n isMine,\n focused,\n onToggle,\n}: {\n team: TecHackathonTeam;\n isMine: boolean;\n /** The focused team (`?teamId=`) is the one the assistant acts on. */\n focused: boolean;\n onToggle: () => void;\n}) {\n const isWinner = team.status === 'WINNER';\n return (\n <GlassCard\n className={cn(\n 'group flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, onToggle)}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <span\n className={cn(\n 'flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-action-primary-text shadow-sm ring-1 ring-action-primary-text/10 transition-transform duration-200 group-hover:scale-105',\n coverGradient(team.name)\n )}\n >\n {isWinner ? <Trophy size={19} /> : <Code2 size={19} />}\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={onToggle}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {team.name}\n </button>\n {isMine ? (\n <Badge variant=\"secondary\" className=\"text-[11px]\">\n Your team\n </Badge>\n ) : null}\n </div>\n {team.projectName ? (\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">{team.projectName}</p>\n ) : (\n <p className=\"mt-0.5 text-sm text-text-muted\">No project submitted yet</p>\n )}\n </div>\n </div>\n <StatusBadge status={team.status} />\n </div>\n\n {team.pitch ? <p className=\"line-clamp-3 text-sm text-text-secondary\">{team.pitch}</p> : null}\n\n <div className=\"mt-auto flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <Users size={13} /> {team.memberIds.length} member{team.memberIds.length === 1 ? '' : 's'}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Flag size={13} /> Score {team.score}\n </span>\n </div>\n\n {team.repoUrl || team.projectUrl ? (\n <div className=\"flex flex-wrap items-center gap-3 border-t border-border-seam pt-3\">\n {team.repoUrl ? (\n <a\n href={team.repoUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline\"\n >\n <Github size={13} /> Repository\n </a>\n ) : null}\n {team.projectUrl ? (\n <a\n href={team.projectUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline\"\n >\n <ExternalLink size={13} /> Live project\n </a>\n ) : null}\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nexport function HackathonsHackathonDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { currentUser } = useTechnoSpam();\n // The focused team lives in `?teamId=` so the assistant's page context names it.\n const [focusedTeamId, setFocusedTeamId] = useFocusedRecordParam(FOCUS_PARAMS.team);\n\n const { data, loading, error, refetch } = useHackathonQuery(id);\n const {\n data: teamsData,\n loading: teamsLoading,\n error: teamsError,\n refetch: refetchTeams,\n } = useTeamsQuery(id);\n\n const [registerTeam, { loading: registering }] = useRegisterTeam();\n const [submitProject, { loading: submitting }] = useSubmitProject();\n\n // register-team form state\n const [teamName, setTeamName] = useState('');\n const [memberIdsRaw, setMemberIdsRaw] = useState('');\n\n // submit-project form state\n const [projectTeamId, setProjectTeamId] = useState('');\n const [projectName, setProjectName] = useState('');\n const [projectUrl, setProjectUrl] = useState('');\n const [repoUrl, setRepoUrl] = useState('');\n const [pitch, setPitch] = useState('');\n\n const hackathon = data?.tecHackathon ?? null;\n const teams = teamsData?.tecListTeams?.items ?? [];\n const isRegistrationOpen = hackathon?.status === 'REGISTRATION_OPEN';\n const captainedTeams = currentUser?.id ? teams.filter((t) => t.captainId === currentUser.id) : [];\n const activeProjectTeamId = projectTeamId || captainedTeams[0]?.id || '';\n\n const handleRegister = async () => {\n if (!id || !teamName.trim()) return;\n const memberIds = memberIdsRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n const res = await registerTeam({\n variables: { input: { hackathonId: id, name: teamName.trim(), memberIds } },\n });\n if (res.data?.tecRegisterTeam) {\n notify.success('Team registered', `\"${teamName.trim()}\" is in the running.`);\n setTeamName('');\n setMemberIdsRaw('');\n await Promise.all([refetchTeams(), refetch()]);\n }\n };\n\n const handleSubmitProject = async () => {\n if (!activeProjectTeamId || !projectName.trim()) return;\n const res = await submitProject({\n variables: {\n input: {\n teamId: activeProjectTeamId,\n projectName: projectName.trim(),\n projectUrl: projectUrl.trim() || undefined,\n repoUrl: repoUrl.trim() || undefined,\n pitch: pitch.trim() || undefined,\n },\n },\n });\n if (res.data?.tecSubmitProject) {\n notify.success('Project submitted', `\"${projectName.trim()}\" is submitted for judging.`);\n setProjectName('');\n setProjectUrl('');\n setRepoUrl('');\n setPitch('');\n await Promise.all([refetchTeams(), refetch()]);\n }\n };\n\n if (loading && !hackathon) {\n return (\n <PageLayout title=\"Hackathon\" backPath=\"/hackathons/hackathons-page\">\n <Skeleton className=\"mb-4 h-32 w-full rounded-xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !hackathon) {\n return (\n <PageLayout title=\"Hackathon\" backPath=\"/hackathons/hackathons-page\">\n {error ? (\n <ErrorView title=\"Couldn't load hackathon\" error={error} />\n ) : (\n <EmptyView\n title=\"Hackathon not found\"\n description=\"This hackathon may no longer be listed.\"\n />\n )}\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n backPath=\"/hackathons/hackathons-page\"\n title={hackathon.title}\n description={hackathon.theme ?? undefined}\n >\n <MediaCover\n height=\"h-40\"\n seed={hackathon.theme ?? hackathon.id}\n icon={Trophy}\n className=\"mb-6 rounded-xl\"\n >\n <div className=\"flex w-full flex-col gap-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={hackathon.status} />\n {hackathon.tags.map((t) => (\n <Badge\n key={t}\n variant=\"secondary\"\n className=\"border-action-primary-text/20 bg-action-primary-text/15 text-[11px] text-action-primary-text\"\n >\n {t}\n </Badge>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <HeroChip\n icon={Award}\n label={`${formatMoney(hackathon.prizePool, hackathon.currency)} prize pool`}\n />\n <HeroChip\n icon={CalendarDays}\n label={`${formatDate(hackathon.startAt)} – ${formatDate(hackathon.endAt)}`}\n />\n <HeroChip icon={Users} label={`Team of ${hackathon.maxTeamSize}`} />\n <HeroChip\n icon={Trophy}\n label={`${teams.length} team${teams.length === 1 ? '' : 's'} registered`}\n />\n </div>\n </div>\n </MediaCover>\n\n {hackathon.description ? (\n <GlassCard className=\"mb-6 p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">About</h2>\n <p className=\"whitespace-pre-line text-sm text-text-secondary\">{hackathon.description}</p>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"mb-6 p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Schedule</h2>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarDays size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Starts</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDate(hackathon.startAt)}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarDays size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Ends</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDate(hackathon.endAt)}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <CalendarClock size={16} className=\"text-accent-teal\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs text-text-secondary\">Submission deadline</div>\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {formatDateTime(hackathon.submissionDeadline)}\n </div>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {isRegistrationOpen ? (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-3 flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-accent-primary/15 text-accent-primary ring-1 ring-accent-primary/20\">\n <UserPlus size={18} />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">Register a team</h2>\n </div>\n <p className=\"mb-4 text-sm text-text-secondary\">\n You'll be the team captain. Add teammates by their user id (optional) — teams can\n be up to {hackathon.maxTeamSize} members.\n </p>\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-secondary\">\n Team name\n </label>\n <Input\n placeholder=\"e.g. Byte Builders\"\n value={teamName}\n onChange={(e) => setTeamName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Teammate user ids{' '}\n <span className=\"text-text-muted\">(optional, comma-separated)</span>\n </label>\n <Input\n placeholder=\"user-id-1, user-id-2\"\n value={memberIdsRaw}\n onChange={(e) => setMemberIdsRaw(e.target.value)}\n />\n </div>\n </div>\n <div className=\"mt-4 flex justify-end\">\n <Button onClick={handleRegister} disabled={registering || !teamName.trim()}>\n <UserPlus size={16} /> {registering ? 'Registering…' : 'Register team'}\n </Button>\n </div>\n </GlassCard>\n ) : null}\n\n {captainedTeams.length > 0 ? (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-3 flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <Send size={18} />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">Submit your project</h2>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {captainedTeams.length > 1 ? (\n <div className=\"sm:col-span-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">Team</label>\n <Select value={activeProjectTeamId} onValueChange={setProjectTeamId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a team\" />\n </SelectTrigger>\n <SelectContent>\n {captainedTeams.map((t) => (\n <SelectItem key={t.id} value={t.id}>\n {t.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : (\n <div className=\"sm:col-span-2 text-sm text-text-secondary\">\n Submitting for{' '}\n <span className=\"font-medium text-text-primary\">{captainedTeams[0]?.name}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Project name\n </label>\n <Input\n placeholder=\"e.g. RealtimeCanvas\"\n value={projectName}\n onChange={(e) => setProjectName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Live project URL <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Input\n placeholder=\"https://…\"\n value={projectUrl}\n onChange={(e) => setProjectUrl(e.target.value)}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Repository URL <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Input\n placeholder=\"https://github.com/…\"\n value={repoUrl}\n onChange={(e) => setRepoUrl(e.target.value)}\n />\n </div>\n <div className=\"sm:col-span-2\">\n <label className=\"mb-1.5 block text-sm font-medium text-text-secondary\">\n Pitch <span className=\"text-text-muted\">(optional)</span>\n </label>\n <Textarea\n rows={3}\n placeholder=\"What did you build, and why does it matter?\"\n value={pitch}\n onChange={(e) => setPitch(e.target.value)}\n />\n </div>\n </div>\n <div className=\"mt-4 flex justify-end\">\n <Button\n onClick={handleSubmitProject}\n disabled={submitting || !activeProjectTeamId || !projectName.trim()}\n >\n <Send size={16} /> {submitting ? 'Submitting…' : 'Submit project'}\n </Button>\n </div>\n </GlassCard>\n ) : null}\n\n <div>\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">\n Teams {teams.length ? <span className=\"text-text-muted\">({teams.length})</span> : null}\n </h2>\n {teamsLoading && !teams.length ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <Skeleton className=\"h-40 w-full rounded-xl\" />\n <Skeleton className=\"h-40 w-full rounded-xl\" />\n </div>\n ) : teamsError ? (\n <ErrorView title=\"Couldn't load teams\" error={teamsError} />\n ) : teams.length === 0 ? (\n <EmptyView\n icon={<Users size={26} />}\n title=\"No teams registered yet\"\n description={\n isRegistrationOpen\n ? 'Be the first to register a team above.'\n : 'Teams will appear here once registration opens.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {teams.map((team) => (\n <TeamCard\n key={team.id}\n team={team}\n isMine={!!currentUser?.id && team.captainId === currentUser.id}\n focused={focusedTeamId === team.id}\n onToggle={() => setFocusedTeamId(focusedTeamId === team.id ? null : team.id)}\n />\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ function I({ mentorId: e, disabled: n, onBooked: r, workspaceId: i }) {
|
|
|
24
24
|
/* @__PURE__ */ k("div", {
|
|
25
25
|
className: "mb-4 flex items-start gap-3",
|
|
26
26
|
children: [/* @__PURE__ */ O("span", {
|
|
27
|
-
className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-accent-primary to-accent-purple text-
|
|
27
|
+
className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-accent-primary to-accent-purple text-action-primary-text shadow-sm",
|
|
28
28
|
children: /* @__PURE__ */ O(b, { size: 20 })
|
|
29
29
|
}), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("h2", {
|
|
30
30
|
className: "text-base font-semibold text-text-primary",
|
|
@@ -224,10 +224,10 @@ function R() {
|
|
|
224
224
|
children: [/* @__PURE__ */ k("div", {
|
|
225
225
|
className: n("relative h-24 overflow-hidden bg-gradient-to-br", a(x.headline)),
|
|
226
226
|
children: [_(D, {
|
|
227
|
-
className: "pointer-events-none absolute -right-3 -top-4 text-
|
|
227
|
+
className: "pointer-events-none absolute -right-3 -top-4 text-action-primary-text/15",
|
|
228
228
|
size: 120,
|
|
229
229
|
strokeWidth: 1.3
|
|
230
|
-
}), /* @__PURE__ */ O("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-
|
|
230
|
+
}), /* @__PURE__ */ O("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-bg-overlay/60 to-transparent" })]
|
|
231
231
|
}), /* @__PURE__ */ k("div", {
|
|
232
232
|
className: "px-6 pb-6",
|
|
233
233
|
children: [
|
|
@@ -236,7 +236,7 @@ function R() {
|
|
|
236
236
|
children: [/* @__PURE__ */ k("div", {
|
|
237
237
|
className: "relative shrink-0",
|
|
238
238
|
children: [/* @__PURE__ */ O("span", {
|
|
239
|
-
className: n("flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br text-
|
|
239
|
+
className: n("flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br text-action-primary-text shadow-lg ring-4 ring-bg-surface", a(x.headline)),
|
|
240
240
|
children: _(D, {
|
|
241
241
|
size: 34,
|
|
242
242
|
strokeWidth: 1.6
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipMentorProfilePagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipMentorProfilePagePage.tsx"],"sourcesContent":["import { createElement, useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Award,\n CalendarClock,\n CreditCard,\n DollarSign,\n MessageSquare,\n Star,\n Video,\n} from 'lucide-react';\nimport { Badge, Button, GlassCard, Input, Skeleton, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n PageLayout,\n StatCard,\n StatusBadge,\n coverGradient,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useBookSession,\n useMentorQuery,\n useSessionCostEstimate,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration, formatMoneyFromMinorUnits } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\n\nfunction BookSessionForm({\n mentorId,\n disabled,\n onBooked,\n workspaceId,\n}: {\n mentorId: string;\n disabled: boolean;\n onBooked?: () => void;\n workspaceId?: string;\n}) {\n const [topic, setTopic] = useState('');\n const [agenda, setAgenda] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [durationMinutes, setDurationMinutes] = useState('60');\n const [bookSession, { loading }] = useBookSession();\n const { emit } = useTechnoSpamAnalytics();\n\n const parsedDate = scheduledAt ? new Date(scheduledAt) : null;\n const validDate = parsedDate != null && !Number.isNaN(parsedDate.getTime());\n const canSubmit = topic.trim().length > 0 && validDate && !loading && !disabled;\n\n // Live cost preview -- computed server-side from the mentor's real rate,\n // the exact same calculation tecBookSession snapshots onto the session.\n // Shown BEFORE booking so the mentee sees the real price up front.\n const parsedDuration = Number.parseInt(durationMinutes, 10);\n const { data: costData, loading: costLoading } = useSessionCostEstimate(\n mentorId,\n Number.isNaN(parsedDuration) ? 0 : parsedDuration\n );\n const estimate = costData?.tecCalculateSessionCost;\n\n const handleSubmit = async () => {\n if (!canSubmit || !parsedDate) return;\n const input: {\n mentorId: string;\n topic: string;\n agenda?: string;\n scheduledAt: string;\n durationMinutes?: number;\n } = {\n mentorId,\n topic: topic.trim(),\n scheduledAt: parsedDate.toISOString(),\n };\n if (agenda.trim()) input.agenda = agenda.trim();\n const dur = Number.parseInt(durationMinutes, 10);\n if (!Number.isNaN(dur) && dur > 0) input.durationMinutes = dur;\n\n emit('technospam.mentor.book_clicked', {\n workspaceId,\n entityId: mentorId,\n entityType: 'TEC_MENTOR',\n });\n const res = await bookSession({ variables: { input } });\n if (res.data?.tecBookSession) {\n emit('technospam.mentor.booked', {\n workspaceId,\n entityId: mentorId,\n entityType: 'TEC_MENTOR',\n sessionId: res.data.tecBookSession.id,\n });\n notify.success('Session requested', 'Your mentor will confirm the booking soon.');\n setTopic('');\n setAgenda('');\n setScheduledAt('');\n setDurationMinutes('60');\n onBooked?.();\n }\n };\n\n return (\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-start 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-purple text-white shadow-sm\">\n <CalendarClock size={20} />\n </span>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Book a session</h2>\n <p className=\"text-sm text-text-secondary\">Schedule 1:1 time with your mentor.</p>\n </div>\n </div>\n\n {disabled ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n This mentor isn't accepting new mentees right now.\n </p>\n ) : null}\n\n <div className=\"mt-4 grid grid-cols-1 gap-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Topic <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n placeholder=\"e.g. System design interview prep\"\n value={topic}\n onChange={(e) => setTopic(e.target.value)}\n disabled={disabled}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Agenda</label>\n <Textarea\n rows={3}\n placeholder=\"What would you like to cover? Share context so your mentor can prepare.\"\n value={agenda}\n onChange={(e) => setAgenda(e.target.value)}\n disabled={disabled}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label\n htmlFor=\"technospam-booking-when\"\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n When <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n id=\"technospam-booking-when\"\n required\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n disabled={disabled}\n />\n </div>\n <div>\n <label\n htmlFor=\"technospam-booking-duration\"\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n Duration (min)\n </label>\n <Input\n id=\"technospam-booking-duration\"\n type=\"number\"\n min={15}\n step={15}\n value={durationMinutes}\n onChange={(e) => setDurationMinutes(e.target.value)}\n disabled={disabled}\n />\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken px-3 py-2.5\">\n {costLoading && !estimate ? (\n <Skeleton className=\"h-5 w-40 rounded\" />\n ) : estimate ? (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Session cost</span>\n <span className=\"font-semibold text-text-primary\">\n {formatMoneyFromMinorUnits(estimate.costAmount, estimate.currency)}\n </span>\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Includes a {(estimate.platformFeeBps / 100).toFixed(0)}% platform fee (\n {formatMoneyFromMinorUnits(estimate.platformFeeAmount, estimate.currency)}) — the\n mentor keeps {formatMoneyFromMinorUnits(estimate.netAmount, estimate.currency)}.\n </p>\n </>\n ) : (\n <p className=\"text-sm text-text-muted\">Enter a duration to preview the session cost.</p>\n )}\n </div>\n\n <Button\n className=\"w-full bg-gradient-to-r from-accent-primary to-accent-purple\"\n onClick={handleSubmit}\n disabled={!canSubmit}\n >\n <CalendarClock size={16} /> {loading ? 'Requesting…' : 'Request session'}\n </Button>\n\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-seam px-3 py-2\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <CreditCard size={14} /> Payment integration pending\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled\n title=\"Online payment collection is not live yet\"\n >\n Pay now\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nfunction SessionSummaryRow({ session }: { session: TecMentorSession }) {\n return (\n <div className=\"flex items-center gap-3 rounded-lg px-1 py-3 transition-colors hover:bg-bg-sunken\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-purple/12 text-accent-purple\">\n <Video size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{session.topic}</div>\n <div className=\"text-xs text-text-muted\">\n {formatDateTime(session.scheduledAt)} · {formatDuration(session.durationMinutes)}\n </div>\n </div>\n <StatusBadge status={session.status} />\n </div>\n );\n}\n\nexport function MentorshipMentorProfilePagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { workspaceId } = useTechnoSpam();\n const { data, loading, error, refetch } = useMentorQuery(id);\n\n const mentor = data?.tecMentor ?? null;\n\n if (loading && !mentor) {\n return (\n <PageLayout title=\"Mentor\" backPath=\"/mentorship/mentors-page\">\n <Skeleton className=\"mb-4 h-40 w-full rounded-xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !mentor) {\n return (\n <PageLayout title=\"Mentor\" backPath=\"/mentorship/mentors-page\">\n {error ? (\n <ErrorView title=\"Couldn't load mentor\" error={error} />\n ) : (\n <EmptyView\n title=\"Mentor not found\"\n description=\"This mentor profile may no longer be available.\"\n />\n )}\n </PageLayout>\n );\n }\n\n const sessions = mentor.sessions ?? [];\n const recentSessions = [...sessions]\n .sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime())\n .slice(0, 5);\n\n const HeroIcon = topicIcon(mentor.expertise.join(' ') || mentor.headline);\n\n return (\n <PageLayout hero backPath=\"/mentorship/mentors-page\" title=\"Mentor Profile\">\n <GlassCard className=\"mb-6 overflow-hidden p-0\">\n <div\n className={cn(\n 'relative h-24 overflow-hidden bg-gradient-to-br',\n coverGradient(mentor.headline)\n )}\n >\n {createElement(HeroIcon, {\n className: 'pointer-events-none absolute -right-3 -top-4 text-white/15',\n size: 120,\n strokeWidth: 1.3,\n })}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-black/25 to-transparent\" />\n </div>\n <div className=\"px-6 pb-6\">\n <div className=\"-mt-10 flex flex-col gap-4 sm:flex-row sm:items-end\">\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n 'flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br text-white shadow-lg ring-4 ring-bg-surface',\n coverGradient(mentor.headline)\n )}\n >\n {createElement(HeroIcon, { size: 34, strokeWidth: 1.6 })}\n </span>\n {mentor.isAcceptingMentees ? (\n <span className=\"absolute bottom-1 right-1 h-4 w-4 rounded-full border-2 border-bg-surface bg-status-success-text\" />\n ) : null}\n </div>\n <div className=\"min-w-0 flex-1 sm:pb-1\">\n <h1 className=\"text-xl font-semibold text-text-primary sm:text-2xl\">\n {mentor.headline}\n </h1>\n {mentor.role ? (\n <p className=\"mt-1 text-sm font-medium text-text-secondary\">{mentor.role}</p>\n ) : null}\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusBadge status={mentor.status} />\n {mentor.isAcceptingMentees ? (\n <Badge\n variant=\"outline\"\n className=\"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\" /> Accepting\n mentees\n </Badge>\n ) : null}\n </div>\n </div>\n </div>\n\n {mentor.bio ? (\n <p className=\"mt-4 max-w-2xl text-sm leading-relaxed text-text-secondary\">\n {mentor.bio}\n </p>\n ) : null}\n\n {mentor.expertise.length ? (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {mentor.expertise.map((skill) => (\n <Badge key={skill} variant=\"secondary\">\n {skill}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label=\"Rate\"\n value={`${formatMoneyFromMinorUnits(mentor.hourlyRate, mentor.currency)}/hr`}\n icon={DollarSign}\n tone=\"blue\"\n />\n <StatCard\n label=\"Rating\"\n value={mentor.ratingAvg > 0 ? mentor.ratingAvg.toFixed(1) : 'New'}\n icon={Star}\n tone=\"amber\"\n />\n <StatCard\n label=\"Sessions\"\n value={mentor.sessionsCount}\n icon={MessageSquare}\n tone=\"purple\"\n />\n <StatCard\n label=\"Status\"\n value={mentor.status === 'ACTIVE' ? 'Active' : 'Pending'}\n icon={Award}\n tone=\"teal\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <BookSessionForm\n mentorId={mentor.id}\n disabled={!mentor.isAcceptingMentees}\n onBooked={() => void refetch()}\n workspaceId={workspaceId}\n />\n\n <GlassCard className=\"p-5\">\n <div className=\"mb-3 flex items-center gap-2\">\n <CalendarClock size={18} className=\"text-accent-teal\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Recent sessions</h2>\n </div>\n {recentSessions.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-muted\">\n No sessions booked yet. Be the first to request time with this mentor.\n </p>\n ) : (\n <div className=\"divide-y divide-border-seam\">\n {recentSessions.map((session) => (\n <SessionSummaryRow key={session.id} session={session} />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MentorshipMentorProfilePagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipMentorProfilePagePage.tsx"],"sourcesContent":["import { createElement, useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Award,\n CalendarClock,\n CreditCard,\n DollarSign,\n MessageSquare,\n Star,\n Video,\n} from 'lucide-react';\nimport { Badge, Button, GlassCard, Input, Skeleton, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n PageLayout,\n StatCard,\n StatusBadge,\n coverGradient,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useBookSession,\n useMentorQuery,\n useSessionCostEstimate,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration, formatMoneyFromMinorUnits } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\n\nfunction BookSessionForm({\n mentorId,\n disabled,\n onBooked,\n workspaceId,\n}: {\n mentorId: string;\n disabled: boolean;\n onBooked?: () => void;\n workspaceId?: string;\n}) {\n const [topic, setTopic] = useState('');\n const [agenda, setAgenda] = useState('');\n const [scheduledAt, setScheduledAt] = useState('');\n const [durationMinutes, setDurationMinutes] = useState('60');\n const [bookSession, { loading }] = useBookSession();\n const { emit } = useTechnoSpamAnalytics();\n\n const parsedDate = scheduledAt ? new Date(scheduledAt) : null;\n const validDate = parsedDate != null && !Number.isNaN(parsedDate.getTime());\n const canSubmit = topic.trim().length > 0 && validDate && !loading && !disabled;\n\n // Live cost preview -- computed server-side from the mentor's real rate,\n // the exact same calculation tecBookSession snapshots onto the session.\n // Shown BEFORE booking so the mentee sees the real price up front.\n const parsedDuration = Number.parseInt(durationMinutes, 10);\n const { data: costData, loading: costLoading } = useSessionCostEstimate(\n mentorId,\n Number.isNaN(parsedDuration) ? 0 : parsedDuration\n );\n const estimate = costData?.tecCalculateSessionCost;\n\n const handleSubmit = async () => {\n if (!canSubmit || !parsedDate) return;\n const input: {\n mentorId: string;\n topic: string;\n agenda?: string;\n scheduledAt: string;\n durationMinutes?: number;\n } = {\n mentorId,\n topic: topic.trim(),\n scheduledAt: parsedDate.toISOString(),\n };\n if (agenda.trim()) input.agenda = agenda.trim();\n const dur = Number.parseInt(durationMinutes, 10);\n if (!Number.isNaN(dur) && dur > 0) input.durationMinutes = dur;\n\n emit('technospam.mentor.book_clicked', {\n workspaceId,\n entityId: mentorId,\n entityType: 'TEC_MENTOR',\n });\n const res = await bookSession({ variables: { input } });\n if (res.data?.tecBookSession) {\n emit('technospam.mentor.booked', {\n workspaceId,\n entityId: mentorId,\n entityType: 'TEC_MENTOR',\n sessionId: res.data.tecBookSession.id,\n });\n notify.success('Session requested', 'Your mentor will confirm the booking soon.');\n setTopic('');\n setAgenda('');\n setScheduledAt('');\n setDurationMinutes('60');\n onBooked?.();\n }\n };\n\n return (\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-start 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-purple text-action-primary-text shadow-sm\">\n <CalendarClock size={20} />\n </span>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Book a session</h2>\n <p className=\"text-sm text-text-secondary\">Schedule 1:1 time with your mentor.</p>\n </div>\n </div>\n\n {disabled ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n This mentor isn't accepting new mentees right now.\n </p>\n ) : null}\n\n <div className=\"mt-4 grid grid-cols-1 gap-4\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Topic <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n placeholder=\"e.g. System design interview prep\"\n value={topic}\n onChange={(e) => setTopic(e.target.value)}\n disabled={disabled}\n />\n </div>\n\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Agenda</label>\n <Textarea\n rows={3}\n placeholder=\"What would you like to cover? Share context so your mentor can prepare.\"\n value={agenda}\n onChange={(e) => setAgenda(e.target.value)}\n disabled={disabled}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label\n htmlFor=\"technospam-booking-when\"\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n When <span className=\"text-status-error-text\">*</span>\n </label>\n <Input\n id=\"technospam-booking-when\"\n required\n type=\"datetime-local\"\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n disabled={disabled}\n />\n </div>\n <div>\n <label\n htmlFor=\"technospam-booking-duration\"\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n Duration (min)\n </label>\n <Input\n id=\"technospam-booking-duration\"\n type=\"number\"\n min={15}\n step={15}\n value={durationMinutes}\n onChange={(e) => setDurationMinutes(e.target.value)}\n disabled={disabled}\n />\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken px-3 py-2.5\">\n {costLoading && !estimate ? (\n <Skeleton className=\"h-5 w-40 rounded\" />\n ) : estimate ? (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Session cost</span>\n <span className=\"font-semibold text-text-primary\">\n {formatMoneyFromMinorUnits(estimate.costAmount, estimate.currency)}\n </span>\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Includes a {(estimate.platformFeeBps / 100).toFixed(0)}% platform fee (\n {formatMoneyFromMinorUnits(estimate.platformFeeAmount, estimate.currency)}) — the\n mentor keeps {formatMoneyFromMinorUnits(estimate.netAmount, estimate.currency)}.\n </p>\n </>\n ) : (\n <p className=\"text-sm text-text-muted\">Enter a duration to preview the session cost.</p>\n )}\n </div>\n\n <Button\n className=\"w-full bg-gradient-to-r from-accent-primary to-accent-purple\"\n onClick={handleSubmit}\n disabled={!canSubmit}\n >\n <CalendarClock size={16} /> {loading ? 'Requesting…' : 'Request session'}\n </Button>\n\n <div className=\"flex items-center justify-between gap-2 rounded-lg border border-border-seam px-3 py-2\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <CreditCard size={14} /> Payment integration pending\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled\n title=\"Online payment collection is not live yet\"\n >\n Pay now\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nfunction SessionSummaryRow({ session }: { session: TecMentorSession }) {\n return (\n <div className=\"flex items-center gap-3 rounded-lg px-1 py-3 transition-colors hover:bg-bg-sunken\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-purple/12 text-accent-purple\">\n <Video size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{session.topic}</div>\n <div className=\"text-xs text-text-muted\">\n {formatDateTime(session.scheduledAt)} · {formatDuration(session.durationMinutes)}\n </div>\n </div>\n <StatusBadge status={session.status} />\n </div>\n );\n}\n\nexport function MentorshipMentorProfilePagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { workspaceId } = useTechnoSpam();\n const { data, loading, error, refetch } = useMentorQuery(id);\n\n const mentor = data?.tecMentor ?? null;\n\n if (loading && !mentor) {\n return (\n <PageLayout title=\"Mentor\" backPath=\"/mentorship/mentors-page\">\n <Skeleton className=\"mb-4 h-40 w-full rounded-xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !mentor) {\n return (\n <PageLayout title=\"Mentor\" backPath=\"/mentorship/mentors-page\">\n {error ? (\n <ErrorView title=\"Couldn't load mentor\" error={error} />\n ) : (\n <EmptyView\n title=\"Mentor not found\"\n description=\"This mentor profile may no longer be available.\"\n />\n )}\n </PageLayout>\n );\n }\n\n const sessions = mentor.sessions ?? [];\n const recentSessions = [...sessions]\n .sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime())\n .slice(0, 5);\n\n const HeroIcon = topicIcon(mentor.expertise.join(' ') || mentor.headline);\n\n return (\n <PageLayout hero backPath=\"/mentorship/mentors-page\" title=\"Mentor Profile\">\n <GlassCard className=\"mb-6 overflow-hidden p-0\">\n <div\n className={cn(\n 'relative h-24 overflow-hidden bg-gradient-to-br',\n coverGradient(mentor.headline)\n )}\n >\n {createElement(HeroIcon, {\n className: 'pointer-events-none absolute -right-3 -top-4 text-action-primary-text/15',\n size: 120,\n strokeWidth: 1.3,\n })}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-bg-overlay/60 to-transparent\" />\n </div>\n <div className=\"px-6 pb-6\">\n <div className=\"-mt-10 flex flex-col gap-4 sm:flex-row sm:items-end\">\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n 'flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br text-action-primary-text shadow-lg ring-4 ring-bg-surface',\n coverGradient(mentor.headline)\n )}\n >\n {createElement(HeroIcon, { size: 34, strokeWidth: 1.6 })}\n </span>\n {mentor.isAcceptingMentees ? (\n <span className=\"absolute bottom-1 right-1 h-4 w-4 rounded-full border-2 border-bg-surface bg-status-success-text\" />\n ) : null}\n </div>\n <div className=\"min-w-0 flex-1 sm:pb-1\">\n <h1 className=\"text-xl font-semibold text-text-primary sm:text-2xl\">\n {mentor.headline}\n </h1>\n {mentor.role ? (\n <p className=\"mt-1 text-sm font-medium text-text-secondary\">{mentor.role}</p>\n ) : null}\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusBadge status={mentor.status} />\n {mentor.isAcceptingMentees ? (\n <Badge\n variant=\"outline\"\n className=\"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\" /> Accepting\n mentees\n </Badge>\n ) : null}\n </div>\n </div>\n </div>\n\n {mentor.bio ? (\n <p className=\"mt-4 max-w-2xl text-sm leading-relaxed text-text-secondary\">\n {mentor.bio}\n </p>\n ) : null}\n\n {mentor.expertise.length ? (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {mentor.expertise.map((skill) => (\n <Badge key={skill} variant=\"secondary\">\n {skill}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label=\"Rate\"\n value={`${formatMoneyFromMinorUnits(mentor.hourlyRate, mentor.currency)}/hr`}\n icon={DollarSign}\n tone=\"blue\"\n />\n <StatCard\n label=\"Rating\"\n value={mentor.ratingAvg > 0 ? mentor.ratingAvg.toFixed(1) : 'New'}\n icon={Star}\n tone=\"amber\"\n />\n <StatCard\n label=\"Sessions\"\n value={mentor.sessionsCount}\n icon={MessageSquare}\n tone=\"purple\"\n />\n <StatCard\n label=\"Status\"\n value={mentor.status === 'ACTIVE' ? 'Active' : 'Pending'}\n icon={Award}\n tone=\"teal\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <BookSessionForm\n mentorId={mentor.id}\n disabled={!mentor.isAcceptingMentees}\n onBooked={() => void refetch()}\n workspaceId={workspaceId}\n />\n\n <GlassCard className=\"p-5\">\n <div className=\"mb-3 flex items-center gap-2\">\n <CalendarClock size={18} className=\"text-accent-teal\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Recent sessions</h2>\n </div>\n {recentSessions.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-muted\">\n No sessions booked yet. Be the first to request time with this mentor.\n </p>\n ) : (\n <div className=\"divide-y divide-border-seam\">\n {recentSessions.map((session) => (\n <SessionSummaryRow key={session.id} session={session} />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipMentorsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipMentorsPagePage.tsx"],"names":[],"mappings":"AAsTA,wBAAgB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"MentorshipMentorsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipMentorsPagePage.tsx"],"names":[],"mappings":"AAsTA,wBAAgB,yBAAyB,gCAkLxC"}
|
|
@@ -20,7 +20,7 @@ function L({ mentor: e, size: t = "md" }) {
|
|
|
20
20
|
return /* @__PURE__ */ j("div", {
|
|
21
21
|
className: "relative shrink-0",
|
|
22
22
|
children: [/* @__PURE__ */ A("span", {
|
|
23
|
-
className: i("flex items-center justify-center rounded-full bg-gradient-to-br text-
|
|
23
|
+
className: i("flex items-center justify-center rounded-full bg-gradient-to-br text-action-primary-text shadow-md ring-1 ring-action-primary-text/15", t === "lg" ? "h-20 w-20" : "h-16 w-16", s(e.headline)),
|
|
24
24
|
children: y(c(e.expertise.join(" ") || e.headline), {
|
|
25
25
|
size: t === "lg" ? 32 : 26,
|
|
26
26
|
strokeWidth: 1.7
|
|
@@ -32,7 +32,7 @@ function R({ mentor: e, onOpen: t }) {
|
|
|
32
32
|
let n = r();
|
|
33
33
|
return /* @__PURE__ */ j(P, {
|
|
34
34
|
"data-testid": "mentor-card",
|
|
35
|
-
className: "group flex cursor-pointer flex-col gap-
|
|
35
|
+
className: "group flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg sm:gap-4 sm:p-5",
|
|
36
36
|
onClick: t,
|
|
37
37
|
children: [
|
|
38
38
|
/* @__PURE__ */ j("div", {
|
|
@@ -136,7 +136,7 @@ function z({ onClose: e, onDone: t, workspaceId: r }) {
|
|
|
136
136
|
children: [/* @__PURE__ */ j("div", {
|
|
137
137
|
className: "flex items-center gap-3",
|
|
138
138
|
children: [/* @__PURE__ */ A("span", {
|
|
139
|
-
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-
|
|
139
|
+
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-action-primary-text shadow-sm",
|
|
140
140
|
children: /* @__PURE__ */ A(D, { size: 20 })
|
|
141
141
|
}), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("h2", {
|
|
142
142
|
className: "text-base font-semibold text-text-primary",
|
|
@@ -278,6 +278,7 @@ function B() {
|
|
|
278
278
|
}, [W, g]), Y = W.filter((e) => e.isAcceptingMentees).length, X = W.reduce((e, t) => e + t.sessionsCount, 0), Z = W.filter((e) => e.ratingAvg > 0), Q = Z.length ? (Z.reduce((e, t) => e + t.ratingAvg, 0) / Z.length).toFixed(1) : "—";
|
|
279
279
|
return /* @__PURE__ */ j(a, {
|
|
280
280
|
hero: !0,
|
|
281
|
+
mobileSection: !0,
|
|
281
282
|
title: m("technospam.mentorship.findAMentorTitle", "Find a Mentor"),
|
|
282
283
|
description: m("technospam.mentorship.description", "Connect with experienced builders for 1:1 guidance, code review, and career coaching."),
|
|
283
284
|
actions: /* @__PURE__ */ j(N, {
|
|
@@ -310,7 +311,7 @@ function B() {
|
|
|
310
311
|
workspaceId: c
|
|
311
312
|
}) : null,
|
|
312
313
|
/* @__PURE__ */ j("div", {
|
|
313
|
-
className: "mb-5
|
|
314
|
+
className: "mb-5 hidden grid-cols-2 gap-3 sm:grid sm:grid-cols-4",
|
|
314
315
|
children: [
|
|
315
316
|
/* @__PURE__ */ A(o, {
|
|
316
317
|
label: m("technospam.mentorship.statMentors", "Mentors"),
|
|
@@ -353,6 +354,7 @@ function B() {
|
|
|
353
354
|
onChange: (e) => y(e.target.value)
|
|
354
355
|
})]
|
|
355
356
|
}), !q && !k ? /* @__PURE__ */ j(N, {
|
|
357
|
+
className: "min-h-11 sm:min-h-0",
|
|
356
358
|
onClick: () => M(!0),
|
|
357
359
|
children: [
|
|
358
360
|
/* @__PURE__ */ A(D, { size: 16 }),
|