From ec6dbcb7fe3fa3b0af43694a7816605c4036b16d Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 09:34:04 +0100 Subject: [PATCH 01/13] add realtime stock tracking --- pages/api/arcade/shop.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index ea5b6224..fce085df 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -1,14 +1,30 @@ import AirtablePlus from "airtable-plus" export const shopParts = async () => { - const airtable = new AirtablePlus({ + const shopItemsTable = 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})) + const records = await shopItemsTable.read() + return records.map(record => { + const fields = record.fields; + let stock = fields["Stock"] + + if (stock) { + // This is a limited item + const orders = fields["Orders"] + orders.forEach(order => { + const orderFields = order.fields; + const status = orderFields["Status"] + if (status === "Fulfilled" || status === "Awaiting Fulfillment") { + stock -= 1 + } + }) + } + return { id: record.id, ...record.fields, "Stock": stock } + }) } export default async function handler(req, res) { From e52016fde18bf7469c7e8eb9f4a77f9d5e60b529 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 15:33:49 +0100 Subject: [PATCH 02/13] Read from Orders table --- pages/api/arcade/shop.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index fce085df..4006e349 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -6,6 +6,11 @@ export const shopParts = async () => { baseID: "app4kCWulfB02bV8Q", tableName: "Shop Items" }) + const ordersTable = new AirtablePlus({ + apiKey: process.env.AIRTABLE_API_KEY, + baseID: "app4kCWulfB02bV8Q", + tableName: "Orders" + }) const records = await shopItemsTable.read() return records.map(record => { @@ -13,15 +18,15 @@ export const shopParts = async () => { let stock = fields["Stock"] if (stock) { - // This is a limited item - const orders = fields["Orders"] - orders.forEach(order => { - const orderFields = order.fields; - const status = orderFields["Status"] - if (status === "Fulfilled" || status === "Awaiting Fulfillment") { - stock -= 1 - } - }) + stock -= ordersTable.read({ + filterByFormula: `AND( + {Item} = "Test", + OR( + {Status} = "Fulfilled", + {Status} = "Awaiting Fulfillment" + ) + )` + }).fields.length; } return { id: record.id, ...record.fields, "Stock": stock } }) From 3170fedbbd5236f4ef3be76f14b6e8d44f11ec27 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 15:36:48 +0100 Subject: [PATCH 03/13] Add missing waiat --- pages/api/arcade/shop.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 4006e349..28bbbfbc 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -13,12 +13,12 @@ export const shopParts = async () => { }) const records = await shopItemsTable.read() - return records.map(record => { + return records.map(async record => { const fields = record.fields; let stock = fields["Stock"] if (stock) { - stock -= ordersTable.read({ + stock -= (await ordersTable.read({ filterByFormula: `AND( {Item} = "Test", OR( @@ -26,7 +26,7 @@ export const shopParts = async () => { {Status} = "Awaiting Fulfillment" ) )` - }).fields.length; + })).fields.length; } return { id: record.id, ...record.fields, "Stock": stock } }) From c8fe3dec66deed6c71653de46718b4a9afbe99ad Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 15:48:50 +0100 Subject: [PATCH 04/13] fields.length ->.length --- pages/api/arcade/shop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 28bbbfbc..69cbcde9 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -26,7 +26,7 @@ export const shopParts = async () => { {Status} = "Awaiting Fulfillment" ) )` - })).fields.length; + })).length; } return { id: record.id, ...record.fields, "Stock": stock } }) From 67d3f254c0df564c24551e292b261d37185e4e95 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 15:59:52 +0100 Subject: [PATCH 05/13] Get stock by record ID --- pages/api/arcade/shop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 69cbcde9..eeafbdb3 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -20,7 +20,7 @@ export const shopParts = async () => { if (stock) { stock -= (await ordersTable.read({ filterByFormula: `AND( - {Item} = "Test", + {Item} = "${record.id}", OR( {Status} = "Fulfilled", {Status} = "Awaiting Fulfillment" From ad511f35cbe5dc2e46feaf730da7da97e39f1873 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 16:01:08 +0100 Subject: [PATCH 06/13] Include record ID --- pages/api/arcade/shop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index eeafbdb3..a7e5b5b7 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -28,7 +28,7 @@ export const shopParts = async () => { )` })).length; } - return { id: record.id, ...record.fields, "Stock": stock } + return { id: record.id, ...record.fields, "Stock": stock ?? null } }) } From 0c45788fa68369c9ac308223e66c8beadaba5ede Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 16:20:26 +0100 Subject: [PATCH 07/13] record.id -> record["Name"] --- pages/api/arcade/shop.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index a7e5b5b7..2cfa3b5b 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -18,15 +18,16 @@ export const shopParts = async () => { let stock = fields["Stock"] if (stock) { - stock -= (await ordersTable.read({ + await ordersTable.read({ filterByFormula: `AND( - {Item} = "${record.id}", + {Item} = "${record["Name"]}", OR( {Status} = "Fulfilled", {Status} = "Awaiting Fulfillment" ) )` - })).length; + }) + stock -= records.length; } return { id: record.id, ...record.fields, "Stock": stock ?? null } }) From 6b802206ef0c0d8874414c54363e8d4d682b02f2 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 16:23:16 +0100 Subject: [PATCH 08/13] Do query thing --- pages/api/arcade/shop.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 2cfa3b5b..15a40842 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -18,16 +18,13 @@ export const shopParts = async () => { let stock = fields["Stock"] if (stock) { - await ordersTable.read({ - filterByFormula: `AND( - {Item} = "${record["Name"]}", - OR( - {Status} = "Fulfilled", - {Status} = "Awaiting Fulfillment" - ) - )` - }) - stock -= records.length; + const records = await ordersTable.read(); + const targetRecordId = record.id; + const matchingRecords = records.filter(recordToFilter => + recordToFilter.id === targetRecordId && + (recordToFilter.fields["Status"] === "Fulfilled" || recordToFilter.fields["Status"] === "Awaiting Fulfillment") + ); + stock -= matchingRecords.length; } return { id: record.id, ...record.fields, "Stock": stock ?? null } }) From ed13ee4c76145c433eb9878f2253ea4e3123bd6a Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 16:31:36 +0100 Subject: [PATCH 09/13] Basic formula because everything is terrible --- pages/api/arcade/shop.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 15a40842..d6d5bfea 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -18,13 +18,12 @@ export const shopParts = async () => { let stock = fields["Stock"] if (stock) { - const records = await ordersTable.read(); - const targetRecordId = record.id; - const matchingRecords = records.filter(recordToFilter => - recordToFilter.id === targetRecordId && - (recordToFilter.fields["Status"] === "Fulfilled" || recordToFilter.fields["Status"] === "Awaiting Fulfillment") - ); - stock -= matchingRecords.length; + stock -= (await ordersTable.read({ + filterByFormula: `OR( + {Status} = "Fulfilled", + {Status} = "Awaiting Fulfillment" + )` + })).length; } return { id: record.id, ...record.fields, "Stock": stock ?? null } }) From 3b530be9dbc18b7bba0f13878e4b5947e4fd9cc1 Mon Sep 17 00:00:00 2001 From: SkyfallWasTaken Date: Sat, 6 Jul 2024 16:48:58 +0100 Subject: [PATCH 10/13] Fix query, yet again --- pages/api/arcade/shop.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index d6d5bfea..19120662 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -19,10 +19,15 @@ export const shopParts = async () => { if (stock) { stock -= (await ordersTable.read({ - filterByFormula: `OR( - {Status} = "Fulfilled", - {Status} = "Awaiting Fulfillment" - )` + filterByFormula: ` + AND( + RECORD_ID(Item) = '${record.id}', + OR( + {Status} = 'Fulfilled', + {Status} = 'Awaiting Fulfillment' + ) + ) + ` })).length; } return { id: record.id, ...record.fields, "Stock": stock ?? null } From d514441d4591134dac9d40b92aa45f7875c58277 Mon Sep 17 00:00:00 2001 From: DillonB07 Date: Sat, 6 Jul 2024 18:06:43 +0100 Subject: [PATCH 11/13] fix stock indicator --- pages/api/arcade/shop.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index 19120662..cef6bfed 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -1,37 +1,45 @@ import AirtablePlus from "airtable-plus" export const shopParts = async () => { + const baseID = "app4kCWulfB02bV8Q" const shopItemsTable = new AirtablePlus({ apiKey: process.env.AIRTABLE_API_KEY, - baseID: "app4kCWulfB02bV8Q", + baseID, tableName: "Shop Items" }) const ordersTable = new AirtablePlus({ apiKey: process.env.AIRTABLE_API_KEY, - baseID: "app4kCWulfB02bV8Q", + baseID, tableName: "Orders" }) const records = await shopItemsTable.read() - return records.map(async record => { + const newRecordsPromise = records.map(async record => { const fields = record.fields; let stock = fields["Stock"] - if (stock) { - stock -= (await ordersTable.read({ + if (stock && fields["Orders"]) { + const orderIds = fields["Orders"] + const ordersFilter = orderIds.map(id => `RECORD_ID() = "${id}"`).join(", ") + const data = await ordersTable.read({ filterByFormula: ` - AND( - RECORD_ID(Item) = '${record.id}', - OR( - {Status} = 'Fulfilled', - {Status} = 'Awaiting Fulfillment' - ) + AND( + OR(${ordersFilter}), + OR( + {Status} = "Fulfilled", + {Status} = "Awaiting Fulfillment" ) - ` - })).length; + )` + }) + + stock -= data.length; } + + return { id: record.id, ...record.fields, "Stock": stock ?? null } }) + const newRecords = await Promise.all(newRecordsPromise) + return newRecords } export default async function handler(req, res) { @@ -44,8 +52,9 @@ export default async function handler(req, res) { description: record['Description'], hours: record['Cost Hours'], imageURL: record['Image URL'], + stock: record['Stock'], } }) - res.json(filteredData) + return res.json(filteredData) } \ No newline at end of file From 5e56d0c6bd8901e059c9c7d334b81a9017eb094c Mon Sep 17 00:00:00 2001 From: DillonB07 Date: Sat, 6 Jul 2024 19:38:55 +0100 Subject: [PATCH 12/13] hour of pain 1 --- components/arcade/shop-component.js | 44 ++++++++++++++++++++++++++- pages/api/arcade/shop.js | 9 +++--- pages/arcade/[userAirtableID]/shop.js | 2 +- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/components/arcade/shop-component.js b/components/arcade/shop-component.js index bdd91bdb..cfb550ed 100644 --- a/components/arcade/shop-component.js +++ b/components/arcade/shop-component.js @@ -50,6 +50,10 @@ export default function ShopComponent({ setTRotate(5 + Math.random() * 14) * (Math.random() > 0.5 ? 1 : -1) }, []); + + const inStockItems = availableItems.filter(item => item['Stock'] === null || item['Stock'] > 0 ); +const outOfStockItems = availableItems.filter(item => item['Stock'] !== null && item['Stock'] <= 0); + return ( <> - {availableItems + {inStockItems .sort((a, b) => a['Cost Hours'] - b['Cost Hours']) .map((item) => ( handleQuantityChange(item.id, q)} // Pass handler to update quantity hoursBalance={hoursBalance} stock={item['Stock']} + inStock={true} + /> + ))} + + Out of stock items + + {outOfStockItems + .sort((a, b) => a['Cost Hours'] - b['Cost Hours']) + .map((item) => ( + handleQuantityChange(item.id, q)} // Pass handler to update quantity + hoursBalance={hoursBalance} + stock={item['Stock']} + inStock={false} /> ))} diff --git a/pages/api/arcade/shop.js b/pages/api/arcade/shop.js index cef6bfed..21eb9b3d 100644 --- a/pages/api/arcade/shop.js +++ b/pages/api/arcade/shop.js @@ -34,11 +34,12 @@ export const shopParts = async () => { stock -= data.length; } - - - return { id: record.id, ...record.fields, "Stock": stock ?? null } + return { id: record.id, ...record.fields, "Stock": (stock == null)? null : (stock >= 0 ? stock : 0) } }) - const newRecords = await Promise.all(newRecordsPromise) + + + const newRecords = await Promise.all(newRecordsPromise) + return newRecords } diff --git a/pages/arcade/[userAirtableID]/shop.js b/pages/arcade/[userAirtableID]/shop.js index 3b60630b..9349c1b5 100644 --- a/pages/arcade/[userAirtableID]/shop.js +++ b/pages/arcade/[userAirtableID]/shop.js @@ -88,7 +88,7 @@ export async function getStaticProps({params}) { id: item.id, 'Image URL': item['Image URL'] || null, 'Max Order Quantity': item['Max Order Quantity'] || 1, - Stock: item['Stock'] || null + Stock: item['Stock'] >= 0 ? item['Stock'] : null })) props.availableItems = availableItems }), From a8042fdb826878016e2fd5a197be79479522a19d Mon Sep 17 00:00:00 2001 From: DillonB07 Date: Sat, 6 Jul 2024 20:06:50 +0100 Subject: [PATCH 13/13] show out of stock items separately --- components/arcade/prizes.js | 77 ++++++++++++++++------------- components/arcade/shop-component.js | 4 +- pages/arcade/shop.js | 2 +- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/components/arcade/prizes.js b/components/arcade/prizes.js index 3da73a24..e1d089bf 100644 --- a/components/arcade/prizes.js +++ b/components/arcade/prizes.js @@ -20,6 +20,7 @@ const Prizes = ({ index, hoursBalance = null, stock, + inStock = true, ...props }) => { const parsedFulfillmentDesc = fulfillmentDescription?.replace( @@ -36,7 +37,7 @@ const Prizes = ({ return ( {text} - {stock && stock != null && stock > 0 && stock <= 100 && ( + {inStock && stock != null && stock > 0 && stock <= 100 && ( + {inStock && ( - + + > You can order up to {quantity} of these @@ -119,10 +122,10 @@ const Prizes = ({ // only show the quantity dropdown if you have enough hours to buy at least 2 of the item (hoursBalance ? hoursBalance / cost < 2 : null) ? null : ( ) } @@ -130,15 +133,15 @@ const Prizes = ({ // only show the buy button if you have enough hours to buy at least 1 of the item (hoursBalance ? hoursBalance / cost < 1 : null) ? null : ( @@ -147,6 +150,7 @@ const Prizes = ({ )} + )} - {cost} {link ? '🎟️' : cost == 1 ? 'ticket' : 'tickets'} + {cost} 🎟️ - { document.getElementById(`${parsedFullName}-info`).showModal() }} - > + > 📦 + )} item['Stock'] === null || item['Stock'] > 0 ); -const outOfStockItems = availableItems.filter(item => item['Stock'] !== null && item['Stock'] <= 0); + const outOfStockItems = availableItems.filter(item => item['Stock'] !== null && item['Stock'] <= 0); return ( <> @@ -107,7 +107,7 @@ const outOfStockItems = availableItems.filter(item => item['Stock'] !== null && /> ))} - Out of stock items + Out of stock items = 0 ? item['Stock'] : null })) props.availableItems = availableItems }),