Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4ce43e1
Ses v3 migration (#19)
sohelshekhIn Jan 22, 2026
1c39021
feat: enhance email verification process and improve user experience
sohelshekhIn Jan 23, 2026
440e4b1
refactor: streamline AWS SDK integration and improve S3 client usage
sohelshekhIn Jan 25, 2026
57cf1ea
feat: update hackathon year across components and emails
sohelshekhIn Jan 25, 2026
5275671
feat: implement phase-aware features and improve application handling
sohelshekhIn Feb 1, 2026
e8bd863
fix: application received email not sending due to tailwind redner is…
sohelshekhIn Feb 1, 2026
8d9270b
fix: build error for phase hook
sohelshekhIn Feb 1, 2026
7a99b5b
feat: add email preview functionality with multiple templates
sohelshekhIn Feb 2, 2026
c326874
feat: update short question and validation limits in HackerApplicatio…
sohelshekhIn Feb 4, 2026
4cbdf64
feat: add new email preview func for dev
sohelshekhIn Feb 4, 2026
2319264
feat: design new email templates
sohelshekhIn Feb 4, 2026
af9b153
refactor: update contact email site wide from hello@ to hi@hc
sohelshekhIn Feb 4, 2026
bc72114
refactor: remove unused package-lock.json and optimize CountdownTimer…
sohelshekhIn Feb 4, 2026
73e3347
feat: use current email for hackerApplication
sohelshekhIn Feb 4, 2026
8c0df6e
feat: add phone number functionality to hacker application
sohelshekhIn Feb 4, 2026
316095e
fix: correct URL in ResetPasswordEmail component and ensure consisten…
sohelshekhIn Feb 4, 2026
c4c29c0
chore: update favicon and sidebar logo
sohelshekhIn Feb 4, 2026
7fbb7e6
refactor: dark aurora login page
sohelshekhIn Feb 4, 2026
45a1828
chore: replace package-lock.json with bun.lock and update .gitignore
sohelshekhIn Feb 4, 2026
b606fa0
Merge branch 'app-open-prep' into dark-login
sohelshekhIn Feb 4, 2026
7ce886b
refactor: enhance authentication UI and improve layout consistency
sohelshekhIn Feb 5, 2026
b804c50
refactor: enhance dark mode styling and layout across components
sohelshekhIn Feb 5, 2026
acf4c08
feat: revamp dashboard components with engaging messaging and visuals
sohelshekhIn Feb 5, 2026
1491d6e
fix: eslint errors
sohelshekhIn Feb 5, 2026
8f89b15
feat: enhance SEO and site structure for Hack Canada Hacker Portal
sohelshekhIn Feb 5, 2026
e8e3a77
fix: seo verification removed
sohelshekhIn Feb 5, 2026
ed9791b
Merge branch 'dark-login' into staging
sohelshekhIn Feb 5, 2026
75b2b37
refactor: update metadata and phase dates
sohelshekhIn Feb 5, 2026
68af50a
refactor: update authentication components for improved layout and st…
sohelshekhIn Feb 5, 2026
2caff68
refactor: update authentication components for improved layout and st…
sohelshekhIn Feb 5, 2026
69bcf1e
Merge branch 'main' into staging
sohelshekhIn Feb 5, 2026
11da2e9
Merge branch 'dark-login' into staging
sohelshekhIn Feb 5, 2026
de1a760
feat: enhance SubmissionSuccess component with animations and timeline
sohelshekhIn Feb 5, 2026
f494470
feat: add review styling
sohelshekhIn Feb 5, 2026
c04dd2e
fix: import FormLabel in ShortAnswersStep
sohelshekhIn Feb 5, 2026
7c166da
refactor: remove hardcoded questions from ShortAnswersStep component
sohelshekhIn Feb 5, 2026
6f8b92c
fix: update Discord invite URLs to the correct links
sohelshekhIn Feb 5, 2026
bef3be4
Post app prep (#30)
sohelshekhIn Feb 5, 2026
10af8dd
Bug fixes (#32)
sohelshekhIn Feb 6, 2026
1a81485
Merge branch 'main' into staging
sohelshekhIn Feb 6, 2026
ff6aa45
Post app prep (#34)
sohelshekhIn Feb 6, 2026
a104bb6
extend hacker apps
sohelshekhIn Feb 23, 2026
d402eec
Merge branch 'main' into staging
sohelshekhIn Feb 23, 2026
b09c6ea
fix timezone issue and add vol-men apps
sohelshekhIn Feb 23, 2026
92d8c74
Merge branch 'staging' of https://github.com/Hack-Canada/Daedalus-Hac…
sohelshekhIn Feb 23, 2026
ea382ce
Implement application reminder email functionality
sohelshekhIn Feb 24, 2026
3526d6c
Update schedule view (#38)
sohelshekhIn Feb 24, 2026
9aaca1e
feat: add shop menu
Feb 26, 2026
fcc5fbc
feat: add auth checks to shop
Feb 26, 2026
d7bf5b4
feat: add the shop items
GuyOnWifi Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/(dashboard)/rsvp/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const RSVPPage = async () => {

// Format the date for display
const sevenDaysLaterFormatted = sevenDaysLater.toLocaleDateString("en-US", {
timeZone: "America/Toronto",
weekday: "long",
year: "numeric",
month: "long",
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/schedule/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function Loading() {
</div>

{/* Schedule Grid Skeleton */}
<div className="border-border space-y-4 rounded-lg border bg-white p-4">
<div className="border-border space-y-4 rounded-lg border bg-background p-4">
<div className="flex gap-4">
<Skeleton className="h-16 w-20 shrink-0" />
<div className="flex-1 space-y-2">
Expand Down
22 changes: 2 additions & 20 deletions app/(dashboard)/schedule/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { getCurrentUser } from "@/auth";
import { ScheduleService } from "@/lib/services/schedule";
import { EmptyPage } from "@/components/EmptyPage";
import PageWrapper from "@/components/PageWrapper";
import ScheduleGrid from "@/components/schedule/ScheduleGrid";
import ScheduleLegend from "@/components/schedule/ScheduleLegend";
import ScheduleView from "@/components/schedule/ScheduleView";

export const metadata: Metadata = {
title: "Schedule",
Expand All @@ -29,37 +28,20 @@ export default async function SchedulePage() {
);
}

// Get schedule data with fallback mechanism
const {
data: scheduleData,
source,
error,
} = await ScheduleService.getSchedule();

// Log the data source for debugging
console.info(`Schedule loaded from: ${source}`);
if (error) {
console.error("Schedule service error:", error);
}

return (
<PageWrapper className="3xl:max-w-screen-2xl max-w-screen-2xl">
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<div className="from-primary to-primary w-fit bg-linear-to-r via-sky-400 bg-clip-text text-transparent">
<h1 className="font-rubik text-3xl font-bold">Event Schedule</h1>
</div>
<p className="text-textSecondary">
All times are in Eastern Time (ET). Events and times are subject to
change.
</p>
</div>

<ScheduleLegend />
<div className="border-border bg-backgroundMuted rounded-lg border p-4">
<ScheduleGrid schedule={scheduleData} />
</div>
</div>
<ScheduleView schedule={scheduleData} />
</PageWrapper>
);
}
119 changes: 119 additions & 0 deletions app/(dashboard)/shops/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { redirect } from "next/navigation";
import { getCurrentUser } from "@/auth";
import { eq } from "drizzle-orm";

import { db } from "@/lib/db";
import { shopItems, userBalance } from "@/lib/db/schema";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { EmptyPage } from "@/components/EmptyPage";

export default async function ShopsPage() {
const currentUser = await getCurrentUser();

if (!currentUser?.id) {
redirect("/sign-in");
}

if (currentUser.role === "unassigned") {
return (
<EmptyPage
title="Shop"
message="Sorry, this feature is only available to participants."
/>
);
}

const items = await db.select().from(shopItems);

const balance = await db
.select()
.from(userBalance)
.where(eq(userBalance.userId, currentUser.id));

return (
<div className="container mx-auto h-full py-8">
<div className="mb-8 flex items-center justify-between">
<h1 className="text-3xl font-bold tracking-tight">Shop</h1>
<div className="bg-primary text-primary-foreground rounded-md px-4 py-2 font-semibold shadow-sm">
Points: {balance.length > 0 ? balance[0].points : 0}
</div>
</div>

{items.length === 0 ? (
<div className="bg-muted/50 text-muted-foreground flex w-full flex-col items-center justify-center rounded-lg border border-dashed p-12">
<p className="text-lg font-medium">No items available</p>
<p className="text-sm">Check back later for new rewards!</p>
</div>
) : (
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{items.map((item) => (
<div
key={item.id}
className="bg-card text-card-foreground hover:border-primary/20 flex flex-col overflow-hidden rounded-xl border shadow-sm transition-all hover:shadow-md"
>
{item.image ? (
<div className="bg-muted relative h-48 w-full border-b">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={item.image}
alt={item.itemName}
className="h-full w-full object-cover"
/>
</div>
) : (
<div className="bg-muted text-muted-foreground relative flex h-48 w-full items-center justify-center border-b">
<span className="text-sm font-medium">No Image</span>
</div>
)}
<div className="flex flex-grow flex-col p-5">
<h3 className="mb-1 line-clamp-1 text-lg font-semibold">
{item.itemName}
</h3>
{item.itemDescription && (
<p className="text-muted-foreground mb-4 line-clamp-3 flex-grow text-sm">
{item.itemDescription}
</p>
)}
<div className="mt-auto flex items-center justify-between border-t pt-4">
<div className="text-primary text-lg font-bold">
{item.purchasePrice}{" "}
<span className="text-muted-foreground text-sm font-normal">
pts
</span>
</div>
<Dialog>
<DialogTrigger asChild>
<button className="bg-primary/10 text-primary hover:bg-primary cursor-pointer rounded-md px-4 py-2 text-sm font-medium transition-colors hover:text-black">
Purchase
</button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Talk to an Organizer</DialogTitle>
<DialogDescription>
To purchase the{" "}
<strong className="text-foreground">
{item.itemName}
</strong>
, please find an organizer to deduct your points and
claim your item!
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
</div>
</div>
))}
</div>
)}
</div>
);
}
102 changes: 102 additions & 0 deletions app/(dashboard)/shops/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { loadEnvConfig } from "@next/env";

loadEnvConfig(process.cwd());

const dummyItems = [
{
itemName: "Additional Stickers",
itemDescription: "Personalize your gear with extra high-quality stickers.",
purchasePrice: 25,
stock: undefined,
},
{
itemName: "Peppero",
itemDescription:
"Crunchy biscuit sticks dipped in delicious chocolate coating.",
purchasePrice: 25,
stock: 36,
image:
"https://haisue.ca/cdn/shop/files/Lotte-Pepero-White-Cookie-NEW.jpg?v=1743470116&width=1214",
},
{
itemName: "Monster Energy Drinks",
itemDescription: "Fuel your grind and keep the caffeine levels high.",
purchasePrice: 25,
stock: 73,
image:
"https://voila.ca/images-v3/2d92d19c-0354-49c0-8a91-5260ed0bf531/6544fd59-97a2-4101-b813-11c9b34b3a6e/500x500.jpg",
},
{
itemName: "Full size candy bar",
itemDescription: "A substantial sugar boost for those late-night sessions.",
purchasePrice: 25,
stock: 24,
},
{
itemName: "GIGANTIC Maple Syrup",
itemDescription: "A massive bottle of liquid gold. Only one available!",
purchasePrice: 25,
stock: 1,
image:
"https://cloudinary.images-iherb.com/image/upload/f_auto,q_auto:eco/images/now/now06948/y/57.jpg",
},
{
itemName: "Microwave Popcorn",
itemDescription: "The classic movie snack, perfect for a quick break.",
purchasePrice: 25,
stock: 44,
image:
"https://mydormstore.ca/cdn/shop/files/microwave-popcorn-8264831.png?v=1758608146",
},
{
itemName: "Fruit Roll Ups",
itemDescription: "Sweet, stretchy, and nostalgic.",
purchasePrice: 25,
stock: 210,
image:
"https://hips.hearstapps.com/vidthumb/images/delish-watermelon-fruit-roll-ups-still002-1536587662.jpg?crop=0.75xw:1xh;center,top&resize=1200:*",
},
{
itemName: "Sponsor Swag",
itemDescription: "Exclusive gear provided by our amazing event partners.",
purchasePrice: 150,
stock: undefined,
},
{
itemName: "Special Plushie",
itemDescription:
"A soft, cuddly companion to keep you company at your desk.",
purchasePrice: 300,
stock: 15,
},
{
itemName: "Camera Lego Set",
itemDescription:
"Build your own vintage-style camera with this detailed brick set.",
purchasePrice: 450,
stock: 5,
image: "https://toynado.ca/cdn/shop/files/31147b_grande.jpg?v=1732563639",
},
];

async function seed() {
const { db } = await import("../../../lib/db/index");
const { shopItems } = await import("../../../lib/db/schema");

console.log("Emptying existing shop items...");
await db.delete(shopItems);

console.log("Seeding dummy shop items...");
for (const item of dummyItems) {
await db.insert(shopItems).values({
...item,
});
}

console.log("Seeded " + dummyItems.length + " shop items successfully!");
process.exit(0);
}

seed().catch((err) => {
console.error("Failed to seed database:", err);
});
10 changes: 10 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@
}
}

.schedule-light {
--color-background: #ffffff;
--color-backgroundMuted: #f8fafc;
--color-textPrimary: #1f2937;
--color-textSecondary: #4b5563;
--color-textMuted: #9ca3af;
--color-border: #e2e8f0;
--color-shadow: rgba(0, 0, 0, 0.05);
}

@media (max-height: 800px) {
.sidebar-content {
@apply space-y-16;
Expand Down
6 changes: 6 additions & 0 deletions components/applications/ApplicationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const ApplicationCard = ({
href,
} = application;

const isExternalLink = href.startsWith("http");

return (
<div
className={cn(
Expand All @@ -43,6 +45,10 @@ export const ApplicationCard = ({
href={disabled ? "" : href}
className={`absolute inset-0 z-20 ${disabled ? "cursor-not-allowed" : "cursor-pointer"}`}
aria-label={`Apply for ${title}`}
{...(isExternalLink && {
target: "_blank",
rel: "noopener noreferrer",
})}
/>
)}
{alreadyApplied && (
Expand Down
Loading