diff --git a/website/app/routes/_layout.$.tsx b/website/app/routes/_layout.$.tsx index d085d83..9a73d7e 100644 --- a/website/app/routes/_layout.$.tsx +++ b/website/app/routes/_layout.$.tsx @@ -11,7 +11,6 @@ import ImportantDates from '~/components/page-components/important-dates' import { Button } from '~/components/ui/button' import getConferenceActions from '~/lib/conference-actions' import { ConferenceState } from '~/lib/config-types' -import { CACHE_CONTROL } from '~/lib/http.server' import { css } from '../../styled-system/css' import { Box, Flex, Grid, styled } from '../../styled-system/jsx' import { conferenceConfig } from '../config/conference-config' @@ -53,7 +52,7 @@ export async function loader({ params, request, context }: LoaderFunctionArgs) { post: post.code, conferenceState: context.conferenceState, }, - { headers: { 'Cache-Control': CACHE_CONTROL.doc } }, + // { headers: { 'Cache-Control': CACHE_CONTROL.doc } }, ) } diff --git a/website/app/routes/_layout._index.tsx b/website/app/routes/_layout._index.tsx index 2b1fe0f..5f3f4d2 100644 --- a/website/app/routes/_layout._index.tsx +++ b/website/app/routes/_layout._index.tsx @@ -1,11 +1,9 @@ -import { HeadersFunction } from '@remix-run/server-runtime' -import { CACHE_CONTROL } from '~/lib/http.server' import { Hero } from '../components/hero/hero' import { SkipToContent } from '../components/skip-to-content' -export const headers: HeadersFunction = () => { - return { 'Cache-Control': CACHE_CONTROL.DEFAULT } -} +// export const headers: HeadersFunction = () => { +// return { 'Cache-Control': CACHE_CONTROL.DEFAULT } +// } export default function Index() { return ( diff --git a/website/app/routes/_layout.agenda.$year.talk.$sessionId.tsx b/website/app/routes/_layout.agenda.$year.talk.$sessionId.tsx index 8f69c47..4e58785 100644 --- a/website/app/routes/_layout.agenda.$year.talk.$sessionId.tsx +++ b/website/app/routes/_layout.agenda.$year.talk.$sessionId.tsx @@ -9,7 +9,6 @@ import { AppLink } from '~/components/app-link' import { SponsorSection } from '~/components/page-components/SponsorSection' import { ConferenceConfigYear, ConferenceImportantInformation, ConferenceYear, Year } from '~/lib/config-types' import { localeTimeFormat } from '~/lib/dates/formatting' -import { CACHE_CONTROL } from '~/lib/http.server' import { conferenceConfig } from '../config/conference-config' import { getConfSessions, sessionsSchema } from '../lib/sessionize.server' @@ -60,7 +59,7 @@ export async function loader({ params, context }: LoaderFunctionArgs) { sessionStart: DateTime.fromISO(session.startsAt).toLocaleString(DateTime.TIME_SIMPLE), sessionEnd: DateTime.fromISO(session.endsAt).toLocaleString(DateTime.TIME_SIMPLE), }, - { headers: { 'Cache-Control': CACHE_CONTROL.conf } }, + // { headers: { 'Cache-Control': CACHE_CONTROL.conf } }, ) } diff --git a/website/app/routes/_layout.agenda.($year).tsx b/website/app/routes/_layout.agenda.($year).tsx index ec8929b..435afe6 100644 --- a/website/app/routes/_layout.agenda.($year).tsx +++ b/website/app/routes/_layout.agenda.($year).tsx @@ -6,127 +6,121 @@ import { Fragment } from 'react' import { $path } from 'remix-routes' import { Box, Flex, styled } from 'styled-system/jsx' import { TypeOf } from 'zod' -import { - ConferenceConfigYear, - ConferenceImportantInformation, - ConferenceYear, - Sponsor, - Year, - YearSponsors, -} from '~/lib/config-types' +import { AppLink } from '~/components/app-link' +import { SponsorSection } from '~/components/page-components/SponsorSection' +import { ConferenceConfigYear, ConferenceImportantInformation, ConferenceYear, Year } from '~/lib/config-types' import { localeTimeFormat } from '~/lib/dates/formatting' -import { CACHE_CONTROL } from '~/lib/http.server' import { conferenceConfig } from '../config/conference-config' import { formatDate, getScheduleGrid, gridSmartSchema } from '../lib/sessionize.server' import { slugify } from '../lib/slugify' export async function loader({ params, context }: LoaderFunctionArgs) { - if (params.year && !/\d{4}/.test(params.year)) { - return redirect($path('/agenda/:year?', { year: undefined })) - } + if (params.year && !/\d{4}/.test(params.year)) { + throw redirect($path('/agenda/:year?', { year: undefined })) + } - const year = - params.year && /\d{4}/.test(params.year) ? (params.year as Year) : context.conferenceState.conference.year + const year = + params.year && /\d{4}/.test(params.year) ? (params.year as Year) : context.conferenceState.conference.year - const yearConfigLookup = (conferenceConfig.conferences as Record)[year] - if (!yearConfigLookup || 'cancelledMessage' in yearConfigLookup) { - if (!params.year) { - throw new Response(JSON.stringify({ message: 'No config for year' }), { status: 404 }) - } + const yearConfigLookup = (conferenceConfig.conferences as Record)[year] + if (!yearConfigLookup || 'cancelledMessage' in yearConfigLookup) { + if (!params.year) { + throw new Response(JSON.stringify({ message: 'No config for year' }), { status: 404 }) + } - return redirect($path('/agenda/:year?', { year: undefined })) - } + throw redirect($path('/agenda/:year?', { year: undefined })) + } - const yearConfig: ConferenceImportantInformation = params.year - ? getImportantInformation(yearConfigLookup) - : context.conferenceState.conference + const yearConfig: ConferenceImportantInformation = params.year + ? getImportantInformation(yearConfigLookup) + : context.conferenceState.conference - if (yearConfig.sessions?.kind === 'sessionize' && !yearConfig.sessions.sessionizeEndpoint) { - throw new Response(JSON.stringify({ message: 'No sessionize endpoint for year' }), { status: 404 }) - } + if (yearConfig.sessions?.kind === 'sessionize' && !yearConfig.sessions.sessionizeEndpoint) { + throw new Response(JSON.stringify({ message: 'No sessionize endpoint for year' }), { status: 404 }) + } - const schedules: TypeOf = - yearConfig.sessions?.kind === 'sessionize' - ? await getScheduleGrid({ - sessionizeEndpoint: yearConfig.sessions.sessionizeEndpoint, - confTimeZone: conferenceConfig.timezone, - }) - : // TODO Deal with data type - [] + const schedules: TypeOf = + yearConfig.sessions?.kind === 'sessionize' + ? await getScheduleGrid({ + sessionizeEndpoint: yearConfig.sessions.sessionizeEndpoint, + confTimeZone: conferenceConfig.timezone, + }) + : // TODO Deal with data type + [] - const schedule = schedules[0] + const schedule = schedules[0] - return json( - { - year, - sponsors: yearConfigLookup.sponsors, - conferences: Object.values(conferenceConfig.conferences).map((conf) => ({ - year: conf.year, - })), - schedule: schedule - ? { - ...schedule, - dateSlug: slugify( - formatDate(schedule.date, { - month: 'short', - day: 'numeric', - }), - ), - dateISO: schedule.date, - dateFormatted: formatDate(schedule.date, { - weekday: 'long', - month: 'long', - day: 'numeric', - }), - dateFormattedShort: formatDate(schedule.date, { - month: 'short', - day: 'numeric', - }), - } - : undefined, - }, - { headers: { 'Cache-Control': CACHE_CONTROL.conf } }, - ) + return json( + { + year, + sponsors: yearConfigLookup.sponsors, + conferences: Object.values(conferenceConfig.conferences).map((conf) => ({ + year: conf.year, + })), + schedule: schedule + ? { + ...schedule, + dateSlug: slugify( + formatDate(schedule.date, { + month: 'short', + day: 'numeric', + }), + ), + dateISO: schedule.date, + dateFormatted: formatDate(schedule.date, { + weekday: 'long', + month: 'long', + day: 'numeric', + }), + dateFormattedShort: formatDate(schedule.date, { + month: 'short', + day: 'numeric', + }), + } + : undefined, + }, + // { headers: { 'Cache-Control': CACHE_CONTROL.conf } }, + ) } export default function Agenda() { - const { schedule, sponsors, conferences, year } = useLoaderData() - const availableTimeSlots = schedule?.timeSlots.map((timeSlot) => timeSlot.slotStart.replace(/:/g, '')) + const { schedule, sponsors, conferences, year } = useLoaderData() + const availableTimeSlots = schedule?.timeSlots.map((timeSlot) => timeSlot.slotStart.replace(/:/g, '')) - if (!schedule) { - return ( - -

- {conferenceConfig.name} {year} agenda has not been announced -

+ if (!schedule) { + return ( + +

+ {conferenceConfig.name} {year} agenda has not been announced +

- + - -
- ) - } + +
+ ) + } - return ( - - + `[time-${timeSlot.slotStart.replace(/:/g, '')}] auto`), - ].join(' '), - '--room-columns': [ - '[times] auto', - ...schedule.rooms.map((room, index, rooms) => - index === 0 - ? `[room-${room.id}-start] 1fr` - : index + 1 === rooms.length - ? `[room-${rooms[index - 1].id}-end room-${room.id}-start] 1fr [room-${room.id}-end]` - : `[room-${rooms[index - 1].id}-end room-${room.id}-start] 1fr`, - ), - ].join(' '), - } as React.CSSProperties - } - md={{ - display: 'grid', - gridTemplateRows: 'var(--slot-rows)', - gridTemplateColumns: 'var(--room-columns)', - gridGap: '1', - }} - > - {schedule.rooms.map((room) => ( - /* hidden on small screens and browsers without grid support */ - - ))} - - {schedule.timeSlots.map((timeSlot, timeSlotIndex) => { - const startTime12 = DateTime.fromISO(timeSlot.slotStart).toFormat('h:mm a').toLowerCase() - const timeSlotSimple = timeSlot.slotStart.replace(/:/g, '') - const nextTimeSlot = schedule.timeSlots[timeSlotIndex + 1] - const nextTimeSlotStart = nextTimeSlot?.slotStart.replace(/:/g, '') - - return ( - - - {startTime12} - {nextTimeSlot?.slotStart ? ( - - {' '} - - {DateTime.fromISO(nextTimeSlot.slotStart).toFormat('h:mm a').toLowerCase()} - - ) : null} - - - {timeSlot.rooms.map((room) => { - const fullSession = schedule.rooms - .find((r) => r.id === room.id) - ?.sessions.find((session) => session.id === room.session.id) - const endsAtTime = fullSession?.endsAt.replace(/\d{4}-\d{2}-\d{2}T/, '') - const endTime12 = fullSession?.endsAt - ? DateTime.fromISO(fullSession.endsAt).toFormat('h:mm a').toLowerCase() - : undefined - - const timeSlotEnd = endsAtTime?.replace(/:/g, '') ?? '' - const earliestEnd = !availableTimeSlots?.includes(timeSlotEnd) - ? nextTimeSlotStart - : timeSlotEnd ?? nextTimeSlotStart - - return ( - - - - {fullSession?.title} - - `[time-${timeSlot.slotStart.replace(/:/g, '')}] auto`, + ), + ].join(' '), + '--room-columns': [ + '[times] auto', + ...schedule.rooms.map((room, index, rooms) => + index === 0 + ? `[room-${room.id}-start] 1fr` + : index + 1 === rooms.length + ? `[room-${rooms[index - 1].id}-end room-${room.id}-start] 1fr [room-${room.id}-end]` + : `[room-${rooms[index - 1].id}-end room-${room.id}-start] 1fr`, + ), + ].join(' '), + } as React.CSSProperties + } + xl={{ + display: 'grid', + gridTemplateRows: 'var(--slot-rows)', + gridTemplateColumns: 'var(--room-columns)', + gridGap: '1', + }} + > + {schedule.rooms.map((room) => ( + /* hidden on small screens and browsers without grid support */ + - - - - + > {room.name} - - {fullSession?.speakers?.length ? ( - - - - - {fullSession?.speakers.map((speaker) => speaker.name)?.join(', ')} - - ) : null} - - - ) - })} - - ) - })} - - - + + ))} - - - ) -} - -function ConferenceBrowser({ conferences }: { conferences: { year: Year }[] }) { - return ( - - - View Previous Conferences - - - {conferences.map((conf) => ( - - {conf.year} - - ))} - - - ) -} + {schedule.timeSlots.map((timeSlot, timeSlotIndex) => { + const startTime12 = DateTime.fromISO(timeSlot.slotStart).toFormat('h:mm a').toLowerCase() + const timeSlotSimple = timeSlot.slotStart.replace(/:/g, '') + const nextTimeSlot = schedule.timeSlots[timeSlotIndex + 1] + const nextTimeSlotStart = nextTimeSlot?.slotStart.replace(/:/g, '') -function SponsorSection({ sponsors, year }: { sponsors: YearSponsors | undefined; year: Year }) { - const sponsorStyles = { - platinum: { gradientFrom: '#496F7F', logoSize: 'lg' as 'lg' }, - gold: { gradientFrom: '#453927', logoSize: 'md' as 'md' }, - silver: { gradientFrom: '#2A3251', logoSize: 'sm' as 'sm' }, - bronze: { gradientFrom: '#452927', logoSize: 'xs' as 'xs' }, - digital: { gradientFrom: '#371F4E', logoSize: 'xs' as 'xs' }, - community: { gradientFrom: '#1F1F4E', logoSize: 'xs' as 'xs' }, - coffeeCart: { gradientFrom: '#1F1F4E', logoSize: 'xs' as 'xs' }, - quietRoom: { gradientFrom: '#1F1F4E', logoSize: 'xs' as 'xs' }, - keynotes: { gradientFrom: '#1F1F4E', logoSize: 'sm' as 'sm' }, - } - const getSponsorStyle = (category: keyof typeof sponsorStyles) => sponsorStyles[category] + return ( + + + {startTime12} + {nextTimeSlot?.slotStart ? ( + + {' '} + - {DateTime.fromISO(nextTimeSlot.slotStart).toFormat('h:mm a').toLowerCase()} + + ) : null} + - const renderSponsor = (sponsor: Sponsor, category: keyof typeof sponsorStyles, zIndex: number) => { - const { gradientFrom, logoSize } = getSponsorStyle(category) + {timeSlot.rooms.map((room) => { + const fullSession = schedule.rooms + .find((r) => r.id === room.id) + ?.sessions.find((session) => session.id === room.session.id) + const endsAtTime = fullSession?.endsAt.replace(/\d{4}-\d{2}-\d{2}T/, '') + const endTime12 = fullSession?.endsAt + ? DateTime.fromISO(fullSession.endsAt).toFormat('h:mm a').toLowerCase() + : undefined - return ( - - - - {category.charAt(0).toUpperCase() + category.slice(1)} Sponsor - - - ) - } + const timeSlotEnd = endsAtTime?.replace(/:/g, '') ?? '' + const earliestEnd = !availableTimeSlots?.includes(timeSlotEnd) + ? nextTimeSlotStart + : (timeSlotEnd ?? nextTimeSlotStart) - const renderSponsorGroup = ( - title: string, - sponsorGroups: { sponsors: Sponsor[] | undefined; category: keyof typeof sponsorStyles }[], - ) => { - const allSponsors = sponsorGroups.flatMap((group) => group.sponsors || []) - if (allSponsors.length === 0) return null + return ( + + + + + {fullSession?.title} + + + + + + + {startTime12} - {endTime12} + + + + + + {room.name} + + {fullSession?.speakers?.length ? ( + + + + + {fullSession?.speakers.map((speaker) => speaker.name)?.join(', ')} + + ) : null} + + + ) + })} + + ) + })} + - let zIndex = allSponsors.length + - return ( - - - {title} - - - {sponsorGroups.map((group) => - group.sponsors?.map((sponsor) => { - const sponsorElement = renderSponsor(sponsor, group.category, zIndex) - zIndex -= 1 - return sponsorElement - }), - )} + - ) - } - - if (!sponsors) return null +} - return ( - - - {year} Sponsors - - {renderSponsorGroup('Major Sponsors', [ - { sponsors: sponsors.platinum, category: 'platinum' }, - { sponsors: sponsors.gold, category: 'gold' }, - { sponsors: sponsors.silver, category: 'silver' }, - { sponsors: sponsors.bronze, category: 'bronze' }, - ])} - {renderSponsorGroup('Minor Sponsors', [ - { sponsors: sponsors.digital, category: 'digital' }, - { sponsors: sponsors.community, category: 'community' }, - { sponsors: sponsors.coffeeCart, category: 'coffeeCart' }, - { sponsors: sponsors.quietRoom, category: 'quietRoom' }, - { sponsors: sponsors.keynotes, category: 'keynotes' }, - ])} - - ) +function ConferenceBrowser({ conferences }: { conferences: { year: Year }[] }) { + return ( + + + View Previous Conferences + + + {conferences.map((conf) => ( + + {conf.year} + + ))} + + + ) } function getImportantInformation(yearConfig: ConferenceYear): ConferenceImportantInformation { - return { - date: yearConfig.conferenceDate?.toISO(), - year: yearConfig.year, - sessions: yearConfig.sessions, - ticketPrice: yearConfig.ticketPrice, - votingOpens: yearConfig.talkVotingDates?.opens.toLocaleString(localeTimeFormat), - } + return { + date: yearConfig.conferenceDate?.toISO(), + year: yearConfig.year, + sessions: yearConfig.sessions, + ticketPrice: yearConfig.ticketPrice, + votingOpens: yearConfig.talkVotingDates?.opens.toLocaleString(localeTimeFormat), + } }