diff --git a/src/lib/utils/api.ts b/src/lib/utils/api.ts index d2e2d7e2..95791885 100644 --- a/src/lib/utils/api.ts +++ b/src/lib/utils/api.ts @@ -2,7 +2,7 @@ import { documentToHtmlString } from "@contentful/rich-text-html-renderer"; import { BLOCKS, INLINES } from "@contentful/rich-text-types"; import type { ContentfulClientApi, ContentType, Entry } from "contentful"; import contentful from "contentful"; - + type ContentWrapperGetOptions = { /** * Use draft data from the Contentful preview API if the ContentWrapper is authorized to do so. @@ -10,7 +10,7 @@ type ContentWrapperGetOptions = { allowPreview: boolean; }; -export class ContentWrapper { +export class ContentWrapper { client: ContentfulClientApi; previewClient: ContentfulClientApi | undefined; @@ -25,7 +25,7 @@ export class ContentWrapper { }); } } - + async get( entity: string, contentfulOptions: any = {}, @@ -74,6 +74,11 @@ export class ContentWrapper { res[id] = documentToHtmlString(res[id], { renderNode: { [BLOCKS.EMBEDDED_ASSET]: (node) => { + // Add this safety check: If the asset was deleted/unpublished, skip it. + if (!node.data?.target?.fields) { + return ""; + } + const { file, title, description } = node.data.target.fields; return ` @@ -119,20 +124,83 @@ export class ContentWrapper { } async transformLink(link: any, type: string | undefined): Promise { + // Undefined type --> this is a primitive value + // Just return the string directly! + if (!type) return link; + + // Now we know it's supposed to be a Link (Asset or Entry). + // Ignore completely empty/broken links. + if (!link || !link.sys) return undefined; + switch (type) { case "Asset": - return link.src !== undefined // why do I need to do this? - ? link - : { src: `https:${link.fields.file.url}`, alt: link.fields.title }; + if (link.src !== undefined) return link; + // Safety check for deleted/unpublished assets + if (!link.fields || !link.fields.file) return undefined; + return { src: `https:${link.fields.file.url}`, alt: link.fields.title }; case "Entry": + // SAFETY CHECK: If the entry is unpublished/unresolved, skip it + if (!link.sys.contentType) return undefined; + return await this.serialize( link, await this.client.getContentType(link.sys.contentType.sys.id) ); - - case undefined: + + default: return link; } + + // WEBSITE FIX ATTEMPT 1 + // Ignore completely empty links + // if (!link || !link.sys) return undefined; + + // switch (type) { + // case "Asset": + // if (link.src !== undefined) return link; + // // Safety check for deleted/unpublished assets + // if (!link.fields || !link.fields.file) return undefined; + // return { src: `https:${link.fields.file.url}`, alt: link.fields.title }; + + // case "Entry": + // // 2. SAFETY CHECK: If the entry is unpublished/unresolved, skip it + // if (!link.sys.contentType) return undefined; + + // return await this.serialize( + // link, + // await this.client.getContentType(link.sys.contentType.sys.id) + // ); + + // case "Array": + // const transformedArray = await Promise.all( + // res[id].map((link: any) => + // this.transformLink(link, field.items!.linkType) + // ) + // ); + // // Filter out any undefined items that were unpublished + // res[id] = transformedArray.filter((item) => item !== undefined); + // break; + + // case undefined: + // return link; + // } + + // ORIGINAL + // switch (type) { + // case "Asset": + // return link.src !== undefined // why do I need to do this? + // ? link + // : { src: `https:${link.fields.file.url}`, alt: link.fields.title }; + + // case "Entry": + // return await this.serialize( + // link, + // await this.client.getContentType(link.sys.contentType.sys.id) + // ); + + // case undefined: + // return link; + // } } } diff --git a/src/lib/utils/projects.ts b/src/lib/utils/projects.ts index b913fdfa..f70a61e9 100644 --- a/src/lib/utils/projects.ts +++ b/src/lib/utils/projects.ts @@ -34,6 +34,8 @@ export function generateProjectsInfo(projects: Project[]): ProjectsInfo { projects.forEach((project) => project.semester.forEach((semester) => { + if (!semester) return; // skip empty/undefined semesters + if (projectArrayMap[semester] !== undefined) { projectArrayMap[semester].push(project); } else { diff --git a/src/lib/utils/schema.ts b/src/lib/utils/schema.ts index da5610c0..e29f6ecb 100644 --- a/src/lib/utils/schema.ts +++ b/src/lib/utils/schema.ts @@ -155,6 +155,8 @@ export function setImageHeight(src: string, height: number): string { } export function parseSemester(semester: string): Semester { + if (!semester) return { season: "", year: 0 }; // protect againt undefined semester + const [season, yearString] = semester.split(" "); const year = parseInt(yearString); @@ -163,6 +165,7 @@ export function parseSemester(semester: string): Semester { } export function semesterToId(semester: string): string { + if (!semester) return ""; // protect againt undefined semester return semester.split(" ").join("-").toLowerCase(); } diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 31d49a06..de8846b0 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -22,7 +22,10 @@ async function getSemesters(contentWrapper: ContentWrapper) { select: "fields.semester", }); - const { semesters } = generateProjectsInfo(projects); + // SAFETY CHECK: Ignore any projects that don't have a semester defined + const validProjects = projects.filter((project) => project && project.semester); + + const { semesters } = generateProjectsInfo(validProjects); return semesters; }