@burdenoff/microfe-technospam 2026.704.2 → 2026.705.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/technospam/api/technospamApi.js +233 -150
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/LoadMore.js +18 -2
- package/dist/technospam/components/LoadMore.js.map +1 -0
- package/dist/technospam/components/MediaCover.js +57 -0
- package/dist/technospam/components/MediaCover.js.map +1 -0
- package/dist/technospam/components/PageLayout.js +1 -1
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/StatCard.js +19 -11
- package/dist/technospam/components/StatCard.js.map +1 -1
- package/dist/technospam/components/StatusBadge.js +1 -1
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- package/dist/technospam/components/index.js +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +748 -352
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +167 -127
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +178 -125
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +89 -93
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +295 -141
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +86 -86
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +122 -88
- package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +232 -199
- package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +128 -106
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +89 -73
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +171 -115
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +214 -151
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +241 -116
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 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 PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\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';\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 ContinueLearningRow({ e, onOpen }: { e: TecEnrollment; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full items-center gap-4 rounded-xl border border-border-seam bg-bg-surface p-3.5 text-left transition-shadow hover:shadow-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-accent-wash text-accent-primary\">\n <BookOpen size={18} />\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 group-hover:text-accent-primary\">\n {e.course?.title ?? 'Course'}\n </span>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-1.5 flex items-center gap-3\">\n <div className=\"flex-1\">\n <ProgressBar pct={e.progressPct} />\n </div>\n <span className=\"shrink-0 text-xs font-medium text-text-secondary\">{e.progressPct}%</span>\n </div>\n </div>\n <PlayCircle\n size={18}\n className=\"shrink-0 text-text-muted transition-colors group-hover:text-accent-primary\"\n />\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 gap-2 rounded-xl border border-border-seam bg-bg-surface p-4 text-left transition-shadow hover:shadow-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={course.level} kind=\"level\" />\n {course.topic ? (\n <span className=\"truncate text-xs text-text-muted\">{course.topic}</span>\n ) : null}\n </div>\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 </button>\n );\n}\n\nfunction SessionRow({ s }: { s: TecMentorSession }) {\n return (\n <div className=\"flex items-start gap-3 border-b border-border-seam py-3 last:border-0\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-wash text-accent-teal\">\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 text-xs text-text-secondary\">{formatDateTime(s.scheduledAt)}</div>\n </div>\n </div>\n );\n}\n\nfunction HackathonRow({ h, onOpen }: { h: TecHackathon; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full flex-col gap-1.5 border-b border-border-seam py-3 text-left last:border-0 focus-visible:outline-none\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary group-hover:text-accent-primary\">\n {h.title}\n </span>\n {h.prizePool > 0 ? (\n <span className=\"shrink-0 text-xs font-semibold text-accent-teal\">\n {formatMoney(h.prizePool, h.currency)}\n </span>\n ) : null}\n </div>\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Calendar size={12} />\n <span>\n {formatDate(h.startAt)} – {formatDate(h.endAt)}\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 learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.data?.tecListEnrollments.items ?? [];\n const submissions = submissionsQ.data?.tecListSubmissions.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\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={`Welcome back, ${greetingName}`}\n description=\"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> 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 label=\"Courses in progress\" value={inProgress.length} icon={BookOpen} />\n <StatCard label=\"Problems solved\" value={problemsSolved} icon={Code2} />\n <StatCard label=\"Upcoming sessions\" value={upcomingSessions.length} icon={CalendarClock} />\n <StatCard label=\"Certificates\" value={certificates.length} icon={Award} />\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\">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\">Practice problems</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\">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\">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=\"Continue learning\"\n actionLabel=\"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 title=\"Couldn't load your courses\" error={enrollmentsQ.error} />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title=\"No courses in progress\"\n description=\"Enroll in a course to start building skill by doing — your active courses show up here.\"\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>Browse courses</Button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {continueLearning.map((e) => (\n <ContinueLearningRow\n key={e.id}\n e={e}\n onOpen={() => navigate(`/courses/course-details-page?id=${e.courseId}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"Recommended for you\"\n actionLabel=\"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 title=\"Couldn't load courses\" error={coursesQ.error} />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title=\"No courses to recommend yet\"\n description=\"New courses are on the way. TechnoSpam is pre-launch — check back soon.\"\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=\"Upcoming sessions\"\n actionLabel=\"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 title=\"Couldn't load sessions\" error={sessionsQ.error} />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title=\"No sessions booked\"\n description=\"Book time with a mentor to get unblocked — your upcoming sessions appear here.\"\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n 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=\"Open hackathons\"\n actionLabel=\"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 title=\"Couldn't load hackathons\" error={hackathonsQ.error} />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title=\"No open hackathons\"\n description=\"When registration opens for a hackathon, it will show up here to join.\"\n />\n ) : (\n <div>\n {openHackathons.map((h) => (\n <HackathonRow\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 title=\"Latest certificate\" />\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-wash 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 ?? 'Certificate'}\n </div>\n <div className=\"text-xs text-text-muted\">\n Score {certificates[0]?.score} · Issued {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 useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\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';\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({ e, onOpen }: { e: TecEnrollment; onOpen: () => void }) {\n const title = e.course?.title ?? 'Course';\n const seed = `${e.course?.topic ?? ''} ${title}`;\n return (\n <button\n type=\"button\"\n onClick={onOpen}\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 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} /> 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 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 learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.data?.tecListEnrollments.items ?? [];\n const submissions = submissionsQ.data?.tecListSubmissions.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\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={`Welcome back, ${greetingName}`}\n description=\"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> 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=\"Courses in progress\"\n value={inProgress.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard label=\"Problems solved\" value={problemsSolved} icon={Code2} tone=\"green\" />\n <StatCard\n label=\"Upcoming sessions\"\n value={upcomingSessions.length}\n icon={CalendarClock}\n tone=\"amber\"\n />\n <StatCard label=\"Certificates\" value={certificates.length} icon={Award} tone=\"purple\" />\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\">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\">Practice problems</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\">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\">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=\"Continue learning\"\n actionLabel=\"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 title=\"Couldn't load your courses\" error={enrollmentsQ.error} />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title=\"No courses in progress\"\n description=\"Enroll in a course to start building skill by doing — your active courses show up here.\"\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>Browse courses</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 onOpen={() => navigate(`/courses/course-details-page?id=${e.courseId}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"Recommended for you\"\n actionLabel=\"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 title=\"Couldn't load courses\" error={coursesQ.error} />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title=\"No courses to recommend yet\"\n description=\"New courses are on the way. TechnoSpam is pre-launch — check back soon.\"\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=\"Upcoming sessions\"\n actionLabel=\"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 title=\"Couldn't load sessions\" error={sessionsQ.error} />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title=\"No sessions booked\"\n description=\"Book time with a mentor to get unblocked — your upcoming sessions appear here.\"\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n 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=\"Open hackathons\"\n actionLabel=\"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 title=\"Couldn't load hackathons\" error={hackathonsQ.error} />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title=\"No open hackathons\"\n description=\"When registration opens for a hackathon, it will show up here to join.\"\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 title=\"Latest certificate\" />\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 ?? 'Certificate'}\n </div>\n <div className=\"text-xs text-text-muted\">\n Score {certificates[0]?.score} · Issued {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,51 +1,69 @@
|
|
|
1
1
|
import { useTechnoSpam as e } from "../providers/TechnoSpamProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../components/PageLayout.js";
|
|
4
|
+
import { MediaCover as r, coverGradient as i } from "../components/MediaCover.js";
|
|
5
|
+
import { formatDate as a, formatDateTime as ee, formatMoney as te } from "../utils/format.js";
|
|
6
|
+
import { StatusBadge as o } from "../components/StatusBadge.js";
|
|
7
|
+
import { EmptyView as s, ErrorView as c } from "../components/StateViews.js";
|
|
7
8
|
import "../components/index.js";
|
|
8
|
-
import { notify as
|
|
9
|
-
import { useHackathonQuery as
|
|
10
|
-
import { useState as
|
|
11
|
-
import { Award as
|
|
12
|
-
import { useSearchParams as
|
|
9
|
+
import { notify as l } from "../lib/toast.js";
|
|
10
|
+
import { useHackathonQuery as ne, useRegisterTeam as re, useSubmitProject as ie, useTeamsQuery as ae } from "../api/technospamApi.js";
|
|
11
|
+
import { useState as u } from "react";
|
|
12
|
+
import { Award as oe, CalendarClock as se, CalendarDays as d, Code2 as f, ExternalLink as p, Flag as m, Github as h, Send as g, Trophy as _, UserPlus as v, Users as y } from "lucide-react";
|
|
13
|
+
import { useSearchParams as ce } from "react-router-dom";
|
|
13
14
|
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
14
|
-
import { Badge as S, Button as C, GlassCard as w, Input as T, Select as E, SelectContent as
|
|
15
|
+
import { Badge as S, Button as C, GlassCard as w, Input as T, Select as E, SelectContent as D, SelectItem as O, SelectTrigger as le, SelectValue as ue, Skeleton as k, Textarea as de } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/technospam/pages/HackathonsHackathonDetailsPagePage.tsx
|
|
16
|
-
function
|
|
17
|
+
function A({ icon: e, label: t }) {
|
|
18
|
+
return /* @__PURE__ */ x("span", {
|
|
19
|
+
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",
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ b(e, { size: 15 }),
|
|
22
|
+
" ",
|
|
23
|
+
t
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function fe({ team: e, isMine: n }) {
|
|
28
|
+
let r = e.status === "WINNER";
|
|
17
29
|
return /* @__PURE__ */ x(w, {
|
|
18
|
-
className: "flex flex-col gap-3 p-4",
|
|
30
|
+
className: "group flex flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
|
|
19
31
|
children: [
|
|
20
32
|
/* @__PURE__ */ x("div", {
|
|
21
33
|
className: "flex items-start justify-between gap-3",
|
|
22
34
|
children: [/* @__PURE__ */ x("div", {
|
|
23
|
-
className: "min-w-0",
|
|
24
|
-
children: [/* @__PURE__ */
|
|
25
|
-
className: "flex items-center
|
|
26
|
-
children:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
className: "flex min-w-0 items-start gap-3",
|
|
36
|
+
children: [/* @__PURE__ */ b("span", {
|
|
37
|
+
className: t("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", i(e.name)),
|
|
38
|
+
children: b(r ? _ : f, { size: 19 })
|
|
39
|
+
}), /* @__PURE__ */ x("div", {
|
|
40
|
+
className: "min-w-0",
|
|
41
|
+
children: [/* @__PURE__ */ x("div", {
|
|
42
|
+
className: "flex flex-wrap items-center gap-2",
|
|
43
|
+
children: [/* @__PURE__ */ b("span", {
|
|
44
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
45
|
+
children: e.name
|
|
46
|
+
}), n ? /* @__PURE__ */ b(S, {
|
|
47
|
+
variant: "secondary",
|
|
48
|
+
className: "text-[11px]",
|
|
49
|
+
children: "Your team"
|
|
50
|
+
}) : null]
|
|
51
|
+
}), e.projectName ? /* @__PURE__ */ b("p", {
|
|
52
|
+
className: "mt-0.5 truncate text-sm text-text-secondary",
|
|
53
|
+
children: e.projectName
|
|
54
|
+
}) : /* @__PURE__ */ b("p", {
|
|
55
|
+
className: "mt-0.5 text-sm text-text-muted",
|
|
56
|
+
children: "No project submitted yet"
|
|
57
|
+
})]
|
|
40
58
|
})]
|
|
41
|
-
}), /* @__PURE__ */ b(
|
|
59
|
+
}), /* @__PURE__ */ b(o, { status: e.status })]
|
|
42
60
|
}),
|
|
43
61
|
e.pitch ? /* @__PURE__ */ b("p", {
|
|
44
62
|
className: "line-clamp-3 text-sm text-text-secondary",
|
|
45
63
|
children: e.pitch
|
|
46
64
|
}) : null,
|
|
47
65
|
/* @__PURE__ */ x("div", {
|
|
48
|
-
className: "flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted",
|
|
66
|
+
className: "mt-auto flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted",
|
|
49
67
|
children: [/* @__PURE__ */ x("span", {
|
|
50
68
|
className: "inline-flex items-center gap-1",
|
|
51
69
|
children: [
|
|
@@ -58,84 +76,88 @@ function de({ team: e, isMine: t }) {
|
|
|
58
76
|
}), /* @__PURE__ */ x("span", {
|
|
59
77
|
className: "inline-flex items-center gap-1",
|
|
60
78
|
children: [
|
|
61
|
-
/* @__PURE__ */ b(
|
|
79
|
+
/* @__PURE__ */ b(m, { size: 13 }),
|
|
62
80
|
" Score ",
|
|
63
81
|
e.score
|
|
64
82
|
]
|
|
65
83
|
})]
|
|
66
84
|
}),
|
|
67
85
|
e.repoUrl || e.projectUrl ? /* @__PURE__ */ x("div", {
|
|
68
|
-
className: "flex flex-wrap items-center gap-3",
|
|
86
|
+
className: "flex flex-wrap items-center gap-3 border-t border-border-seam pt-3",
|
|
69
87
|
children: [e.repoUrl ? /* @__PURE__ */ x("a", {
|
|
70
88
|
href: e.repoUrl,
|
|
71
89
|
target: "_blank",
|
|
72
90
|
rel: "noreferrer",
|
|
73
91
|
className: "inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline",
|
|
74
|
-
children: [/* @__PURE__ */ b(
|
|
92
|
+
children: [/* @__PURE__ */ b(h, { size: 13 }), " Repository"]
|
|
75
93
|
}) : null, e.projectUrl ? /* @__PURE__ */ x("a", {
|
|
76
94
|
href: e.projectUrl,
|
|
77
95
|
target: "_blank",
|
|
78
96
|
rel: "noreferrer",
|
|
79
97
|
className: "inline-flex items-center gap-1 text-xs font-medium text-accent-primary hover:underline",
|
|
80
|
-
children: [/* @__PURE__ */ b(
|
|
98
|
+
children: [/* @__PURE__ */ b(p, { size: 13 }), " Live project"]
|
|
81
99
|
}) : null]
|
|
82
100
|
}) : null
|
|
83
101
|
]
|
|
84
102
|
});
|
|
85
103
|
}
|
|
86
|
-
function
|
|
87
|
-
let [
|
|
88
|
-
return
|
|
104
|
+
function j() {
|
|
105
|
+
let [t] = ce(), i = t.get("id") ?? "", { currentUser: f } = e(), { data: p, loading: m, error: h, refetch: j } = ne(i), { data: pe, loading: me, error: M, refetch: N } = ae(i), [he, { loading: P }] = re(), [F, { loading: I }] = ie(), [L, R] = u(""), [z, B] = u(""), [ge, _e] = u(""), [V, H] = u(""), [U, W] = u(""), [G, K] = u(""), [q, J] = u(""), Y = p?.tecHackathon ?? null, X = pe?.tecListTeams ?? [], Z = Y?.status === "REGISTRATION_OPEN", Q = f?.id ? X.filter((e) => e.captainId === f.id) : [], $ = ge || Q[0]?.id || "";
|
|
106
|
+
return m && !Y ? /* @__PURE__ */ x(n, {
|
|
89
107
|
title: "Hackathon",
|
|
90
108
|
backPath: "/hackathons/hackathons-page",
|
|
91
|
-
children: [/* @__PURE__ */ b(
|
|
92
|
-
}) :
|
|
109
|
+
children: [/* @__PURE__ */ b(k, { className: "mb-4 h-32 w-full rounded-xl" }), /* @__PURE__ */ b(k, { className: "h-64 w-full rounded-xl" })]
|
|
110
|
+
}) : h || !Y ? /* @__PURE__ */ b(n, {
|
|
93
111
|
title: "Hackathon",
|
|
94
112
|
backPath: "/hackathons/hackathons-page",
|
|
95
|
-
children:
|
|
113
|
+
children: h ? /* @__PURE__ */ b(c, {
|
|
96
114
|
title: "Couldn't load hackathon",
|
|
97
|
-
error:
|
|
98
|
-
}) : /* @__PURE__ */ b(
|
|
115
|
+
error: h
|
|
116
|
+
}) : /* @__PURE__ */ b(s, {
|
|
99
117
|
title: "Hackathon not found",
|
|
100
118
|
description: "This hackathon may no longer be listed."
|
|
101
119
|
})
|
|
102
|
-
}) : /* @__PURE__ */ x(
|
|
103
|
-
hero: !0,
|
|
120
|
+
}) : /* @__PURE__ */ x(n, {
|
|
104
121
|
backPath: "/hackathons/hackathons-page",
|
|
105
122
|
title: Y.title,
|
|
106
123
|
description: Y.theme ?? void 0,
|
|
107
124
|
children: [
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}),
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
/* @__PURE__ */ b(r, {
|
|
126
|
+
height: "h-40",
|
|
127
|
+
seed: Y.theme ?? Y.id,
|
|
128
|
+
icon: _,
|
|
129
|
+
className: "mb-6 rounded-xl",
|
|
130
|
+
children: /* @__PURE__ */ x("div", {
|
|
131
|
+
className: "flex w-full flex-col gap-3",
|
|
132
|
+
children: [/* @__PURE__ */ x("div", {
|
|
133
|
+
className: "flex flex-wrap items-center gap-2",
|
|
134
|
+
children: [/* @__PURE__ */ b(o, { status: Y.status }), Y.tags.map((e) => /* @__PURE__ */ b(S, {
|
|
135
|
+
variant: "secondary",
|
|
136
|
+
className: "border-white/20 bg-white/15 text-[11px] text-white",
|
|
137
|
+
children: e
|
|
138
|
+
}, e))]
|
|
139
|
+
}), /* @__PURE__ */ x("div", {
|
|
140
|
+
className: "flex flex-wrap items-center gap-2",
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ b(A, {
|
|
143
|
+
icon: oe,
|
|
144
|
+
label: `${te(Y.prizePool, Y.currency)} prize pool`
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ b(A, {
|
|
147
|
+
icon: d,
|
|
148
|
+
label: `${a(Y.startAt)} – ${a(Y.endAt)}`
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ b(A, {
|
|
151
|
+
icon: y,
|
|
152
|
+
label: `Team of ${Y.maxTeamSize}`
|
|
153
|
+
}),
|
|
154
|
+
/* @__PURE__ */ b(A, {
|
|
155
|
+
icon: _,
|
|
156
|
+
label: `${X.length} team${X.length === 1 ? "" : "s"} registered`
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
})]
|
|
160
|
+
})
|
|
139
161
|
}),
|
|
140
162
|
Y.description ? /* @__PURE__ */ x(w, {
|
|
141
163
|
className: "mb-6 p-5",
|
|
@@ -157,7 +179,7 @@ function O() {
|
|
|
157
179
|
children: [
|
|
158
180
|
/* @__PURE__ */ x("div", {
|
|
159
181
|
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3",
|
|
160
|
-
children: [/* @__PURE__ */ b(
|
|
182
|
+
children: [/* @__PURE__ */ b(d, {
|
|
161
183
|
size: 16,
|
|
162
184
|
className: "text-accent-teal"
|
|
163
185
|
}), /* @__PURE__ */ x("div", {
|
|
@@ -167,13 +189,13 @@ function O() {
|
|
|
167
189
|
children: "Starts"
|
|
168
190
|
}), /* @__PURE__ */ b("div", {
|
|
169
191
|
className: "truncate text-sm font-medium text-text-primary",
|
|
170
|
-
children:
|
|
192
|
+
children: a(Y.startAt)
|
|
171
193
|
})]
|
|
172
194
|
})]
|
|
173
195
|
}),
|
|
174
196
|
/* @__PURE__ */ x("div", {
|
|
175
197
|
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3",
|
|
176
|
-
children: [/* @__PURE__ */ b(
|
|
198
|
+
children: [/* @__PURE__ */ b(d, {
|
|
177
199
|
size: 16,
|
|
178
200
|
className: "text-accent-teal"
|
|
179
201
|
}), /* @__PURE__ */ x("div", {
|
|
@@ -183,13 +205,13 @@ function O() {
|
|
|
183
205
|
children: "Ends"
|
|
184
206
|
}), /* @__PURE__ */ b("div", {
|
|
185
207
|
className: "truncate text-sm font-medium text-text-primary",
|
|
186
|
-
children:
|
|
208
|
+
children: a(Y.endAt)
|
|
187
209
|
})]
|
|
188
210
|
})]
|
|
189
211
|
}),
|
|
190
212
|
/* @__PURE__ */ x("div", {
|
|
191
213
|
className: "flex items-center gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3",
|
|
192
|
-
children: [/* @__PURE__ */ b(
|
|
214
|
+
children: [/* @__PURE__ */ b(se, {
|
|
193
215
|
size: 16,
|
|
194
216
|
className: "text-accent-teal"
|
|
195
217
|
}), /* @__PURE__ */ x("div", {
|
|
@@ -210,10 +232,10 @@ function O() {
|
|
|
210
232
|
className: "mb-6 p-5",
|
|
211
233
|
children: [
|
|
212
234
|
/* @__PURE__ */ x("div", {
|
|
213
|
-
className: "mb-3 flex items-center gap-
|
|
214
|
-
children: [/* @__PURE__ */ b(
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
className: "mb-3 flex items-center gap-3",
|
|
236
|
+
children: [/* @__PURE__ */ b("span", {
|
|
237
|
+
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",
|
|
238
|
+
children: /* @__PURE__ */ b(v, { size: 18 })
|
|
217
239
|
}), /* @__PURE__ */ b("h2", {
|
|
218
240
|
className: "text-base font-semibold text-text-primary",
|
|
219
241
|
children: "Register a team"
|
|
@@ -256,13 +278,13 @@ function O() {
|
|
|
256
278
|
className: "mt-4 flex justify-end",
|
|
257
279
|
children: /* @__PURE__ */ x(C, {
|
|
258
280
|
onClick: async () => {
|
|
259
|
-
if (!
|
|
281
|
+
if (!i || !L.trim()) return;
|
|
260
282
|
let e = z.split(",").map((e) => e.trim()).filter(Boolean);
|
|
261
|
-
(await
|
|
262
|
-
hackathonId:
|
|
283
|
+
(await he({ variables: { input: {
|
|
284
|
+
hackathonId: i,
|
|
263
285
|
name: L.trim(),
|
|
264
286
|
memberIds: e
|
|
265
|
-
} } })).data?.tecRegisterTeam && (
|
|
287
|
+
} } })).data?.tecRegisterTeam && (l.success("Team registered", `"${L.trim()}" is in the running.`), R(""), B(""), await Promise.all([N(), j()]));
|
|
266
288
|
},
|
|
267
289
|
disabled: P || !L.trim(),
|
|
268
290
|
children: [
|
|
@@ -278,10 +300,10 @@ function O() {
|
|
|
278
300
|
className: "mb-6 p-5",
|
|
279
301
|
children: [
|
|
280
302
|
/* @__PURE__ */ x("div", {
|
|
281
|
-
className: "mb-3 flex items-center gap-
|
|
282
|
-
children: [/* @__PURE__ */ b(
|
|
283
|
-
|
|
284
|
-
|
|
303
|
+
className: "mb-3 flex items-center gap-3",
|
|
304
|
+
children: [/* @__PURE__ */ b("span", {
|
|
305
|
+
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",
|
|
306
|
+
children: /* @__PURE__ */ b(g, { size: 18 })
|
|
285
307
|
}), /* @__PURE__ */ b("h2", {
|
|
286
308
|
className: "text-base font-semibold text-text-primary",
|
|
287
309
|
children: "Submit your project"
|
|
@@ -297,8 +319,8 @@ function O() {
|
|
|
297
319
|
children: "Team"
|
|
298
320
|
}), /* @__PURE__ */ x(E, {
|
|
299
321
|
value: $,
|
|
300
|
-
onValueChange:
|
|
301
|
-
children: [/* @__PURE__ */ b(
|
|
322
|
+
onValueChange: _e,
|
|
323
|
+
children: [/* @__PURE__ */ b(le, { children: /* @__PURE__ */ b(ue, { placeholder: "Choose a team" }) }), /* @__PURE__ */ b(D, { children: Q.map((e) => /* @__PURE__ */ b(O, {
|
|
302
324
|
value: e.id,
|
|
303
325
|
children: e.name
|
|
304
326
|
}, e.id)) })]
|
|
@@ -352,7 +374,7 @@ function O() {
|
|
|
352
374
|
className: "text-text-muted",
|
|
353
375
|
children: "(optional)"
|
|
354
376
|
})]
|
|
355
|
-
}), /* @__PURE__ */ b(
|
|
377
|
+
}), /* @__PURE__ */ b(de, {
|
|
356
378
|
rows: 3,
|
|
357
379
|
placeholder: "What did you build, and why does it matter?",
|
|
358
380
|
value: q,
|
|
@@ -371,11 +393,11 @@ function O() {
|
|
|
371
393
|
projectUrl: U.trim() || void 0,
|
|
372
394
|
repoUrl: G.trim() || void 0,
|
|
373
395
|
pitch: q.trim() || void 0
|
|
374
|
-
} } })).data?.tecSubmitProject && (
|
|
396
|
+
} } })).data?.tecSubmitProject && (l.success("Project submitted", `"${V.trim()}" is submitted for judging.`), H(""), W(""), K(""), J(""), await Promise.all([N(), j()]));
|
|
375
397
|
},
|
|
376
398
|
disabled: I || !$ || !V.trim(),
|
|
377
399
|
children: [
|
|
378
|
-
/* @__PURE__ */ b(
|
|
400
|
+
/* @__PURE__ */ b(g, { size: 16 }),
|
|
379
401
|
" ",
|
|
380
402
|
I ? "Submitting…" : "Submit project"
|
|
381
403
|
]
|
|
@@ -395,25 +417,25 @@ function O() {
|
|
|
395
417
|
}) : null]
|
|
396
418
|
}), me && !X.length ? /* @__PURE__ */ x("div", {
|
|
397
419
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
398
|
-
children: [/* @__PURE__ */ b(
|
|
399
|
-
}) :
|
|
420
|
+
children: [/* @__PURE__ */ b(k, { className: "h-40 w-full rounded-xl" }), /* @__PURE__ */ b(k, { className: "h-40 w-full rounded-xl" })]
|
|
421
|
+
}) : M ? /* @__PURE__ */ b(c, {
|
|
400
422
|
title: "Couldn't load teams",
|
|
401
|
-
error:
|
|
402
|
-
}) : X.length === 0 ? /* @__PURE__ */ b(
|
|
423
|
+
error: M
|
|
424
|
+
}) : X.length === 0 ? /* @__PURE__ */ b(s, {
|
|
403
425
|
icon: /* @__PURE__ */ b(y, { size: 26 }),
|
|
404
426
|
title: "No teams registered yet",
|
|
405
427
|
description: Z ? "Be the first to register a team above." : "Teams will appear here once registration opens."
|
|
406
428
|
}) : /* @__PURE__ */ b("div", {
|
|
407
429
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
408
|
-
children: X.map((e) => /* @__PURE__ */ b(
|
|
430
|
+
children: X.map((e) => /* @__PURE__ */ b(fe, {
|
|
409
431
|
team: e,
|
|
410
|
-
isMine: !!
|
|
432
|
+
isMine: !!f?.id && e.captainId === f.id
|
|
411
433
|
}, e.id))
|
|
412
434
|
})] })
|
|
413
435
|
]
|
|
414
436
|
});
|
|
415
437
|
}
|
|
416
438
|
//#endregion
|
|
417
|
-
export {
|
|
439
|
+
export { j as HackathonsHackathonDetailsPagePage };
|
|
418
440
|
|
|
419
441
|
//# sourceMappingURL=HackathonsHackathonDetailsPagePage.js.map
|