From bb695ff729aa10b6d22069f81232a6398f744854 Mon Sep 17 00:00:00 2001 From: grace Date: Fri, 17 Jan 2025 00:17:42 -0700 Subject: [PATCH 001/115] move nav to layout --- src/lib/components/breadcrumb.svelte | 14 + src/lib/contentful/content-types.json | 599 +++++++++++++++++------- src/lib/contentful/global-settings.ts | 2 + src/lib/contentful/home.ts | 2 +- src/lib/contentful/index.ts | 52 +- src/lib/contentful/session.ts | 22 + src/lib/contentful/speaker.ts | 1 + src/lib/contentful/sponsor.ts | 4 +- src/lib/contentful/time-slot.ts | 21 + src/routes/+layout.server.ts | 6 +- src/routes/+layout.svelte | 2 + src/routes/+page.svelte | 2 - src/routes/code-of-conduct/+page.svelte | 2 +- src/routes/speakers/+page.svelte | 0 src/routes/speakers/[slug]/+page.svelte | 32 ++ tailwind.config.ts | 3 + 16 files changed, 583 insertions(+), 181 deletions(-) create mode 100644 src/lib/components/breadcrumb.svelte create mode 100644 src/lib/contentful/session.ts create mode 100644 src/lib/contentful/time-slot.ts create mode 100644 src/routes/speakers/+page.svelte create mode 100644 src/routes/speakers/[slug]/+page.svelte diff --git a/src/lib/components/breadcrumb.svelte b/src/lib/components/breadcrumb.svelte new file mode 100644 index 0000000..f49497a --- /dev/null +++ b/src/lib/components/breadcrumb.svelte @@ -0,0 +1,14 @@ + + + + + + < {text} + \ No newline at end of file diff --git a/src/lib/contentful/content-types.json b/src/lib/contentful/content-types.json index 13532e5..c493142 100644 --- a/src/lib/contentful/content-types.json +++ b/src/lib/contentful/content-types.json @@ -1,4 +1,128 @@ [ + { + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "0u17lhw8p27f" + } + }, + "id": "speaker", + "type": "ContentType", + "createdAt": "2024-12-02T20:19:27.016Z", + "updatedAt": "2025-01-14T18:07:54.500Z", + "environment": { + "sys": { + "id": "master", + "type": "Link", + "linkType": "Environment" + } + }, + "publishedVersion": 7, + "publishedAt": "2025-01-14T18:07:54.500Z", + "firstPublishedAt": "2024-12-02T20:19:27.494Z", + "createdBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } + }, + "updatedBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } + }, + "publishedCounter": 4, + "version": 8, + "publishedBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } + }, + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/speaker" + }, + "displayField": "fullName", + "name": "Speaker", + "description": "", + "fields": [ + { + "id": "fullName", + "name": "Full Name", + "type": "Symbol", + "localized": false, + "required": true, + "validations": [], + "disabled": false, + "omitted": false + }, + { + "id": "image", + "name": "Image", + "type": "Link", + "localized": false, + "required": true, + "validations": [ + { + "linkMimetypeGroup": [ + "image" + ] + } + ], + "disabled": false, + "omitted": false, + "linkType": "Asset" + }, + { + "id": "jobTitle", + "name": "Job Title", + "type": "Symbol", + "localized": false, + "required": true, + "validations": [], + "disabled": false, + "omitted": false + }, + { + "id": "companyName", + "name": "Company Name", + "type": "Symbol", + "localized": false, + "required": true, + "validations": [], + "disabled": false, + "omitted": false + }, + { + "id": "bio", + "name": "Bio", + "type": "Text", + "localized": false, + "required": false, + "validations": [], + "disabled": false, + "omitted": false + }, + { + "id": "keynote", + "name": "Keynote", + "type": "Boolean", + "localized": false, + "required": true, + "validations": [], + "defaultValue": { + "en-US": false + }, + "disabled": false, + "omitted": false + } + ] + }, { "sys": { "space": { @@ -11,7 +135,7 @@ "id": "home", "type": "ContentType", "createdAt": "2024-12-02T20:18:23.839Z", - "updatedAt": "2024-12-06T19:53:27.293Z", + "updatedAt": "2025-01-10T17:04:20.156Z", "environment": { "sys": { "id": "master", @@ -19,8 +143,8 @@ "linkType": "Environment" } }, - "publishedVersion": 21, - "publishedAt": "2024-12-06T19:53:27.293Z", + "publishedVersion": 23, + "publishedAt": "2025-01-10T17:04:20.156Z", "firstPublishedAt": "2024-12-02T20:18:24.237Z", "createdBy": { "sys": { @@ -36,8 +160,8 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "publishedCounter": 11, - "version": 22, + "publishedCounter": 12, + "version": 24, "publishedBy": { "sys": { "type": "Link", @@ -392,7 +516,7 @@ "name": "Theme Description", "type": "Text", "localized": false, - "required": true, + "required": false, "validations": [], "disabled": false, "omitted": false @@ -445,10 +569,10 @@ "id": "0u17lhw8p27f" } }, - "id": "globalSettings", + "id": "sponsor", "type": "ContentType", - "createdAt": "2024-12-03T02:41:08.173Z", - "updatedAt": "2024-12-03T02:41:08.486Z", + "createdAt": "2024-12-02T20:55:50.099Z", + "updatedAt": "2025-01-08T22:47:49.041Z", "environment": { "sys": { "id": "master", @@ -456,9 +580,9 @@ "linkType": "Environment" } }, - "publishedVersion": 1, - "publishedAt": "2024-12-03T02:41:08.486Z", - "firstPublishedAt": "2024-12-03T02:41:08.486Z", + "publishedVersion": 7, + "publishedAt": "2025-01-08T22:47:49.041Z", + "firstPublishedAt": "2024-12-02T20:55:50.493Z", "createdBy": { "sys": { "type": "Link", @@ -473,8 +597,8 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "publishedCounter": 1, - "version": 2, + "publishedCounter": 4, + "version": 8, "publishedBy": { "sys": { "type": "Link", @@ -482,61 +606,119 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/globalSettings" + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/sponsor" }, - "displayField": "title", - "name": "Global Settings", + "displayField": "name", + "name": "Sponsor", "description": "", "fields": [ { - "id": "title", - "name": "Title (for reference only)", + "id": "name", + "name": "Name", "type": "Symbol", "localized": false, "required": true, - "validations": [], + "validations": [ + { + "unique": true + } + ], "disabled": false, "omitted": false }, { - "id": "price", - "name": "Price", - "type": "Number", + "id": "sponsorType", + "name": "Sponsor Type", + "type": "Symbol", "localized": false, "required": true, "validations": [ { - "range": { - "min": 0 - } + "in": [ + "Elite", + "Premier", + "Impact" + ] } ], "disabled": false, "omitted": false }, { - "id": "discountedPrice", - "name": "Discounted Price", - "type": "Number", + "id": "darkLogo", + "name": "Dark Logo", + "type": "Link", "localized": false, "required": true, "validations": [ { - "range": { - "min": 0 - } + "linkMimetypeGroup": [ + "image" + ] + } + ], + "disabled": false, + "omitted": false, + "linkType": "Asset" + }, + { + "id": "lightLogo", + "name": "Light Logo", + "type": "Link", + "localized": false, + "required": true, + "validations": [ + { + "linkMimetypeGroup": [ + "image" + ] + } + ], + "disabled": false, + "omitted": false, + "linkType": "Asset" + }, + { + "id": "colorLogo", + "name": "Color Logo", + "type": "Link", + "localized": false, + "required": false, + "validations": [ + { + "linkMimetypeGroup": [ + "image" + ] } ], "disabled": false, + "omitted": false, + "linkType": "Asset" + }, + { + "id": "description", + "name": "Description", + "type": "Text", + "localized": false, + "required": true, + "validations": [], + "disabled": false, "omitted": false }, { - "id": "pricingFootnote", - "name": "Pricing Footnote", + "id": "externalUrl", + "name": "External URL", "type": "Symbol", "localized": false, "required": false, - "validations": [], + "validations": [ + { + "regexp": { + "pattern": "^(ftp|http|https):\\/\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\/|\\/([\\w#!:.?+=&%@!\\-/]))?$", + "flags": null + } + } + ], "disabled": false, "omitted": false } @@ -551,10 +733,10 @@ "id": "0u17lhw8p27f" } }, - "id": "cta", + "id": "talk", "type": "ContentType", - "createdAt": "2024-12-03T02:01:02.821Z", - "updatedAt": "2024-12-03T02:02:42.759Z", + "createdAt": "2025-01-03T04:59:12.609Z", + "updatedAt": "2025-01-08T21:48:00.463Z", "environment": { "sys": { "id": "master", @@ -562,9 +744,9 @@ "linkType": "Environment" } }, - "publishedVersion": 7, - "publishedAt": "2024-12-03T02:02:42.759Z", - "firstPublishedAt": "2024-12-03T02:01:03.221Z", + "publishedVersion": 17, + "publishedAt": "2025-01-08T21:48:00.463Z", + "firstPublishedAt": "2025-01-03T04:59:12.953Z", "createdBy": { "sys": { "type": "Link", @@ -579,8 +761,8 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "publishedCounter": 4, - "version": 8, + "publishedCounter": 9, + "version": 18, "publishedBy": { "sys": { "type": "Link", @@ -588,15 +770,15 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/cta" + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/talk" }, "displayField": "title", - "name": "CTA", + "name": "Session", "description": "", "fields": [ { "id": "title", - "name": "Title (for reference only)", + "name": "Title", "type": "Symbol", "localized": false, "required": true, @@ -605,8 +787,8 @@ "omitted": false }, { - "id": "text", - "name": "Text", + "id": "url", + "name": "URL", "type": "Symbol", "localized": false, "required": true, @@ -615,16 +797,47 @@ "omitted": false }, { - "id": "url", - "name": "URL", - "type": "Text", + "id": "location", + "name": "Location", + "type": "Symbol", "localized": false, - "required": true, + "required": false, "validations": [], "disabled": false, "omitted": false - } - ] + }, + { + "id": "speakers", + "name": "Speakers", + "type": "Array", + "localized": false, + "required": false, + "validations": [], + "disabled": false, + "omitted": false, + "items": { + "type": "Link", + "validations": [ + { + "linkContentType": [ + "speaker" + ] + } + ], + "linkType": "Entry" + } + }, + { + "id": "description", + "name": "Description", + "type": "Text", + "localized": false, + "required": true, + "validations": [], + "disabled": false, + "omitted": false + } + ] }, { "sys": { @@ -635,10 +848,10 @@ "id": "0u17lhw8p27f" } }, - "id": "speaker", + "id": "timeSlot", "type": "ContentType", - "createdAt": "2024-12-02T20:19:27.016Z", - "updatedAt": "2024-12-02T21:02:16.825Z", + "createdAt": "2025-01-03T05:03:09.047Z", + "updatedAt": "2025-01-03T05:33:39.231Z", "environment": { "sys": { "id": "master", @@ -646,9 +859,9 @@ "linkType": "Environment" } }, - "publishedVersion": 5, - "publishedAt": "2024-12-02T21:02:16.825Z", - "firstPublishedAt": "2024-12-02T20:19:27.494Z", + "publishedVersion": 9, + "publishedAt": "2025-01-03T05:33:39.231Z", + "firstPublishedAt": "2025-01-03T05:03:09.446Z", "createdBy": { "sys": { "type": "Link", @@ -663,8 +876,8 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "publishedCounter": 3, - "version": 6, + "publishedCounter": 5, + "version": 10, "publishedBy": { "sys": { "type": "Link", @@ -672,15 +885,15 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/speaker" + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/timeSlot" }, - "displayField": "fullName", - "name": "Speaker", + "displayField": "title", + "name": "Time Slot", "description": "", "fields": [ { - "id": "fullName", - "name": "Full Name", + "id": "title", + "name": "Title", "type": "Symbol", "localized": false, "required": true, @@ -689,54 +902,75 @@ "omitted": false }, { - "id": "image", - "name": "Image", - "type": "Link", + "id": "startTime", + "name": "Start Time", + "type": "Date", "localized": false, "required": true, "validations": [ { - "linkMimetypeGroup": [ - "image" - ] + "dateRange": { + "after": null, + "before": null, + "min": "2025-03-02", + "max": "2025-03-06" + } } ], - "disabled": false, - "omitted": false, - "linkType": "Asset" - }, - { - "id": "jobTitle", - "name": "Job Title", - "type": "Symbol", - "localized": false, - "required": true, - "validations": [], + "defaultValue": { + "en-US": "2025-03-03T00:00" + }, "disabled": false, "omitted": false }, { - "id": "companyName", - "name": "Company Name", - "type": "Symbol", + "id": "endTime", + "name": "End Time", + "type": "Date", "localized": false, "required": true, - "validations": [], + "validations": [ + { + "dateRange": { + "after": null, + "before": null, + "min": "2025-03-02", + "max": "2025-03-06" + } + } + ], + "defaultValue": { + "en-US": "2025-03-03T00:00" + }, "disabled": false, "omitted": false }, { - "id": "keynote", - "name": "Keynote", - "type": "Boolean", + "id": "talk", + "name": "Talk", + "type": "Array", "localized": false, - "required": true, - "validations": [], - "defaultValue": { - "en-US": false - }, + "required": false, + "validations": [ + { + "size": { + "max": 2 + } + } + ], "disabled": false, - "omitted": false + "omitted": false, + "items": { + "type": "Link", + "validations": [ + { + "linkContentType": [ + "talk" + ] + } + ], + "linkType": "Entry" + } } ] }, @@ -749,10 +983,10 @@ "id": "0u17lhw8p27f" } }, - "id": "sponsor", + "id": "globalSettings", "type": "ContentType", - "createdAt": "2024-12-02T20:55:50.099Z", - "updatedAt": "2024-12-02T20:56:53.315Z", + "createdAt": "2024-12-03T02:41:08.173Z", + "updatedAt": "2025-01-03T04:39:49.951Z", "environment": { "sys": { "id": "master", @@ -761,8 +995,8 @@ } }, "publishedVersion": 3, - "publishedAt": "2024-12-02T20:56:53.315Z", - "firstPublishedAt": "2024-12-02T20:55:50.493Z", + "publishedAt": "2025-01-03T04:39:49.951Z", + "firstPublishedAt": "2024-12-03T02:41:08.486Z", "createdBy": { "sys": { "type": "Link", @@ -786,119 +1020,162 @@ "id": "4q4LLY8UGRv7xQhTpKIf8E" } }, - "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/sponsor" + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/globalSettings" }, - "displayField": "name", - "name": "Sponsor", + "displayField": "title", + "name": "Global Settings", "description": "", "fields": [ { - "id": "name", - "name": "Name", + "id": "title", + "name": "Title (for reference only)", "type": "Symbol", "localized": false, "required": true, - "validations": [ - { - "unique": true - } - ], + "validations": [], "disabled": false, "omitted": false }, { - "id": "sponsorType", - "name": "Sponsor Type", - "type": "Symbol", + "id": "price", + "name": "Price", + "type": "Number", "localized": false, "required": true, "validations": [ { - "in": [ - "Elite", - "Premier", - "Impact" - ] + "range": { + "min": 0 + } } ], "disabled": false, "omitted": false }, { - "id": "darkLogo", - "name": "Dark Logo", - "type": "Link", + "id": "discountedPrice", + "name": "Discounted Price", + "type": "Number", "localized": false, "required": true, "validations": [ { - "linkMimetypeGroup": [ - "image" - ] + "range": { + "min": 0 + } } ], "disabled": false, - "omitted": false, - "linkType": "Asset" + "omitted": false }, { - "id": "lightLogo", - "name": "Light Logo", - "type": "Link", + "id": "pricingFootnote", + "name": "Pricing Footnote", + "type": "Symbol", "localized": false, "required": false, - "validations": [ - { - "linkMimetypeGroup": [ - "image" - ] - } - ], + "validations": [], "disabled": false, - "omitted": false, - "linkType": "Asset" + "omitted": false }, { - "id": "colorLogo", - "name": "Color Logo", + "id": "ticketCta", + "name": "Ticket CTA", "type": "Link", "localized": false, - "required": false, + "required": true, "validations": [ { - "linkMimetypeGroup": [ - "image" + "linkContentType": [ + "cta" ] } ], "disabled": false, "omitted": false, - "linkType": "Asset" + "linkType": "Entry" + } + ] + }, + { + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "0u17lhw8p27f" + } + }, + "id": "cta", + "type": "ContentType", + "createdAt": "2024-12-03T02:01:02.821Z", + "updatedAt": "2024-12-03T02:02:42.759Z", + "environment": { + "sys": { + "id": "master", + "type": "Link", + "linkType": "Environment" + } + }, + "publishedVersion": 7, + "publishedAt": "2024-12-03T02:02:42.759Z", + "firstPublishedAt": "2024-12-03T02:01:03.221Z", + "createdBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } + }, + "updatedBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } }, + "publishedCounter": 4, + "version": 8, + "publishedBy": { + "sys": { + "type": "Link", + "linkType": "User", + "id": "4q4LLY8UGRv7xQhTpKIf8E" + } + }, + "urn": "crn:contentful:::content:spaces/0u17lhw8p27f/environments/master/content_types/cta" + }, + "displayField": "title", + "name": "CTA", + "description": "", + "fields": [ { - "id": "description", - "name": "Description", - "type": "Text", + "id": "title", + "name": "Title (for reference only)", + "type": "Symbol", "localized": false, - "required": false, + "required": true, "validations": [], "disabled": false, "omitted": false }, { - "id": "externalUrl", - "name": "External URL", + "id": "text", + "name": "Text", "type": "Symbol", "localized": false, - "required": false, - "validations": [ - { - "regexp": { - "pattern": "^(ftp|http|https):\\/\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\/|\\/([\\w#!:.?+=&%@!\\-/]))?$", - "flags": null - } - } - ], + "required": true, + "validations": [], + "disabled": false, + "omitted": false + }, + { + "id": "url", + "name": "URL", + "type": "Text", + "localized": false, + "required": true, + "validations": [], "disabled": false, "omitted": false } diff --git a/src/lib/contentful/global-settings.ts b/src/lib/contentful/global-settings.ts index 953c491..ecde581 100644 --- a/src/lib/contentful/global-settings.ts +++ b/src/lib/contentful/global-settings.ts @@ -5,12 +5,14 @@ import { type EntrySkeletonType, type LocaleCode, } from 'contentful'; +import { type CTASkeleton } from './cta'; export interface GlobalSettingsFields { title: EntryFieldTypes.Symbol; price: EntryFieldTypes.Number; discountedPrice: EntryFieldTypes.Number; pricingFootnote?: EntryFieldTypes.Symbol; + ticketCta: EntryFieldTypes.EntryLink; } export type GlobalSettingsSkeleton = EntrySkeletonType; diff --git a/src/lib/contentful/home.ts b/src/lib/contentful/home.ts index 4293daa..8255316 100644 --- a/src/lib/contentful/home.ts +++ b/src/lib/contentful/home.ts @@ -32,7 +32,7 @@ export interface HomeFields { feature1: EntryFieldTypes.Text; feature2: EntryFieldTypes.Text; feature3: EntryFieldTypes.Text; - themeDescription: EntryFieldTypes.Text; + themeDescription?: EntryFieldTypes.Text; location: EntryFieldTypes.Text; preFooterHeader: EntryFieldTypes.Symbol; preFooterCta: EntryFieldTypes.EntryLink; diff --git a/src/lib/contentful/index.ts b/src/lib/contentful/index.ts index cecf7cb..60bd39e 100644 --- a/src/lib/contentful/index.ts +++ b/src/lib/contentful/index.ts @@ -1,34 +1,64 @@ /** Do not modify. This file was automatically generated. */ import { CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_SPACE_ID } from '$env/static/private'; - import { createClient, type EntriesQueries, type EntrySkeletonType } from 'contentful'; - import { type SpeakerSkeleton as Speaker } from './speaker'; +import { type HomeSkeleton as Home } from './home'; import { type SponsorSkeleton as Sponsor } from './sponsor'; +import { type SessionSkeleton as Session } from './session'; +import { type TimeSlotSkeleton as TimeSlot } from './time-slot'; +import { type GlobalSettingsSkeleton as GlobalSettings } from './global-settings'; +import { type CTASkeleton as CTA } from './cta'; +export * from './speaker'; export * from './home'; +export * from './sponsor'; +export * from './session'; +export * from './time-slot'; export * from './global-settings'; export * from './cta'; -export * from './speaker'; -export * from './sponsor'; type ContentfulEntries = Omit< EntriesQueries, 'content_type' >; -export type ContentType = 'home' | 'globalSettings' | 'cta' | 'speaker' | 'sponsor'; +export type ContentType = + | 'speaker' + | 'home' + | 'sponsor' + | 'talk' + | 'timeSlot' + | 'globalSettings' + | 'cta'; const client = createClient({ accessToken: CONTENTFUL_ACCESS_TOKEN, space: CONTENTFUL_SPACE_ID, }); - /** Get all of the `Speaker` entries from Contentful. */ -export const getSpeakers = (query: ContentfulEntries = {}) => { - return client.withoutUnresolvableLinks.getEntries({ ...query, content_type: 'speaker' }); +export const getSpeakerEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'speaker' }); +}; +/** Get all of the `Home` entries from Contentful. */ +export const getHomeEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'home' }); }; - /** Get all of the `Sponsor` entries from Contentful. */ -export const getSponsors = (query: ContentfulEntries = {}) => { - return client.withoutUnresolvableLinks.getEntries({ ...query, content_type: 'sponsor' }); +export const getSponsorEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'sponsor' }); +}; +/** Get all of the `Session` entries from Contentful. */ +export const getSessionEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'talk' }); +}; +/** Get all of the `TimeSlot` entries from Contentful. */ +export const getTimeSlotEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'timeSlot' }); +}; +/** Get all of the `GlobalSettings` entries from Contentful. */ +export const getGlobalSettingsEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'globalSettings' }); +}; +/** Get all of the `CTA` entries from Contentful. */ +export const getCTAEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'cta' }); }; diff --git a/src/lib/contentful/session.ts b/src/lib/contentful/session.ts new file mode 100644 index 0000000..4c88898 --- /dev/null +++ b/src/lib/contentful/session.ts @@ -0,0 +1,22 @@ +import { + type ChainModifiers, + type Entry, + type EntryFieldTypes, + type EntrySkeletonType, + type LocaleCode, +} from 'contentful'; +import { type SpeakerSkeleton } from './speaker'; + +export interface SessionFields { + title: EntryFieldTypes.Symbol; + url: EntryFieldTypes.Symbol; + location?: EntryFieldTypes.Symbol; + speakers?: EntryFieldTypes.Array>; + description: EntryFieldTypes.Text; +} + +export type SessionSkeleton = EntrySkeletonType; +export type Session< + Modifiers extends ChainModifiers, + Locales extends LocaleCode = LocaleCode, +> = Entry; diff --git a/src/lib/contentful/speaker.ts b/src/lib/contentful/speaker.ts index c1fa0e8..99c66e7 100644 --- a/src/lib/contentful/speaker.ts +++ b/src/lib/contentful/speaker.ts @@ -11,6 +11,7 @@ export interface SpeakerFields { image: EntryFieldTypes.AssetLink; jobTitle: EntryFieldTypes.Symbol; companyName: EntryFieldTypes.Symbol; + bio?: EntryFieldTypes.Text; keynote: EntryFieldTypes.Boolean; } diff --git a/src/lib/contentful/sponsor.ts b/src/lib/contentful/sponsor.ts index 374d77f..11b9125 100644 --- a/src/lib/contentful/sponsor.ts +++ b/src/lib/contentful/sponsor.ts @@ -10,9 +10,9 @@ export interface SponsorFields { name: EntryFieldTypes.Symbol; sponsorType: EntryFieldTypes.Symbol<'Elite' | 'Premier' | 'Impact'>; darkLogo: EntryFieldTypes.AssetLink; - lightLogo?: EntryFieldTypes.AssetLink; + lightLogo: EntryFieldTypes.AssetLink; colorLogo?: EntryFieldTypes.AssetLink; - description?: EntryFieldTypes.Text; + description: EntryFieldTypes.Text; externalUrl?: EntryFieldTypes.Symbol; } diff --git a/src/lib/contentful/time-slot.ts b/src/lib/contentful/time-slot.ts new file mode 100644 index 0000000..3c4b0e8 --- /dev/null +++ b/src/lib/contentful/time-slot.ts @@ -0,0 +1,21 @@ +import { + type ChainModifiers, + type Entry, + type EntryFieldTypes, + type EntrySkeletonType, + type LocaleCode, +} from 'contentful'; +import { type SessionSkeleton } from './session'; + +export interface TimeSlotFields { + title: EntryFieldTypes.Symbol; + startTime: EntryFieldTypes.Date; + endTime: EntryFieldTypes.Date; + talk?: EntryFieldTypes.Array>; +} + +export type TimeSlotSkeleton = EntrySkeletonType; +export type TimeSlot< + Modifiers extends ChainModifiers, + Locales extends LocaleCode = LocaleCode, +> = Entry; diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 66da646..290d800 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,12 +1,12 @@ -import { getSpeakers, getSponsors } from '../lib/contentful/index.js'; +import { getSpeakerEntries, getSponsorEntries } from '../lib/contentful/index.js'; import type { LayoutServerLoad } from './$types.js'; export const prerender = true; export const load: LayoutServerLoad = async () => { - const speakers = await getSpeakers(); - const sponsors = await getSponsors(); + const speakers = await getSpeakerEntries(); + const sponsors = await getSponsorEntries(); return { speakers: speakers.items, sponsors: sponsors.items }; }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 4c02d09..b12bcdf 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -2,6 +2,7 @@ import '../app.css'; import metadata from '$content/metadata.yaml'; + import { Navigation } from '$components/navigation'; import openGraph from '$assets/open-graph.png'; import CookieConsent from '$components/cookie-consent.svelte'; @@ -10,6 +11,7 @@
+ {@render children()}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 928da42..8e27c5a 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,6 +1,5 @@ + + + +
+ +

{title}

+

{time}

+ {#if description} +
+

{description}

+
+ {/if} +
diff --git a/tailwind.config.ts b/tailwind.config.ts index acaa458..10cc72f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -14,6 +14,9 @@ export default { dark: '#61682C', DEFAULT: '#DDEE59', }, + grey: { + DEFAULT: '#374761', + }, lilac: { DEFAULT: '#D9CAFB', dark: '#47588E', From 303d19e54685247978a1ab39cd9bd11e89f3d27a Mon Sep 17 00:00:00 2001 From: grace Date: Fri, 17 Jan 2025 01:47:44 -0700 Subject: [PATCH 002/115] trying to get contentful functions setup so speaker slugs and talk generation works --- src/lib/components/speaker/variants.ts | 1 + src/lib/components/stonehenge.svelte | 58 ++++++++ src/lib/contentful/client.ts | 133 +++++++++++++++++++ src/lib/contentful/index.ts | 2 +- src/routes/speakers/+layout.server.ts | 11 ++ src/routes/speakers/+page.svelte | 17 +++ src/routes/speakers/[slug]/+layout.server.ts | 0 src/routes/speakers/[slug]/+page.svelte | 41 +++--- 8 files changed, 240 insertions(+), 23 deletions(-) create mode 100644 src/lib/components/stonehenge.svelte create mode 100644 src/lib/contentful/client.ts create mode 100644 src/routes/speakers/+layout.server.ts create mode 100644 src/routes/speakers/[slug]/+layout.server.ts diff --git a/src/lib/components/speaker/variants.ts b/src/lib/components/speaker/variants.ts index 9574976..5d7d0b5 100644 --- a/src/lib/components/speaker/variants.ts +++ b/src/lib/components/speaker/variants.ts @@ -59,4 +59,5 @@ export type SpeakerProps = VariantProps & name: string; company: string; jobTitle: string; + bio?: string; }; diff --git a/src/lib/components/stonehenge.svelte b/src/lib/components/stonehenge.svelte new file mode 100644 index 0000000..a0c1a21 --- /dev/null +++ b/src/lib/components/stonehenge.svelte @@ -0,0 +1,58 @@ +
+ + diff --git a/src/lib/contentful/client.ts b/src/lib/contentful/client.ts new file mode 100644 index 0000000..964d1c8 --- /dev/null +++ b/src/lib/contentful/client.ts @@ -0,0 +1,133 @@ +import { CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_SPACE_ID, CONTENTFUL_HOST } from '$env/static/private'; +import { + createClient, + type EntriesQueries, + type EntrySkeletonType, + type EntryCollection, + type Entry, +} from 'contentful'; + +import { isType } from '$lib/utilities/is-type'; + +// Create a single Contentful client instance +const client = createClient({ + space: CONTENTFUL_SPACE_ID, + accessToken: CONTENTFUL_ACCESS_TOKEN, + host: CONTENTFUL_HOST, +}); + +/** Helper type for Contentful queries */ +type ContentfulEntries = Omit< + EntriesQueries, + 'content_type' +>; + +/** List of all content types in Contentful */ +export type ContentType = + | 'speaker' + | 'home' + | 'sponsor' + | 'talk' + | 'timeSlot' + | 'globalSettings' + | 'cta'; + +/** Fetch all entries of a given Content Type */ +export const getEntries = async ( + contentType: ContentType, + query: ContentfulEntries = {}, +): Promise> => { + return client.withoutUnresolvableLinks.getEntries({ + content_type: contentType, + limit: 1000, + include: 10, + ...query, + }); +}; + +/** Fetch a single entry by ID */ +export const getEntry = async ( + id: string, +): Promise> => { + return client.withoutUnresolvableLinks.getEntry(id, { include: 10 }); +}; + +/** Paginate entries of a given content type */ +export const paginateEntries = + (content_type: ContentType) => + async ( + pageSize: number, + page: number, + query?: Record, + ): Promise<{ + items: Entry[]; + total: number; + }> => { + const response = await client.withoutUnresolvableLinks.getEntries({ + content_type, + ...query, + skip: pageSize * (page - 1), + limit: pageSize, + }); + + return { + items: response.items as Entry[], + total: response.total, + }; + }; + +/** Slug type for fetching slugs from Contentful */ +export type Slug = { + slug: string; +}; + +/** Fetch slugs for a given content type */ +export const getSlugs = async ( + content_type: ContentType, + query: Omit< + EntriesQueries, + 'content_type' | 'skip' | 'select' + > = {}, +): Promise => { + let items: Entry[] = []; + let total = 0; + + do { + const content = await client.withoutUnresolvableLinks.getEntries({ + content_type, + skip: items.length, + select: ['fields.slug'], + ...query, + }); + + total = content.total; + items = [...items, ...content.items]; + } while (items.length < total); + + return items + .map((item) => (item.fields?.slug ? { slug: item.fields.slug } : undefined)) + .filter(isType); +}; + +/** Fetch all speaker entries */ +export const getSpeakerEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'speaker' }); +}; + +/** Fetch all session entries */ +export const getSessionEntries = (query: ContentfulEntries = {}) => { + return client.getEntries({ ...query, content_type: 'talk' }); +}; + +/** Fetch sessions by speaker */ +export const getSessionsBySpeaker = async ( + speakerId: string, +): Promise[]> => { + try { + const response = await getSessionEntries({ 'fields.speakers.sys.id': speakerId }); + return response.items; + } catch (error) { + console.error('Error fetching sessions by speaker:', error); + return []; + } +}; diff --git a/src/lib/contentful/index.ts b/src/lib/contentful/index.ts index 60bd39e..9dfd350 100644 --- a/src/lib/contentful/index.ts +++ b/src/lib/contentful/index.ts @@ -61,4 +61,4 @@ export const getGlobalSettingsEntries = (query: ContentfulEntries = {}) => { return client.getEntries({ ...query, content_type: 'cta' }); -}; +}; \ No newline at end of file diff --git a/src/routes/speakers/+layout.server.ts b/src/routes/speakers/+layout.server.ts new file mode 100644 index 0000000..b3e00ea --- /dev/null +++ b/src/routes/speakers/+layout.server.ts @@ -0,0 +1,11 @@ +import { getSpeakerEntries } from './../../lib/contentful/index'; + +import type { LayoutServerLoad } from './$types.js'; + +export const prerender = true; + +export const load: LayoutServerLoad = async () => { + const speakers = await getSpeakerEntries(); + + return { speakers: speakers.items }; +}; diff --git a/src/routes/speakers/+page.svelte b/src/routes/speakers/+page.svelte index e69de29..6b3abb8 100644 --- a/src/routes/speakers/+page.svelte +++ b/src/routes/speakers/+page.svelte @@ -0,0 +1,17 @@ + + + + Replay 2025 — March 3–5 — London, UK + + + + + +