@burdenoff/microfe-technospam 2026.1031.4 → 2026.1031.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CoursesCoursesPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { BookOpen, Clock, GraduationCap, Layers, Search, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useCoursesQuery,\n usePageLimit,\n type TecCourse,\n type TecCourseLevel,\n} from '../api/technospamApi';\nimport { formatDuration } from '../utils/format';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction CourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n >\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={course.level} kind=\"level\" />\n {course.isPremiumOnly ? <PremiumBadge locked={course.isLocked} /> : null}\n </div>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{course.summary}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {course.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {course.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(course.estimatedMinutes)}\n </span>\n </div>\n {course.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesCoursesPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [level, setLevel] = useState<'ALL' | TecCourseLevel>('ALL');\n const [search, setSearch] = useState('');\n\n const levels = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.courses.filterAllLevels', 'All levels') },\n {\n value: 'BEGINNER' as TecCourseLevel,\n label: tr('technospam.courses.filterBeginner', 'Beginner'),\n },\n {\n value: 'INTERMEDIATE' as TecCourseLevel,\n label: tr('technospam.courses.filterIntermediate', 'Intermediate'),\n },\n {\n value: 'ADVANCED' as TecCourseLevel,\n label: tr('technospam.courses.filterAdvanced', 'Advanced'),\n },\n { value: 'EXPERT' as TecCourseLevel, label: tr('technospam.courses.filterExpert', 'Expert') },\n ],\n [tr]\n );\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useCoursesQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(level !== 'ALL' ? { level } : {}),\n });\n\n const courses = data?.tecListCourses?.items ?? [];\n const hasMore = data?.tecListCourses?.pageInfo?.hasMore ?? false;\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return courses;\n return courses.filter(\n (c) =>\n c.title.toLowerCase().includes(q) ||\n (c.topic ?? '').toLowerCase().includes(q) ||\n c.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [courses, search]);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.courses.title', 'Course Catalog')}\n description={tr(\n 'technospam.courses.description',\n 'Learn technology by doing — practical, project-based courses across the stack.'\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.common.myLearning', 'My Learning')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.courses.statCourses', 'Courses')}\n value={courses.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.courses.statBeginnerFriendly', 'Beginner-friendly')}\n value={courses.filter((c) => c.level === 'BEGINNER').length}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.courses.statAdvancedPlus', 'Advanced+')}\n value={courses.filter((c) => c.level === 'ADVANCED' || c.level === 'EXPERT').length}\n icon={GraduationCap}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.courses.statTotalContent', 'Total content')}\n value={formatDuration(courses.reduce((s, c) => s + c.estimatedMinutes, 0))}\n icon={Clock}\n tone=\"teal\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={level}\n onValueChange={(v) => setLevel(v as 'ALL' | TecCourseLevel)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {levels.map((l) => (\n <TabsTrigger key={l.value} value={l.value}>\n {l.label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n aria-label={tr('technospam.courses.searchLabel', 'Search courses')}\n placeholder={tr('technospam.courses.searchPlaceholder', 'Search courses…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'course',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <TabsContent value={level}>\n {loading && !courses.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title={\n search || level !== 'ALL'\n ? tr('technospam.courses.emptySearchTitle', 'No matching courses')\n : tr('technospam.courses.emptyDefaultTitle', 'No courses published yet')\n }\n description={\n search || level !== 'ALL'\n ? tr(\n 'technospam.courses.emptySearchDesc',\n 'Try a different search or level filter.'\n )\n : tr(\n 'technospam.courses.emptyDefaultDesc',\n 'Published courses will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onOpen={() => {\n emit('technospam.course.viewed', {\n workspaceId,\n entityId: course.id,\n entityType: 'TEC_COURSE',\n });\n navigate(`/courses/course-details-page?id=${course.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoursesCoursesPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { BookOpen, Clock, GraduationCap, Layers, Search, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { TAB_PANEL_FOCUS_CLASS } from '../lib/a11y';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useCoursesQuery,\n usePageLimit,\n type TecCourse,\n type TecCourseLevel,\n} from '../api/technospamApi';\nimport { formatDuration } from '../utils/format';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction CourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n >\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={course.level} kind=\"level\" />\n {course.isPremiumOnly ? <PremiumBadge locked={course.isLocked} /> : null}\n </div>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{course.summary}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {course.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {course.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(course.estimatedMinutes)}\n </span>\n </div>\n {course.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesCoursesPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [level, setLevel] = useState<'ALL' | TecCourseLevel>('ALL');\n const [search, setSearch] = useState('');\n\n const levels = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.courses.filterAllLevels', 'All levels') },\n {\n value: 'BEGINNER' as TecCourseLevel,\n label: tr('technospam.courses.filterBeginner', 'Beginner'),\n },\n {\n value: 'INTERMEDIATE' as TecCourseLevel,\n label: tr('technospam.courses.filterIntermediate', 'Intermediate'),\n },\n {\n value: 'ADVANCED' as TecCourseLevel,\n label: tr('technospam.courses.filterAdvanced', 'Advanced'),\n },\n { value: 'EXPERT' as TecCourseLevel, label: tr('technospam.courses.filterExpert', 'Expert') },\n ],\n [tr]\n );\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useCoursesQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(level !== 'ALL' ? { level } : {}),\n });\n\n const courses = data?.tecListCourses?.items ?? [];\n const hasMore = data?.tecListCourses?.pageInfo?.hasMore ?? false;\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return courses;\n return courses.filter(\n (c) =>\n c.title.toLowerCase().includes(q) ||\n (c.topic ?? '').toLowerCase().includes(q) ||\n c.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [courses, search]);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.courses.title', 'Course Catalog')}\n description={tr(\n 'technospam.courses.description',\n 'Learn technology by doing — practical, project-based courses across the stack.'\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.common.myLearning', 'My Learning')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.courses.statCourses', 'Courses')}\n value={courses.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.courses.statBeginnerFriendly', 'Beginner-friendly')}\n value={courses.filter((c) => c.level === 'BEGINNER').length}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.courses.statAdvancedPlus', 'Advanced+')}\n value={courses.filter((c) => c.level === 'ADVANCED' || c.level === 'EXPERT').length}\n icon={GraduationCap}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.courses.statTotalContent', 'Total content')}\n value={formatDuration(courses.reduce((s, c) => s + c.estimatedMinutes, 0))}\n icon={Clock}\n tone=\"teal\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={level}\n onValueChange={(v) => setLevel(v as 'ALL' | TecCourseLevel)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {levels.map((l) => (\n <TabsTrigger key={l.value} value={l.value}>\n {l.label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n aria-label={tr('technospam.courses.searchLabel', 'Search courses')}\n placeholder={tr('technospam.courses.searchPlaceholder', 'Search courses…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'course',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <TabsContent value={level} className={TAB_PANEL_FOCUS_CLASS}>\n {loading && !courses.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title={\n search || level !== 'ALL'\n ? tr('technospam.courses.emptySearchTitle', 'No matching courses')\n : tr('technospam.courses.emptyDefaultTitle', 'No courses published yet')\n }\n description={\n search || level !== 'ALL'\n ? tr(\n 'technospam.courses.emptySearchDesc',\n 'Try a different search or level filter.'\n )\n : tr(\n 'technospam.courses.emptyDefaultDesc',\n 'Published courses will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onOpen={() => {\n emit('technospam.course.viewed', {\n workspaceId,\n entityId: course.id,\n entityType: 'TEC_COURSE',\n });\n navigate(`/courses/course-details-page?id=${course.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,SAAS,EAAW,EAAE,WAAQ,aAAqD;AACjF,QACE,kBAAC,GAAD;EACE,WAAU;EACV,SAAS;YAFX,CAIE,kBAAC,GAAD;GACE,MAAM,GAAG,EAAO,SAAS,GAAG,GAAG,EAAO;GACtC,MAAM,EAAU,EAAO,SAAS,EAAO,MAAM;aAE7C,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAa,QAAQ,EAAO;KAAO,MAAK;KAAU,CAAA,EACjD,EAAO,gBAAgB,kBAAC,GAAD,EAAc,QAAQ,EAAO,UAAY,CAAA,GAAG,KAChE;;GACK,CAAA,EACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAO;MACL,CAAA,EACJ,EAAO,UACN,kBAAC,KAAD;MAAG,WAAU;gBAAiD,EAAO;MAAY,CAAA,GAC/E,KACA;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAO,QACN,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;;OAAE,EAAO;OACxB;UACL,MACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;OAAE,EAAe,EAAO,iBAAiB;OACvD;QACH;;IACL,EAAO,KAAK,SACX,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC5B,kBAAC,GAAD;MAAe,SAAQ;MAAY,WAAU;gBAC1C;MACK,EAFI,EAEJ,CACR;KACE,CAAA,GACJ;IACA;KACI;;;AAIhB,SAAgB,IAAyB;CACvC,IAAM,IAAW,GAAuB,EAClC,EAAE,mBAAgB,GAAe,EACjC,EAAE,YAAS,GAAwB,EACnC,IAAK,GAAO,EACZ,CAAC,GAAO,KAAY,EAAiC,MAAM,EAC3D,CAAC,GAAQ,KAAa,EAAS,GAAG,EAElC,IAAS,QACP;EACJ;GAAE,OAAO;GAAgB,OAAO,EAAG,sCAAsC,aAAa;GAAE;EACxF;GACE,OAAO;GACP,OAAO,EAAG,qCAAqC,WAAW;GAC3D;EACD;GACE,OAAO;GACP,OAAO,EAAG,yCAAyC,eAAe;GACnE;EACD;GACE,OAAO;GACP,OAAO,EAAG,qCAAqC,WAAW;GAC3D;EACD;GAAE,OAAO;GAA4B,OAAO,EAAG,mCAAmC,SAAS;GAAE;EAC9F,EACD,CAAC,EAAG,CACL,EAEK,EAAE,UAAO,YAAS,GAAc,EAChC,EAAE,SAAM,YAAS,aAAU,EAAgB;EAC/C,QAAQ;EACR,YAAY,EAAQ,EAAM;EAC1B,GAAI,MAAU,QAAoB,EAAE,GAAd,EAAE,UAAO;EAChC,CAAC,EAEI,IAAU,GAAM,gBAAgB,SAAS,EAAE,EAC3C,IAAU,GAAM,gBAAgB,UAAU,WAAW,IACrD,IAAW,QAAc;EAC7B,IAAM,IAAI,EAAO,MAAM,CAAC,aAAa;AAErC,SADK,IACE,EAAQ,QACZ,MACC,EAAE,MAAM,aAAa,CAAC,SAAS,EAAE,KAChC,EAAE,SAAS,IAAI,aAAa,CAAC,SAAS,EAAE,IACzC,EAAE,KAAK,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CAClD,GANc;IAOd,CAAC,GAAS,EAAO,CAAC;AAErB,QACE,kBAAC,GAAD;EACE,MAAA;EACA,OAAO,EAAG,4BAA4B,iBAAiB;EACvD,aAAa,EACX,kCACA,iFACD;EACD,SACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAS,4BAA4B;aAA9E;IACE,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA;;IAAE,EAAG,gCAAgC,cAAc;IACvE;;YAVb,CAaE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAO,EAAG,kCAAkC,UAAU;KACtD,OAAO,EAAQ;KACf,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,2CAA2C,oBAAoB;KACzE,OAAO,EAAQ,QAAQ,MAAM,EAAE,UAAU,WAAW,CAAC;KACrD,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,uCAAuC,YAAY;KAC7D,OAAO,EAAQ,QAAQ,MAAM,EAAE,UAAU,cAAc,EAAE,UAAU,SAAS,CAAC;KAC7E,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,uCAAuC,gBAAgB;KACjE,OAAO,EAAe,EAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,kBAAkB,EAAE,CAAC;KAC1E,MAAM;KACN,MAAK;KACL,CAAA;IACE;MAKN,kBAAC,GAAD;GACE,WAAU;GACV,OAAO;GACP,gBAAgB,MAAM,EAAS,EAA4B;aAH7D,CAKE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAA,UACG,EAAO,KAAK,MACX,kBAAC,GAAD;KAA2B,OAAO,EAAE;eACjC,EAAE;KACS,EAFI,EAAE,MAEN,CACd,EACO,CAAA,EACX,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,WAAU;MACV,MAAM;MACN,CAAA,EACF,kBAAC,GAAD;MACE,WAAU;MACV,cAAY,EAAG,kCAAkC,iBAAiB;MAClE,aAAa,EAAG,wCAAwC,kBAAkB;MAC1E,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,cAAc;OACZ,IAAM,IAAQ,EAAO,MAAM;AAC3B,OAAI,KACF,EAAK,+BAA+B;QAClC;QACA,YAAY;QACZ,aAAa,EAAM;QACpB,CAAC;;MAGN,CAAA,CACE;OACF;OAEN,kBAAC,GAAD;IAAa,OAAO;IAAO,WAAW;cAAtC,CACG,KAAW,CAAC,EAAQ,SACnB,kBAAC,GAAD,EAAe,CAAA,GACb,IACF,kBAAC,GAAD;KACE,OAAO,EAAG,sCAAsC,wBAAwB;KACjE;KACP,CAAA,GACA,EAAS,WAAW,IACtB,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;KAC5B,OACE,KAAU,MAAU,QAChB,EAAG,uCAAuC,sBAAsB,GAChE,EAAG,wCAAwC,2BAA2B;KAE5E,aACE,KAAU,MAAU,QAChB,EACE,sCACA,0CACD,GACD,EACE,uCACA,kFACD;KAEP,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAS,KAAK,MACb,kBAAC,GAAD;MAEU;MACR,cAAc;AAMZ,OALA,EAAK,4BAA4B;QAC/B;QACA,UAAU,EAAO;QACjB,YAAY;QACb,CAAC,EACF,EAAS,mCAAmC,EAAO,KAAK;;MAE1D,EAVK,EAAO,GAUZ,CACF;KACE,CAAA,EAER,kBAAC,GAAD;KAAmB;KAAkB;KAAS,SAAS;KAAQ,CAAA,CACnD;MACT;KACI"}
@@ -1 +1 @@
1
- {"version":3,"file":"HackathonsHackathonsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/HackathonsHackathonsPagePage.tsx"],"names":[],"mappings":"AAuFA,wBAAgB,4BAA4B,gCA4K3C"}
1
+ {"version":3,"file":"HackathonsHackathonsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/HackathonsHackathonsPagePage.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,4BAA4B,gCA4K3C"}
@@ -10,66 +10,67 @@ import { LoadMore as u } from "../components/LoadMore.js";
10
10
  import { EmptyView as d, ErrorView as f, LoadingGrid as p } from "../components/StateViews.js";
11
11
  import { pageArg as m, useHackathonsQuery as h, usePageLimit as g } from "../api/technospamApi.js";
12
12
  import "../components/index.js";
13
- import { useMemo as _, useState as v } from "react";
14
- import { Award as y, CalendarDays as b, Search as x, Sparkles as S, Trophy as C, Users as w } from "lucide-react";
15
- import { jsx as T, jsxs as E } from "react/jsx-runtime";
16
- import { Badge as D, GlassCard as O, Input as k, Tabs as A, TabsContent as j, TabsList as M, TabsTrigger as N } from "@burdenoff/fe-libs/ui";
13
+ import { TAB_PANEL_FOCUS_CLASS as _ } from "../lib/a11y.js";
14
+ import { useMemo as v, useState as y } from "react";
15
+ import { Award as b, CalendarDays as x, Search as S, Sparkles as C, Trophy as w, Users as T } from "lucide-react";
16
+ import { jsx as E, jsxs as D } from "react/jsx-runtime";
17
+ import { Badge as O, GlassCard as k, Input as A, Tabs as j, TabsContent as M, TabsList as N, TabsTrigger as P } from "@burdenoff/fe-libs/ui";
17
18
  //#region src/technospam/pages/HackathonsHackathonsPagePage.tsx
18
- function P({ hackathon: e, onOpen: t }) {
19
+ function F({ hackathon: e, onOpen: t }) {
19
20
  let n = r();
20
- return /* @__PURE__ */ E(O, {
21
+ return /* @__PURE__ */ D(k, {
21
22
  className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
22
23
  onClick: t,
23
- children: [/* @__PURE__ */ T(o, {
24
+ children: [/* @__PURE__ */ E(o, {
24
25
  height: "h-32",
25
26
  seed: e.theme ?? e.id,
26
- icon: C,
27
- children: /* @__PURE__ */ T(l, { status: e.status })
28
- }), /* @__PURE__ */ E("div", {
27
+ icon: w,
28
+ children: /* @__PURE__ */ E(l, { status: e.status })
29
+ }), /* @__PURE__ */ D("div", {
29
30
  className: "flex flex-1 flex-col gap-3 p-4",
30
31
  children: [
31
- /* @__PURE__ */ E("div", {
32
+ /* @__PURE__ */ D("div", {
32
33
  className: "min-w-0",
33
- children: [/* @__PURE__ */ T("h3", {
34
+ children: [/* @__PURE__ */ E("h3", {
34
35
  className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
35
36
  children: e.title
36
- }), e.theme ? /* @__PURE__ */ T("p", {
37
+ }), e.theme ? /* @__PURE__ */ E("p", {
37
38
  className: "mt-1 line-clamp-2 text-sm text-text-secondary",
38
39
  children: e.theme
39
40
  }) : null]
40
41
  }),
41
- /* @__PURE__ */ E("div", {
42
+ /* @__PURE__ */ D("div", {
42
43
  className: "mt-auto flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted",
43
- children: [/* @__PURE__ */ E("span", {
44
+ children: [/* @__PURE__ */ D("span", {
44
45
  className: "inline-flex items-center gap-1",
45
46
  children: [
46
- /* @__PURE__ */ T(b, { size: 13 }),
47
+ /* @__PURE__ */ E(x, { size: 13 }),
47
48
  " ",
48
49
  s(e.startAt),
49
50
  " –",
50
51
  " ",
51
52
  s(e.endAt)
52
53
  ]
53
- }), /* @__PURE__ */ E("span", {
54
+ }), /* @__PURE__ */ D("span", {
54
55
  className: "inline-flex items-center gap-1",
55
56
  children: [
56
- /* @__PURE__ */ T(w, { size: 13 }),
57
+ /* @__PURE__ */ E(T, { size: 13 }),
57
58
  " ",
58
59
  n("technospam.hackathons.teamSize", "Team of {{size}}", { size: e.maxTeamSize })
59
60
  ]
60
61
  })]
61
62
  }),
62
- /* @__PURE__ */ E("div", {
63
+ /* @__PURE__ */ D("div", {
63
64
  className: "flex items-center gap-1.5 text-sm font-medium text-accent-teal",
64
65
  children: [
65
- /* @__PURE__ */ T(y, { size: 15 }),
66
+ /* @__PURE__ */ E(b, { size: 15 }),
66
67
  " ",
67
68
  n("technospam.hackathons.prizePool", "{{prize}} prize pool", { prize: c(e.prizePool, e.currency) })
68
69
  ]
69
70
  }),
70
- e.tags.length ? /* @__PURE__ */ T("div", {
71
+ e.tags.length ? /* @__PURE__ */ E("div", {
71
72
  className: "flex flex-wrap gap-1.5",
72
- children: e.tags.slice(0, 4).map((e) => /* @__PURE__ */ T(D, {
73
+ children: e.tags.slice(0, 4).map((e) => /* @__PURE__ */ E(O, {
73
74
  variant: "secondary",
74
75
  className: "text-[11px]",
75
76
  children: e
@@ -79,83 +80,83 @@ function P({ hackathon: e, onOpen: t }) {
79
80
  })]
80
81
  });
81
82
  }
82
- function F() {
83
- let o = t(), { workspaceId: s } = e(), { emit: l } = n(), b = r(), [w, D] = v("ALL"), [O, F] = v(""), { limit: I, more: L } = g(), R = _(() => [
83
+ function I() {
84
+ let o = t(), { workspaceId: s } = e(), { emit: l } = n(), x = r(), [T, O] = y("ALL"), [k, I] = y(""), { limit: L, more: R } = g(), z = v(() => [
84
85
  {
85
86
  value: "ALL",
86
- label: b("technospam.hackathons.filterAll", "All")
87
+ label: x("technospam.hackathons.filterAll", "All")
87
88
  },
88
89
  {
89
90
  value: "REGISTRATION_OPEN",
90
- label: b("technospam.hackathons.filterRegistrationOpen", "Registration open")
91
+ label: x("technospam.hackathons.filterRegistrationOpen", "Registration open")
91
92
  },
92
93
  {
93
94
  value: "IN_PROGRESS",
94
- label: b("technospam.hackathons.filterInProgress", "In progress")
95
+ label: x("technospam.hackathons.filterInProgress", "In progress")
95
96
  },
96
97
  {
97
98
  value: "JUDGING",
98
- label: b("technospam.hackathons.filterJudging", "Judging")
99
+ label: x("technospam.hackathons.filterJudging", "Judging")
99
100
  },
100
101
  {
101
102
  value: "COMPLETED",
102
- label: b("technospam.hackathons.filterCompleted", "Completed")
103
+ label: x("technospam.hackathons.filterCompleted", "Completed")
103
104
  }
104
- ], [b]), { data: z, loading: B, error: V } = h({
105
- pagination: m(I),
106
- ...w === "ALL" ? {} : { status: w }
107
- }), H = z?.tecListHackathons?.items ?? [], U = z?.tecListHackathons?.pageInfo?.hasMore ?? !1, W = _(() => {
108
- let e = O.trim().toLowerCase();
109
- return e ? H.filter((t) => t.title.toLowerCase().includes(e) || (t.theme ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : H;
110
- }, [H, O]), G = W.filter((e) => e.status === "REGISTRATION_OPEN").length, K = W.reduce((e, t) => e + t.prizePool, 0), q = W[0]?.currency ?? "USD";
111
- return /* @__PURE__ */ E(i, {
105
+ ], [x]), { data: B, loading: V, error: H } = h({
106
+ pagination: m(L),
107
+ ...T === "ALL" ? {} : { status: T }
108
+ }), U = B?.tecListHackathons?.items ?? [], W = B?.tecListHackathons?.pageInfo?.hasMore ?? !1, G = v(() => {
109
+ let e = k.trim().toLowerCase();
110
+ return e ? U.filter((t) => t.title.toLowerCase().includes(e) || (t.theme ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : U;
111
+ }, [U, k]), K = G.filter((e) => e.status === "REGISTRATION_OPEN").length, q = G.reduce((e, t) => e + t.prizePool, 0), J = G[0]?.currency ?? "USD";
112
+ return /* @__PURE__ */ D(i, {
112
113
  hero: !0,
113
- title: b("technospam.hackathons.title", "Hackathons"),
114
- description: b("technospam.hackathons.description", "Compete, collaborate, and ship real projects with a team in a fixed window."),
115
- children: [/* @__PURE__ */ E("div", {
114
+ title: x("technospam.hackathons.title", "Hackathons"),
115
+ description: x("technospam.hackathons.description", "Compete, collaborate, and ship real projects with a team in a fixed window."),
116
+ children: [/* @__PURE__ */ D("div", {
116
117
  className: "mb-5 grid grid-cols-1 gap-3 sm:grid-cols-3",
117
118
  children: [
118
- /* @__PURE__ */ T(a, {
119
- label: b("technospam.hackathons.statHackathons", "Hackathons"),
120
- value: W.length,
121
- icon: C,
119
+ /* @__PURE__ */ E(a, {
120
+ label: x("technospam.hackathons.statHackathons", "Hackathons"),
121
+ value: G.length,
122
+ icon: w,
122
123
  tone: "purple"
123
124
  }),
124
- /* @__PURE__ */ T(a, {
125
- label: b("technospam.hackathons.statRegistrationOpen", "Registration open"),
126
- value: G,
127
- icon: S,
125
+ /* @__PURE__ */ E(a, {
126
+ label: x("technospam.hackathons.statRegistrationOpen", "Registration open"),
127
+ value: K,
128
+ icon: C,
128
129
  tone: "green"
129
130
  }),
130
- /* @__PURE__ */ T(a, {
131
- label: b("technospam.hackathons.statTotalPrizePool", "Total prize pool"),
132
- value: c(K, q),
133
- icon: y,
131
+ /* @__PURE__ */ E(a, {
132
+ label: x("technospam.hackathons.statTotalPrizePool", "Total prize pool"),
133
+ value: c(q, J),
134
+ icon: b,
134
135
  tone: "amber"
135
136
  })
136
137
  ]
137
- }), /* @__PURE__ */ E(A, {
138
+ }), /* @__PURE__ */ D(j, {
138
139
  className: "gap-0",
139
- value: w,
140
- onValueChange: (e) => D(e),
141
- children: [/* @__PURE__ */ E("div", {
140
+ value: T,
141
+ onValueChange: (e) => O(e),
142
+ children: [/* @__PURE__ */ D("div", {
142
143
  className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
143
- children: [/* @__PURE__ */ T(M, { children: R.map((e) => /* @__PURE__ */ T(N, {
144
+ children: [/* @__PURE__ */ E(N, { children: z.map((e) => /* @__PURE__ */ E(P, {
144
145
  value: e.value,
145
146
  children: e.label
146
- }, e.value)) }), /* @__PURE__ */ E("div", {
147
+ }, e.value)) }), /* @__PURE__ */ D("div", {
147
148
  className: "relative w-full sm:w-72",
148
- children: [/* @__PURE__ */ T(x, {
149
+ children: [/* @__PURE__ */ E(S, {
149
150
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
150
151
  size: 16
151
- }), /* @__PURE__ */ T(k, {
152
+ }), /* @__PURE__ */ E(A, {
152
153
  className: "pl-9",
153
- "aria-label": b("technospam.hackathons.searchLabel", "Search hackathons"),
154
- placeholder: b("technospam.hackathons.searchPlaceholder", "Search hackathons…"),
155
- value: O,
156
- onChange: (e) => F(e.target.value),
154
+ "aria-label": x("technospam.hackathons.searchLabel", "Search hackathons"),
155
+ placeholder: x("technospam.hackathons.searchPlaceholder", "Search hackathons…"),
156
+ value: k,
157
+ onChange: (e) => I(e.target.value),
157
158
  onBlur: () => {
158
- let e = O.trim();
159
+ let e = k.trim();
159
160
  e && l("technospam.search.performed", {
160
161
  workspaceId: s,
161
162
  entityType: "hackathon",
@@ -164,18 +165,19 @@ function F() {
164
165
  }
165
166
  })]
166
167
  })]
167
- }), /* @__PURE__ */ E(j, {
168
- value: w,
169
- children: [B && !W.length ? /* @__PURE__ */ T(p, {}) : V ? /* @__PURE__ */ T(f, {
170
- title: b("technospam.common.errorLoadHackathons", "Couldn't load hackathons"),
171
- error: V
172
- }) : W.length === 0 ? /* @__PURE__ */ T(d, {
173
- icon: /* @__PURE__ */ T(C, { size: 26 }),
174
- title: w === "ALL" ? b("technospam.hackathons.emptyDefaultTitle", "No hackathons yet") : b("technospam.hackathons.emptyFilteredTitle", "No hackathons in this stage"),
175
- description: w === "ALL" ? b("technospam.hackathons.emptyDefaultDesc", "Upcoming hackathons will appear here. Check back soon — TechnoSpam is pre-launch.") : b("technospam.hackathons.emptyFilteredDesc", "Try a different stage filter.")
176
- }) : /* @__PURE__ */ T("div", {
168
+ }), /* @__PURE__ */ D(M, {
169
+ value: T,
170
+ className: _,
171
+ children: [V && !G.length ? /* @__PURE__ */ E(p, {}) : H ? /* @__PURE__ */ E(f, {
172
+ title: x("technospam.common.errorLoadHackathons", "Couldn't load hackathons"),
173
+ error: H
174
+ }) : G.length === 0 ? /* @__PURE__ */ E(d, {
175
+ icon: /* @__PURE__ */ E(w, { size: 26 }),
176
+ title: T === "ALL" ? x("technospam.hackathons.emptyDefaultTitle", "No hackathons yet") : x("technospam.hackathons.emptyFilteredTitle", "No hackathons in this stage"),
177
+ description: T === "ALL" ? x("technospam.hackathons.emptyDefaultDesc", "Upcoming hackathons will appear here. Check back soon — TechnoSpam is pre-launch.") : x("technospam.hackathons.emptyFilteredDesc", "Try a different stage filter.")
178
+ }) : /* @__PURE__ */ E("div", {
177
179
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
178
- children: W.map((e) => /* @__PURE__ */ T(P, {
180
+ children: G.map((e) => /* @__PURE__ */ E(F, {
179
181
  hackathon: e,
180
182
  onOpen: () => {
181
183
  l("technospam.hackathon.viewed", {
@@ -185,16 +187,16 @@ function F() {
185
187
  }), o(`/hackathons/hackathon-details-page?id=${e.id}`);
186
188
  }
187
189
  }, e.id))
188
- }), /* @__PURE__ */ T(u, {
189
- hasMore: U,
190
- loading: B,
191
- onClick: L
190
+ }), /* @__PURE__ */ E(u, {
191
+ hasMore: W,
192
+ loading: V,
193
+ onClick: R
192
194
  })]
193
195
  })]
194
196
  })]
195
197
  });
196
198
  }
197
199
  //#endregion
198
- export { F as HackathonsHackathonsPagePage };
200
+ export { I as HackathonsHackathonsPagePage };
199
201
 
200
202
  //# sourceMappingURL=HackathonsHackathonsPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HackathonsHackathonsPagePage.js","names":[],"sources":["../../../src/technospam/pages/HackathonsHackathonsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Award, CalendarDays, Search, Sparkles, Trophy, Users } from 'lucide-react';\nimport {\n Badge,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useHackathonsQuery,\n usePageLimit,\n type TecHackathon,\n type TecHackathonStatus,\n} from '../api/technospamApi';\nimport { formatDate, formatMoney } from '../utils/format';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction HackathonCard({ hackathon, onOpen }: { hackathon: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <MediaCover height=\"h-32\" seed={hackathon.theme ?? hackathon.id} icon={Trophy}>\n <StatusBadge status={hackathon.status} />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {hackathon.title}\n </h3>\n {hackathon.theme ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{hackathon.theme}</p>\n ) : null}\n </div>\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 <CalendarDays size={13} /> {formatDate(hackathon.startAt)} –{' '}\n {formatDate(hackathon.endAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Users size={13} />{' '}\n {tr('technospam.hackathons.teamSize', 'Team of {{size}}', {\n size: hackathon.maxTeamSize,\n })}\n </span>\n </div>\n\n <div className=\"flex items-center gap-1.5 text-sm font-medium text-accent-teal\">\n <Award size={15} />{' '}\n {tr('technospam.hackathons.prizePool', '{{prize}} prize pool', {\n prize: formatMoney(hackathon.prizePool, hackathon.currency),\n })}\n </div>\n\n {hackathon.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {hackathon.tags.slice(0, 4).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function HackathonsHackathonsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [status, setStatus] = useState<'ALL' | TecHackathonStatus>('ALL');\n const [search, setSearch] = useState('');\n const { limit, more } = usePageLimit();\n\n const statuses = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.hackathons.filterAll', 'All') },\n {\n value: 'REGISTRATION_OPEN' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterRegistrationOpen', 'Registration open'),\n },\n {\n value: 'IN_PROGRESS' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterInProgress', 'In progress'),\n },\n {\n value: 'JUDGING' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterJudging', 'Judging'),\n },\n {\n value: 'COMPLETED' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterCompleted', 'Completed'),\n },\n ],\n [tr]\n );\n\n const { data, loading, error } = useHackathonsQuery({\n pagination: pageArg(limit),\n ...(status !== 'ALL' ? { status } : {}),\n });\n\n const allHackathons = data?.tecListHackathons?.items ?? [];\n const hasMore = data?.tecListHackathons?.pageInfo?.hasMore ?? false;\n const hackathons = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return allHackathons;\n return allHackathons.filter(\n (h) =>\n h.title.toLowerCase().includes(q) ||\n (h.theme ?? '').toLowerCase().includes(q) ||\n h.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [allHackathons, search]);\n const openCount = hackathons.filter((h) => h.status === 'REGISTRATION_OPEN').length;\n const totalPrize = hackathons.reduce((s, h) => s + h.prizePool, 0);\n const prizeCurrency = hackathons[0]?.currency ?? 'USD';\n\n return (\n <PageLayout\n hero\n title={tr('technospam.hackathons.title', 'Hackathons')}\n description={tr(\n 'technospam.hackathons.description',\n 'Compete, collaborate, and ship real projects with a team in a fixed window.'\n )}\n >\n <div className=\"mb-5 grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatCard\n label={tr('technospam.hackathons.statHackathons', 'Hackathons')}\n value={hackathons.length}\n icon={Trophy}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.hackathons.statRegistrationOpen', 'Registration open')}\n value={openCount}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.hackathons.statTotalPrizePool', 'Total prize pool')}\n value={formatMoney(totalPrize, prizeCurrency)}\n icon={Award}\n tone=\"amber\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={status}\n onValueChange={(v) => setStatus(v as 'ALL' | TecHackathonStatus)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {statuses.map((s) => (\n <TabsTrigger key={s.value} value={s.value}>\n {s.label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n aria-label={tr('technospam.hackathons.searchLabel', 'Search hackathons')}\n placeholder={tr('technospam.hackathons.searchPlaceholder', 'Search hackathons…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'hackathon',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <TabsContent value={status}>\n {loading && !hackathons.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={error}\n />\n ) : hackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={26} />}\n title={\n status !== 'ALL'\n ? tr('technospam.hackathons.emptyFilteredTitle', 'No hackathons in this stage')\n : tr('technospam.hackathons.emptyDefaultTitle', 'No hackathons yet')\n }\n description={\n status !== 'ALL'\n ? tr('technospam.hackathons.emptyFilteredDesc', 'Try a different stage filter.')\n : tr(\n 'technospam.hackathons.emptyDefaultDesc',\n 'Upcoming hackathons will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {hackathons.map((hackathon) => (\n <HackathonCard\n key={hackathon.id}\n hackathon={hackathon}\n onOpen={() => {\n emit('technospam.hackathon.viewed', {\n workspaceId,\n entityId: hackathon.id,\n entityType: 'TEC_HACKATHON',\n });\n navigate(`/hackathons/hackathon-details-page?id=${hackathon.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"HackathonsHackathonsPagePage.js","names":[],"sources":["../../../src/technospam/pages/HackathonsHackathonsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Award, CalendarDays, Search, Sparkles, Trophy, Users } from 'lucide-react';\nimport {\n Badge,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { TAB_PANEL_FOCUS_CLASS } from '../lib/a11y';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useHackathonsQuery,\n usePageLimit,\n type TecHackathon,\n type TecHackathonStatus,\n} from '../api/technospamApi';\nimport { formatDate, formatMoney } from '../utils/format';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction HackathonCard({ hackathon, onOpen }: { hackathon: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <MediaCover height=\"h-32\" seed={hackathon.theme ?? hackathon.id} icon={Trophy}>\n <StatusBadge status={hackathon.status} />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {hackathon.title}\n </h3>\n {hackathon.theme ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{hackathon.theme}</p>\n ) : null}\n </div>\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 <CalendarDays size={13} /> {formatDate(hackathon.startAt)} –{' '}\n {formatDate(hackathon.endAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Users size={13} />{' '}\n {tr('technospam.hackathons.teamSize', 'Team of {{size}}', {\n size: hackathon.maxTeamSize,\n })}\n </span>\n </div>\n\n <div className=\"flex items-center gap-1.5 text-sm font-medium text-accent-teal\">\n <Award size={15} />{' '}\n {tr('technospam.hackathons.prizePool', '{{prize}} prize pool', {\n prize: formatMoney(hackathon.prizePool, hackathon.currency),\n })}\n </div>\n\n {hackathon.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {hackathon.tags.slice(0, 4).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function HackathonsHackathonsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [status, setStatus] = useState<'ALL' | TecHackathonStatus>('ALL');\n const [search, setSearch] = useState('');\n const { limit, more } = usePageLimit();\n\n const statuses = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.hackathons.filterAll', 'All') },\n {\n value: 'REGISTRATION_OPEN' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterRegistrationOpen', 'Registration open'),\n },\n {\n value: 'IN_PROGRESS' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterInProgress', 'In progress'),\n },\n {\n value: 'JUDGING' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterJudging', 'Judging'),\n },\n {\n value: 'COMPLETED' as TecHackathonStatus,\n label: tr('technospam.hackathons.filterCompleted', 'Completed'),\n },\n ],\n [tr]\n );\n\n const { data, loading, error } = useHackathonsQuery({\n pagination: pageArg(limit),\n ...(status !== 'ALL' ? { status } : {}),\n });\n\n const allHackathons = data?.tecListHackathons?.items ?? [];\n const hasMore = data?.tecListHackathons?.pageInfo?.hasMore ?? false;\n const hackathons = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return allHackathons;\n return allHackathons.filter(\n (h) =>\n h.title.toLowerCase().includes(q) ||\n (h.theme ?? '').toLowerCase().includes(q) ||\n h.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [allHackathons, search]);\n const openCount = hackathons.filter((h) => h.status === 'REGISTRATION_OPEN').length;\n const totalPrize = hackathons.reduce((s, h) => s + h.prizePool, 0);\n const prizeCurrency = hackathons[0]?.currency ?? 'USD';\n\n return (\n <PageLayout\n hero\n title={tr('technospam.hackathons.title', 'Hackathons')}\n description={tr(\n 'technospam.hackathons.description',\n 'Compete, collaborate, and ship real projects with a team in a fixed window.'\n )}\n >\n <div className=\"mb-5 grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatCard\n label={tr('technospam.hackathons.statHackathons', 'Hackathons')}\n value={hackathons.length}\n icon={Trophy}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.hackathons.statRegistrationOpen', 'Registration open')}\n value={openCount}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.hackathons.statTotalPrizePool', 'Total prize pool')}\n value={formatMoney(totalPrize, prizeCurrency)}\n icon={Award}\n tone=\"amber\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={status}\n onValueChange={(v) => setStatus(v as 'ALL' | TecHackathonStatus)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {statuses.map((s) => (\n <TabsTrigger key={s.value} value={s.value}>\n {s.label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n aria-label={tr('technospam.hackathons.searchLabel', 'Search hackathons')}\n placeholder={tr('technospam.hackathons.searchPlaceholder', 'Search hackathons…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'hackathon',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <TabsContent value={status} className={TAB_PANEL_FOCUS_CLASS}>\n {loading && !hackathons.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={error}\n />\n ) : hackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={26} />}\n title={\n status !== 'ALL'\n ? tr('technospam.hackathons.emptyFilteredTitle', 'No hackathons in this stage')\n : tr('technospam.hackathons.emptyDefaultTitle', 'No hackathons yet')\n }\n description={\n status !== 'ALL'\n ? tr('technospam.hackathons.emptyFilteredDesc', 'Try a different stage filter.')\n : tr(\n 'technospam.hackathons.emptyDefaultDesc',\n 'Upcoming hackathons will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {hackathons.map((hackathon) => (\n <HackathonCard\n key={hackathon.id}\n hackathon={hackathon}\n onOpen={() => {\n emit('technospam.hackathon.viewed', {\n workspaceId,\n entityId: hackathon.id,\n entityType: 'TEC_HACKATHON',\n });\n navigate(`/hackathons/hackathon-details-page?id=${hackathon.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MentorshipSessionsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"names":[],"mappings":"AAySA,wBAAgB,0BAA0B,gCAmPzC"}
1
+ {"version":3,"file":"MentorshipSessionsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"names":[],"mappings":"AA0SA,wBAAgB,0BAA0B,gCAmPzC"}