diff --git a/app/(main)/(author)/author/[slug]/page.tsx b/app/(main)/(author)/author/[slug]/page.tsx index 78574899..886f9d36 100644 --- a/app/(main)/(author)/author/[slug]/page.tsx +++ b/app/(main)/(author)/author/[slug]/page.tsx @@ -9,6 +9,7 @@ import type { AuthorQueryWithRelatedResult, } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { authorQuery, authorQueryWithRelated } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import CoverMedia from "@/components/cover-media"; @@ -19,6 +20,8 @@ import UserRelated from "@/components/user-related"; type Params = Promise<{ slug: string }>; +export const revalidate = 3600; + export async function generateMetadata( { params }: { params: Params }, parent: ResolvingMetadata, @@ -45,6 +48,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "author" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function AuthorPage({ params }: { params: Params }) { const { slug } = await params; diff --git a/app/(main)/(author)/authors/page/[num]/page.tsx b/app/(main)/(author)/authors/page/[num]/page.tsx index c0f84b99..871b4d0e 100644 --- a/app/(main)/(author)/authors/page/[num]/page.tsx +++ b/app/(main)/(author)/authors/page/[num]/page.tsx @@ -1,14 +1,27 @@ import MoreContent from "@/components/more-content"; import type { DocCountResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import PaginateList from "@/components/paginate-list"; import { docCount } from "@/sanity/lib/queries"; +import { groq } from "next-sanity"; const LIMIT = 10; type Params = Promise<{ num: string }>; +export const revalidate = 60; + +export async function generateStaticParams() { + const count = await client.fetch( + groq`count(*[_type == "author" && defined(slug.current)])`, + ); + const perPage = LIMIT; + const pages = Math.ceil(count / perPage); + return Array.from({ length: pages }, (_, i) => ({ num: String(i + 1) })); +} + export default async function Page({ params }: { params: Params }) { const { num } = await params; diff --git a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-client-only.tsx b/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-client-only.tsx deleted file mode 100644 index 59c1c138..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-client-only.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; -import type { - LessonQueryResult, - LessonsInCourseQueryResult, -} from "@/sanity/types"; -import { useEffect, useState } from "react"; -import LessonPanel from "./lesson-panel"; - -export default function LessonPanelClientOnly({ - lesson, - course, -}: { - lesson: NonNullable; - course: NonNullable; -}) { - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - setIsClient(true); - }, []); - - //TODO: Make this match better? - if (!isClient) return
Loading Lesson...
; - - return ( - <> - - - ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-complete.tsx b/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-complete.tsx deleted file mode 100644 index 05de57ef..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-complete.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client"; - -export default function LessonComplete() { - return <>; -} diff --git a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-panel.tsx b/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-panel.tsx deleted file mode 100644 index aa05e29d..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/lesson-panel.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client"; -import { - ResizablePanelGroup, - ResizablePanel, - ResizableHandle, -} from "@/components/ui/resizable"; -import Link from "next/link"; - -import type { - LessonQueryResult, - LessonsInCourseQueryResult, -} from "@/sanity/types"; -import BadgePro from "@/components/badge-pro"; -import NavLesson from "./nav-lesson"; -import CoverMedia from "@/components/cover-media"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Button } from "@/components/ui/button"; -import { - FaCircleArrowLeft, - FaCircleArrowRight, - FaHouse, -} from "react-icons/fa6"; - -import { useLocalStorage } from "@uidotdev/usehooks"; - -export default function LessonPanel({ - lesson, - course, -}: { - lesson: NonNullable; - course: NonNullable; -}) { - const [defaultLayout, saveDefaultLayout] = useLocalStorage( - "codingcatdev:lesson:layout", - [25, 75], - ); - - const onLayout = (sizes: number[]) => { - saveDefaultLayout(sizes); - }; - - const getLessons = () => { - const lessons: NonNullable< - NonNullable["sections"] - >[0]["lesson"] = []; - course?.sections?.map((section) => - section.lesson?.map((lesson) => lessons.push(lesson)), - ); - return lessons; - }; - - const lessonIndex = getLessons().findIndex((l) => l.slug === lesson.slug); - const lessonNoContent = getLessons()[lessonIndex]; - - const main = () => { - return ( -
-
-
-

{lesson?.title}

-
- -
-
- -
-
-
- {lessonIndex > 0 && ( - - )} - - {lessonIndex < getLessons().length - 1 && ( - - )} -
-
-
- ); - }; - - return ( - <> -
- - - {course?.sections && ( - <> -
- - {course.title} - -
- -
- -
- - )} -
- - - {main()} - -
-
-
- {main()} - - {course?.sections && ( - <> -
- -
- - )} -
-
- - ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/nav-lesson.tsx b/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/nav-lesson.tsx deleted file mode 100644 index 98107a89..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/nav-lesson.tsx +++ /dev/null @@ -1,53 +0,0 @@ -"use client"; - -import type { LessonsInCourseQueryResult } from "@/sanity/types"; -import Link from "next/link"; - -import { useActivePath } from "@/lib/hooks"; -import { Separator } from "@/components/ui/separator"; -import BadgePro from "@/components/badge-pro"; - -interface Props { - course: LessonsInCourseQueryResult | undefined; -} -export default function NavLesson({ course }: Props) { - const checkActivePath = useActivePath(); - return ( - <> - {course?.sections?.map((section, i) => ( -
- {section?.title && ( -
-

- {section.title} -

- -
- )} - {section.lesson?.map((l) => ( -
- - {l.title} - -
- -
-
- ))} -
- ))} - - ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/page.tsx b/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/page.tsx deleted file mode 100644 index 91b6dab7..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lesson/[lessonSlug]/page.tsx +++ /dev/null @@ -1,101 +0,0 @@ -export const dynamic = "force-dynamic"; - -import type { Metadata, ResolvingMetadata } from "next"; -import { notFound, redirect } from "next/navigation"; -import { Suspense } from "react"; - -import type { - LessonQueryResult, - LessonsInCourseQueryResult, -} from "@/sanity/types"; -import { sanityFetch } from "@/sanity/lib/live"; -import { lessonQuery, lessonsInCourseQuery } from "@/sanity/lib/queries"; -import { resolveOpenGraphImage } from "@/sanity/lib/utils"; -import LessonPanelClientOnly from "./lesson-client-only"; -import MoreContent from "@/components/more-content"; -import MoreHeader from "@/components/more-header"; -import PortableText from "@/components/portable-text"; -import type { PortableTextBlock } from "next-sanity"; -import { cookies } from "next/headers"; -import { jwtDecode } from "jwt-decode"; - -type Params = Promise<{ lessonSlug: string; courseSlug: string }>; - -export async function generateMetadata( - { params }: { params: Params }, - parent: ResolvingMetadata, -): Promise { - const resolvedParams = await params; - const lesson = ( - await sanityFetch({ - query: lessonQuery, - params: resolvedParams, - stega: false, - }) - ).data as LessonQueryResult; - const previousImages = (await parent).openGraph?.images || []; - const ogImage = resolveOpenGraphImage(lesson?.coverImage); - - return { - authors: - lesson?.author?.map((a) => { - return { name: a.title }; - }) || [], - title: lesson?.title, - description: lesson?.excerpt, - openGraph: { - images: ogImage ? ogImage : previousImages, - }, - } satisfies Metadata; -} - -export default async function LessonPage({ params }: { params: Params }) { - const resolvedParams = await params; - const [lesson, course] = ( - await Promise.all([ - sanityFetch({ query: lessonQuery, params: resolvedParams }), - sanityFetch({ query: lessonsInCourseQuery, params: resolvedParams }), - ]) - ).map((res) => res.data) as [ - LessonQueryResult, - NonNullable, - ]; - - if (!lesson && !course) { - return notFound(); - } - - // Check if user is either a pro or paid for lesson - if (course?.stripeProduct && lesson?.locked) { - //First check if user session is valid - const cookieStore = await cookies(); - const sessionCookie = cookieStore.get("app.at"); - if (!sessionCookie) { - return redirect(`/course/${course?.slug}?showPro=true`); - } - } - - return ( - <> - {lesson?._id && course?._id && ( -
- Loading Lesson Panel...}> - - - {lesson?.content?.length && ( - - )} - -
- )} - - ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/lessons.tsx b/app/(main)/(course)/course/[courseSlug]/lessons.tsx deleted file mode 100644 index 631a832c..00000000 --- a/app/(main)/(course)/course/[courseSlug]/lessons.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import Link from "next/link"; - -import CoverImage from "@/components/cover-image"; -import type { LessonsInCourseQueryResult } from "@/sanity/types"; -import { sanityFetch } from "@/sanity/lib/live"; -import { lessonsInCourseQuery } from "@/sanity/lib/queries"; -import { - Card, - CardContent, - CardFooter, - CardHeader, -} from "@/components/ui/card"; - -export default async function Lessons(params: { courseSlug: string }) { - const { courseSlug } = await params; - const course = ( - await sanityFetch({ - query: lessonsInCourseQuery, - params: { courseSlug }, - }) - ).data as LessonsInCourseQueryResult; - - return ( - <> - {course?.sections && ( -
-
-

- Lessons -

- {course?.sections?.map((section) => ( -
-
-

{section?.title}

-
-
- {section?.lesson?.map((post) => { - const { - _id, - _type, - title, - slug, - coverImage, - excerpt, - locked, - } = post; - return ( - - - - - - - -

- - {title} - -

- - {excerpt && ( -

- {excerpt} -

- )} -
- - {locked && course?.stripeProduct && course?.title && ( -
- )} -
-
- ); - })} -
-
- ))} -
- )} - - ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/page.tsx b/app/(main)/(course)/course/[courseSlug]/page.tsx deleted file mode 100644 index 3f04dc7f..00000000 --- a/app/(main)/(course)/course/[courseSlug]/page.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import type { Metadata, ResolvingMetadata } from "next"; -import { groq, type PortableTextBlock } from "next-sanity"; -import { notFound } from "next/navigation"; -import { Suspense } from "react"; - -import Avatar from "@/components/avatar"; -import CoverMedia from "@/components/cover-media"; -import DateComponent from "@/components/date"; -import MoreContent from "@/components/more-content"; -import PortableText from "@/components/portable-text"; - -import type { CourseQueryResult } from "@/sanity/types"; -import { sanityFetch } from "@/sanity/lib/live"; -import { courseQuery } from "@/sanity/lib/queries"; -import { resolveOpenGraphImage } from "@/sanity/lib/utils"; -import Lessons from "./lessons"; -import MoreHeader from "@/components/more-header"; -import { BreadcrumbLinks } from "@/components/breadrumb-links"; -import Link from "next/link"; -import ShowPro from "./show-pro"; - -type Params = Promise<{ courseSlug: string }>; - -export async function generateMetadata( - { params }: { params: Params }, - parent: ResolvingMetadata, -): Promise { - const { courseSlug } = await params; - const course = ( - await sanityFetch({ - query: courseQuery, - params: { courseSlug }, - stega: false, - }) - ).data as CourseQueryResult; - const previousImages = (await parent).openGraph?.images || []; - const ogImage = resolveOpenGraphImage(course?.coverImage); - - return { - authors: - course?.author?.map((a) => { - return { name: a.title }; - }) || [], - title: course?.title, - description: course?.excerpt, - openGraph: { - images: ogImage ? ogImage : previousImages, - }, - } satisfies Metadata; -} - -export default async function CoursePage({ params }: { params: Params }) { - const { courseSlug } = await params; - - const course = ( - await sanityFetch({ - query: courseQuery, - params: { courseSlug }, - stega: false, - }) - ).data as CourseQueryResult; - - if (!course?._id) { - return notFound(); - } - - return ( -
- - -
-

- {course.title} -

-
- -
-
-
- {course.author && ( -
- {course.author.map((a) => ( - - ))} -
- )} -
-
- -
-
- {course?.stripeProduct && course?.title && ( -
- )} -
-
- {course.content?.length && ( - - )} -
-
- - - - -
- ); -} diff --git a/app/(main)/(course)/course/[courseSlug]/show-pro.tsx b/app/(main)/(course)/course/[courseSlug]/show-pro.tsx deleted file mode 100644 index 61acc049..00000000 --- a/app/(main)/(course)/course/[courseSlug]/show-pro.tsx +++ /dev/null @@ -1,22 +0,0 @@ -"use client"; - -import GoPro from "@/components/user-go-pro"; -import { usePathname, useSearchParams, useRouter } from "next/navigation"; -import { useState, useEffect } from "react"; - -export default function ShowPro() { - const [showGoPro, setShowGoPro] = useState(false); - const searchParams = useSearchParams(); - const router = useRouter(); - const pathname = usePathname(); - const showPro = searchParams.get("showPro"); - useEffect(() => { - if (showPro) { - router.replace(pathname); - setShowGoPro(true); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [showPro]); - - return <>{showGoPro && }; -} diff --git a/app/(main)/(course)/courses/page.tsx b/app/(main)/(course)/courses/page.tsx deleted file mode 100644 index fa0bb7b3..00000000 --- a/app/(main)/(course)/courses/page.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import Link from "next/link"; -import { Suspense } from "react"; - -import Avatar from "@/components/avatar"; -import CoverImage from "@/components/cover-image"; -import DateComponent from "@/components/date"; -import MoreContent from "@/components/more-content"; -import Onboarding from "@/components/onboarding"; - -import type { CoursesQueryResult } from "@/sanity/types"; -import { sanityFetch } from "@/sanity/lib/live"; -import { coursesQuery } from "@/sanity/lib/queries"; -import MoreHeader from "@/components/more-header"; - -function HeroCourse({ - title, - slug, - excerpt, - coverImage, - date, - author, -}: Pick< - Exclude, - "title" | "coverImage" | "date" | "excerpt" | "author" | "slug" ->) { - return ( -
- - - -
-
-

- - {title} - -

-
- -
-
-
- {excerpt && ( -

- {excerpt} -

- )} - {author && ( -
- {author.map((a) => ( - - ))} -
- )} -
-
-
- ); -} - -export default async function Page() { - const [heroPost] = ( - await Promise.all([sanityFetch({ query: coursesQuery })]) - ).map((res) => res.data) as [CoursesQueryResult]; - - return ( -
- {heroPost ? ( - - ) : ( - - )} - - {heroPost?._id && ( - - )} -
- ); -} diff --git a/app/(main)/(course)/courses/page/[num]/page.tsx b/app/(main)/(course)/courses/page/[num]/page.tsx deleted file mode 100644 index 0bb9d0c5..00000000 --- a/app/(main)/(course)/courses/page/[num]/page.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import MoreContent from "@/components/more-content"; -import type { DocCountResult } from "@/sanity/types"; -import { sanityFetch } from "@/sanity/lib/live"; - -import PaginateList from "@/components/paginate-list"; -import { docCount } from "@/sanity/lib/queries"; - -const LIMIT = 10; - -type Params = Promise<{ num: string }>; - -export default async function Page({ params }: { params: Params }) { - const [count] = ( - await Promise.all([ - sanityFetch({ - query: docCount, - params: { - type: "course", - }, - }), - ]) - ).map((res) => res.data) as [DocCountResult]; - - const { num } = await params; - const pageNumber = Number(num); - const offset = (pageNumber - 1) * LIMIT; - const limit = offset + LIMIT; - - return ( -
- - -
- ); -} diff --git a/app/(main)/(course)/courses/page/page.tsx b/app/(main)/(course)/courses/page/page.tsx deleted file mode 100644 index f58ee0d1..00000000 --- a/app/(main)/(course)/courses/page/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { redirect } from "next/navigation"; - -export default async function Page() { - redirect("/courses/page/1"); -} diff --git a/app/(main)/(course)/courses/rss.json/route.ts b/app/(main)/(course)/courses/rss.json/route.ts deleted file mode 100644 index 2ccbe249..00000000 --- a/app/(main)/(course)/courses/rss.json/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const dynamic = "force-dynamic"; // defaults to auto - -import { buildFeed } from "@/lib/rss"; -import { ContentType } from "@/lib/types"; - -export async function GET() { - const feed = await buildFeed({ - type: ContentType.course, - }); - return new Response(feed.json1(), { - headers: { - "content-type": "application/json", - "cache-control": "max-age=0, s-maxage=3600", - }, - }); -} diff --git a/app/(main)/(course)/courses/rss.xml/route.ts b/app/(main)/(course)/courses/rss.xml/route.ts deleted file mode 100644 index e0f1a1dd..00000000 --- a/app/(main)/(course)/courses/rss.xml/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const dynamic = "force-dynamic"; // defaults to auto - -import { buildFeed } from "@/lib/rss"; -import { ContentType } from "@/lib/types"; - -export async function GET() { - const feed = await buildFeed({ - type: ContentType.course, - }); - return new Response(feed.rss2(), { - headers: { - "content-type": "application/rss+xml; charset=utf-8", - "cache-control": "max-age=0, s-maxage=3600", - }, - }); -} diff --git a/app/(main)/(guest)/guest/[slug]/page.tsx b/app/(main)/(guest)/guest/[slug]/page.tsx index da7fbf04..6a893a89 100644 --- a/app/(main)/(guest)/guest/[slug]/page.tsx +++ b/app/(main)/(guest)/guest/[slug]/page.tsx @@ -9,6 +9,7 @@ import type { GuestQueryWithRelatedResult, } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { guestQuery, guestQueryWithRelated } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import CoverMedia from "@/components/cover-media"; @@ -20,6 +21,8 @@ import Avatar from "@/components/avatar"; type Params = Promise<{ slug: string }>; +export const revalidate = 3600; + export async function generateMetadata( { params }: { params: Params }, parent: ResolvingMetadata, @@ -45,6 +48,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "guest" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function GuestPage({ params }: { params: Params }) { const { slug } = await params; diff --git a/app/(main)/(guest)/guests/page/[num]/page.tsx b/app/(main)/(guest)/guests/page/[num]/page.tsx index 877e3870..d633f12f 100644 --- a/app/(main)/(guest)/guests/page/[num]/page.tsx +++ b/app/(main)/(guest)/guests/page/[num]/page.tsx @@ -1,14 +1,27 @@ import MoreContent from "@/components/more-content"; import type { DocCountResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import PaginateList from "@/components/paginate-list"; import { docCount } from "@/sanity/lib/queries"; +import { groq } from "next-sanity"; const LIMIT = 10; type Params = Promise<{ num: string }>; +export const revalidate = 60; + +export async function generateStaticParams() { + const count = await client.fetch( + groq`count(*[_type == "guest" && defined(slug.current)])`, + ); + const perPage = LIMIT; + const pages = Math.ceil(count / perPage); + return Array.from({ length: pages }, (_, i) => ({ num: String(i + 1) })); +} + export default async function Page({ params }: { params: Params }) { const [count] = ( await Promise.all([ diff --git a/app/(main)/(podcast)/podcast/[slug]/page.tsx b/app/(main)/(podcast)/podcast/[slug]/page.tsx index 06c9701c..058757e4 100644 --- a/app/(main)/(podcast)/podcast/[slug]/page.tsx +++ b/app/(main)/(podcast)/podcast/[slug]/page.tsx @@ -2,12 +2,16 @@ import type { Metadata, ResolvingMetadata } from "next"; import { notFound } from "next/navigation"; import type { PodcastQueryResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { podcastQuery } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import Podcast from "../Podcast"; +import { groq } from "next-sanity"; type Params = Promise<{ slug: string }>; +export const revalidate = 3600; + export async function generateMetadata( { params }: { params: Params }, parent: ResolvingMetadata, @@ -37,6 +41,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "podcast" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function PodcastPage({ params }: { params: Params }) { const { slug } = await params; diff --git a/app/(main)/(podcast)/podcasts/page.tsx b/app/(main)/(podcast)/podcasts/page.tsx index e7a638a3..fd03f00c 100644 --- a/app/(main)/(podcast)/podcasts/page.tsx +++ b/app/(main)/(podcast)/podcasts/page.tsx @@ -16,6 +16,7 @@ import MoreHeader from "@/components/more-header"; import PodmatchBadge from "@/components/podmatch-badge"; +export const revalidate = 60; function HeroPodcast({ title, slug, diff --git a/app/(main)/(podcast)/podcasts/page/[num]/page.tsx b/app/(main)/(podcast)/podcasts/page/[num]/page.tsx index f920156b..605ab84e 100644 --- a/app/(main)/(podcast)/podcasts/page/[num]/page.tsx +++ b/app/(main)/(podcast)/podcasts/page/[num]/page.tsx @@ -1,14 +1,27 @@ import MoreContent from "@/components/more-content"; import type { DocCountResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import PaginateList from "@/components/paginate-list"; import { docCount } from "@/sanity/lib/queries"; +import { groq } from "next-sanity"; const LIMIT = 10; type Params = Promise<{ num: string }>; +export const revalidate = 60; + +export async function generateStaticParams() { + const count = await client.fetch( + groq`count(*[_type == "podcast" && defined(slug.current)])`, + ); + const perPage = LIMIT; + const pages = Math.ceil(count / perPage); + return Array.from({ length: pages }, (_, i) => ({ num: String(i + 1) })); +} + export default async function Page({ params }: { params: Params }) { const [count] = ( await Promise.all([ diff --git a/app/(main)/(post)/blog/page.tsx b/app/(main)/(post)/blog/page.tsx index b980d579..0e284d05 100644 --- a/app/(main)/(post)/blog/page.tsx +++ b/app/(main)/(post)/blog/page.tsx @@ -15,6 +15,7 @@ import { Separator } from "@/components/ui/separator"; import MoreHeader from "@/components/more-header"; +export const revalidate = 60; function HeroPost({ title, slug, diff --git a/app/(main)/(post)/blog/page/[num]/page.tsx b/app/(main)/(post)/blog/page/[num]/page.tsx index 1595e67b..0be78de0 100644 --- a/app/(main)/(post)/blog/page/[num]/page.tsx +++ b/app/(main)/(post)/blog/page/[num]/page.tsx @@ -1,14 +1,27 @@ import MoreContent from "@/components/more-content"; import type { DocCountResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import PaginateList from "@/components/paginate-list"; import { docCount } from "@/sanity/lib/queries"; +import { groq } from "next-sanity"; const LIMIT = 10; type Params = Promise<{ num: string }>; +export const revalidate = 60; + +export async function generateStaticParams() { + const count = await client.fetch( + groq`count(*[_type == "post" && defined(slug.current)])`, + ); + const perPage = LIMIT; + const pages = Math.ceil(count / perPage); + return Array.from({ length: pages }, (_, i) => ({ num: String(i + 1) })); +} + export default async function Page({ params }: { params: Params }) { const [count] = ( await Promise.all([ diff --git a/app/(main)/(post)/post/[slug]/page.tsx b/app/(main)/(post)/post/[slug]/page.tsx index db9deba8..b735009e 100644 --- a/app/(main)/(post)/post/[slug]/page.tsx +++ b/app/(main)/(post)/post/[slug]/page.tsx @@ -10,6 +10,7 @@ import PortableText from "@/components/portable-text"; import type { PostQueryResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { postQuery } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import CoverMedia from "@/components/cover-media"; @@ -19,6 +20,8 @@ import SponsorCard from "@/components/sponsor-card"; type Params = Promise<{ slug: string }>; +export const revalidate = 3600; + export async function generateMetadata( { params }: { params: Params }, parent: ResolvingMetadata, @@ -48,6 +51,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "post" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function PostPage({ params }: { params: Params }) { const { slug } = await params; diff --git a/app/(main)/(sponsor)/sponsor/[slug]/page.tsx b/app/(main)/(sponsor)/sponsor/[slug]/page.tsx index 711a0df2..383792aa 100644 --- a/app/(main)/(sponsor)/sponsor/[slug]/page.tsx +++ b/app/(main)/(sponsor)/sponsor/[slug]/page.tsx @@ -9,6 +9,7 @@ import type { SponsorQueryWithRelatedResult, } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { sponsorQuery, sponsorQueryWithRelated } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import CoverMedia from "@/components/cover-media"; @@ -19,6 +20,8 @@ import UserRelated from "@/components/user-related"; type Params = Promise<{ slug: string }>; +export const revalidate = 3600; + export async function generateMetadata( { params }: { params: Params }, parent: ResolvingMetadata, @@ -44,6 +47,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "sponsor" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function SponsorPage({ params }: { params: Params }) { const { slug } = await params; diff --git a/app/(main)/(sponsor)/sponsors/page/[num]/page.tsx b/app/(main)/(sponsor)/sponsors/page/[num]/page.tsx index fe418507..af3d45c1 100644 --- a/app/(main)/(sponsor)/sponsors/page/[num]/page.tsx +++ b/app/(main)/(sponsor)/sponsors/page/[num]/page.tsx @@ -4,14 +4,27 @@ import Link from "next/link"; import MoreContent from "@/components/more-content"; import type { DocCountResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import PaginateList from "@/components/paginate-list"; import { docCount } from "@/sanity/lib/queries"; +import { groq } from "next-sanity"; const LIMIT = 10; type Params = Promise<{ num: string }>; +export const revalidate = 60; + +export async function generateStaticParams() { + const count = await client.fetch( + groq`count(*[_type == "sponsor" && defined(slug.current)])`, + ); + const perPage = LIMIT; + const pages = Math.ceil(count / perPage); + return Array.from({ length: pages }, (_, i) => ({ num: String(i + 1) })); +} + export default async function Page({ params }: { params: Params }) { const [count] = ( await Promise.all([ diff --git a/app/(main)/(top-level-pages)/[slug]/page.tsx b/app/(main)/(top-level-pages)/[slug]/page.tsx index 7423e0fd..e325e877 100644 --- a/app/(main)/(top-level-pages)/[slug]/page.tsx +++ b/app/(main)/(top-level-pages)/[slug]/page.tsx @@ -1,11 +1,12 @@ import type { Metadata, ResolvingMetadata } from "next"; -import type { PortableTextBlock } from "next-sanity"; +import { groq, type PortableTextBlock } from "next-sanity"; import { notFound } from "next/navigation"; import PortableText from "@/components/portable-text"; import type { PageQueryResult } from "@/sanity/types"; import { sanityFetch } from "@/sanity/lib/live"; +import { client } from "@/sanity/lib/client"; import { pageQuery } from "@/sanity/lib/queries"; import { resolveOpenGraphImage } from "@/sanity/lib/utils"; @@ -14,6 +15,8 @@ type Props = { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }; +export const revalidate = 86400; + export async function generateMetadata( { params, searchParams }: Props, parent: ResolvingMetadata, @@ -39,6 +42,13 @@ export async function generateMetadata( } satisfies Metadata; } +export async function generateStaticParams() { + const slugs = await client.fetch( + groq`*[_type == "page" && defined(slug.current)].slug.current`, + ); + return slugs.map((slug) => ({ slug })); +} + export default async function PagePage({ params, searchParams }: Props) { const { slug } = await params; diff --git a/app/(main)/(top-level-pages)/pro/page.tsx b/app/(main)/(top-level-pages)/pro/page.tsx index 504ea44c..6c67c04e 100644 --- a/app/(main)/(top-level-pages)/pro/page.tsx +++ b/app/(main)/(top-level-pages)/pro/page.tsx @@ -16,6 +16,8 @@ type Props = { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }; +export const revalidate = 86400; + export async function generateMetadata( { params, searchParams }: Props, parent: ResolvingMetadata, diff --git a/app/(main)/(top-level-pages)/sponsorships/page.tsx b/app/(main)/(top-level-pages)/sponsorships/page.tsx index 997dc29b..d0aadf63 100644 --- a/app/(main)/(top-level-pages)/sponsorships/page.tsx +++ b/app/(main)/(top-level-pages)/sponsorships/page.tsx @@ -7,6 +7,7 @@ import { resolveOpenGraphImage } from "@/sanity/lib/utils"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { SponsorshipForm } from "./sponsorship-form"; +export const revalidate = 86400; const sponsorshipTiers = [ { name: "Dedicated Video", @@ -53,7 +54,6 @@ export async function generateMetadata( await sanityFetch({ query: pageQuery, params: { slug: "sponsorships" }, - tags: ["page:sponsorships"], }) ).data as PageQueryResult; diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index 850c14a4..6875a702 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -74,7 +74,6 @@ export async function generateMetadata(): Promise { types: { "application/rss+xml": [ { url: "/blog/rss.xml", title: "Blog" }, - { url: "/courses/rss.xml", title: "Courses" }, { url: "/podcasts/rss.xml", title: "Podcasts" }, ], }, diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx index 9338e4e8..21da33b4 100644 --- a/app/(main)/page.tsx +++ b/app/(main)/page.tsx @@ -8,6 +8,8 @@ import { homePageQuery } from "@/sanity/lib/queries"; import Link from "next/link"; import CoverMedia from "@/components/cover-media"; +export const revalidate = 60; + export default async function HomePage() { const [homePageFetch] = await Promise.all([ sanityFetch({ diff --git a/app/api/webhooks/sanity-revalidate/route.ts b/app/api/webhooks/sanity-revalidate/route.ts new file mode 100644 index 00000000..cf39938a --- /dev/null +++ b/app/api/webhooks/sanity-revalidate/route.ts @@ -0,0 +1,43 @@ +import { revalidateTag } from "next/cache"; +import { type NextRequest, NextResponse } from "next/server"; +import { parseBody } from "next-sanity/webhook"; + +export async function POST(request: NextRequest) { + try { + const { isValidSignature, body } = await parseBody<{ + _type: string; + slug?: string; + }>(request, process.env.SANITY_REVALIDATE_SECRET); + + if (!isValidSignature) { + return NextResponse.json( + { message: "Invalid signature" }, + { status: 401 }, + ); + } + + if (!body?._type) { + return NextResponse.json( + { message: "Bad request" }, + { status: 400 }, + ); + } + + // Revalidate all sanity-tagged caches (the "heavy hammer" approach) + // This is a backup for when no visitors are active to trigger SanityLive revalidation + revalidateTag("sanity"); + + return NextResponse.json({ + revalidated: true, + type: body._type, + slug: body.slug, + now: Date.now(), + }); + } catch (error) { + console.error("Revalidation error:", error); + return NextResponse.json( + { message: "Error revalidating" }, + { status: 500 }, + ); + } +} diff --git a/app/sitemap.ts b/app/sitemap.ts index 7b691740..dec59d51 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -4,6 +4,8 @@ import { sanityFetch } from "@/sanity/lib/live"; import type { SitemapQueryResult } from "@/sanity/types"; import { ContentType } from "@/lib/types"; +export const revalidate = 3600; + export default async function sitemap(): Promise { const productionDomain = process.env.VERCEL_PROJECT_PRODUCTION_URL; @@ -37,17 +39,8 @@ export default async function sitemap(): Promise { url: `${site}${c._type === ContentType.page ? `/${c.slug}` : `/${c._type}/${c.slug}`}`, lastModified: new Date(), changeFrequency: "monthly", - priority: c._type === ContentType.course ? 0.8 : 0.5, + priority: 0.5, }); - c?.sections?.map((s) => - s?.lesson?.map((l) => { - sitemap.push({ - url: `${site}/course/${c.slug}/lesson/${l.slug}`, - lastModified: new Date(), - changeFrequency: "monthly", - }); - }), - ); } return sitemap; diff --git a/components/algolia-search.tsx b/components/algolia-search.tsx index abe42ccf..6abcdff4 100644 --- a/components/algolia-search.tsx +++ b/components/algolia-search.tsx @@ -6,8 +6,6 @@ import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { FaX } from "react-icons/fa6"; -import { GiTeacher } from "react-icons/gi"; // Course -import { PiStudentBold } from "react-icons/pi"; // Lesson import { FaPodcast } from "react-icons/fa"; // Podcast import { HiOutlinePencilAlt } from "react-icons/hi"; //Post import { FaCat } from "react-icons/fa"; // Author @@ -76,12 +74,8 @@ export default function AlgoliaSearch({ switch (type) { case ContentType.author: return ; - case ContentType.course: - return ; case ContentType.guest: return ; - case ContentType.lesson: - return ; case ContentType.podcast: return ; case ContentType.post: @@ -145,13 +139,6 @@ export default function AlgoliaSearch({ future={{ preserveSharedStateOnUnmount: true, }} - // initialUiState={{ - // [indexName]: { - // refinementList: { - // _type: ["course"], - // }, - // }, - // }} >
{showFacets && ( diff --git a/components/more-content.tsx b/components/more-content.tsx index 2e556c5b..ea73522e 100644 --- a/components/more-content.tsx +++ b/components/more-content.tsx @@ -13,7 +13,6 @@ import { sanityFetch } from "@/sanity/lib/live"; import { morePodcastQuery, morePostQuery, - moreCourseQuery, moreAuthorQuery, moreGuestQuery, moreSponsorQuery, @@ -32,8 +31,6 @@ export default async function MoreContent(params: { switch (params.type) { case ContentType.author: return moreAuthorQuery; - case ContentType.course: - return moreCourseQuery; case ContentType.guest: return moreGuestQuery; case ContentType.podcast: diff --git a/components/pro-benefits.tsx b/components/pro-benefits.tsx index a00ee545..30427af8 100644 --- a/components/pro-benefits.tsx +++ b/components/pro-benefits.tsx @@ -47,7 +47,7 @@ export default function ProBenefits({

Unlock premium benefits with our CodingCat.dev Pro plan, - including advanced courses, lifetime access, and personalized + including premium content, lifetime access, and personalized support.

@@ -80,15 +80,15 @@ export default function ProBenefits({

As a CodingCat.dev Pro member, you'll gain access to our - advanced course library, covering topics like machine learning, - data science, and cloud architecture. + premium content library, covering topics like web development, + cloud architecture, and modern frameworks.

- Explore Courses + Explore Content
diff --git a/components/user-related.tsx b/components/user-related.tsx index 6d01edcb..2ded3bed 100644 --- a/components/user-related.tsx +++ b/components/user-related.tsx @@ -12,7 +12,6 @@ export default async function UserRelated( related: NonNullable["related"], ) { if ( - !related?.course?.length && !related?.podcast?.length && !related?.post?.length ) { diff --git a/lib/rss.ts b/lib/rss.ts index 8797c03c..7b83e76c 100644 --- a/lib/rss.ts +++ b/lib/rss.ts @@ -17,8 +17,6 @@ function typePath(type: string): string { return "blog"; case "podcast": return "podcasts"; - case "course": - return "courses"; default: return type + "s"; } @@ -42,6 +40,7 @@ export async function buildFeed(params: { limit: params.limit || 10000, offset: params.offset || 0, }, + tags: isPodcast ? ["podcast-rss", "podcast"] : [params.type + "-rss", params.type], }) ).data as RssQueryResult; diff --git a/lib/types.ts b/lib/types.ts index 908ef4af..c71b01c2 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,5 +1,4 @@ import type { - LessonsInCourseQueryResult, PageQueryResult, } from "@/sanity/types"; @@ -7,13 +6,11 @@ export type NonNull = Exclude; export enum ContentType { author = "author", - course = "course", framework = "framework", forum = "forum", guest = "guest", group = "group", language = "language", - lesson = "lesson", page = "page", podcast = "podcast", post = "post", @@ -40,13 +37,3 @@ export interface BookmarkPath extends BaseBookmarkContent { _cc_updated: number; } -export type BaseCompletedLesson = NonNullable< - NonNullable< - NonNullable< - NonNullable["sections"] - >[0]["lesson"] - >[0] ->; -export interface CompletedLesson extends BaseCompletedLesson { - _cc_updated: number; -} diff --git a/sanity.config.ts b/sanity.config.ts index ee5d433f..95075aa9 100644 --- a/sanity.config.ts +++ b/sanity.config.ts @@ -35,8 +35,6 @@ import { sharePreviewAction } from "@/sanity/components/documentActions/sharePre import { assistWithPresets } from "@/sanity/plugins/assist"; import author from "@/sanity/schemas/documents/author"; import previewSession from "@/sanity/schemas/previewSession"; -import course from "@/sanity/schemas/documents/course"; -import lesson from "@/sanity/schemas/documents/lesson"; import guest from "@/sanity/schemas/documents/guest"; import page from "@/sanity/schemas/documents/page"; import podcast from "@/sanity/schemas/documents/podcast"; @@ -142,8 +140,6 @@ export default defineConfig({ dashboardSettings, // Documents author, - course, - lesson, guest, page, podcast, diff --git a/sanity/lib/fetch.ts b/sanity/lib/fetch.ts new file mode 100644 index 00000000..f3e27587 --- /dev/null +++ b/sanity/lib/fetch.ts @@ -0,0 +1,57 @@ +import "server-only"; + +import type { QueryParams } from "next-sanity"; +import { draftMode } from "next/headers"; +import { client } from "@/sanity/lib/client"; +import { token } from "@/sanity/lib/token"; + +/** + * Server-only Sanity fetch with ISR caching and tag-based revalidation. + * + * In draft mode: bypasses cache, uses token for draft content. + * In production: uses CDN with Next.js cache tags for on-demand revalidation. + */ +export async function sanityFetch({ + query, + params = {}, + tags = [], + revalidate, + stega, +}: { + query: string; + params?: QueryParams; + tags?: string[]; + revalidate?: number | false; + stega?: boolean; +}): Promise<{ data: T }> { + const isDraft = (await draftMode()).isEnabled; + + if (isDraft) { + // Draft mode: bypass cache, use token, get draft content + const data = await client + .withConfig({ + useCdn: false, + token: token || undefined, + perspective: "drafts", + stega: stega !== undefined ? { enabled: stega } : undefined, + }) + .fetch(query, params, { + cache: "no-store", + }); + return { data }; + } + + // Production: use CDN with ISR caching + const data = await client + .withConfig({ + useCdn: true, + stega: stega !== undefined ? { enabled: stega } : undefined, + }) + .fetch(query, params, { + next: { + revalidate: revalidate ?? 60, + tags, + }, + }); + return { data }; +} diff --git a/sanity/lib/live.ts b/sanity/lib/live.ts index 413dad02..ee013787 100644 --- a/sanity/lib/live.ts +++ b/sanity/lib/live.ts @@ -5,7 +5,6 @@ import { defineLive } from "next-sanity/live"; import { client } from "@/sanity/lib/client"; import { token } from "@/sanity/lib/token"; -import { stegaClean } from "@sanity/client/stega"; export const { sanityFetch, SanityLive } = defineLive({ client, diff --git a/sanity/lib/queries.ts b/sanity/lib/queries.ts index ac5537ca..cf1390ce 100644 --- a/sanity/lib/queries.ts +++ b/sanity/lib/queries.ts @@ -73,14 +73,6 @@ const podcastFields = ` spotify `; -const courseFields = ` - stripeProduct -`; - -const lessonFields = ` - locked, - videoCloudinary -`; const userFields = ` socials, @@ -89,9 +81,6 @@ const userFields = ` const userRelated = ` "related":{ - "course": *[_type == "course" && (^._id in author[]._ref || ^._id in guest[]._ref)] | order(date desc) [0...4] { - ${baseFieldsNoContent} - }, "podcast": *[_type == "podcast" && (^._id in author[]._ref || ^._id in guest[]._ref)] | order(date desc) [0...4] { ${baseFieldsNoContent} }, @@ -103,9 +92,6 @@ const userRelated = ` const sponsorRelated = ` "related":{ - "course": *[_type == "course" && ^._id in sponsor[]._ref] | order(date desc) [] { - ${baseFieldsNoContent} - }, "podcast": *[_type == "podcast" && ^._id in sponsor[]._ref] | order(date desc) [] { ${baseFieldsNoContent} }, @@ -201,56 +187,6 @@ export const podcastQuery = groq`*[_type == "podcast" && slug.current == $slug] ${contentFields}, ${podcastFields} }`; - -// Courses - -export const coursesQuery = groq`*[_type == "course" && defined(slug.current)] | order(date desc, _updatedAt desc) [0] { - ${baseFieldsNoContent}, - ${courseFields}, - author[]->{ - ..., - "title": coalesce(title, "Anonymous"), - "slug": slug.current, - } -}`; - -export const moreCourseQuery = groq`*[_type == "course" && _id != $skip && defined(slug.current)] | order(date desc, _updatedAt desc) [$offset...$limit] { - ${baseFieldsNoContent}, - ${courseFields}, - author[]->{ - ..., - "title": coalesce(title, "Anonymous"), - "slug": slug.current, - } -}`; - -export const courseQuery = groq`*[_type == "course" && slug.current == $courseSlug] [0] { - ${baseFieldsNoContent}, - ${courseFields}, - ${contentFields}, - ${podcastFields} -}`; - -// Lessons - -export const lessonsInCourseQuery = groq`*[_type == "course" && slug.current == $courseSlug] [0] { - ${baseFieldsNoContent}, - ${courseFields}, - sections[]{ - title, - lesson[]->{ - ${baseFieldsNoContent}, - ${lessonFields} - } - } -}`; - -export const lessonQuery = groq`*[_type == "lesson" && slug.current == $lessonSlug] [0] { - ${baseFieldsNoContent}, - ${contentFields}, - ${lessonFields} -}`; - // Author export const moreAuthorQuery = groq`*[_type == "author" && _id != $skip && defined(slug.current)] | order(title) [$offset...$limit] { @@ -322,15 +258,8 @@ export const rssPodcastQuery = groq`*[_type == "podcast" && _id != $skip && defi }`; // Sitemaps -export const sitemapQuery = groq`*[_type in ["author", "course", "guest", "page", "podcast", "post", "sponsor"] && defined(slug.current)] | order(_type asc) | order(_updated desc) { +export const sitemapQuery = groq`*[_type in ["author", "guest", "page", "podcast", "post", "sponsor"] && defined(slug.current)] | order(_type asc) | order(_updated desc) { _type, _updatedAt, "slug": slug.current, - sections[]{ - lesson[]->{ - _type, - _updatedAt, - "slug": slug.current, - } - } }`; diff --git a/sanity/lib/token.ts b/sanity/lib/token.ts index 57ec28b5..c04969ed 100644 --- a/sanity/lib/token.ts +++ b/sanity/lib/token.ts @@ -1,5 +1,7 @@ export const token = process.env.SANITY_API_READ_TOKEN; if (!token) { - throw new Error("Missing SANITY_API_READ_TOKEN"); + console.warn( + "Missing SANITY_API_READ_TOKEN — draft mode and live preview will not work", + ); } diff --git a/sanity/schemas/custom/internalLink.ts b/sanity/schemas/custom/internalLink.ts index 73d2451a..065fc37c 100644 --- a/sanity/schemas/custom/internalLink.ts +++ b/sanity/schemas/custom/internalLink.ts @@ -14,7 +14,6 @@ export default defineType({ to: [ { type: "post" }, { type: "podcast" }, - { type: "course" }, { type: "page" }, // other types you may want to link to ], diff --git a/sanity/schemas/documents/course.ts b/sanity/schemas/documents/course.ts deleted file mode 100644 index fe1fb0ad..00000000 --- a/sanity/schemas/documents/course.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { GiTeacher } from "react-icons/gi"; -import { format, parseISO } from "date-fns"; -import { defineField, defineType } from "sanity"; - -import contentType from "../partials/content"; -import lessonType from "./lesson"; - -export default defineType({ - ...contentType, - name: "course", - title: "Course", - icon: GiTeacher, - type: "document", - groups: [ - ...(contentType.groups || []), - { - name: "sections", - title: "Sections / Lessons", - default: true, - }, - ], - fields: [ - ...contentType.fields, - defineField({ - name: "stripeProduct", - title: "Stripe Product Id", - type: "string", - }), - defineField({ - name: "sections", - title: "Sections", - type: "array", - group: "sections", - of: [ - { - name: "section", - title: "Section", - type: "object", - fields: [ - defineField({ - name: "title", - title: "Title", - type: "string", - }), - defineField({ - name: "lesson", - title: "Lessons", - type: "array", - of: [ - { - type: "reference", - to: [{ type: lessonType.name }], - }, - ], - }), - ], - }, - ], - }), - ], -}); diff --git a/sanity/schemas/documents/lesson.ts b/sanity/schemas/documents/lesson.ts deleted file mode 100644 index 38ca93be..00000000 --- a/sanity/schemas/documents/lesson.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { PiStudentBold } from "react-icons/pi"; -import { format, parseISO } from "date-fns"; -import { defineField, defineType } from "sanity"; - -import contentType from "../partials/content"; - -export default defineType({ - ...contentType, - name: "lesson", - title: "Lesson", - icon: PiStudentBold, - type: "document", - fields: [ - ...contentType.fields, - defineField({ - name: "locked", - title: "Locked", - type: "boolean", - }), - ], -});