From ecb1808fe76b2bb3d175b70810c0a90095e31150 Mon Sep 17 00:00:00 2001 From: Malted Date: Mon, 3 Feb 2025 12:00:28 -0500 Subject: [PATCH 1/4] Start new tavern form --- src/app/harbor/tabs/tabs.tsx | 2 +- src/app/harbor/tavern/tavern-utils.ts | 4 ++ src/app/harbor/tavern/tavern.tsx | 89 +++++++++++++++++++++++---- 3 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/app/harbor/tabs/tabs.tsx b/src/app/harbor/tabs/tabs.tsx index 99e8c1b8..9e7637f5 100644 --- a/src/app/harbor/tabs/tabs.tsx +++ b/src/app/harbor/tabs/tabs.tsx @@ -226,7 +226,7 @@ export default function Harbor({ { name: <>Tavern 🍻, path: 'tavern', - component: , + component: , //, // component: process.env.NEXT_PUBLIC_LOW_RATE_LIMIT ? ( // // ) : ( diff --git a/src/app/harbor/tavern/tavern-utils.ts b/src/app/harbor/tavern/tavern-utils.ts index 8f53ba79..5a5e2519 100644 --- a/src/app/harbor/tavern/tavern-utils.ts +++ b/src/app/harbor/tavern/tavern-utils.ts @@ -102,3 +102,7 @@ export const getTavernEvents = async () => { lastEventsFetch = Date.now() return items } + +export async function rspvForTavern(formData: FormData) { + console.log(formData) +} diff --git a/src/app/harbor/tavern/tavern.tsx b/src/app/harbor/tavern/tavern.tsx index 231b07a9..72945482 100644 --- a/src/app/harbor/tavern/tavern.tsx +++ b/src/app/harbor/tavern/tavern.tsx @@ -15,6 +15,7 @@ import dynamic from 'next/dynamic' import { getTavernEvents, getTavernPeople, + rspvForTavern, TavernEventItem, TavernPersonItem, } from './tavern-utils' @@ -27,7 +28,20 @@ const Map = dynamic(() => import('./map'), { ssr: false, }) -const RsvpStatusSwitcher = ({ tavernEvents, onTavernSelect }) => { +type TavernDatafetchCategory = + | 'tavernEvents' + | 'myTavernLocation' + | 'tavernPeople' + +const RsvpStatusSwitcher = ({ + tavernEvents, + onTavernSelect, + erroredFetches, +}: { + tavernEvents: TavernEventItem[] + onTavernSelect: (tavernId: string | null) => void + erroredFetches: TavernDatafetchCategory[] +}) => { const [rsvpStatus, setRsvpStatus] = useLocalStorageState( 'cache.rsvpStatus', 'none', @@ -110,7 +124,44 @@ const RsvpStatusSwitcher = ({ tavernEvents, onTavernSelect }) => {
Please consider volunteering to organize this tavern, me hearty! -
+ {tavernEvents && + (rsvpStatus === 'participant' || rsvpStatus === 'organizer') ? ( + <> + + + ) : ( +

Not a participant or organizer

+ )} + + + + + {/*
@@ -178,7 +229,7 @@ const RsvpStatusSwitcher = ({ tavernEvents, onTavernSelect }) => { ) : null} -
+
*/} ) } @@ -189,18 +240,29 @@ export default function Tavern() { const [selectedTavern, setSelectedTavern] = useState( null, ) + const [erroredFetches, setErroredFetches] = useState< + TavernDatafetchCategory[] + >([]) useEffect(() => { - Promise.all([ - getTavernPeople(), - getTavernEvents(), - getMyTavernLocation(), - ]).then(([tp, te, myTavernLocation]) => { - setTavernPeople(tp) - setTavernEvents(te) - setSelectedTavern(myTavernLocation) - console.log("ARRR TH TAVERN YE BE GOEN T' BE", myTavernLocation) - }) + getTavernEvents() + .then(setTavernEvents) + .catch((err) => { + console.error(err) + setErroredFetches((p) => [...p, 'tavernEvents']) + }) + getMyTavernLocation() + .then(setSelectedTavern) + .catch((err) => { + console.error(err) + setErroredFetches((p) => [...p, 'myTavernLocation']) + }) + getTavernPeople() + .then(setTavernPeople) + .catch((err) => { + console.error(err) + setErroredFetches((p) => [...p, 'tavernPeople']) + }) }, []) const handleTavernSelect = (tavernId: string | null) => { @@ -264,6 +326,7 @@ export default function Tavern() { {selectedTavern?.eventDate ? ( From 40ca2235978d5696a31cc1be3436b92ab11b00e3 Mon Sep 17 00:00:00 2001 From: Malted Date: Mon, 3 Feb 2025 12:14:54 -0500 Subject: [PATCH 2/4] Rewrite tavern location rsvp (frontend) --- src/app/harbor/tavern/tavern.tsx | 65 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/app/harbor/tavern/tavern.tsx b/src/app/harbor/tavern/tavern.tsx index 72945482..8a340adb 100644 --- a/src/app/harbor/tavern/tavern.tsx +++ b/src/app/harbor/tavern/tavern.tsx @@ -35,11 +35,11 @@ type TavernDatafetchCategory = const RsvpStatusSwitcher = ({ tavernEvents, - onTavernSelect, + selectedTavern, erroredFetches, }: { tavernEvents: TavernEventItem[] - onTavernSelect: (tavernId: string | null) => void + selectedTavern: TavernEventItem | null erroredFetches: TavernDatafetchCategory[] }) => { const [rsvpStatus, setRsvpStatus] = useLocalStorageState( @@ -125,35 +125,42 @@ const RsvpStatusSwitcher = ({ Please consider volunteering to organize this tavern, me hearty! + {erroredFetches.includes('tavernEvents') ? ( +

Failed to fetch tavern events

+ ) : erroredFetches.includes('myTavernLocation') ? ( +

Failed to fetch your tavern location

+ ) : !tavernEvents || !selectedTavern ? ( +

Loading...

+ ) : ( + + )} +
{tavernEvents && (rsvpStatus === 'participant' || rsvpStatus === 'organizer') ? ( - <> - - + <> ) : (

Not a participant or organizer

)} @@ -325,7 +332,7 @@ export default function Tavern() { From 441f5836b3d406536843dc71df86b3b93f59c9ad Mon Sep 17 00:00:00 2001 From: Malted Date: Mon, 3 Feb 2025 12:40:32 -0500 Subject: [PATCH 3/4] RSVP frontend done, starting shirt size --- src/app/harbor/tavern/tavern-utils.ts | 2 +- src/app/harbor/tavern/tavern.tsx | 240 +++++++++++++++----------- src/app/utils/tavern.ts | 2 +- 3 files changed, 143 insertions(+), 101 deletions(-) diff --git a/src/app/harbor/tavern/tavern-utils.ts b/src/app/harbor/tavern/tavern-utils.ts index 5a5e2519..a888c1fa 100644 --- a/src/app/harbor/tavern/tavern-utils.ts +++ b/src/app/harbor/tavern/tavern-utils.ts @@ -8,7 +8,7 @@ Airtable.configure({ endpointUrl: process.env.AIRTABLE_ENDPOINT_URL, }) -type RsvpStatus = 'none' | 'organizer' | 'participant' +export type RsvpStatus = 'none' | 'organizer' | 'participant' export type TavernPersonItem = { status: RsvpStatus coordinates: string diff --git a/src/app/harbor/tavern/tavern.tsx b/src/app/harbor/tavern/tavern.tsx index 8a340adb..347b235e 100644 --- a/src/app/harbor/tavern/tavern.tsx +++ b/src/app/harbor/tavern/tavern.tsx @@ -18,6 +18,7 @@ import { rspvForTavern, TavernEventItem, TavernPersonItem, + RsvpStatus, } from './tavern-utils' import Modal from '@/components/ui/modal' import { Button } from '@/components/ui/button' @@ -29,75 +30,75 @@ const Map = dynamic(() => import('./map'), { }) type TavernDatafetchCategory = + | 'rsvpStatus' | 'tavernEvents' | 'myTavernLocation' | 'tavernPeople' const RsvpStatusSwitcher = ({ + rsvpStatus, tavernEvents, selectedTavern, + shirtSize, erroredFetches, }: { + rsvpStatus: RsvpStatus tavernEvents: TavernEventItem[] selectedTavern: TavernEventItem | null + shirtSize // getShirtSize().then((ss) => setShirtSize(ss)) erroredFetches: TavernDatafetchCategory[] }) => { - const [rsvpStatus, setRsvpStatus] = useLocalStorageState( - 'cache.rsvpStatus', - 'none', - ) - const [whichTavern, setWhichTavern] = useLocalStorageState( - 'cache.whichTavern', - 'none', - ) - const [shirtSize, setShirtSize] = useLocalStorageState( - 'cache.shirtSize', - 'none', - ) + // const [whichTavern, setWhichTavern] = useLocalStorageState( + // 'cache.whichTavern', + // 'none', + // ) + // const [shirtSize, setShirtSize] = useLocalStorageState( + // 'cache.shirtSize', + // 'none', + // ) const [attendeeNoOrganizerModal, setAttendeeNoOrganizerModal] = useState(false) - const { toast } = useToast() - useEffect(() => { - toast({ - title: 'Saved', - description: - editMessages[Math.floor(Math.random() * editMessages.length)], - }) - }, [rsvpStatus, whichTavern, shirtSize]) + // const { toast } = useToast() + // useEffect(() => { + // toast({ + // title: 'Saved', + // description: + // editMessages[Math.floor(Math.random() * editMessages.length)], + // }) + // }, [rsvpStatus, whichTavern, shirtSize]) - useEffect(() => { - // set rsvp status - getTavernRsvpStatus().then((status) => setRsvpStatus(status)) - getShirtSize().then((ss) => setShirtSize(ss)) - }, []) + // useEffect(() => { + // // set rsvp status + // getShirtSize().then((ss) => setShirtSize(ss)) + // }, []) - const onOptionChangeHandler = (e) => { - const status = e.target.value - setRsvpStatus(status) - setTavernRsvpStatus(status) + // const onOptionChangeHandler = (e) => { + // const status = e.target.value + // setRsvpStatus(status) + // setTavernRsvpStatus(status) - if (status !== 'participant' && status !== 'organizer') { - setWhichTavern('none') - submitMyTavernLocation(null) - onTavernSelect(null) - } - } + // if (status !== 'participant' && status !== 'organizer') { + // setWhichTavern('none') + // submitMyTavernLocation(null) + // onTavernSelect(null) + // } + // } - const onTavernChangeHandler = (event) => { - const tavernId = event.target.value - setWhichTavern(tavernId) - submitMyTavernLocation(tavernId).catch(console.error) - onTavernSelect(tavernId) + // const onTavernChangeHandler = (event) => { + // const tavernId = event.target.value + // setWhichTavern(tavernId) + // submitMyTavernLocation(tavernId).catch(console.error) + // onTavernSelect(tavernId) - if ( - rsvpStatus === 'participant' && - tavernEvents.find((te) => te.id === tavernId).organizers.length === 0 - ) { - console.log('u shoiuld vhe an organizer') - setAttendeeNoOrganizerModal(true) - } - } + // if ( + // rsvpStatus === 'participant' && + // tavernEvents.find((te) => te.id === tavernId).organizers.length === 0 + // ) { + // console.log('u shoiuld vhe an organizer') + // setAttendeeNoOrganizerModal(true) + // } + // } const eventsByCountry = tavernEvents.reduce((acc, event) => { const country = event.locality.split(', ').at(-1) @@ -125,37 +126,64 @@ const RsvpStatusSwitcher = ({ Please consider volunteering to organize this tavern, me hearty! - {erroredFetches.includes('tavernEvents') ? ( -

Failed to fetch tavern events

- ) : erroredFetches.includes('myTavernLocation') ? ( -

Failed to fetch your tavern location

- ) : !tavernEvents || !selectedTavern ? ( -

Loading...

- ) : ( - - )} +
+ {erroredFetches.includes('rsvpStatus') ? ( +

+ Failed to load your current RSVP status. +

+ ) : !rsvpStatus ? ( +

Loading RSVP status selection...

+ ) : ( + + )} +
+ +
+ {erroredFetches.includes('tavernEvents') ? ( +

Failed to fetch tavern events

+ ) : erroredFetches.includes('myTavernLocation') ? ( +

Failed to fetch your tavern location

+ ) : !tavernEvents || !selectedTavern ? ( +

Loading tavern events selection...

+ ) : ( + + )} +
{tavernEvents && @@ -172,21 +200,7 @@ const RsvpStatusSwitcher = ({ className="text-center mb-6 mt-12 flex flex-col gap-2" id="region-select" > - + {tavernEvents && (rsvpStatus === 'participant' || rsvpStatus === 'organizer') ? ( @@ -242,6 +256,10 @@ const RsvpStatusSwitcher = ({ } export default function Tavern() { + const [rsvpStatus, setRsvpStatus] = useLocalStorageState( + 'cache.rsvpStatus', + 'none', + ) const [tavernPeople, setTavernPeople] = useState([]) const [tavernEvents, setTavernEvents] = useState([]) const [selectedTavern, setSelectedTavern] = useState( @@ -252,6 +270,13 @@ export default function Tavern() { >([]) useEffect(() => { + getTavernRsvpStatus() + .then(setRsvpStatus) + .catch((err) => { + console.error(err) + setErroredFetches((p) => [...p, 'rsvpStatus']) + }) + getTavernEvents() .then(setTavernEvents) .catch((err) => { @@ -331,6 +356,7 @@ export default function Tavern() {

) : null} - + {erroredFetches.includes('tavernEvents') ? ( +

+ Failed to load tavern events for the tavern map. +

+ ) : erroredFetches.includes('myTavernLocation') ? ( +

+ Failed to load your chosen tavern location for the tavern map. +

+ ) : erroredFetches.includes('tavernPeople') ? ( +

+ Failed to load tavern people for the tavern map. +

+ ) : !tavernEvents || !tavernPeople ? ( +

Loading tavern map...

+ ) : ( + + )} ) diff --git a/src/app/utils/tavern.ts b/src/app/utils/tavern.ts index 1a607e3f..f33227bb 100644 --- a/src/app/utils/tavern.ts +++ b/src/app/utils/tavern.ts @@ -9,7 +9,7 @@ Airtable.configure({ endpointUrl: process.env.AIRTABLE_ENDPOINT_URL, }) -type RsvpStatus = 'none' | 'organizer' | 'participant' +export type RsvpStatus = 'none' | 'organizer' | 'participant' export const setTavernRsvpStatus = async (rsvpStatus: RsvpStatus) => { // check auth const session = await getSession() From 51887530124196d636229b0ecef0a0a3f53ad457 Mon Sep 17 00:00:00 2001 From: Malted Date: Mon, 3 Feb 2025 14:28:59 -0500 Subject: [PATCH 4/4] Fix RSVP --- src/app/harbor/tavern/tavern-utils.ts | 19 ++- src/app/harbor/tavern/tavern.tsx | 237 ++++++++++++++++++-------- 2 files changed, 183 insertions(+), 73 deletions(-) diff --git a/src/app/harbor/tavern/tavern-utils.ts b/src/app/harbor/tavern/tavern-utils.ts index a888c1fa..ab66623b 100644 --- a/src/app/harbor/tavern/tavern-utils.ts +++ b/src/app/harbor/tavern/tavern-utils.ts @@ -1,6 +1,11 @@ 'use server' import { getSession } from '@/app/utils/auth' +import { + setTavernRsvpStatus, + submitMyTavernLocation, + submitShirtSize, +} from '@/app/utils/tavern' import Airtable from 'airtable' Airtable.configure({ @@ -104,5 +109,17 @@ export const getTavernEvents = async () => { } export async function rspvForTavern(formData: FormData) { - console.log(formData) + let res = { success: true, error: null } + + await Promise.all([ + setTavernRsvpStatus(formData.get('rsvp') as RsvpStatus), + submitMyTavernLocation(formData.get('tavern') as string), + submitShirtSize(formData.get('shirt') as string), + ]).catch((error) => { + console.error('Error submitting tavern RSVP', error) + res = { success: false, error: error.toString() } + }) + + console.log('Successfully saved tavern RSVP') + return JSON.stringify(res) } diff --git a/src/app/harbor/tavern/tavern.tsx b/src/app/harbor/tavern/tavern.tsx index 347b235e..a5a6b1b0 100644 --- a/src/app/harbor/tavern/tavern.tsx +++ b/src/app/harbor/tavern/tavern.tsx @@ -34,32 +34,33 @@ type TavernDatafetchCategory = | 'tavernEvents' | 'myTavernLocation' | 'tavernPeople' + | 'shirtSize' const RsvpStatusSwitcher = ({ rsvpStatus, + setRsvpStatus, tavernEvents, selectedTavern, + setSelectedTavern, shirtSize, + setShirtSize, erroredFetches, }: { rsvpStatus: RsvpStatus + setRsvpStatus: (status: RsvpStatus) => void tavernEvents: TavernEventItem[] selectedTavern: TavernEventItem | null - shirtSize // getShirtSize().then((ss) => setShirtSize(ss)) + setSelectedTavern: (tavernId: string | null) => void + shirtSize: any + setShirtSize: (size: string) => void erroredFetches: TavernDatafetchCategory[] }) => { - // const [whichTavern, setWhichTavern] = useLocalStorageState( - // 'cache.whichTavern', - // 'none', - // ) - // const [shirtSize, setShirtSize] = useLocalStorageState( - // 'cache.shirtSize', - // 'none', - // ) + const [editedFlag, setEditedFlag] = useState(false) const [attendeeNoOrganizerModal, setAttendeeNoOrganizerModal] = useState(false) - // const { toast } = useToast() + const { toast } = useToast() + // useEffect(() => { // toast({ // title: 'Saved', @@ -126,7 +127,25 @@ const RsvpStatusSwitcher = ({ Please consider volunteering to organize this tavern, me hearty! -
+ { + const rsvpResponse = JSON.parse(await rspvForTavern(formData)) + + if (rsvpResponse.success) { + toast({ + title: 'Saved', + description: + editMessages[Math.floor(Math.random() * editMessages.length)], + }) + } else { + toast({ + title: 'Error', + description: `Failed to save your changes:\n${rsvpResponse.error}`, + }) + } + }} + className="flex flex-col justify-items-stretch" + > {erroredFetches.includes('rsvpStatus') ? (

Failed to load your current RSVP status. @@ -134,66 +153,120 @@ const RsvpStatusSwitcher = ({ ) : !rsvpStatus ? (

Loading RSVP status selection...

) : ( - - )} -
+ <> + -
- {erroredFetches.includes('tavernEvents') ? ( -

Failed to fetch tavern events

- ) : erroredFetches.includes('myTavernLocation') ? ( -

Failed to fetch your tavern location

- ) : !tavernEvents || !selectedTavern ? ( -

Loading tavern events selection...

- ) : ( - - )} -
+ {rsvpStatus === 'participant' || rsvpStatus === 'organizer' ? ( + <> +
+ {erroredFetches.includes('tavernEvents') ? ( +

+ Failed to fetch tavern events +

+ ) : erroredFetches.includes('myTavernLocation') ? ( +

+ Failed to fetch your tavern location +

+ ) : !tavernEvents || !selectedTavern ? ( +

Loading tavern events selection...

+ ) : ( + + )} +
- - {tavernEvents && - (rsvpStatus === 'participant' || rsvpStatus === 'organizer') ? ( - <> - ) : ( -

Not a participant or organizer

+
+ {erroredFetches.includes('shirtSize') ? ( +

+ Failed to fetch your shirt size +

+ ) : !shirtSize ? ( +

Loading shirt size selection...

+ ) : ( + + )} +
+ + ) : null} + )} - +
+ {editedFlag ? ( +

You have unsaved changes!

+ ) : null} + + +
{/*
( null, ) + const [shirtSize, setShirtSize] = useLocalStorageState( + 'cache.shirtSize', + 'none', + ) const [erroredFetches, setErroredFetches] = useState< TavernDatafetchCategory[] >([]) useEffect(() => { getTavernRsvpStatus() - .then(setRsvpStatus) - .catch((err) => { + .then((d) => { + console.log({ travernrspv: d }) + setRsvpStatus(d) + }) + .catch((err: Error) => { console.error(err) setErroredFetches((p) => [...p, 'rsvpStatus']) }) getTavernEvents() .then(setTavernEvents) - .catch((err) => { + .catch((err: Error) => { console.error(err) setErroredFetches((p) => [...p, 'tavernEvents']) }) + getMyTavernLocation() .then(setSelectedTavern) - .catch((err) => { + .catch((err: Error) => { console.error(err) setErroredFetches((p) => [...p, 'myTavernLocation']) }) + getTavernPeople() .then(setTavernPeople) - .catch((err) => { + .catch((err: Error) => { console.error(err) setErroredFetches((p) => [...p, 'tavernPeople']) }) + + getShirtSize() + .then(setShirtSize) + .catch((err: Error) => { + console.error(err) + setErroredFetches((p) => [...p, 'shirtSize']) + }) }, []) const handleTavernSelect = (tavernId: string | null) => { @@ -357,8 +446,12 @@ export default function Tavern() {