diff --git a/apps/keystone/keystone.ts b/apps/keystone/keystone.ts index 731ecae..65fb304 100644 --- a/apps/keystone/keystone.ts +++ b/apps/keystone/keystone.ts @@ -110,12 +110,6 @@ export default withAuth( }, resolve(source, { apiKey, stripeID }, context: Context) { if (apiKey !== process.env.API_KEY) throw new Error("Incorrect API Key"); - // if (apiKey !== process.env.API_KEY) { - // // Debug only - // console.log(`Tried to confirm stripe but had an API key error. Got ${apiKey}, expected ${process.env.API_KEY}`); - // return; - // } - console.log('bruh'); return context.sudo().db.Ticket.updateOne({ where: { stripeID }, data: { paid: true } diff --git a/apps/nextjs/components/ShirtOrder/ShirtOrder.tsx b/apps/nextjs/components/ShirtOrder/ShirtOrder.tsx index 2cd8f2e..4b3e3d8 100644 --- a/apps/nextjs/components/ShirtOrder/ShirtOrder.tsx +++ b/apps/nextjs/components/ShirtOrder/ShirtOrder.tsx @@ -3,8 +3,7 @@ import Image from "next/image"; import JSBarcode from "jsbarcode"; import styles from "./ShirtOrder.module.scss"; -import ShirtImage from "../../styles/img/ASM2023ShirtMockup.png"; -import ASM2023Shirt from "../../styles/img/asm2023-tickets-bundle-card.png"; +import ShirtImage from "../../styles/img/events/asm24/asm24-shirt.png"; import { useEffect, useRef } from "react"; interface ASMShirtProps { diff --git a/apps/nextjs/components/Ticket/TicketSale.tsx b/apps/nextjs/components/Ticket/TicketSale.tsx index e0fa507..5f99ff0 100644 --- a/apps/nextjs/components/Ticket/TicketSale.tsx +++ b/apps/nextjs/components/Ticket/TicketSale.tsx @@ -6,6 +6,7 @@ import { UseMutationResponse, useQuery, gql, useMutation } from "urql"; import { useAuth } from "../auth"; import ASM2024Tickets from "../../styles/img/events/asm24/asm24-ticket.jpg"; +import Link from "next/link"; interface BankTicketResponse { generateTicket: { @@ -152,6 +153,7 @@ export function TicketProduct() { All attendees, including runners and staff must purchase tickets to attend the event. Volunteers will receive a $15 rebate administered on site at ASM2024.

+

ASM2024 Shirt now on sale!


{auth.ready && !auth?.sessionData && ( diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 26d154e..4eb4cae 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -48,14 +48,49 @@ const nextConfig = { ]; }, images: { - domains: [ - "localhost", - "127.0.0.1", - "ausspeedruns.com", - "beta.ausspeedruns.com", - "ausrunsstoragebeta.blob.core.windows.net", - "ausrunsstorage.blob.core.windows.net", - "ausspeedruns.sharepoint.com", + remotePatterns: [ + { + protocol: 'http', + hostname: 'localhost', + port: '', + pathname: '/**', + }, + { + protocol: 'http', + hostname: '127.0.0.1', + port: '9999', + pathname: '/devstoreaccount1/keystone-uploads/**', + }, + { + protocol: 'https', + hostname: 'ausspeedruns.com', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'beta.ausspeedruns.com', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'ausrunsstoragebeta.blob.core.windows.net', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'ausrunsstorage.blob.core.windows.net', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'ausspeedruns.sharepoint.com', + port: '', + pathname: '/**', + }, ], }, nx: { diff --git a/apps/nextjs/pages/ASM2024/shirt.tsx b/apps/nextjs/pages/ASM2024/shirt.tsx new file mode 100644 index 0000000..aa07b77 --- /dev/null +++ b/apps/nextjs/pages/ASM2024/shirt.tsx @@ -0,0 +1,318 @@ +import React, { useEffect, useState } from "react"; +import Head from "next/head"; +import Image from "next/image"; +import { loadStripe } from "@stripe/stripe-js"; +import { Box, Button, Skeleton, ThemeProvider, ToggleButton, ToggleButtonGroup } from "@mui/material"; +import { UseMutationResponse, useQuery, gql } from "urql"; + +import styles from "../../styles/Store.module.scss"; +import DiscordEmbed from "../../components/DiscordEmbed"; +import { theme } from "../../components/mui-theme"; +import { useAuth } from "../../components/auth"; +import ASMShirt from "../../components/ShirtOrder/ShirtOrder"; + +import ASM2024Logo from "../../styles/img/events/asm24/ASM24 SVG.svg"; +import ASM2024Shirt from "../../styles/img/events/asm24/asm24-shirt.png"; +import ShirtMeasurements from "../../styles/img/ShirtMeasurements.png"; + +const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY!); + +interface BankShirtResponse { + generateShirt: { + shirtID: string; + size: string; + colour: "blue" | "purple"; + paid: boolean; + error?: Record; + }; +} + +const Shirt = () => { + const auth = useAuth(); + const [shirtSize, setShirtSize] = useState("m"); + const [genShirtLoading, setGenShirtLoading] = useState(false); + const [waitForShirt, setWaitForShirt] = useState(false); + const [bankShirtData, setBankShirtData] = useState[0]>(); + + const [profileQueryRes] = useQuery({ + query: gql` + query Profile($userId: ID!) { + user(where: { id: $userId }) { + verified + } + event(where: { shortname: "ASM2024" }) { + acceptingShirts + } + } + `, + variables: { + userId: auth.ready ? auth.sessionData?.id ?? "" : "", + }, + pause: !auth.ready || !auth?.sessionData?.id, + }); + + const successfulShirt = Boolean(bankShirtData) && !Boolean(bankShirtData?.error); + + const disableBuying = + !auth.ready || + (auth.ready && !auth.sessionData) || + !profileQueryRes.data?.user?.verified || + !profileQueryRes.data?.event.acceptingShirts; + const disableBank = disableBuying || genShirtLoading || successfulShirt; + + useEffect(() => { + let timeout: NodeJS.Timeout; + if (genShirtLoading) { + timeout = setTimeout(() => { + setWaitForShirt(true); + }, 2500); + } + return () => clearTimeout(timeout); + }, [genShirtLoading, successfulShirt]); + + useEffect(() => { + let interval: NodeJS.Timeout; + if (waitForShirt) { + interval = setInterval(() => { + if (successfulShirt) { + setGenShirtLoading(false); + } + }, 500); + } + return () => clearInterval(interval); + }, [waitForShirt, successfulShirt]); + + async function generateShirt() { + if (!auth.ready) { + console.error("Tried to generate tickets but auth was not ready"); + return; + } + + if (disableBuying) return; + + setGenShirtLoading(true); + const res = await fetch( + `/api/create_bank_shirt?account=${auth.ready ? auth?.sessionData?.id : ""}&colour=blue&size=${shirtSize}`, + ); + if (res.status === 200) { + setBankShirtData(await res.json()); + } else { + console.log(res); + } + } + + let accId = ""; + if (auth.ready) { + accId = auth.sessionData?.id ?? ""; + } + + let accUsername = ""; + if (auth.ready) { + accUsername = auth.sessionData?.username ?? ""; + } + + if (bankShirtData?.error) console.error(bankShirtData.error); + + return ( + +
+ + ASM2024 Shirt - AusSpeedruns + + +
+
+ ASM2024 Logo +
+
+
+ Shirt design +
+
+

ASM2024 Shirt

+

+ Selling out June 16th 23:59 ACST. +

+

The official shirt for the Australian Speedrun Marathon 2024.

+

+ Please note that all ASM2024 Shirt purchases{" "} + must be collected at the ASM2024 event. No shipping will be offered. +

+

+ Design by{" "} + + Genba + + . +

+

+ Please note that any shirt ordered using the bank transfer method will be considered + cancelled if payment is not cleared in the AusSpeedruns bank account before June 16 + ACST 23:59. +

+ +

Size

+
+ (value !== null ? setShirtSize(value) : undefined)}> + S + M + L + XL + 2XL + 3XL + 4XL + +
+
+
+ + {auth.ready && !auth?.sessionData && ( +
+ You must be logged in and email verified to purchase shirts. +
+ )} + {auth.ready && auth?.sessionData && !profileQueryRes.data?.user?.verified && ( +
+ Your email must be verified to purchase shirts. +
+ )} +
+
+ +
+
+
+ +
+
+
+ {bankShirtData?.error && ( +

It seems like there was an error. Please try again or let Clubwho know on Discord!

+ )} + {successfulShirt && !genShirtLoading && bankShirtData?.data && ( + + )} + {genShirtLoading && !bankShirtData?.error && } +
+

Garment Sizing

+
+
+ Shirt with markers showing where the width and length are measured from +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size + Width (cm) + Chest width at underarm + + Length (cm) + Shoulder point at neck to hem +
S4671
M5174
L5676
XL6179
2XL6681
3XL7184
4XL7686
+
+
+
+

Refund Policy

+

+ AusSpeedruns does not offer any refunds for purchased ASM2024 shirts, except as required by + Australian law (e.g. the Australian Consumer Law). Individual exceptions may be considered + on a case by case basis, however we acknowledge our full discretion to not grant exceptions + that are sought. +

+

Please contact Lacey via the AusSpeedruns Discord for any inquires.

+
+
+
+
+ ); +}; + +const ASMShirtSkeleton: React.FC = () => { + return ( + + +
+ + + +
+
+ + + + +
+
+ ); +}; + +export default Shirt; diff --git a/apps/nextjs/pages/api/checkout_shirt.ts b/apps/nextjs/pages/api/checkout_shirt.ts index 72309ff..ef9d9a9 100644 --- a/apps/nextjs/pages/api/checkout_shirt.ts +++ b/apps/nextjs/pages/api/checkout_shirt.ts @@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Check valid shirt size - if (!req.query.size || Array.isArray(req.query.size) || !['m', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { + if (!req.query.size || Array.isArray(req.query.size) || !['s', 'm', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { throw new Error('Invalid Size'); } @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) line_items: [ { // Provide the exact Price ID (for example, pr_1234) of the product you want to sell - price: 'ASM2023SHIRT', + price: 'price_1PNuceKT8G4cNWT5B4zcKH8f', quantity: 1, }, ], diff --git a/apps/nextjs/pages/api/create_bank_shirt.ts b/apps/nextjs/pages/api/create_bank_shirt.ts index 2106e96..d20696f 100644 --- a/apps/nextjs/pages/api/create_bank_shirt.ts +++ b/apps/nextjs/pages/api/create_bank_shirt.ts @@ -17,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Check valid shirt size - if (!req.query.size || Array.isArray(req.query.size) || !['m', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { + if (!req.query.size || Array.isArray(req.query.size) || !['s', 'm', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { throw new Error('Invalid Size'); } diff --git a/apps/nextjs/pages/api/set_shirt_size.ts b/apps/nextjs/pages/api/set_shirt_size.ts index 0dc69e5..8d4a548 100644 --- a/apps/nextjs/pages/api/set_shirt_size.ts +++ b/apps/nextjs/pages/api/set_shirt_size.ts @@ -17,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Check valid shirt size - if (!req.query.size || Array.isArray(req.query.size) || !['m', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { + if (!req.query.size || Array.isArray(req.query.size) || !['s', 'm', 'l', 'xl', 'xl2', 'xl3', 'xl4'].includes(req.query.size)) { throw new Error('Invalid Size'); } diff --git a/apps/nextjs/pages/api/webhooks.ts b/apps/nextjs/pages/api/webhooks.ts index e7658cd..ceb7d18 100644 --- a/apps/nextjs/pages/api/webhooks.ts +++ b/apps/nextjs/pages/api/webhooks.ts @@ -50,7 +50,7 @@ const webhookHandler = async (req: IncomingMessage, res: any) => { stripe.checkout.sessions.listLineItems(checkout.id, {}).then(data => { console.log(JSON.stringify(data)) // This is mega dumb btw - if (data.data[0].description === "ASM2023 Shirt") { + if (data.data[0].description === "ASM2024 Shirt") { // SHIRT fulfilShirtOrder(checkout.id); } else if (data.data[0].description === "ASM2023 Bundle") { @@ -77,8 +77,7 @@ const webhookHandler = async (req: IncomingMessage, res: any) => { const fulfilOrder = async (sessionID: any) => { // Update ticket information - console.log(`AAAAAAAAAAAAAAA ${sessionID}`); - const mutRes = await urqlClient.mutation(gql` + await urqlClient.mutation(gql` mutation ($sessionID: String!, $apiKey: String!) { confirmStripe(stripeID: $sessionID, apiKey: $apiKey) { __typename @@ -91,7 +90,7 @@ export default cors(webhookHandler); async function fulfilShirtOrder(sessionID: any) { // Update shirt information - const mutRes = await urqlClient.mutation(gql` + await urqlClient.mutation(gql` mutation ($sessionID: String!, $apiKey: String!) { confirmShirtStripe(stripeID: $sessionID, apiKey: $apiKey) { __typename @@ -102,7 +101,7 @@ async function fulfilShirtOrder(sessionID: any) { async function fulfilBundleOrder(sessionID: any) { // Update shirt information - const mutRes = await urqlClient.mutation(gql` + await urqlClient.mutation(gql` mutation ($sessionID: String!, $apiKey: String!) { confirmShirtStripe(stripeID: $sessionID, apiKey: $apiKey) { __typename diff --git a/apps/nextjs/styles/img/events/asm24/asm24-shirt.png b/apps/nextjs/styles/img/events/asm24/asm24-shirt.png new file mode 100644 index 0000000..73b183c Binary files /dev/null and b/apps/nextjs/styles/img/events/asm24/asm24-shirt.png differ