-
Notifications
You must be signed in to change notification settings - Fork 3
Commit that shit v2 #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e0e3333
e9aa0d5
438101d
7101dce
6ef6398
4766ef2
93e6a51
73399c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import React from "react"; | ||
|
|
||
| interface PointRow { | ||
| activity: string; | ||
| points: string; | ||
| note?: string; | ||
| } | ||
|
|
||
| interface PointCategory { | ||
| label: string; | ||
| rows: PointRow[]; | ||
| } | ||
|
|
||
| const categories: PointCategory[] = [ | ||
| { | ||
| label: "Meetings", | ||
| rows: [ | ||
| { activity: "Attend in-person GBM", points: "10 pts" }, | ||
| { activity: "Attend online GBM", points: "~5 pts" }, | ||
| { activity: "Attend lounge hours", points: "5 pts" }, | ||
| ], | ||
| }, | ||
| { | ||
| label: "Participation", | ||
| rows: [ | ||
| { activity: "Ask questions / interact", points: "1 pt", note: "per time" }, | ||
| { activity: "Bonus participation", points: "5 pts" }, | ||
| { activity: "Submit workshop challenge", points: "20 pts" }, | ||
| ], | ||
| }, | ||
| { | ||
| label: "Discord", | ||
| rows: [ | ||
| { activity: "General engagement", points: "2 pts", note: "per mo · max 10/sem" }, | ||
| { activity: "Helpful post / solve query", points: "5 pts", note: "max 15/sem" }, | ||
| ], | ||
| }, | ||
| ]; | ||
|
|
||
| const PointsBreakdownTable: React.FC = () => { | ||
| return ( | ||
| <div className="bg-black/40 backdrop-blur-xl shadow-2xl text-gray-300 w-full rounded-xl overflow-hidden border border-white/10"> | ||
| {/* 2-D Table */} | ||
| <table className="w-full text-sm border-collapse"> | ||
| <caption className="sr-only"> | ||
| Points breakdown by category, activity, and points | ||
| </caption> | ||
| <thead> | ||
| <tr className="bg-white/[0.06]"> | ||
| <th | ||
| scope="col" | ||
| className="text-left px-3 py-2.5 text-gray-400 font-semibold uppercase tracking-widest text-[10px] border-b border-r border-white/10 w-[24%]" | ||
| > | ||
| Category | ||
| </th> | ||
| <th | ||
| scope="col" | ||
| className="text-left px-3 py-2.5 text-gray-400 font-semibold uppercase tracking-widest text-[10px] border-b border-r border-white/10" | ||
| > | ||
| Activity | ||
| </th> | ||
| <th | ||
| scope="col" | ||
|
Comment on lines
+59
to
+63
|
||
| className="text-center px-3 py-2.5 text-gray-400 font-semibold uppercase tracking-widest text-[10px] border-b border-white/10 w-[20%]" | ||
| > | ||
| Pts | ||
| </th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {categories.map((cat, ci) => | ||
| cat.rows.map((row, ri) => ( | ||
| <tr | ||
| key={`${ci}-${ri}`} | ||
| className="border-b border-white/[0.05] last:border-0 hover:bg-white/[0.04] transition-colors" | ||
| > | ||
| {/* Category cell – only on first row of each category */} | ||
| {ri === 0 ? ( | ||
| <td | ||
| rowSpan={cat.rows.length} | ||
| className="px-3 py-3 align-middle border-r border-white/10 text-[11px] font-semibold text-gray-400 uppercase tracking-widest leading-snug" | ||
| > | ||
| {cat.label} | ||
| </td> | ||
| ) : null} | ||
|
|
||
| {/* Activity */} | ||
| <td className="px-3 py-3 border-r border-white/10 text-gray-200 leading-snug"> | ||
| {row.activity} | ||
| {row.note && ( | ||
| <span className="block text-gray-500 text-[11px] mt-0.5">{row.note}</span> | ||
| )} | ||
| </td> | ||
|
|
||
| {/* Points badge */} | ||
| <td className="px-3 py-3 text-center"> | ||
| <span className="inline-block bg-blue-500/20 border border-blue-400/30 text-blue-300 font-bold text-xs px-2 py-1 rounded-md tabular-nums whitespace-nowrap"> | ||
| {row.points} | ||
| </span> | ||
| </td> | ||
| </tr> | ||
| )) | ||
| )} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default PointsBreakdownTable; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,16 +12,25 @@ interface ProductCarouselProps { | |||||||||||||||||||||||||||||||||||||||||||||
| slides: CarouselSlide[]; | ||||||||||||||||||||||||||||||||||||||||||||||
| autoplayDelay?: number; | ||||||||||||||||||||||||||||||||||||||||||||||
| videoUrl?: string; | ||||||||||||||||||||||||||||||||||||||||||||||
| rightPanel?: React.ReactNode; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const ProductCarousel: React.FC<ProductCarouselProps> = ({ | ||||||||||||||||||||||||||||||||||||||||||||||
| slides, | ||||||||||||||||||||||||||||||||||||||||||||||
| autoplayDelay = 5000, | ||||||||||||||||||||||||||||||||||||||||||||||
| videoUrl = "https://framerusercontent.com/assets/sRXQsZpCuTpukMUfotGcRUuvg.mp4", | ||||||||||||||||||||||||||||||||||||||||||||||
| rightPanel, | ||||||||||||||||||||||||||||||||||||||||||||||
| }) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const [currentIndex, setCurrentIndex] = useState(0); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [videoLoaded, setVideoLoaded] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [videoError, setVideoError] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| const [isMobile, setIsMobile] = useState(() => window.innerWidth < 1024); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const handleResize = () => setIsMobile(window.innerWidth < 1024); | ||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener("resize", handleResize); | ||||||||||||||||||||||||||||||||||||||||||||||
| return () => window.removeEventListener("resize", handleResize); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+32
|
||||||||||||||||||||||||||||||||||||||||||||||
| const [isMobile, setIsMobile] = useState(() => window.innerWidth < 1024); | |
| useEffect(() => { | |
| const handleResize = () => setIsMobile(window.innerWidth < 1024); | |
| window.addEventListener("resize", handleResize); | |
| return () => window.removeEventListener("resize", handleResize); | |
| const [isMobile, setIsMobile] = useState(false); | |
| useEffect(() => { | |
| if (typeof window === "undefined") { | |
| return; | |
| } | |
| const handleResize = () => setIsMobile(window.innerWidth < 1024); | |
| // Set initial value on mount in the browser | |
| handleResize(); | |
| window.addEventListener("resize", handleResize); | |
| return () => { | |
| window.removeEventListener("resize", handleResize); | |
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For better table accessibility, consider adding an (sr-only)
<caption>describing the table and usingscope="col"on the header cells so screen readers can correctly associate headers with data cells.