Skip to content
Open
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"prepare": "node .husky/install.mjs || true"
},
"dependencies": {
"@clerk/nextjs": "5.2.10",
"@clerk/clerk-js": "^5.91.1",
"@clerk/clerk-react": "^5.46.0",
"@clerk/nextjs": "6.31.8",
"@hookform/resolvers": "3.3.4",
"@material-symbols/font-300": "0.17.1",
"@radix-ui/react-accordion": "1.1.2",
Expand Down
934 changes: 658 additions & 276 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions src/app/_components/clients/EditProfile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ const EditProfile = ({ setIsEditing, id, refetch }: EditProfileProps) => {
const [showCircleProgress, setShowCircleProgress] = useState(false)
const { data: user } = api.users.get.useQuery(id)
const { mutateAsync: updateUser } = api.users.update.useMutation()
const { mutateAsync: updateSocials } = api.users.updateSocials.useMutation()

const userDefaultValues = user && {
name: user.name,
Expand Down Expand Up @@ -148,12 +147,6 @@ const EditProfile = ({ setIsEditing, id, refetch }: EditProfileProps) => {
student_number: data.isUWA ? data.student_number : null,
uni: data.isUWA ? "UWA" : data.uni,
}),

updateSocials({
...data,
github: data.github ?? null,
discord: data.discord ?? null,
}),
])

await refetch()
Expand Down
49 changes: 8 additions & 41 deletions src/app/dashboard/(root)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import PaymentFormWrapper from "~/components/payment/online/wrapper"

import { getIsMembershipOpen } from "~/lib/utils"
import { api } from "~/trpc/server"

import DashboardContent from "./content"
Expand All @@ -10,44 +7,14 @@ export default async function Dashboard() {

return (
<>
<div className="container flex flex-wrap gap-x-8 gap-y-4 py-12">
<div className="flex-grow">
<h2 className="font-bold">Hey, {user?.preferred_name}</h2>
<p className="mt-4">
Here you can see our upcoming projects and the projects you have participated in. You can apply for new
projects here if we link the application form.
</p>
<div className="flex h-full mb-8">
<DashboardContent />
</div>
</div>
<div className="max-w-md">
{user?.role === null && (
<div className="space-y-4 ">
<div className="space-y-2">
<h2 className="font-semibold leading-none tracking-tight">Membership</h2>
<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:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
<li>the ability to vote and run for committee positions</li>
<li>the ability to join our projects run during the winter and summer breaks.</li>
</ul>
</div>
</div>
{getIsMembershipOpen() ? (
<PaymentFormWrapper />
) : (
<p className="text-sm text-warning">
Memberships are temporarily closed for the new year. Please check back later.
</p>
)}
</div>
)}
<div className="container py-12">
<h2 className="font-bold">Hey, {user?.preferred_name}</h2>
<p className="mt-4">
Here you can see our upcoming projects and the projects you have participated in. You can apply for new
projects here if we link the application form.
</p>
<div className="flex h-full mb-8">
<DashboardContent />
</div>
</div>
</>
Expand Down
12 changes: 8 additions & 4 deletions src/app/join/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ export default function Join() {

try {
const attempt = await signIn.create({ identifier: email })
const emailFactor = attempt.supportedFirstFactors.find((factor) => factor.strategy === "email_code")

const emailFactor = attempt.supportedFirstFactors?.find((factor) => factor.strategy === "email_code")
if (!emailFactor || emailFactor.strategy !== "email_code") {
throw new Error("Email code factor not supported")
toast({
variant: "destructive",
title: "Error",
description: "Email code factor not supported",
})
return
}

const si = await attempt.prepareFirstFactor({
Expand All @@ -79,7 +83,7 @@ export default function Join() {
setStep("email")
window.scrollTo({
top: 0,
behavior: "smooth", // smooth scrolling
behavior: "smooth",
})
} catch (error) {
const { errors = [] } = error as ClerkError
Expand Down
2 changes: 1 addition & 1 deletion src/app/profile/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import NotFound from "~/app/not-found"
import { api } from "~/trpc/server"

import ProfilePage from "./profile/page"
import ProfilePage from "./profile"

const Profile = async ({ params: { id } }: { params: { id: string } }) => {
const currentUser = await api.users.getCurrent.query()
Expand Down
180 changes: 180 additions & 0 deletions src/app/profile/[id]/profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"use client"

import Link from "next/link"
import { siDiscord, siGithub } from "simple-icons"

import PaymentFormWrapper from "~/components/payment/online/wrapper"
import { Badge } from "~/components/ui/badge"
import { Button } from "~/components/ui/button"

import ProfilePageSkeleton from "~/app/_components/clients/ProfilePageSkeleton/page"
import TitleText from "~/app/_components/title-text"
import { DashboardCard } from "~/app/dashboard/(root)/card"
import { UNIVERSITIES } from "~/lib/constants"
import { getIsMembershipOpen } from "~/lib/utils"
import { api } from "~/trpc/react"
import { type RouterOutputs } from "~/trpc/shared"

interface ProfilePageProps {
id: string
currentUser: RouterOutputs["users"]["getCurrent"]
}

const ProfilePage = ({ id, currentUser }: ProfilePageProps) => {
const { data: user, refetch } = api.users.get.useQuery(id)
const { isLoading: p1Loading, data: pastProjects } = api.projects.getProjectByUser.useQuery({
user: user?.email ?? "",
isPublic: true,
})
let universityLabel: string | undefined
if (user?.student_number?.length) {
universityLabel = "University of Western Australia"
} else if (!UNIVERSITIES.find((u) => u.value === user?.university) && user?.university !== "UWA") {
universityLabel = user?.university || undefined
} else {
universityLabel = user ? UNIVERSITIES.find((u) => u.value === user.university)?.label : undefined
}

if (user) {
return (
<main className="main">
<TitleText typed>./profile</TitleText>
<div className="container mx-auto p-4">
<div className="flex flex-col gap-4 space-y-2 md:flex-row md:py-12">
<div className={`space-y-2 p-2 ${currentUser?.id === user.id ? "md:w-1/2" : "md:min-w-80"}`}>
<div>
{user.role && <Badge className="bg-primary/80 capitalize">{user.role}</Badge>}
<h2 className="text-2xl font-bold">{user.preferred_name}</h2>
<div className="flex flex-row space-x-1 italic text-primary/80">
<p>{user.name}</p>{" "}
{user.pronouns && (
<>
<p>·</p>
<p className="capitalize">{user.pronouns}</p>
</>
)}
</div>
</div>
<div className="flex flex-row space-x-2">
{user.github && (
<Badge className="items-center hover:bg-primary">
<svg
aria-label="GitHub logo"
viewBox="0 0 24 24"
height={12}
width={12}
className="mr-1 fill-current"
>
<path d={siGithub.path} />
</svg>
{user.github}
</Badge>
)}
{user.discord && (
<Badge className="items-center bg-[#5865F2] text-white hover:bg-[#5865F2]">
<svg
aria-label="Discord logo"
viewBox="0 0 24 24"
height={12}
width={12}
className="mr-1 fill-current"
>
<path d={siDiscord.path} />
</svg>
{user.discord}
</Badge>
)}
</div>
<div className="flex flex-col text-primary">
{user.student_number && (
<div className="flex items-center space-x-1">
<span className="material-symbols-sharp text-2xl">badge</span>
<p className="text-xs">{user.student_number}</p>
</div>
)}
{user.university ? (
<div className="flex items-center space-x-1">
<span className="material-symbols-sharp text-2xl">school</span>
<p className="text-xs capitalize">{universityLabel}</p>
</div>
) : (
<div className="flex items-center space-x-1">
<span className="material-symbols-sharp text-2xl">school</span>
<p className="text-xs">The University of Western Australia</p>
</div>
)}
</div>
{currentUser?.id === user.id && (
<div className="pt-6">
<Link href="/profile/settings">
<Button>Edit Profile</Button>
</Link>
</div>
)}
</div>
<div className="p-2 w-auto">
<div className="">
{currentUser?.id === user.id ? (
user?.role === null && (
<div className="space-y-4 max-w-md">
<div className="space-y-2">
<h2 className="font-semibold leading-none tracking-tight">Membership</h2>
<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:
</p>
<ul className="list-inside list-disc">
<li>discounts to paid events such as industry nights</li>
<li>the ability to vote and run for committee positions</li>
<li>the ability to join our projects run during the winter and summer breaks.</li>
</ul>
</div>
</div>
{getIsMembershipOpen() ? (
<PaymentFormWrapper />
) : (
<p className="text-sm text-warning">
Memberships are temporarily closed for the new year. Please check back later.
</p>
)}
</div>
)
) : (
<>
{" "}
<h1 className="text-2xl font-bold">Projects</h1>
<div className="pt-6">
<div className="space-y-6">
{p1Loading ? (
<h2 className="font-mono text-3xl text-primary">Loading...</h2>
) : !pastProjects || pastProjects.length == 0 ? (
<h2 className="font-mono text-3xl text-primary">
This user has not participated in any projects
</h2>
) : (
<div className="grid grid-cols-[repeat(auto-fit,minmax(14rem,300px))] gap-4">
{pastProjects.map((project, index) => (
<div key={index}>
<DashboardCard project={project} />
</div>
))}
</div>
)}
</div>
</div>
</>
)}
</div>
</div>
</div>
</div>
</main>
)
}

return <ProfilePageSkeleton />
}

export default ProfilePage
Loading