Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ pnpm i <dep_name> -E

# Update all packages
pnpm upgrade -L

# Database migration
pnpm drizzle-kit pull
pnpm drizzle-kit generate
pnpm drizzle-kit migrate
```

## Planned Features
Expand Down
1 change: 1 addition & 0 deletions declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module "*.css"
3,991 changes: 3,304 additions & 687 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/app/about/clients.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import clients from "data/clients.json"
import clients from "@/data/clients.json"

import { type ImageProps } from "~/lib/types"

Expand Down
2 changes: 1 addition & 1 deletion src/app/about/committee.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import committee from "data/committee.json"
import committee from "@/data/committee.json"
import Image from "next/image"
import * as React from "react"
import { siGithub } from "simple-icons"
Expand Down
2 changes: 1 addition & 1 deletion src/app/about/sponsors.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sponsors from "data/sponsors.json"
import sponsors from "@/data/sponsors.json"

import { type ImageProps } from "~/lib/types"

Expand Down
4 changes: 2 additions & 2 deletions src/app/create-account/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const Loading = () => {
<h2 className="font-semibold leading-none tracking-tight">Payment</h2>
<div className="text-sm text-muted-foreground">
<p>
Become a paying member of Coders for Causes for just $5 a year (ends on 31st Dec{" "}
{new Date().getFullYear()}). There are many benefits to becoming a member which include:
Become a paying member of Coders for Causes for just $5 a year (ends one year from now). There are many
benefits to becoming a member which include:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
Expand Down
4 changes: 2 additions & 2 deletions src/app/create-account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,8 @@ export default function CreateAccount() {
<h2 className="font-semibold leading-none tracking-tight">Payment</h2>
<div className="text-sm text-muted-foreground">
<p>
Become a paying member of Coders for Causes for just $5 a year (ends on 31st Dec{" "}
{new Date().getFullYear()}). There are many benefits to becoming a member which include:
Become a paying member of Coders for Causes for just $5 a year (ends one year from now). There are many
benefits to becoming a member which include:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
Expand Down
5 changes: 2 additions & 3 deletions src/app/create-account/payment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ const PaymentBlock = () => {
<h2 className="font-semibold leading-none tracking-tight">Payment</h2>
<div className="text-sm text-muted-foreground">
<p>
Become a paying member of Coders for Causes for just $5 a year (ends on 31st Dec{" "}
{new Date().getFullYear()}
). There are many benefits to becoming a member which include:
Become a paying member of Coders for Causes for just $5 a year (ends one year from now). There are many
benefits to becoming a member which include:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
Expand Down
18 changes: 18 additions & 0 deletions src/app/dashboard/admin/@users/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ const columns = (updateRole: ({ id, role }: UpdateUserRoleFunctionProps) => void
cell: (cell) => <span className="text-xs">{cell.getValue<React.ReactNode>()}</span>,
accessorFn: (user) => format(user.createdAt, "Pp", { locale: enAU }),
},
{
id: "Membership expiry",
header: "Membership expiry",
cell: ({ row }) => {
const user = row.original
if (!user.membership_expiry) {
return
}
const isExpired = new Date(user.membership_expiry) < new Date()
return (
<span className={`text-xs ${isExpired ? "text-destructive" : "text-foreground"}`}>
{format(user.membership_expiry, "Pp", { locale: enAU })}
</span>
)
},
accessorFn: (user) =>
user.membership_expiry ? format(user.membership_expiry, "Pp", { locale: enAU }) : "No membership",
},
{
header: "Socials",
cell: ({ row }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/app/events/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import eventList from "data/events.json"
import eventList from "@/data/events.json"
import { format, isAfter, isBefore, isSameDay, parse } from "date-fns"
import Link from "next/link"

Expand Down
2 changes: 1 addition & 1 deletion src/app/hack-for-homes/agenda/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import agendaList from "data/hackathon2025/agenda.json"
import agendaList from "@/data/hackathon2025/agenda.json"

import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"

Expand Down
2 changes: 1 addition & 1 deletion src/app/hack-for-homes/participants/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import participantList from "data/hackathon2025/participants.json"
import participantList from "@/data/hackathon2025/participants.json"

import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"

Expand Down
2 changes: 1 addition & 1 deletion src/app/hack-for-homes/sponsors.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sponsors from "data/hackathon2025/sponsors.json"
import sponsors from "@/data/hackathon2025/sponsors.json"

import { type ImageProps } from "~/lib/types"

Expand Down
4 changes: 2 additions & 2 deletions src/app/profile/[id]/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ const ProfilePage = ({ id, currentUser }: ProfilePageProps) => {
<div className="text-sm text-muted-foreground">
<p>
You&apos;re not a member with us yet. Become a paying member of Coders for Causes for just
$5 a year (ends on 31st Dec {new Date().getFullYear()}). There are many benefits to becoming
a member which include:
$5 a year (ends one year from now). There are many benefits to becoming a member which
include:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
Expand Down
2 changes: 1 addition & 1 deletion src/app/projects/(default)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import projectData from "data/projects.json"
import projectData from "@/data/projects.json"
import type { Metadata, Viewport } from "next"
import dynamic from "next/dynamic"

Expand Down
2 changes: 1 addition & 1 deletion src/app/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import projects from "data/projects.json"
import projects from "@/data/projects.json"
import Image from "next/image"
import Link from "next/link"

Expand Down
18 changes: 10 additions & 8 deletions src/components/email-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export const MembershipRenewalReminderEmail = ({
Firstname,
WebsiteLink = "https://codersforcauses.org/join",
}: MembershipRenewalReminderEmailProps) => {
let year = new Date().getFullYear()
const month = new Date().getMonth()
if (month < 6) {
year -= 1
}
// let year = new Date().getFullYear()
// const month = new Date().getMonth()
// if (month < 6) {
// year -= 1
// }

return (
<Html>
Expand All @@ -29,13 +29,15 @@ export const MembershipRenewalReminderEmail = ({
<Section>
<Text style={text}>Hi {Firstname},</Text>
<Text style={text}>
{`Your membership of Coders for Causes ends on 31st Dec ${year}. To keep your membership active, please renew it by clicking the button below.`}
{`Your membership of Coders for Causes ends on 31st Dec 2025. To keep your membership active, please renew it by clicking the button below.`}
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded year "2025" in the email template will become outdated after December 31, 2025. Since this is a reminder email that will be sent annually, the year should be dynamic. Consider using a prop or computing it dynamically based on the user's membership expiry date to ensure the email remains accurate in future years.

Copilot uses AI. Check for mistakes.
</Text>
<Text
style={text}
>{`Please note that, following an amendment to our constitution last year, subscriptions will expire one year from the date of purchase, effective from 2026.`}</Text>
<Button style={button} href={WebsiteLink}>
Login to my account
</Button>

<Text style={text}>Happy coding!</Text>
<Text style={text}>Happy new year and happy coding!</Text>
<Img
src="https://codersforcauses.org/logo/cfc_logo_white_circle.png"
width="50"
Expand Down
4 changes: 2 additions & 2 deletions src/components/payment/online/apple-pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ const ApplePay = ({ paymentInstance, payRequest, ...props }: ApplePayProps) => {
}

let message = `Tokenization failed with status: ${result.status}`
if (result?.errors) {
message += ` and errors: ${JSON.stringify(result?.errors)}`
if (result.status !== "OK" && "errors" in result && result.errors) {
message += ` and errors: ${JSON.stringify(result.errors)}`

throw new Error(message)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/payment/online/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ const Card = ({ amount, paymentInstance, theme, setFocus, ...props }: CardProps)
}

let message = `Tokenization failed with status: ${result.status}`
if (result?.errors) {
message += ` and errors: ${JSON.stringify(result?.errors)}`
if (result.status !== "OK" && "errors" in result && result.errors) {
message += ` and errors: ${JSON.stringify(result.errors)}`

throw new Error(message)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/payment/online/google-pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ const GooglePay = ({ paymentInstance, payRequest, theme, ...props }: GooglePayPr
}

let message = `Tokenization failed with status: ${result.status}`
if (result?.errors) {
message += ` and errors: ${JSON.stringify(result?.errors, null, 2)}`
if (result.status !== "OK" && "errors" in result && result.errors) {
message += ` and errors: ${JSON.stringify(result.errors, null, 2)}`

throw new Error(message)
}
Expand Down
31 changes: 22 additions & 9 deletions src/components/payment/online/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,36 +122,49 @@ const OnlinePaymentForm = ({
}, [])

const cardTokenizeResponseReceived = async (result: TokenResult) => {
if (result.errors) {
// TODO handle errors
if (result.status !== "OK") {
toast({
variant: "destructive",
title: "Payment tokenization failed",
description: "There was an issue processing your payment method. Please try again.",
})
return
}

let paymentID: string | undefined = ""
if (cardDetails[0] && (result?.details?.method as string) === "Card") {
if (cardDetails[0] && (result.details?.method as string) === "Card") {
try {
const id = await storeCard.mutateAsync({
sourceID: result.token!,
sourceID: result.token,
})
paymentID = await pay.mutateAsync({
sourceID: id!,
label,
amount,
})
} catch (error) {
// TODO: handle error
console.log(error)
toast({
variant: "destructive",
title: "Payment failed",
description:
"Unable to process your payment with stored card. Please try again or use a different payment method.",
})
console.error("Stored card payment error:", error)
}
} else {
try {
paymentID = await pay.mutateAsync({
sourceID: result.token!,
sourceID: result.token,
label,
amount,
})
} catch (error) {
// TODO: handle error
console.log(error)
toast({
variant: "destructive",
title: "Payment failed",
description: "Unable to process your payment. Please try again or use a different payment method.",
})
console.error("Direct payment error:", error)
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/components/payment/online/saved-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ const SavedCardsForm = ({ amount, cards, ...props }: SavedCardsProps) => {
try {
props.loadingState[1](true)
await props.cardTokenizeResponseReceived({
// fuck you square
status: "OK" as TokenStatusType,
status: "OK",
token: values.card,
})
details: {
method: "Card",
},
} as TokenResult)
} catch (error) {
console.error(error)
} finally {
Expand Down
8 changes: 8 additions & 0 deletions src/server/api/routers/admin/users/update-role.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TRPCError } from "@trpc/server"
import { addYears } from "date-fns"
import { eq } from "drizzle-orm"
import { z } from "zod"

Expand All @@ -23,5 +24,12 @@ export const updateRole = adminProcedure
throw new TRPCError({ code: "NOT_FOUND", message: `User with id: ${input.id} does not exist` })
}

if (input.role === "member") {
await ctx.db
.update(User)
.set({ membership_expiry: addYears(new Date(), 1) })
.where(eq(User.id, input.id))
}

return user
})
9 changes: 8 additions & 1 deletion src/server/api/routers/payments/pay.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TRPCError } from "@trpc/server"
import { Ratelimit } from "@upstash/ratelimit"
import { randomUUID } from "crypto"
import { addYears } from "date-fns"
import { eq } from "drizzle-orm"
import { env } from "process"
import { z } from "zod"
Expand Down Expand Up @@ -74,7 +75,13 @@ export const pay = protectedRatedProcedure(Ratelimit.fixedWindow(2, "30s"))
currency: result.payment.amountMoney.currency,
})

await ctx.db.update(User).set({ role: "member" }).where(eq(User.id, currentUser.id))
await ctx.db
.update(User)
.set({
role: "member",
membership_expiry: addYears(new Date(), 1),
})
.where(eq(User.id, currentUser.id))

return result.payment.id
})
1 change: 0 additions & 1 deletion src/server/api/routers/users/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export const update = protectedRatedProcedure(Ratelimit.fixedWindow(4, "30s"))
.mutation(async ({ ctx, input }) => {
const currentUser = ctx.user
if (!currentUser) throw new Error("Not authenticated")
// TODO: update clerk email
// TODO: Wrap in a transaction
try {
const clerk = await clerkClient()
Expand Down
1 change: 1 addition & 0 deletions src/server/db/migrations/0005_daffy_victor_mancha.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "cfc-website_user" ADD COLUMN "membership_expiry" timestamp;
Loading