diff --git a/contentlayer.config.ts b/contentlayer.config.ts index d299d37a0..e97ea86b4 100644 --- a/contentlayer.config.ts +++ b/contentlayer.config.ts @@ -1,4 +1,10 @@ -import { defineDocumentType, makeSource } from 'contentlayer2/source-files' +import { + ComputedFields, + defineDocumentType, + defineNestedType, + FieldDefs, + makeSource, +} from 'contentlayer2/source-files' import { extractTocHeadings } from './src/mdx/remark-toc-headings.mjs' import path from 'path' import fs from 'fs' @@ -7,87 +13,132 @@ const contentDirPath = 'docs' const branch = process.env.NEXT_PUBLIC_GITHUB_BRANCH || 'main' +const baseFields: FieldDefs = { + title_seo: { + type: 'string', + description: + 'The meta title of the doc, this will override the title extracted from the markdown and the nav title', + }, + description: { + type: 'string', + description: 'The description of the doc', + }, + image: { + type: 'string', + description: 'The image of the doc', + }, + image_alt: { + type: 'string', + description: 'The image alt of the doc', + }, + disable_edit: { + type: 'boolean', + description: 'Disable the github edit button', + }, +} + +const computedFields: ComputedFields = { + slug: { + type: 'string', + resolve: (doc) => doc._raw.flattenedPath, + }, + toc: { type: 'json', resolve: (doc) => extractTocHeadings(doc.body.raw) }, + title: { + type: 'string', + resolve: async (doc) => { + const headings = await extractTocHeadings(doc.body.raw, [1]) + + return headings[0]?.value + }, + }, + editUrl: { + type: 'string', + resolve: (doc) => + `https://github.com/nitrictech/docs/edit/${branch}/docs/${doc._raw.sourceFilePath}`, + }, + lastModified: { + type: 'date', + resolve: (doc) => { + // Get the full path to the markdown file + const filePath = path.join( + process.cwd(), + contentDirPath, + doc._raw.sourceFilePath, + ) + // Extract and return the last modified date + const stats = fs.statSync(filePath) + return stats.mtime // This is the last modified date + }, + }, +} + const Doc = defineDocumentType(() => ({ name: 'Doc', - filePathPattern: '**/*.mdx', + filePathPattern: '!**/guides/**/*.mdx', + fields: baseFields, + computedFields, +})) + +const Featured = defineNestedType(() => ({ + name: 'Featured', fields: { - title_seo: { + image: { type: 'string', description: - 'The meta title of the doc, this will override the title extracted from the markdown and the nav title', + 'The featured image of the post, not the same as og image. Use 1024x1024 with transparent background.', + required: true, }, - description: { + image_alt: { type: 'string', - description: 'The description of the doc', + description: 'The featured image alt of the post', + required: true, }, - image: { - type: 'string', - description: 'The image of the doc', + }, +})) + +const Guide = defineDocumentType(() => ({ + name: 'Guide', + filePathPattern: '**/guides/**/*.mdx', + fields: { + ...baseFields, + published_at: { + type: 'date', + description: 'The date the guide was published', + required: true, }, - image_alt: { - type: 'string', - description: 'The image alt of the doc', + updated_at: { + type: 'date', + description: + 'The date the guide was last updated, will be set to published_at if not set', }, - disable_edit: { - type: 'boolean', - description: 'Disable the github edit button', + featured: { + type: 'nested', + of: Featured, }, tags: { type: 'list', of: { type: 'string', }, - description: 'The tags of the post, used by guides', + description: 'The tags of the post', + required: true, }, languages: { type: 'list', of: { type: 'string', }, - description: 'The languages of the content, used by guides', + description: 'The languages of the content', }, start_steps: { type: 'markdown', - description: 'The start steps of the doc, used by guides', - }, - }, - computedFields: { - slug: { - type: 'string', - resolve: (doc) => doc._raw.flattenedPath, - }, - toc: { type: 'json', resolve: (doc) => extractTocHeadings(doc.body.raw) }, - title: { - type: 'string', - resolve: async (doc) => { - const headings = await extractTocHeadings(doc.body.raw, [1]) - - return headings[0]?.value - }, - }, - editUrl: { - type: 'string', - resolve: (doc) => - `https://github.com/nitrictech/docs/edit/${branch}/docs/${doc._raw.sourceFilePath}`, - }, - lastModified: { - type: 'date', - resolve: (doc) => { - // Get the full path to the markdown file - const filePath = path.join( - process.cwd(), - contentDirPath, - doc._raw.sourceFilePath, - ) - // Extract and return the last modified date - const stats = fs.statSync(filePath) - return stats.mtime // This is the last modified date - }, + description: 'The start steps of the doc', }, }, + computedFields, })) export default makeSource({ contentDirPath, - documentTypes: [Doc], + documentTypes: [Doc, Guide], }) diff --git a/docs/guides/dart/flutter.mdx b/docs/guides/dart/flutter.mdx index b3db251c4..907b33015 100644 --- a/docs/guides/dart/flutter.mdx +++ b/docs/guides/dart/flutter.mdx @@ -13,6 +13,10 @@ start_steps: | cd examples/v1/flutter flutter pub get nitric start +featured: + image: /docs/images/guides/flutter/featured.png + image_alt: 'Building a Full Stack Flutter Application in Dart featured image' +published_at: 2024-10-30 --- # Building a Full Stack Flutter Application in Dart diff --git a/docs/guides/dart/serverless-rest-api-example.mdx b/docs/guides/dart/serverless-rest-api-example.mdx index b4a8b33ba..b12d3d8af 100644 --- a/docs/guides/dart/serverless-rest-api-example.mdx +++ b/docs/guides/dart/serverless-rest-api-example.mdx @@ -6,6 +6,7 @@ tags: - Key Value Store languages: - dart +published_at: 2024-04-24 --- # Building a REST API with Nitric diff --git a/docs/guides/deploying/azure-pipelines.mdx b/docs/guides/deploying/azure-pipelines.mdx index eb2ff0195..c3c44ffe4 100644 --- a/docs/guides/deploying/azure-pipelines.mdx +++ b/docs/guides/deploying/azure-pipelines.mdx @@ -3,6 +3,8 @@ description: Deploy to AWS, Google Cloud or Microsoft Azure using Azure DevOps a tags: - CI/CD - Azure +published_at: 2023-11-01 +updated_at: 2024-02-04 --- # Deployment Automation with Azure Pipelines and Nitric diff --git a/docs/guides/deploying/github-actions.mdx b/docs/guides/deploying/github-actions.mdx index 0b2dc43a4..2d95ecefb 100644 --- a/docs/guides/deploying/github-actions.mdx +++ b/docs/guides/deploying/github-actions.mdx @@ -2,6 +2,8 @@ description: Deploy to AWS, Google Cloud or Microsoft Azure using GitHub Actions and the Nitric CLI tags: - CI/CD +published_at: 2023-12-21 +updated_at: 2024-08-20 --- # Deployment Automation with GitHub Actions and Nitric diff --git a/docs/guides/deploying/gitlab-ci.mdx b/docs/guides/deploying/gitlab-ci.mdx index 7cd9605f5..4fb25cac1 100644 --- a/docs/guides/deploying/gitlab-ci.mdx +++ b/docs/guides/deploying/gitlab-ci.mdx @@ -2,6 +2,8 @@ description: Deploy to AWS, Google Cloud or Microsoft Azure using GitLab CI and the Nitric CLI tags: - CI/CD +published_at: 2023-11-01 +updated_at: 2024-08-20 --- # Deployment Automation with GitLab CI and Nitric diff --git a/docs/guides/deploying/google-cloud-build.mdx b/docs/guides/deploying/google-cloud-build.mdx index e14b3ccaf..648d7c9f2 100644 --- a/docs/guides/deploying/google-cloud-build.mdx +++ b/docs/guides/deploying/google-cloud-build.mdx @@ -3,6 +3,8 @@ description: Deploy to AWS, Google Cloud or Microsoft Azure using Google Cloud B tags: - CI/CD - Google +published_at: 2023-11-01 +updated_at: 2024-08-20 --- # Deployment Automation with Google Cloud Build and Nitric diff --git a/docs/guides/go/realtime-messaging.mdx b/docs/guides/go/realtime-messaging.mdx index a9eef89f9..094fed9a8 100644 --- a/docs/guides/go/realtime-messaging.mdx +++ b/docs/guides/go/realtime-messaging.mdx @@ -10,6 +10,10 @@ start_steps: | cd examples/v1/websocket-app go mod tidy nitric start +featured: + image: /docs/images/guides/realtime-messaging/featured.png + image_alt: 'Building a chat app in Go with WebSockets and Nitric featured image' +published_at: 2024-10-16 --- # Building a chat app in Go with WebSockets and Nitric diff --git a/docs/guides/go/serverless-rest-api-example.mdx b/docs/guides/go/serverless-rest-api-example.mdx index 0c7ad7cf7..2503f3ffc 100644 --- a/docs/guides/go/serverless-rest-api-example.mdx +++ b/docs/guides/go/serverless-rest-api-example.mdx @@ -6,6 +6,8 @@ tags: - Key Value Store languages: - go +published_at: 2023-08-11 +updated_at: 2024-10-03 --- # Building your first API with Nitric diff --git a/docs/guides/jvm/serverless-rest-api-example.mdx b/docs/guides/jvm/serverless-rest-api-example.mdx index 60b8e6a9d..5c4a24dd6 100644 --- a/docs/guides/jvm/serverless-rest-api-example.mdx +++ b/docs/guides/jvm/serverless-rest-api-example.mdx @@ -4,6 +4,7 @@ description: Use the Nitric framework to easily build and deploy JVM REST APIs f tags: - API - Key Value Store +published_at: 2023-10-12 --- diff --git a/docs/guides/nodejs/amazon-cognito.mdx b/docs/guides/nodejs/amazon-cognito.mdx index 59e5d3122..cb99c8d58 100644 --- a/docs/guides/nodejs/amazon-cognito.mdx +++ b/docs/guides/nodejs/amazon-cognito.mdx @@ -7,6 +7,8 @@ tags: languages: - typescript - javascript +published_at: 2023-10-09 +updated_at: 2024-05-15 --- # Securing APIs with Amazon Cognito diff --git a/docs/guides/nodejs/api-with-nextjs.mdx b/docs/guides/nodejs/api-with-nextjs.mdx index d975a538e..ac88af196 100644 --- a/docs/guides/nodejs/api-with-nextjs.mdx +++ b/docs/guides/nodejs/api-with-nextjs.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2023-08-28 +updated_at: 2024-10-11 --- # Next.js and Nitric example application diff --git a/docs/guides/nodejs/byo-database.mdx b/docs/guides/nodejs/byo-database.mdx index cc012bac4..e9a2adb6e 100644 --- a/docs/guides/nodejs/byo-database.mdx +++ b/docs/guides/nodejs/byo-database.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2022-10-13 +updated_at: 2024-06-14 --- # BYO Database diff --git a/docs/guides/nodejs/debugging.mdx b/docs/guides/nodejs/debugging.mdx index 66727fb8f..622665e3d 100644 --- a/docs/guides/nodejs/debugging.mdx +++ b/docs/guides/nodejs/debugging.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2023-06-16 +updated_at: 2023-08-22 --- # Debugging with Nitric diff --git a/docs/guides/nodejs/expressjs.mdx b/docs/guides/nodejs/expressjs.mdx index 5914942ea..1a66f8f03 100644 --- a/docs/guides/nodejs/expressjs.mdx +++ b/docs/guides/nodejs/expressjs.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2023-07-10 +updated_at: 2024-03-18 --- # Enhance Express.js Apps with Cloud Resources diff --git a/docs/guides/nodejs/fastify.mdx b/docs/guides/nodejs/fastify.mdx index 0c5f04cc0..94edbf78e 100644 --- a/docs/guides/nodejs/fastify.mdx +++ b/docs/guides/nodejs/fastify.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2023-07-12 +updated_at: 2024-03-18 --- # Add Cloud Resources to Fastify Apps diff --git a/docs/guides/nodejs/graphql.mdx b/docs/guides/nodejs/graphql.mdx index 9601515a0..50d2367d1 100644 --- a/docs/guides/nodejs/graphql.mdx +++ b/docs/guides/nodejs/graphql.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2022-11-17 +updated_at: 2024-03-18 --- # Building a GraphQL API with Nitric diff --git a/docs/guides/nodejs/nestjs.mdx b/docs/guides/nodejs/nestjs.mdx index 721db5542..2264f6ab5 100644 --- a/docs/guides/nodejs/nestjs.mdx +++ b/docs/guides/nodejs/nestjs.mdx @@ -7,6 +7,8 @@ tags: languages: - typescript - javascript +published_at: 2023-07-05 +updated_at: 2024-05-15 --- # Nest.js integration with Nitric diff --git a/docs/guides/nodejs/nitric-and-drizzle.mdx b/docs/guides/nodejs/nitric-and-drizzle.mdx index 7414e1012..e6705767b 100644 --- a/docs/guides/nodejs/nitric-and-drizzle.mdx +++ b/docs/guides/nodejs/nitric-and-drizzle.mdx @@ -6,6 +6,7 @@ tags: languages: - typescript - javascript +published_at: 2024-06-14 --- # Nitric SQL Databases with Drizzle diff --git a/docs/guides/nodejs/nitric-and-prisma.mdx b/docs/guides/nodejs/nitric-and-prisma.mdx index 0cff91732..90e222daf 100644 --- a/docs/guides/nodejs/nitric-and-prisma.mdx +++ b/docs/guides/nodejs/nitric-and-prisma.mdx @@ -6,6 +6,7 @@ tags: languages: - typescript - javascript +published_at: 2024-06-14 --- # Nitric SQL Databases with Prisma diff --git a/docs/guides/nodejs/nitric-and-supabase.mdx b/docs/guides/nodejs/nitric-and-supabase.mdx index 11abdeda0..389a481f3 100644 --- a/docs/guides/nodejs/nitric-and-supabase.mdx +++ b/docs/guides/nodejs/nitric-and-supabase.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2022-03-26 +updated_at: 2024-02-20 --- # Extend Supabase Apps with Nitric diff --git a/docs/guides/nodejs/secure-api-auth0.mdx b/docs/guides/nodejs/secure-api-auth0.mdx index 3a8c83503..f17b8c892 100644 --- a/docs/guides/nodejs/secure-api-auth0.mdx +++ b/docs/guides/nodejs/secure-api-auth0.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2022-05-23 +updated_at: 2024-10-11 --- # Securing your API with Auth0 diff --git a/docs/guides/nodejs/serverless-api-with-planetscale-and-prisma.mdx b/docs/guides/nodejs/serverless-api-with-planetscale-and-prisma.mdx index edbc5453b..b4cb51dfe 100644 --- a/docs/guides/nodejs/serverless-api-with-planetscale-and-prisma.mdx +++ b/docs/guides/nodejs/serverless-api-with-planetscale-and-prisma.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2022-03-28 +updated_at: 2024-03-18 --- # APIs with PlanetScale, Prisma and Nitric diff --git a/docs/guides/nodejs/serverless-rest-api-example.mdx b/docs/guides/nodejs/serverless-rest-api-example.mdx index d92833028..19c4eba0b 100644 --- a/docs/guides/nodejs/serverless-rest-api-example.mdx +++ b/docs/guides/nodejs/serverless-rest-api-example.mdx @@ -7,6 +7,8 @@ tags: languages: - typescript - javascript +published_at: 2023-06-16 +updated_at: 2024-05-15 --- # Building a REST API with Nitric diff --git a/docs/guides/nodejs/stripe.mdx b/docs/guides/nodejs/stripe.mdx index d64cf0a2f..9f24f8859 100644 --- a/docs/guides/nodejs/stripe.mdx +++ b/docs/guides/nodejs/stripe.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2022-04-14 +updated_at: 2024-05-15 --- # Integrating Stripe with Nitric diff --git a/docs/guides/nodejs/testing.mdx b/docs/guides/nodejs/testing.mdx index 7addfeb87..a4c434e22 100644 --- a/docs/guides/nodejs/testing.mdx +++ b/docs/guides/nodejs/testing.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2022-03-31 +updated_at: 2024-10-11 --- # Testing diff --git a/docs/guides/nodejs/twilio.mdx b/docs/guides/nodejs/twilio.mdx index 3e1d57c0d..fde2c0fa5 100644 --- a/docs/guides/nodejs/twilio.mdx +++ b/docs/guides/nodejs/twilio.mdx @@ -6,6 +6,8 @@ tags: languages: - typescript - javascript +published_at: 2022-04-11 +updated_at: 2024-10-11 --- # Integrating Twilio with Nitric diff --git a/docs/guides/nodejs/uptime.mdx b/docs/guides/nodejs/uptime.mdx index aa49a6035..5533c517d 100644 --- a/docs/guides/nodejs/uptime.mdx +++ b/docs/guides/nodejs/uptime.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2024-04-11 +updated_at: 2024-10-11 --- # Build an Uptime Monitoring Tool with Nitric diff --git a/docs/guides/nodejs/websockets.mdx b/docs/guides/nodejs/websockets.mdx index 27a81a7a4..69301b6b5 100644 --- a/docs/guides/nodejs/websockets.mdx +++ b/docs/guides/nodejs/websockets.mdx @@ -5,6 +5,8 @@ tags: languages: - typescript - javascript +published_at: 2023-07-17 +updated_at: 2024-10-11 --- # Websockets guide diff --git a/docs/guides/python/ai-podcast-part-1.mdx b/docs/guides/python/ai-podcast-part-1.mdx index 73b902180..9ec9d8cd1 100644 --- a/docs/guides/python/ai-podcast-part-1.mdx +++ b/docs/guides/python/ai-podcast-part-1.mdx @@ -6,6 +6,10 @@ languages: - python image: /docs/images/guides/ai-podcast/part-1/banner.png image_alt: 'AI Podcast Part 1 Banner' +featured: + image: /docs/images/guides/ai-podcast/part-1/featured.png + image_alt: 'AI Podcast Part 1 featured image' +published_at: 2024-10-28 --- # Building AI Workflows: Combining LLMs and Voice Models - Part 1 diff --git a/docs/guides/python/create-histogram.mdx b/docs/guides/python/create-histogram.mdx index 42bebd5e4..46f43d4db 100644 --- a/docs/guides/python/create-histogram.mdx +++ b/docs/guides/python/create-histogram.mdx @@ -5,6 +5,8 @@ tags: - Data Visualization languages: - python +published_at: 2022-12-20 +updated_at: 2024-10-17 --- # Building a data visualization API with Nitric diff --git a/docs/guides/python/graphql.mdx b/docs/guides/python/graphql.mdx index d16bdaf89..f7eca8ab4 100644 --- a/docs/guides/python/graphql.mdx +++ b/docs/guides/python/graphql.mdx @@ -5,6 +5,8 @@ tags: - API languages: - python +published_at: 2022-04-14 +updated_at: 2024-10-17 --- # Building a GraphQL API with Nitric diff --git a/docs/guides/python/scheduled-report.mdx b/docs/guides/python/scheduled-report.mdx index 0cf15e56c..4085c23b9 100644 --- a/docs/guides/python/scheduled-report.mdx +++ b/docs/guides/python/scheduled-report.mdx @@ -7,6 +7,8 @@ tags: - Schedules languages: - python +published_at: 2024-04-16 +updated_at: 2024-10-17 --- # Generate a report with Google Sheets and share it with Google Drive diff --git a/docs/guides/python/serverless-rest-api-example.mdx b/docs/guides/python/serverless-rest-api-example.mdx index 480fe9dbf..7c07496bc 100644 --- a/docs/guides/python/serverless-rest-api-example.mdx +++ b/docs/guides/python/serverless-rest-api-example.mdx @@ -6,6 +6,8 @@ tags: - Key Value Store languages: - python +published_at: 2022-09-11 +updated_at: 2024-10-17 --- # Building your first API with Nitric diff --git a/docs/guides/python/text-prediction.mdx b/docs/guides/python/text-prediction.mdx index ef3197678..ccafe1d00 100644 --- a/docs/guides/python/text-prediction.mdx +++ b/docs/guides/python/text-prediction.mdx @@ -4,6 +4,8 @@ tags: - AI & Machine Learning languages: - python +published_at: 2022-12-20 +updated_at: 2024-10-17 --- # Building serverless text prediction from training to deployment diff --git a/docs/guides/terraform/api-gateway-throttle.mdx b/docs/guides/terraform/api-gateway-throttle.mdx index 9d868be05..58cad137a 100644 --- a/docs/guides/terraform/api-gateway-throttle.mdx +++ b/docs/guides/terraform/api-gateway-throttle.mdx @@ -4,6 +4,7 @@ tags: - AWS - Terraform - Customize Nitric +published_at: 2024-07-26 --- # Add Throttle Limits to API Gateway diff --git a/docs/guides/terraform/s3-encryption.mdx b/docs/guides/terraform/s3-encryption.mdx index 302e6420e..9e95e2bac 100644 --- a/docs/guides/terraform/s3-encryption.mdx +++ b/docs/guides/terraform/s3-encryption.mdx @@ -4,6 +4,7 @@ tags: - AWS - Terraform - Customize Nitric +published_at: 2024-07-26 --- # Add server-side encryption diff --git a/docs/guides/terraform/s3-replicate.mdx b/docs/guides/terraform/s3-replicate.mdx index ce4d5139e..298b8612d 100644 --- a/docs/guides/terraform/s3-replicate.mdx +++ b/docs/guides/terraform/s3-replicate.mdx @@ -4,6 +4,7 @@ tags: - AWS - Terraform - Customize Nitric +published_at: 2024-07-26 --- # Replicate S3 buckets into another region diff --git a/docs/guides/terraform/terratest.mdx b/docs/guides/terraform/terratest.mdx index 97a8674eb..cdfdc448a 100644 --- a/docs/guides/terraform/terratest.mdx +++ b/docs/guides/terraform/terratest.mdx @@ -5,6 +5,7 @@ tags: - Testing languages: - go +published_at: 2024-10-16 --- # Testing AWS resources with Terratest diff --git a/docs/index.mdx b/docs/index.mdx index d06682ea5..db2f1752c 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -543,3 +543,4 @@ If you need to use a service/resource that Nitric doesn't support, you do that l - Start building with the [quick start guide](/get-started/quickstart). - Checkout out the [foundations](/get-started/foundations/why-nitric) section for a more detailed introduction to Nitric. +- Dive into the [guides](/guides) section for more in-depth tutorials. diff --git a/public/images/guides/ai-podcast/part-1/featured.png b/public/images/guides/ai-podcast/part-1/featured.png new file mode 100644 index 000000000..7d3c41696 Binary files /dev/null and b/public/images/guides/ai-podcast/part-1/featured.png differ diff --git a/public/images/guides/flutter/featured.png b/public/images/guides/flutter/featured.png new file mode 100644 index 000000000..eeede0fa9 Binary files /dev/null and b/public/images/guides/flutter/featured.png differ diff --git a/public/images/guides/realtime-messaging/featured.png b/public/images/guides/realtime-messaging/featured.png new file mode 100644 index 000000000..181eb6c10 Binary files /dev/null and b/public/images/guides/realtime-messaging/featured.png differ diff --git a/scripts/cypress-fixtures.mjs b/scripts/cypress-fixtures.mjs index 22e65d3f5..22e2171f4 100644 --- a/scripts/cypress-fixtures.mjs +++ b/scripts/cypress-fixtures.mjs @@ -3,7 +3,7 @@ import fs from 'fs/promises' import { XMLParser } from 'fast-xml-parser' const parser = new XMLParser() -import { allDocs } from '../.contentlayer/generated/index.mjs' +import { allDocuments } from '../.contentlayer/generated/index.mjs' import basePages from '../src/assets/sitemap.json' with { type: 'json' } const FIXTURE_PATH = 'cypress/fixtures/pages.json' @@ -14,7 +14,7 @@ async function run() { try { const staticPages = basePages.map((page) => `/docs${page}`) - const docPages = allDocs.map((doc) => { + const docPages = allDocuments.map((doc) => { return !doc.slug ? '/docs' : `/docs/${doc.slug}` }) diff --git a/src/app/(sitemaps)/sitemap-0.xml/route.ts b/src/app/(sitemaps)/sitemap-0.xml/route.ts index f6583e33a..a6d7dd19c 100644 --- a/src/app/(sitemaps)/sitemap-0.xml/route.ts +++ b/src/app/(sitemaps)/sitemap-0.xml/route.ts @@ -1,4 +1,4 @@ -import { allDocs } from '@/content' +import { allDocuments } from '@/content' import staticPaths from '@/assets/sitemap.json' import { BASE_URL } from '@/lib/constants' @@ -22,7 +22,7 @@ export async function GET() { priority: 0.7, })) - const docPages: SitemapItem[] = allDocs.map((page) => ({ + const docPages: SitemapItem[] = allDocuments.map((page) => ({ loc: page.slug === '' ? URL : `${URL}/${page.slug}`, lastmod: new Date(page.lastModified).toISOString(), changefreq: 'daily', diff --git a/src/app/[[...slug]]/layout.tsx b/src/app/[[...slug]]/layout.tsx index 49b95ad76..031c25d6c 100644 --- a/src/app/[[...slug]]/layout.tsx +++ b/src/app/[[...slug]]/layout.tsx @@ -1,7 +1,7 @@ import { Prose } from '@/components/Prose' import React from 'react' -import { allDocs } from '@/content' +import { allDocuments, Guide, isType } from '@/content' import DocToc from '@/components/layout/DocToC' import { Button } from '@/components/ui/button' import { GitHubIcon } from '@/components/icons/GitHubIcon' @@ -17,19 +17,19 @@ export default function DocLayout({ params: { slug: string[] } }) { const slug = params.slug ? decodeURI(params.slug.join('/')) : '' - const doc = allDocs.find((p) => p.slug === slug) + const doc = allDocuments.find((p) => p.slug === slug) if (!doc) { return <>{children} } - const startSteps = doc.start_steps && ( + const startSteps = isType('Guide') && (doc as Guide).start_steps && ( ) @@ -60,7 +60,11 @@ export default function DocLayout({
Last updated on{' '} - {new Date(doc.lastModified).toLocaleDateString('en-US', { + {new Date( + doc.type === 'Guide' + ? doc.updated_at || doc.published_at + : doc.lastModified, + ).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', diff --git a/src/app/[[...slug]]/page.tsx b/src/app/[[...slug]]/page.tsx index 5a92c148e..6ea0170ed 100644 --- a/src/app/[[...slug]]/page.tsx +++ b/src/app/[[...slug]]/page.tsx @@ -2,7 +2,7 @@ import { notFound } from 'next/navigation' import MDXContent from '@/components/MDXContent' import { Metadata } from 'next/types' import { BASE_URL } from '@/lib/constants' -import { allDocs } from '@/content' +import { allDocuments } from '@/content' import { getNavInfo } from '@/lib/getNavInfo' import { title } from 'radash' import { OpenGraph } from 'next/dist/lib/metadata/types/opengraph-types' @@ -13,7 +13,7 @@ export async function generateMetadata({ params: { slug: string[] } }): Promise { const slug = params.slug ? decodeURI(params.slug.join('/')) : '' - const doc = allDocs.find((p) => p.slug === slug) + const doc = allDocuments.find((p) => p.slug === slug) if (!doc) { return @@ -76,7 +76,7 @@ export async function generateMetadata({ } export const generateStaticParams = async () => { - return allDocs.map((p) => ({ + return allDocuments.map((p) => ({ slug: p.slug.split('/').map((name) => decodeURI(name)), })) } @@ -84,7 +84,7 @@ export const generateStaticParams = async () => { export default function Page({ params }: { params: { slug: string[] } }) { const slug = params.slug ? decodeURI(params.slug.join('/')) : '' - const doc = allDocs.find((p) => p.slug === slug) + const doc = allDocuments.find((p) => p.slug === slug) if (!doc) { return notFound() diff --git a/src/app/guides/page.tsx b/src/app/guides/page.tsx index e6eb4702f..4b64c65d4 100644 --- a/src/app/guides/page.tsx +++ b/src/app/guides/page.tsx @@ -1,4 +1,5 @@ import GuidePage from '@/components/guides/GuidePage' +import GuidesFeatured from '@/components/guides/GuidesFeatured' import { Breadcrumb, BreadcrumbItem, @@ -8,7 +9,7 @@ import { BreadcrumbSeparator, } from '@/components/ui/breadcrumb' import { Heading } from '@/components/ui/heading' -import { allDocs } from '@/content' +import { allGuides } from '@/content' import { BASE_URL } from '@/lib/constants' import Link from 'next/link' import { Metadata } from 'next/types' @@ -34,8 +35,6 @@ export const metadata: Metadata = { } export default function GuidesPage() { - const allGuides = allDocs.filter((doc) => doc.slug.startsWith('guides/')) - const allTags = allGuides .reduce((acc: string[], guide) => { if (guide.tags) { @@ -66,10 +65,13 @@ export default function GuidesPage() { Guides +
+ +
- +
diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index ab9f63457..328978293 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -7,12 +7,12 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from './ui/breadcrumb' -import { Doc } from '@/content' +import { Doc, Guide } from '@/content' import { getNavInfo, NavInfo } from '@/lib/getNavInfo' import Link from 'next/link' interface Props { - doc: Doc + doc: Doc | Guide className?: string } diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index e0aaa3710..8ddf9a596 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -79,6 +79,18 @@ function SmallPrint() { > Support + + Foundations + + + Guides +
diff --git a/src/components/guides/GuideItem.tsx b/src/components/guides/GuideItem.tsx index d3552d45b..4fe5e6bbc 100644 --- a/src/components/guides/GuideItem.tsx +++ b/src/components/guides/GuideItem.tsx @@ -1,4 +1,4 @@ -import type { Doc } from '@/content' +import type { Guide } from '@/content' import React from 'react' import { cn } from '@/lib/utils' import Link from 'next/link' @@ -6,7 +6,7 @@ import { LanguageIcon } from '../icons/LanguageIcon' import { Language } from '@/lib/constants' interface Props { - guide: Doc + guide: Guide featured?: boolean } @@ -21,14 +21,27 @@ export const GuideItem: React.FC = ({ guide, featured }) => { >
-

- {guide.title} -

+
+

+ {guide.title} +

+ +
+

= ({ guide, featured }) => {

{guide.tags?.length ? ( -
+
{guide.tags.map((tag) => ( {tag} diff --git a/src/components/guides/GuideList.tsx b/src/components/guides/GuideList.tsx index ebcc7e2db..cf48fa559 100644 --- a/src/components/guides/GuideList.tsx +++ b/src/components/guides/GuideList.tsx @@ -1,49 +1,131 @@ 'use client' -import React from 'react' +import React, { useMemo } from 'react' import { GuideItem } from './GuideItem' -import { Doc } from '@/content' import { cn } from '@/lib/utils' import useParams from '@/hooks/useParams' +import type { Guide } from '@/content' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '../ui/select' +import { Label } from '../ui/label' +import { Input } from '../ui/input' interface Props { - guides: Doc[] className?: string + allGuides: Guide[] } -const GuideList: React.FC = ({ className, guides }) => { - const { searchParams } = useParams() +const GuideList: React.FC = ({ className, allGuides }) => { + const { searchParams, setParams } = useParams() + const sortBy = searchParams?.get('sort') || 'published_date' const selectedTags = searchParams?.get('tags')?.split(',') || [] const selectedLangs = searchParams?.get('langs')?.split(',') || [] + const query = searchParams?.get('q') || '' - const filteredGuides = guides - .filter((guide) => { - let include = true + const filteredGuides = useMemo(() => { + return allGuides + .filter((guide) => { + let include = true - if (selectedLangs.length) { - include = selectedLangs.some((lang) => guide.languages?.includes(lang)) - } + if (selectedLangs.length) { + include = selectedLangs.some((lang) => + guide.languages?.includes(lang), + ) + } - if (!selectedTags.length) return include + if (query.trim()) { + include = + include && + (guide.title.toLowerCase().includes(query.toLowerCase()) || + (guide.description || '') + .toLowerCase() + .includes(query.toLowerCase())) + } - return include && selectedTags.some((tag) => guide.tags?.includes(tag)) - }) - .sort((a, b) => a.title.localeCompare(b.title)) + if (!selectedTags.length) return include - return filteredGuides.length === 0 ? ( -
-

- No guides found. Please try selecting different filters. -

+ return include && selectedTags.some((tag) => guide.tags?.includes(tag)) + }) + .sort((a, b) => { + if (sortBy === 'published_date') { + const dateDiff = + new Date(b.published_at).getTime() - + new Date(a.published_at).getTime() + + return dateDiff !== 0 ? dateDiff : a.title.localeCompare(b.title) + } + + return sortBy === 'alpha-reverse' + ? b.title.localeCompare(a.title) + : a.title.localeCompare(b.title) + }) + }, [allGuides, selectedTags, selectedLangs, sortBy, query]) + + // Handle input change and pass the input value directly + const handleChange = (e: React.ChangeEvent) => { + setParams('q', e.target.value) + } + + return ( +
+
+
+ + +
+ +
+ +
    + {filteredGuides.length === 0 ? ( +
  • + No guides found. Please try selecting different filters. +
  • + ) : ( + filteredGuides.map((guide) => ( +
  • + +
  • + )) + )} +
- ) : ( -
    - {filteredGuides.map((guide) => ( -
  • - -
  • - ))} -
) } diff --git a/src/components/guides/GuidePage.tsx b/src/components/guides/GuidePage.tsx index 6f999613f..9af6654fe 100644 --- a/src/components/guides/GuidePage.tsx +++ b/src/components/guides/GuidePage.tsx @@ -1,18 +1,15 @@ -import { Doc } from '@/content' import React, { Suspense } from 'react' import { GuideFilters } from './GuideFilters' import GuideList from './GuideList' -import { Button } from '../ui/button' -import { AdjustmentsHorizontalIcon } from '@heroicons/react/24/outline' import GuideMobileFilters from './GuideMobileFilters' +import { allGuides } from '@/content' interface Props { - guides: Doc[] allTags: string[] } -const GuidePage: React.FC = ({ guides, allTags }) => { +const GuidePage: React.FC = ({ allTags }) => { return (
@@ -28,8 +25,8 @@ const GuidePage: React.FC = ({ guides, allTags }) => {
diff --git a/src/components/guides/GuidesFeatured.tsx b/src/components/guides/GuidesFeatured.tsx new file mode 100644 index 000000000..8a05c4c51 --- /dev/null +++ b/src/components/guides/GuidesFeatured.tsx @@ -0,0 +1,92 @@ +import React from 'react' +import { Heading } from '../ui/heading' +import Link from 'next/link' +import Image from 'next/image' +import { ArrowUpRightIcon } from '@heroicons/react/24/outline' +import { allGuides, Guide } from '@/content' + +type RequiredFeaturedGuide = Guide & { + featured: NonNullable +} + +const GuidesFeatured: React.FC = ({ take = 3 }: { take?: number }) => { + const featuredGuides = allGuides + .filter((guide): guide is RequiredFeaturedGuide => !!guide.featured) + .sort((a, b) => { + return a.published_at > b.published_at ? -1 : 1 + }) + .slice(0, take) + + return featuredGuides.length === 0 ? null : ( +
+ + Featured + +
+ {featuredGuides.map((guide) => ( +
+ + + + + + + + + + + {guide.featured.image_alt} +
+
+ +

+ + + {guide.title} + +

+

+ {guide.description} +

+
+ ))} +
+
+ ) +} + +export default GuidesFeatured diff --git a/src/components/layout/DocToC.tsx b/src/components/layout/DocToC.tsx index 55828d359..a451fbfd6 100644 --- a/src/components/layout/DocToC.tsx +++ b/src/components/layout/DocToC.tsx @@ -1,6 +1,6 @@ 'use client' -import type { Doc } from '@/content' +import type { Doc, Guide } from '@/content' import { useState, useEffect } from 'react' import Link from 'next/link' import { DocTracingBeam } from './DocTracingBeam' @@ -19,7 +19,7 @@ const DocToC = ({ doc, startSteps, }: { - doc: Doc + doc: Doc | Guide startSteps?: React.JSX.Element | undefined }) => { const initial = 14 diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 59b83b96d..5c8b5b439 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -16,17 +16,26 @@ import { ThemeToggle } from '@/components/ThemeToggle' import { cn } from '@/lib/utils' import GitHubStarCount from '../GitHubStarCount' import { GitHubIcon } from '../icons/GitHubIcon' +import { usePathname } from 'next/navigation' function TopLevelNavItem({ className, + parentHref, ...props -}: ComponentPropsWithoutRef) { +}: ComponentPropsWithoutRef & { parentHref?: string }) { + const pathname = usePathname() + + const isActive = parentHref + ? pathname.startsWith(parentHref) + : pathname === props.href + return (
  • @@ -73,9 +82,7 @@ export const Header = forwardRef< -
    - -
    +
    @@ -83,15 +90,18 @@ export const Header = forwardRef<
    -