mirror of
https://github.com/System-End/site.git
synced 2026-04-19 22:05:11 +00:00
Add initial version of signed-out arcade shop
This commit is contained in:
parent
e3046ff4b4
commit
ed3bac8d49
4 changed files with 376 additions and 19 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import { Box, Button, Text, Flex, Grid, Card, Link, Close, Divider } from 'theme-ui'
|
||||
import React from 'react'
|
||||
import { Button, Text, Flex, Close, Divider } from 'theme-ui'
|
||||
import Balancer from 'react-wrap-balancer'
|
||||
import Quantity from './quantity'
|
||||
/** @jsxImportSource theme-ui */
|
||||
|
|
@ -14,11 +14,11 @@ const Prizes = ({
|
|||
cost,
|
||||
polaroidRotation,
|
||||
ticketRotation,
|
||||
link,
|
||||
link = null,
|
||||
quantity,
|
||||
onQuantityChange,
|
||||
index,
|
||||
hoursBalance,
|
||||
hoursBalance = null,
|
||||
...props
|
||||
}) => {
|
||||
const parsedFulfillmentDesc = fulfillmentDescription?.replace(
|
||||
|
|
@ -96,11 +96,11 @@ const Prizes = ({
|
|||
</Balancer>
|
||||
<Flex>
|
||||
{// only show the quantity dropdown if you have enough hours to buy at least 2 of the item
|
||||
hoursBalance / cost < 2 ? (null) : <Quantity numOptions={Math.min(quantity, Math.floor(hoursBalance / cost))} label={text} onQuantityChange={onQuantityChange} index={index} />
|
||||
(hoursBalance ? hoursBalance / cost < 2 : (null)) ? (null) : <Quantity numOptions={Math.min(quantity, Math.floor(hoursBalance / cost))} label={text} onQuantityChange={onQuantityChange} index={index} />
|
||||
}
|
||||
{
|
||||
// only show the buy button if you have enough hours to buy at least 1 of the item
|
||||
hoursBalance / cost < 1 ? (null) :
|
||||
(hoursBalance ? hoursBalance / cost < 1 : (null)) ? (null) :
|
||||
<Button
|
||||
sx={{
|
||||
borderRadius: '5px',
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default function ShopComponent({
|
|||
return `https://forms.hackclub.com/arcade-order?user_id=${userAirtableID}&item_id=${itemID}&quantity=${quantity}`;
|
||||
}
|
||||
|
||||
const includeBuyLink = userAirtableID !== null;
|
||||
const canPurchaseItems = userAirtableID !== null;
|
||||
useEffect(() => {
|
||||
setPRotate(2 + Math.random() * 4) * (Math.random() > 0.5 ? 1 : -1)
|
||||
setTRotate(5 + Math.random() * 14) * (Math.random() > 0.5 ? 1 : -1)
|
||||
|
|
@ -89,11 +89,10 @@ export default function ShopComponent({
|
|||
fullName={item['Full Name']}
|
||||
polaroidRotation={pRotate}
|
||||
ticketRotation={tRotate}
|
||||
link={buyLink(item.id)}
|
||||
link={canPurchaseItems ? buyLink(item.id) : null}
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
onQuantityChange={(id, q) => handleQuantityChange(item.id, q)} // Pass handler to update quantity
|
||||
hoursBalance={hoursBalance}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,289 @@
|
|||
import AirtablePlus from "airtable-plus"
|
||||
|
||||
export const shopParts = async () => {
|
||||
const airtable = new AirtablePlus({
|
||||
apiKey: process.env.AIRTABLE_API_KEY,
|
||||
baseID: "app4kCWulfB02bV8Q",
|
||||
tableName: "Shop Items"
|
||||
})
|
||||
|
||||
const records = await airtable.read()
|
||||
return records.map(record => ({id: record.id, ...record.fields}))
|
||||
}
|
||||
return [
|
||||
{
|
||||
"Name": "Pile of stickers",
|
||||
"Small Name": null,
|
||||
"Full Name": "Pile of stickers ",
|
||||
"Description": "Dealer's choice! We'll send you 3 random stickers. You can have whatever you want, as long as it's random!",
|
||||
"Fulfillment Description": "Available anywhere\n",
|
||||
"Cost Hours": 1,
|
||||
"id": "rec7rzTHzjLa1ZysL",
|
||||
"Image URL": "https://cloud-c1gqq7ttf-hack-club-bot.vercel.app/0sticker_pile_2.png",
|
||||
"Max Order Quantity": 10
|
||||
},
|
||||
{
|
||||
"Name": "Sticker",
|
||||
"Small Name": "of your choice",
|
||||
"Full Name": "Sticker of your choice",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available anywhere\n",
|
||||
"Cost Hours": 2,
|
||||
"id": "recvsJVFthUTnozTO",
|
||||
"Image URL": "https://cloud-50x4zfjwx-hack-club-bot.vercel.app/0enjoy_1.png",
|
||||
"Max Order Quantity": 10
|
||||
},
|
||||
{
|
||||
"Name": "OpenAI credits",
|
||||
"Small Name": null,
|
||||
"Full Name": "OpenAI credits ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available anywhere\n",
|
||||
"Cost Hours": 4,
|
||||
"id": "recJN0RO9obEGqP6e",
|
||||
"Image URL": "https://cloud-nozsvexvx-hack-club-bot.vercel.app/0universal_paperclips_title_screen.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Domain",
|
||||
"Small Name": "for 1 year",
|
||||
"Full Name": "Domain for 1 year",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available anywhere\n",
|
||||
"Cost Hours": 4,
|
||||
"id": "reca4tNvbZ6JLTcUH",
|
||||
"Image URL": "https://cloud-5ttcbislu-hack-club-bot.vercel.app/2screenshot_2024-06-16_at_19.34.55.png",
|
||||
"Max Order Quantity": 10
|
||||
},
|
||||
{
|
||||
"Name": "Notebook",
|
||||
"Small Name": "from GitHub",
|
||||
"Full Name": "Notebook from GitHub",
|
||||
"Description": "A Denik Layflat from the [GitHub Shop](https://www.thegithubshop.com/1455318-00-denik-layflat-notebook)",
|
||||
"Fulfillment Description": "Ships to any country, but you may need to pay customs fees. Order shipped through \n",
|
||||
"Cost Hours": 5,
|
||||
"id": "recPP2L3kPwDYmu0B",
|
||||
"Image URL": "https://cloud-2wpezbt6a-hack-club-bot.vercel.app/0notebook.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Logic Analyzer",
|
||||
"Small Name": null,
|
||||
"Full Name": "Logic Analyzer ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, IN, EU, CA, SG & AU. Fulfilled by Amazon.\n",
|
||||
"Cost Hours": 5,
|
||||
"id": "recYa7NF3Amqcvf0i",
|
||||
"Image URL": "https://cloud-re8ou8f0i-hack-club-bot.vercel.app/0image.png",
|
||||
"Max Order Quantity": 5
|
||||
},
|
||||
{
|
||||
"Name": "Breadboard",
|
||||
"Small Name": "+ jumper wires",
|
||||
"Full Name": "Breadboard + jumper wires",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, IN, EU, CA, SG & AU. Fulfilled by Amazon.\n",
|
||||
"Cost Hours": 6,
|
||||
"id": "rec3kwLZQjCB84lpT",
|
||||
"Image URL": "https://cloud-ogt9k4r4u-hack-club-bot.vercel.app/0breadboard.png",
|
||||
"Max Order Quantity": 10
|
||||
},
|
||||
{
|
||||
"Name": "Multimeter",
|
||||
"Small Name": null,
|
||||
"Full Name": "Multimeter ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, IN, EU, CA, SG & AU. Fulfilled by Amazon.\n",
|
||||
"Cost Hours": 7,
|
||||
"id": "recDJkGhWEU60NmBV",
|
||||
"Image URL": "https://cloud-fyxfbdwdl-hack-club-bot.vercel.app/0multimeter.png",
|
||||
"Max Order Quantity": 5
|
||||
},
|
||||
{
|
||||
"Name": "Arcade Ticket Counter",
|
||||
"Small Name": "(Timer)",
|
||||
"Full Name": "Arcade Ticket Counter (Timer)",
|
||||
"Description": "(formerly the Hack Hour Clock)\n",
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 7,
|
||||
"id": "recFlQknheFuFs4e9",
|
||||
"Image URL": "https://cloud-for73aw9b-hack-club-bot.vercel.app/019719_3.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Soldering iron",
|
||||
"Small Name": "+ solder",
|
||||
"Full Name": "Soldering iron + solder",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, IN, EU, CA, SG & AU. Fulfilled by Amazon.\n",
|
||||
"Cost Hours": 8,
|
||||
"id": "rec5ssvcMqrSKneac",
|
||||
"Image URL": "https://cloud-amz50nt0a-hack-club-bot.vercel.app/0soldering_iron.png",
|
||||
"Max Order Quantity": 5
|
||||
},
|
||||
{
|
||||
"Name": "Pinecil",
|
||||
"Small Name": null,
|
||||
"Full Name": "Pinecil ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 14,
|
||||
"id": "recSXzyxP636j4dee",
|
||||
"Image URL": "https://cloud-3lhacr9fo-hack-club-bot.vercel.app/061ix8eet_dl._ac_sx679_.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "YubiKey",
|
||||
"Small Name": null,
|
||||
"Full Name": "YubiKey ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 15,
|
||||
"id": "rec1pCxBpw737NP3I",
|
||||
"Image URL": "https://cloud-elfpck7gj-hack-club-bot.vercel.app/0screenshot_2024-06-14_at_07.42.35.png",
|
||||
"Max Order Quantity": 5
|
||||
},
|
||||
{
|
||||
"Name": "GitHub Keycaps",
|
||||
"Small Name": "x8",
|
||||
"Full Name": "GitHub Keycaps x8",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 15,
|
||||
"id": "recCEtkEWr6u8mCfd",
|
||||
"Image URL": "https://cloud-q3hnx73my-hack-club-bot.vercel.app/01542509_z_copy.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Octocat",
|
||||
"Small Name": "Plushie",
|
||||
"Full Name": "Octocat Plushie",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 15,
|
||||
"id": "reczeivgpqIj7ajA2",
|
||||
"Image URL": "https://cloud-n716uf1pe-hack-club-bot.vercel.app/0gh_0008_z_dd7b_copy.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Wacom",
|
||||
"Small Name": "Intuos S",
|
||||
"Full Name": "Wacom Intuos S",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, IN, EU, CA, SG & AU. Fulfilled by Amazon.\n",
|
||||
"Cost Hours": 25,
|
||||
"id": "recoatqwqCXrsiAoz",
|
||||
"Image URL": "https://cloud-bj39a8875-hack-club-bot.vercel.app/061pz9ub2wul._ac_sx679_.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Invertocat Backback",
|
||||
"Small Name": "MIIR",
|
||||
"Full Name": "Invertocat Backback MIIR",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 50,
|
||||
"id": "rec5i2AZtzHAG5znC",
|
||||
"Image URL": "https://cloud-2wpezbt6a-hack-club-bot.vercel.app/1miir.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Flipper",
|
||||
"Small Name": "Zero",
|
||||
"Full Name": "Flipper Zero",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US & EU. Fulfilled by [Flipper Zero](https://shop.flipperzero.one/policies/shipping-policy).\n",
|
||||
"Cost Hours": 70,
|
||||
"id": "recDmVbngx6NdN6pA",
|
||||
"Image URL": "https://cloud-p2gdpsmd3-hack-club-bot.vercel.app/0top.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Mechanical Keyboard",
|
||||
"Small Name": "MX",
|
||||
"Full Name": "Mechanical Keyboard MX",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Fulfilled by USPS. Customs fees may apply if outside the US.\n",
|
||||
"Cost Hours": 75,
|
||||
"id": "recrybqL6E6d4tzoe",
|
||||
"Image URL": "https://cloud-gt96uxjmh-hack-club-bot.vercel.app/061__ok6aqtl._ac_uf894_1000_ql80_.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Framework",
|
||||
"Small Name": "factory seconds",
|
||||
"Full Name": "Framework factory seconds",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in [specific countries](https://knowledgebase.frame.work/what-countries-and-regions-do-you-ship-to-r1899ikiO). Ships via USPS– customs fees may apply.\n",
|
||||
"Cost Hours": 120,
|
||||
"id": "reciRscdVv46bC7cf",
|
||||
"Image URL": "https://cloud-elfpck7gj-hack-club-bot.vercel.app/1screenshot_2024-06-14_at_07.39.22.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Prusa",
|
||||
"Small Name": "MINI+",
|
||||
"Full Name": "Prusa MINI+",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US & EU. Customs fees outside the EU [may apply](https://help.prusa3d.com/article/vat-value-added-tax-customs-fees_1505#orders-outside-eu).\n",
|
||||
"Cost Hours": 130,
|
||||
"id": "recCeMp5iK7tRpqc3",
|
||||
"Image URL": "https://cloud-cqnd9gu78-hack-club-bot.vercel.app/1prusa.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Bambu Lab",
|
||||
"Small Name": "A1 mini",
|
||||
"Full Name": "Bambu Lab A1 mini",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in US, CA, & EU. Shipping provided by Bambu Lab.\n",
|
||||
"Cost Hours": 135,
|
||||
"id": "rec58YSzn7V5x1GPR",
|
||||
"Image URL": "https://cloud-n8ijhwk64-hack-club-bot.vercel.app/0a1_mini.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Framework",
|
||||
"Small Name": "13 inch",
|
||||
"Full Name": "Framework 13 inch",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in [specific countries](https://knowledgebase.frame.work/what-countries-and-regions-do-you-ship-to-r1899ikiO). Ships via USPS– customs fees may apply.\n",
|
||||
"Cost Hours": 175,
|
||||
"id": "recj3qt349e2KXW6X",
|
||||
"Image URL": "https://cloud-g0bjmr0sz-hack-club-bot.vercel.app/013in.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Quest 3",
|
||||
"Small Name": null,
|
||||
"Full Name": "Quest 3 ",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in [specific countries](https://www.meta.com/help/orders-and-returns/articles/quest-supported-countries/). Fulfilled by Meta.\n",
|
||||
"Cost Hours": 200,
|
||||
"id": "recuol7Uk5Z2sjViv",
|
||||
"Image URL": "https://cloud-7x2qyu0b9-hack-club-bot.vercel.app/0screenshot_2024-06-14_at_08.46.20.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "Framework",
|
||||
"Small Name": "16 inch",
|
||||
"Full Name": "Framework 16 inch",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in [specific countries](https://knowledgebase.frame.work/what-countries-and-regions-do-you-ship-to-r1899ikiO). Ships via USPS– customs fees may apply.\n",
|
||||
"Cost Hours": 400,
|
||||
"id": "rec9cXQSlAJVnfJkv",
|
||||
"Image URL": "https://cloud-cqnd9gu78-hack-club-bot.vercel.app/0framework.png",
|
||||
"Max Order Quantity": 1
|
||||
},
|
||||
{
|
||||
"Name": "MacBook",
|
||||
"Small Name": "Air M2",
|
||||
"Full Name": "MacBook Air M2",
|
||||
"Description": null,
|
||||
"Fulfillment Description": "Available in any country Apple ships to (e.g. US or IN).\n",
|
||||
"Cost Hours": 400,
|
||||
"id": "recFT8SlSOxFrG9ha",
|
||||
"Image URL": "https://cloud-9zwbzfbtw-hack-club-bot.vercel.app/00image_from_ios-removebg-preview.png",
|
||||
"Max Order Quantity": 1
|
||||
}
|
||||
]}
|
||||
|
||||
// FIXME: does not connect to Airtable (for local development)
|
||||
export default async function handler(req, res) {
|
||||
const data = await shopParts()
|
||||
|
||||
const filteredData = data.filter(record => record["Enabled"]).map(record => {
|
||||
const filteredData = data.map(record => {
|
||||
return {
|
||||
name: record['Name'],
|
||||
smallName: record['Small Name'],
|
||||
|
|
|
|||
89
pages/arcade/shop.js
Normal file
89
pages/arcade/shop.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import ShopComponent from "../../components/arcade/shop-component"
|
||||
import { shopParts } from "../api/arcade/shop"
|
||||
import { Link, Text } from 'theme-ui'
|
||||
import { Balancer } from "react-wrap-balancer"
|
||||
import Meta from '@hackclub/meta'
|
||||
import Head from 'next/head'
|
||||
|
||||
/** @jsxImportSource theme-ui */
|
||||
|
||||
const styled = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Slackey&family=Emblema+One&family=Gaegu&display=swap');
|
||||
|
||||
.slackey {
|
||||
font-family: "Slackey", sans-serif;
|
||||
}
|
||||
|
||||
.gaegu {
|
||||
font-family: "Gaegu", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FAEFD6;
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
export default function Shop({ availableItems, userAirtableID = null, hoursBalance = 0 }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta
|
||||
as={Head}
|
||||
title="Arcade Shop"
|
||||
description="Check out the prizes at the Arcade Shop!"
|
||||
image="https://cloud-luaw423i2-hack-club-bot.vercel.app/0frame_33__1_.png"
|
||||
/>
|
||||
<style>
|
||||
{`
|
||||
._title-container {
|
||||
width: 100%;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<Balancer className="_title-container">
|
||||
<h1
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
fontSize: 5,
|
||||
color: '#FF8C37',
|
||||
my: 0,
|
||||
pt: 5,
|
||||
display: 'block',
|
||||
width: '100%'
|
||||
}}
|
||||
className="slackey"
|
||||
>
|
||||
Welcome to the shop
|
||||
</h1>
|
||||
</Balancer>
|
||||
|
||||
<Text sx={{ display: 'block', textAlign: 'center', color: '#35290F' }} className='gaegu' variant='subtitle' >Like what you see? Check out <Link href="/arcade">the Hack Club Arcade!</Link></Text>
|
||||
<ShopComponent availableItems={availableItems} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const props = {};
|
||||
|
||||
await Promise.all([
|
||||
shopParts().then(items => {
|
||||
// FIXME: should be filtered by item['Enabled'] in production
|
||||
const availableItems = items.map(item => ({
|
||||
'Name': item['Name'] || null,
|
||||
'Small Name': item['Small Name'] || null,
|
||||
'Full Name': item['Full Name'] || null,
|
||||
'Description': item['Description'] || null,
|
||||
'Fulfillment Description': item['Fulfillment Description'] || null,
|
||||
'Cost Hours': item['Cost Hours'] || 0,
|
||||
id: item.id,
|
||||
'Image URL': item['Image URL'] || null,
|
||||
'Max Order Quantity': item['Max Order Quantity'] || 1
|
||||
}))
|
||||
props.availableItems = availableItems
|
||||
}),
|
||||
])
|
||||
|
||||
return { props, revalidate: 10 }
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue