From ce375e74607cdff00bb3835aafae04f48e30742d Mon Sep 17 00:00:00 2001 From: Emmanuel Itakpe <62019510+Emmanuel-Develops@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:38:33 +0100 Subject: [PATCH] feat: add types and use refine taxonomies structure for sources (#56) * feat: create sources page (#41) * feat: create sources page * add svg assets * update gitignore and tailwind wind config * add reusable functions * create sources data * feat: add transcript details card * fix: update breadcrumb functionality * chore: update sources page to use nested directories * chore: add media responsiveness, fix explore navigation and breadcrumb active state * chore: reduce size of body * scale down size of source data * filter index from getStaticPaths * fix: make changes to generateStaticParams function, add correct type for slugParams in contentLayer * chore: add source link to page header * fix: inconsistent page scroll for large number of items * fix: scaling for transcript details cards * Add custom error page (#48) * feat: add custom error page * create: re-usable error skeleton, add no search result component * fix: scroll, header and add footer (#47) * fix: scroll, header and add footer * fix: switch to fixed height for explore content * fix: switch layout to template * Feat/update contentlayer algo (#51) * build: initialize submodules * add source directory * modify types algorithm, contentTree algorithm * move sources to base route * fix: correct back buttton * fix: text corrections, explore navigation state * chore: add additional sources * fix: capitalize types title * sort transcript page * chore: fix count and sort for types and categories * chore: route types to types page * chore: point submodule to main and delete refine-taxonomies submodule branch (#55) --------- Co-authored-by: Solomon eze <87058633+IgboPharaoh@users.noreply.github.com> --- .gitignore | 2 + .gitmodules | 2 +- contentlayer.config.ts | 229 ++++++++++++------ next.config.mjs | 12 +- public/images/not-found-img.png | Bin 0 -> 52081 bytes public/svgs/date-icon.svg | 7 + public/svgs/link-icon.svg | 4 + public/svgs/tags-icon.svg | 4 + public/svgs/world-icon.svg | 6 + src/app/(explore)/[...slug]/page.tsx | 122 ++++++++++ src/app/(explore)/layout.tsx | 18 -- src/app/(explore)/sources/page.tsx | 19 ++ src/app/(explore)/template.tsx | 23 ++ src/app/(explore)/types/page.tsx | 19 ++ src/app/globals.css | 7 +- src/app/layout.tsx | 2 +- src/app/not-found.tsx | 22 ++ src/components/common/BreadCrumbs.tsx | 58 +++-- src/components/common/ErrorPageSkeleton.tsx | 37 +++ src/components/common/NoSearchResult.tsx | 19 ++ .../common/TranscriptDetailsCard.tsx | 122 ++++++++++ src/components/explore/ContentGrouping.tsx | 2 + src/components/explore/ExploreNavigation.tsx | 57 ++--- .../explore/GroupedTranscriptContent.tsx | 4 +- .../explore/SingleTranscriptContent.tsx | 16 +- .../explore/TranscriptContentPage.tsx | 81 ++----- .../landing-page/TranscriptCard.tsx | 2 +- .../ExploreTranscriptClient.tsx | 16 +- .../ExploreTranscripts.tsx | 2 +- src/components/layout/Header.tsx | 4 +- src/components/layout/Wrapper.tsx | 2 +- src/utils/data.ts | 37 +-- src/utils/index.ts | 128 ++++++++-- tailwind.config.ts | 7 +- 34 files changed, 819 insertions(+), 273 deletions(-) create mode 100644 public/images/not-found-img.png create mode 100644 public/svgs/date-icon.svg create mode 100644 public/svgs/link-icon.svg create mode 100644 public/svgs/tags-icon.svg create mode 100644 public/svgs/world-icon.svg create mode 100644 src/app/(explore)/[...slug]/page.tsx delete mode 100644 src/app/(explore)/layout.tsx create mode 100644 src/app/(explore)/sources/page.tsx create mode 100644 src/app/(explore)/template.tsx create mode 100644 src/app/(explore)/types/page.tsx create mode 100644 src/app/not-found.tsx create mode 100644 src/components/common/ErrorPageSkeleton.tsx create mode 100644 src/components/common/NoSearchResult.tsx create mode 100644 src/components/common/TranscriptDetailsCard.tsx diff --git a/.gitignore b/.gitignore index 68503cd..d55e5e4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ /public/aliases.json /public/topics-data.json /public/speaker-data.json +/public/source-count-data.json +/public/sources-data.json # misc .DS_Store diff --git a/.gitmodules b/.gitmodules index 3c9eb36..dac4e11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,4 @@ [submodule "public/gh-pages"] path = public/gh-pages url = https://github.com/bitcointranscripts/bitcointranscripts.github.io.git - branch = gh-pages \ No newline at end of file + branch = gh-pages diff --git a/contentlayer.config.ts b/contentlayer.config.ts index d9aa52a..1b927a8 100644 --- a/contentlayer.config.ts +++ b/contentlayer.config.ts @@ -1,13 +1,8 @@ -import { createSlug, SpeakerData, TopicsData } from "./src/utils"; -import { - defineDocumentType, - defineNestedType, - makeSource, -} from "contentlayer2/source-files"; -import { writeFileSync } from "fs"; import path from "path"; import * as fs from "fs"; -import { Transcript as ContentTranscriptType } from "./.contentlayer/generated/types"; +import { createSlug, createText, SpeakerData, TopicsData, unsluggify } from "./src/utils"; +import { defineDocumentType, defineNestedType, makeSource } from "contentlayer2/source-files"; +import { Transcript as ContentTranscriptType, Source as ContentSourceType } from "./.contentlayer/generated/types"; const Resources = defineNestedType(() => ({ name: "Resources", @@ -16,7 +11,6 @@ const Resources = defineNestedType(() => ({ url: { type: "string" }, }, })); - export interface CategoryInfo { title: string; slug: string; @@ -32,6 +26,10 @@ interface TagInfo { count: number; } +interface ContentTree { + [key: string]: ContentTree | ContentTranscriptType[]; +} + /** * Count the occurrences of all tags across transcripts and write to json file */ @@ -65,7 +63,7 @@ const getTranscriptAliases = (allTranscripts: ContentTranscriptType[]) => { } } - writeFileSync("./public/aliases.json", JSON.stringify(aliases)); + fs.writeFileSync("./public/aliases.json", JSON.stringify(aliases)); }; const getCategories = () => { @@ -96,11 +94,7 @@ function organizeTags(transcripts: ContentTranscriptType[]) { }); // Process all tags at once - const allTags = new Set( - transcripts.flatMap( - (transcript) => transcript.tags?.map((tag) => tag) || [] - ) - ); + const allTags = new Set(transcripts.flatMap((transcript) => transcript.tags?.map((tag) => tag) || [])); allTags.forEach((tag) => { const catInfo = categoryMap.get(tag); @@ -122,13 +116,11 @@ function organizeTags(transcripts: ContentTranscriptType[]) { // Add "Miscellaneous" category with remaining uncategorized tags if (tagsWithoutCategory.size > 0) { - tagsByCategory["Miscellaneous"] = Array.from(tagsWithoutCategory).map( - (tag) => ({ - name: tag, - slug: tag, - count: tagCounts[tag] || 0, - }) - ); + tagsByCategory["Miscellaneous"] = Array.from(tagsWithoutCategory).map((tag) => ({ + name: tag, + slug: tag, + count: tagCounts[tag] || 0, + })); } // Sort tags alphabetically within each category @@ -136,7 +128,7 @@ function organizeTags(transcripts: ContentTranscriptType[]) { tagsByCategory[category].sort((a, b) => a.name.localeCompare(b.name)); }); - writeFileSync("./public/tag-data.json", JSON.stringify(tagsByCategory)); + fs.writeFileSync("./public/tag-data.json", JSON.stringify(tagsByCategory)); return { tagsByCategory, tagsWithoutCategory }; } @@ -146,11 +138,11 @@ function organizeTopics(transcripts: ContentTranscriptType[]) { transcripts.forEach((transcript) => { const slugTags = transcript.tags?.map((tag) => ({ - slug:createSlug(tag), - name:tag + slug: createSlug(tag), + name: tag, })); - slugTags?.forEach(({slug, name}) => { + slugTags?.forEach(({ slug, name }) => { if (slugTopics[slug] !== undefined) { const index = slugTopics[slug]; topicsArray[index].count += 1; @@ -166,44 +158,8 @@ function organizeTopics(transcripts: ContentTranscriptType[]) { }); }); - writeFileSync("./public/topics-data.json", JSON.stringify(topicsArray)); + fs.writeFileSync("./public/topics-data.json", JSON.stringify(topicsArray)); } -/** - * Count the occurrences of all types across transcripts and write to json file - */ -const createTypesCount = (allTranscripts: ContentTranscriptType[]) => { - const typesAndCount: Record = {}; - const relevantTypes = [ - "video", - "core-dev-tech", - "podcast", - "conference", - "meeting", - "club", - "meetup", - "hackathon", - "workshop", - "residency", - "developer-tools", - ]; - - allTranscripts.forEach((transcript) => { - if (transcript.categories) { - transcript.categories.forEach((type: string) => { - const formattedType = createSlug(type); - if (relevantTypes.includes(formattedType)) { - if (formattedType in typesAndCount) { - typesAndCount[formattedType] += 1; - } else { - typesAndCount[formattedType] = 1; - } - } - }); - } - }); - - writeFileSync("./public/types-data.json", JSON.stringify(typesAndCount)); -}; function createSpeakers(transcripts: ContentTranscriptType[]) { const slugSpeakers: any = {}; @@ -211,11 +167,11 @@ function createSpeakers(transcripts: ContentTranscriptType[]) { transcripts.forEach((transcript) => { const slugSpeakersArray = transcript.speakers?.map((speaker) => ({ - slug:createSlug(speaker), + slug: createSlug(speaker), name: speaker, })); - slugSpeakersArray?.forEach(({slug, name}) => { + slugSpeakersArray?.forEach(({ slug, name }) => { if (slugSpeakers[slug] !== undefined) { const index = slugSpeakers[slug]; speakerArray[index].count += 1; @@ -231,8 +187,95 @@ function createSpeakers(transcripts: ContentTranscriptType[]) { }); }); + fs.writeFileSync("./public/speaker-data.json", JSON.stringify(speakerArray)); +} + +function generateSourcesCount(transcripts: ContentTranscriptType[], sources: ContentSourceType[]) { + const sourcesArray: TagInfo[] = []; + const slugSources: Record = {}; + + transcripts.forEach((transcript) => { + const slug = transcript._raw.flattenedPath.split("/")[0]; - writeFileSync("./public/speaker-data.json", JSON.stringify(speakerArray)); + if (slugSources[slug] !== undefined) { + sourcesArray[slugSources[slug]].count += 1; + } else { + const sourcesLength = sourcesArray.length; + slugSources[slug] = sourcesLength; + + const getSourceName = (slug: string) => + sources.find((source) => source.language === "en" && source.slugAsParams[0] === slug)?.title ?? unsluggify(slug); + + sourcesArray[sourcesLength] = { + slug, + name: getSourceName(slug), + count: 1, + }; + } + }); + + fs.writeFileSync("./public/source-count-data.json", JSON.stringify(sourcesArray)); + return { sourcesArray, slugSources }; +} + +const createTypesCount = (transcripts: ContentTranscriptType[], sources: ContentSourceType[]) => { + const { sourcesArray, slugSources } = generateSourcesCount(transcripts, sources); + const nestedTypes: any = {}; + + sources.forEach((transcript) => { + if (transcript.types) { + transcript.types.forEach((type) => { + const slugType = type.charAt(0).toUpperCase() + type.slice(1); + const slug = transcript.slugAsParams[0]; + + const sourceIndex = slugSources[slug]; + const getSource = sourcesArray[sourceIndex] ?? null; + + if (!nestedTypes[slugType]) { + nestedTypes[slugType] = []; + } else { + if (nestedTypes[slugType].includes(getSource) || getSource === null) return; + nestedTypes[slugType].push(getSource); + } + }); + } + }); + + fs.writeFileSync("./public/types-data.json", JSON.stringify(nestedTypes)); +}; + +function organizeContent(transcripts: ContentTranscriptType[]) { + const tree: ContentTree = {}; + + transcripts.forEach((transcript) => { + const parts = transcript.slugAsParams; + let current = tree; + + const isNonEnglishDir = /\w+\.[a-z]{2}\b/.test(parts[parts.length - 1]); + if (isNonEnglishDir) return; + + for (let i = 0; i < parts.length - 1; i++) { + if (!current[parts[i]]) { + current[parts[i]] = i === parts.length - 2 ? [] : {}; + } + current = current[parts[i]] as ContentTree; + } + + (current as unknown as any[]).push({ + title: transcript.title, + speakers: transcript.speakers, + date: transcript.date, + tags: transcript.tags, + sourceFilePath: transcript._raw.sourceFilePath, + flattenedPath: transcript._raw.flattenedPath, + summary: transcript.summary, + body: createText(transcript.body), + source: transcript.source, + }); + }); + + // Save the result as JSON + fs.writeFileSync("./public/sources-data.json", JSON.stringify(tree, null, 2)); } export const Transcript = defineDocumentType(() => ({ @@ -261,6 +304,8 @@ export const Transcript = defineDocumentType(() => ({ aditional_resources: { type: "list", of: Resources }, additional_resources: { type: "list", of: Resources }, weight: { type: "number" }, + types: { type: "list", of: { type: "string" } }, + source_file: { type: "string" }, }, computedFields: { url: { @@ -268,15 +313,49 @@ export const Transcript = defineDocumentType(() => ({ resolve: (doc) => `/${doc._raw.flattenedPath}`, }, slugAsParams: { - type: "string", + type: "list", resolve: (doc) => doc._raw.flattenedPath.split("/"), }, }, })); +export const Source = defineDocumentType(() => ({ + name: "Source", + filePathPattern: `**/_index{,.??}.md`, + contentType: "markdown", + fields: { + title: { type: "string", required: true }, + source: { type: "string" }, + transcription_coverage: { type: "string" }, + hosts: { type: "list", of: { type: "string" } }, + weight: { type: "number" }, + website: { type: "string" }, + types: { type: "list", of: { type: "string" } }, + additional_resources: { type: "list", of: Resources }, + }, + computedFields: { + url: { + type: "string", + resolve: (doc) => `/${doc._raw.flattenedPath.split("/").slice(0, -1).join("/")}`, + }, + language: { + type: "string", + resolve: (doc) => { + const index = doc._raw.flattenedPath.split("/").pop(); + const lan = index?.split(".").length === 2 ? index?.split(".")[1] : "en"; + return lan; + }, + }, + slugAsParams: { + type: "list", + resolve: (doc) => doc._raw.flattenedPath.split("/").slice(0, -1), + }, + }, +})); + export default makeSource({ contentDirPath: path.join(process.cwd(), "public", "bitcoin-transcript"), - documentTypes: [Transcript], + documentTypes: [Source, Transcript], contentDirExclude: [ ".github", ".gitignore", @@ -288,11 +367,13 @@ export default makeSource({ "2018-08-17-richard-bondi-bitcoin-cli-regtest.es.md", ], onSuccess: async (importData) => { - const { allDocuments } = await importData(); - organizeTags(allDocuments); - createTypesCount(allDocuments); - organizeTopics(allDocuments); - getTranscriptAliases(allDocuments); - createSpeakers(allDocuments); + const { allTranscripts, allSources } = await importData(); + organizeTags(allTranscripts); + createTypesCount(allTranscripts, allSources); + organizeTopics(allTranscripts); + getTranscriptAliases(allTranscripts); + createSpeakers(allTranscripts); + generateSourcesCount(allTranscripts, allSources); + organizeContent(allTranscripts); }, }); diff --git a/next.config.mjs b/next.config.mjs index 6f15486..f3b54d8 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -5,8 +5,8 @@ const nextConfig = { return { fallback: [ { - source: '/:path*.:ext([^/]+)', // intercept all paths ending with a file extension - destination: '/gh-pages/:path*.:ext', // rewrite to gh-pages/[path_here].ext + source: "/:path*.:ext([^/]+)", // intercept all paths ending with a file extension + destination: "/gh-pages/:path*.:ext", // rewrite to gh-pages/[path_here].ext }, { source: "/transcripts", @@ -20,8 +20,12 @@ const nextConfig = { source: "/:path*", destination: "/gh-pages/:path*/index.html", }, - ] - } + { + source: "/sources/:path((?!.*\\.[^/]+).*)", // Matches /source/[any path without a file extension] + destination: "/[...slug]/:path*", // Replace with your catch-all route + }, + ], + }; }, }; diff --git a/public/images/not-found-img.png b/public/images/not-found-img.png new file mode 100644 index 0000000000000000000000000000000000000000..b7ebbb028f9a022111a23bc0da38129d09f90062 GIT binary patch literal 52081 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 literal 0 HcmV?d00001 diff --git a/public/svgs/date-icon.svg b/public/svgs/date-icon.svg new file mode 100644 index 0000000..ef03eca --- /dev/null +++ b/public/svgs/date-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/svgs/link-icon.svg b/public/svgs/link-icon.svg new file mode 100644 index 0000000..a682520 --- /dev/null +++ b/public/svgs/link-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/svgs/tags-icon.svg b/public/svgs/tags-icon.svg new file mode 100644 index 0000000..a53bb62 --- /dev/null +++ b/public/svgs/tags-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/svgs/world-icon.svg b/public/svgs/world-icon.svg new file mode 100644 index 0000000..5b3a70c --- /dev/null +++ b/public/svgs/world-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/app/(explore)/[...slug]/page.tsx b/src/app/(explore)/[...slug]/page.tsx new file mode 100644 index 0000000..90ed7ef --- /dev/null +++ b/src/app/(explore)/[...slug]/page.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { notFound } from "next/navigation"; +import { ContentTreeArray } from "@/utils/data"; +import LinkIcon from "/public/svgs/link-icon.svg"; +import WorldIcon from "/public/svgs/world-icon.svg"; +import allSources from "@/public/sources-data.json"; +import { ContentTree, filterOutIndexes } from "@/utils"; +import BreadCrumbs from "@/components/common/BreadCrumbs"; +import { ArrowLinkRight } from "@bitcoin-dev-project/bdp-ui/icons"; +import { allSources as allContentSources } from "contentlayer/generated"; +import TranscriptDetailsCard from "@/components/common/TranscriptDetailsCard"; + +// forces 404 for paths not generated from `generateStaticParams` function. +export const dynamicParams = false; + +export function generateStaticParams() { + return allContentSources.map(({ slugAsParams }) => ({ slug: slugAsParams })); +} + +const page = ({ params }: { params: { slug: string[] } }) => { + const slug = params.slug ?? []; + const contentTree = allSources; + + let current: any = contentTree; + + for (const part of slug) { + if (typeof current === "object" && !Array.isArray(current) && part in current) { + current = current[part] as ContentTree | ContentTreeArray[]; + } else { + notFound(); + } + } + + const displayCurrent = filterOutIndexes(current); + + const pageDetails = allContentSources.find((source) => { + return source.slugAsParams.join("/") === slug.join("/") && source.language === "en"; + }); + + const isDirectoryList = Array.isArray(current); + + return ( +
+
+
+ +
+ + +

Back

+ + +

{pageDetails?.title ?? slug[slug.length - 1]}

+ {isDirectoryList && pageDetails?.website ? ( +
+ world icon + + {pageDetails.website ?? ""} + +
+ ) : null} + + {isDirectoryList && pageDetails?.additional_resources ? ( +
+ link icon +
+ {pageDetails.additional_resources.map((resource, index) => ( + + {resource.title} + + ))} +
+
+ ) : null} +
+
+ + {isDirectoryList ? ( +
+ {(displayCurrent as ContentTreeArray[]) + .sort((a, b) => new Date(b.date!).getTime() - new Date(a.date!).getTime() || a.title.localeCompare(b.title)) + .map((item, i) => ( + + ))} +
+ ) : ( +
+
+ {(displayCurrent as string[]).map((key, i) => ( + + {key} + + ))} +
+
+ )} +
+
+
+ ); +}; + +export default page; diff --git a/src/app/(explore)/layout.tsx b/src/app/(explore)/layout.tsx deleted file mode 100644 index 87b0d99..0000000 --- a/src/app/(explore)/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import ExploreNavigation from "@/components/explore/ExploreNavigation"; - -export default function ExploreLayout({ - children, // will be a page or nested layout -}: { - children: React.ReactNode; -}) { - return ( -
-
- -
{children}
- - {/* Include shared UI here e.g. a header or sidebar */} -
-
- ); -} diff --git a/src/app/(explore)/sources/page.tsx b/src/app/(explore)/sources/page.tsx new file mode 100644 index 0000000..cc8ad9d --- /dev/null +++ b/src/app/(explore)/sources/page.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import TranscriptContentPage from "@/components/explore/TranscriptContentPage"; +import allSources from "@/public/source-count-data.json"; + +const SourcesPage = () => { + return ( +
+ +
+ ); +}; + +export default SourcesPage; diff --git a/src/app/(explore)/template.tsx b/src/app/(explore)/template.tsx new file mode 100644 index 0000000..73ce3c6 --- /dev/null +++ b/src/app/(explore)/template.tsx @@ -0,0 +1,23 @@ +import ExploreNavigation from "@/components/explore/ExploreNavigation"; +import FooterComponent from "@/components/layout/FooterComponent"; +import Wrapper from "@/components/layout/Wrapper"; + +export default function ExploreLayout({ + children, // will be a page or nested layout +}: { + children: React.ReactNode; +}) { + return ( + <> +
+ + +
{children}
+ + {/* Include shared UI here e.g. a header or sidebar */} +
+ +
+ + ); +} diff --git a/src/app/(explore)/types/page.tsx b/src/app/(explore)/types/page.tsx new file mode 100644 index 0000000..c61cd31 --- /dev/null +++ b/src/app/(explore)/types/page.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import TranscriptContentPage from "@/components/explore/TranscriptContentPage"; +import allTypesData from "@/public/types-data.json"; + +const CategoriesPage = () => { + return ( +
+ +
+ ); +}; + +export default CategoriesPage; diff --git a/src/app/globals.css b/src/app/globals.css index b497b56..f5fbf76 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,7 +32,7 @@ --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; --basic-mono-font: basic-mono; - --header-height: 100px; + --header-height: 86px; } /* @media (prefers-color-scheme: dark) { @@ -49,6 +49,11 @@ body { color: rgb(var(--foreground-rgb)); } */ +@media (min-width: 768px) { + :root { + --header-height: 100px; + } +} @layer utilities { .text-balance { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f2f3e1c..3b8a60c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -24,7 +24,7 @@ export default function RootLayout({
-
{children}
+ {children} diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 0000000..ccc3220 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import ErrorPageSkeleton from "@/components/common/ErrorPageSkeleton"; + +export default function NotFound() { + return ( + +
+

+ 404 +

+
+

Page not found

+

+ But maybe we found a new Transcript Reviewer? (we mean you!) +
+ (We mean you!) +

+
+
+
+ ); +} diff --git a/src/components/common/BreadCrumbs.tsx b/src/components/common/BreadCrumbs.tsx index 9dd6e45..cd0645c 100644 --- a/src/components/common/BreadCrumbs.tsx +++ b/src/components/common/BreadCrumbs.tsx @@ -1,29 +1,47 @@ +"use client"; + import Link from "next/link"; -import { useParams, usePathname } from "next/navigation"; +import { usePathname } from "next/navigation"; import React from "react"; +import { ExploreNavigationItems } from "@/utils/data"; const BreadCrumbs = () => { - const pathname = usePathname() - const allRoutes= pathname.split("/").map(path=> ({name:path || "home", link:`/${path||""}`})) + const pathname = usePathname(); + + const navListWithoutSources = ExploreNavigationItems.filter((item) => item.href !== "/sources").map((item) => item.href.slice(1)); + + const pathnameArray = pathname.split("/"); + const isNotSourcesPage = navListWithoutSources.includes(pathnameArray[1]); + + const allRoutes = pathnameArray.map((path, idx) => { + const route = pathname + .split("/") + .slice(0, idx + 1) + .join("/"); + return { name: path || "home", link: route || "/" }; + }); + + if (!isNotSourcesPage && pathnameArray[1] !== "sources") { + allRoutes.splice(1, 0, { name: "Sources", link: "/sources" }); + } + + const isActive = allRoutes[allRoutes.length - 1]; return ( -
- { - allRoutes.map((link, i) => ( -
- - {link.name} - - {i !== allRoutes.length - 1 && ( -

/

- )} -
- ))} +
+ {allRoutes.map((link, i) => ( +
+ + {link.name} + + {i !== allRoutes.length - 1 &&

/

} +
+ ))}
); }; diff --git a/src/components/common/ErrorPageSkeleton.tsx b/src/components/common/ErrorPageSkeleton.tsx new file mode 100644 index 0000000..7ef6c1c --- /dev/null +++ b/src/components/common/ErrorPageSkeleton.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { ArrowLinkRight } from "@bitcoin-dev-project/bdp-ui/icons"; + +const ErrorPageSkeleton = ({ children }: { children: React.ReactNode }) => { + return ( +
+
+
{children}
+
+ not found image +
+ +
+ + + Return Home + + + Review Transcripts + + +
+
+
+ ); +}; + +export default ErrorPageSkeleton; diff --git a/src/components/common/NoSearchResult.tsx b/src/components/common/NoSearchResult.tsx new file mode 100644 index 0000000..956fe0e --- /dev/null +++ b/src/components/common/NoSearchResult.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import ErrorPageSkeleton from "./ErrorPageSkeleton"; + +const NoSearchResult = () => { + return ( + +
+

+ No Search Results Found +

+
+

Help us review transcripts to make sure this never happens again!

+
+
+
+ ); +}; + +export default NoSearchResult; diff --git a/src/components/common/TranscriptDetailsCard.tsx b/src/components/common/TranscriptDetailsCard.tsx new file mode 100644 index 0000000..e11d10a --- /dev/null +++ b/src/components/common/TranscriptDetailsCard.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { ContentTreeArray } from "@/utils/data"; +import DateIcon from "/public/svgs/date-icon.svg"; +import TagsIcon from "/public/svgs/tags-icon.svg"; +import { createSlug, formatDate, unsluggify } from "@/utils"; +import { MicIcon } from "@bitcoin-dev-project/bdp-ui/icons"; + +const TranscriptDetailsCard = ({ data, slug }: { data: ContentTreeArray; slug: string[] }) => { + const { speakers, tags, summary, date, title, body, flattenedPath: url } = data; + + const calculateRemaining = (data: string[]) => (data?.length && data.length > 3 ? data.length - 3 : 0); + + return ( +
+
+
+
+
+ {slug + .join(" / ") + .split(" ") + .map((slg, i) => ( +

+ {unsluggify(slg)} +

+ ))} +
+ + {date && ( +
+ date icon +

{formatDate(date!)}

+
+ )} +
+ + + {title} + +
+
+ +
+ {speakers?.length ? ( +
+ <> + + + +
+
+ {speakers.slice(0, 3).map((speaker, idx) => ( + + {speaker} + + ))} + + {calculateRemaining(speakers) === 0 ? null : ( +

+ + {calculateRemaining(speakers)} more +

+ )} +
+
+ +
+ ) : null} + +
+ {tags?.length ? ( + <> + + date icon + +
+
+ {tags.slice(0, 3).map((tag, idx) => ( + + {unsluggify(tag)} + + ))} + + {calculateRemaining(tags) === 0 ? null : ( +

+ + {calculateRemaining(tags)} more +

+ )} +
+
+ + ) : null} +
+
+ + {summary || body ? ( +
+

{summary ? summary : body}

+
+ ) : null} +
+ ); +}; + +export default TranscriptDetailsCard; diff --git a/src/components/explore/ContentGrouping.tsx b/src/components/explore/ContentGrouping.tsx index 3bd50a6..09d0a76 100644 --- a/src/components/explore/ContentGrouping.tsx +++ b/src/components/explore/ContentGrouping.tsx @@ -1,3 +1,5 @@ +"use client" + import { createSlug, GroupedData } from "@/utils"; import Link from "next/link"; import { useEffect, useRef } from "react"; diff --git a/src/components/explore/ExploreNavigation.tsx b/src/components/explore/ExploreNavigation.tsx index 3751e9c..2c19ab3 100644 --- a/src/components/explore/ExploreNavigation.tsx +++ b/src/components/explore/ExploreNavigation.tsx @@ -7,29 +7,22 @@ import { ArrowLinkUpRight } from "@bitcoin-dev-project/bdp-ui/icons"; const ExploreNavigation = () => { return ( -
-
+
+
{ExploreNavigationItems.map((item) => ( - + ))}
-
-

Review Transcripts

+
+

Review Transcripts

- Earn Sats - - + Earn Sats + +
@@ -37,15 +30,25 @@ const ExploreNavigation = () => { ); }; -const ExploreNavigationItem = ({ - href, - title, -}: { - href: string; - title: string; -}) => { +const ExploreNavigationItem = ({ href, title }: { href: string; title: string }) => { const pathname = usePathname(); - const isActive = pathname === href; + let pagePath = pathname.split("/")[1].toLowerCase(); + + const switchState = () => { + let isActive = false; + const navList = ExploreNavigationItems.map((item) => item.title.toLowerCase()).includes(pagePath); + + if (navList) { + isActive = pagePath === title.toLowerCase(); + } else if (!navList) { + pagePath = "sources"; + isActive = pagePath === title.toLowerCase(); + } + + return { isActive }; + }; + + const { isActive } = switchState(); return ( {title} diff --git a/src/components/explore/GroupedTranscriptContent.tsx b/src/components/explore/GroupedTranscriptContent.tsx index c4ea5a2..8afccdc 100644 --- a/src/components/explore/GroupedTranscriptContent.tsx +++ b/src/components/explore/GroupedTranscriptContent.tsx @@ -38,7 +38,7 @@ const GroupedTranscriptContent = ({ id={topicsByAlphabet[0].toLowerCase()} className="flex flex-col gap-7 " > -

{topicsByAlphabet[0]}

+

{topicsByAlphabet[0]}

{topicsByAlphabet[1].map((topics, i) => ( -

{topicsByAlphabet[0]}

+

{topicsByAlphabet[0]}

{topicsByAlphabet[1] && topicsByAlphabet[1].map((data, i) => ( diff --git a/src/components/explore/SingleTranscriptContent.tsx b/src/components/explore/SingleTranscriptContent.tsx index 4a5599f..bdcce6f 100644 --- a/src/components/explore/SingleTranscriptContent.tsx +++ b/src/components/explore/SingleTranscriptContent.tsx @@ -3,16 +3,18 @@ import Link from "next/link"; import React from "react"; type SingleContent = { - linkName: DepreciatedCategories -} & TopicsData -const SingleTranscriptContent = ({ count, slug, name, linkName }: SingleContent ) => { + linkName: DepreciatedCategories; +} & TopicsData; +const SingleTranscriptContent = ({ count, slug, name, linkName }: SingleContent) => { + const url = linkName === "sources" ? `/${slug}` : `/${linkName}/${slug}`; + return ( - {name} - {getDoubleDigits(count)} + {name} + {getDoubleDigits(count)} ); }; diff --git a/src/components/explore/TranscriptContentPage.tsx b/src/components/explore/TranscriptContentPage.tsx index 95909bb..2d05077 100644 --- a/src/components/explore/TranscriptContentPage.tsx +++ b/src/components/explore/TranscriptContentPage.tsx @@ -1,15 +1,10 @@ -"use client" +"use client"; import React, { FC, useState } from "react"; import BreadCrumbs from "../common/BreadCrumbs"; import GroupedTranscriptContent from "./GroupedTranscriptContent"; import AlphabetGrouping from "./AlphabetGrouping"; -import { - createSlug, - DepreciatedCategories, - groupDataByAlphabet, - sortKeysAlphabetically, -} from "@/utils"; +import { createSlug, DepreciatedCategories, groupDataByAlphabet, sortKeysAlphabetically } from "@/utils"; import MobileAlphabetGrouping from "./MobileAlphabetGrouping"; import ContentGrouping from "./ContentGrouping"; @@ -22,52 +17,27 @@ interface ITranscriptContentPage { linkName: DepreciatedCategories; } -const TranscriptContentPage: FC = ({ - header, - data, - description, - mobileDescription, - linkName, - type, -}) => { - const groupedData = - type === "alphabet" - ? groupDataByAlphabet(data) - : sortKeysAlphabetically(data); - const [currentGroup, setCurrentGroup] = useState( type === "alphabet"? "A": createSlug(Object.keys(groupedData)[0])); +const TranscriptContentPage: FC = ({ header, data, description, mobileDescription, linkName, type }) => { + const groupedData = type === "alphabet" ? groupDataByAlphabet(data) : sortKeysAlphabetically(data); + const [currentGroup, setCurrentGroup] = useState(type === "alphabet" ? "A" : createSlug(Object.keys(groupedData)[0])); return ( -
-
-
- {type == "alphabet" && ( - - )} - {type == "words" && ( - - )} +
+
+
+ {type == "alphabet" && } + {type == "words" && }
-
+
-
-

{header}

-

- {description} -

-

- {mobileDescription || description} -

+
+

{header}

+

{description}

+

{mobileDescription || description}

-
+
{groupedData && type === "alphabet" && Object.entries(groupedData).map((arg, i) => ( @@ -94,23 +64,12 @@ const TranscriptContentPage: FC = ({
-
- {type === "alphabet" && ( - - )} - {type == "words" && ( - - )} +
+ {type === "alphabet" && } + {type == "words" && }
); }; -export default TranscriptContentPage; \ No newline at end of file +export default TranscriptContentPage; diff --git a/src/components/landing-page/TranscriptCard.tsx b/src/components/landing-page/TranscriptCard.tsx index 493ba24..be2226d 100644 --- a/src/components/landing-page/TranscriptCard.tsx +++ b/src/components/landing-page/TranscriptCard.tsx @@ -79,7 +79,7 @@ export const ExploreTranscriptCard = ({ linkUrl = `/tags/#${parseUrl}`; break; case "TYPE": - linkUrl = `/categories/${parseUrl}`; + linkUrl = `/types#${parseUrl}`; break; default: break; diff --git a/src/components/landing-page/explore-transcripts/ExploreTranscriptClient.tsx b/src/components/landing-page/explore-transcripts/ExploreTranscriptClient.tsx index 34cb169..1127a8e 100644 --- a/src/components/landing-page/explore-transcripts/ExploreTranscriptClient.tsx +++ b/src/components/landing-page/explore-transcripts/ExploreTranscriptClient.tsx @@ -5,6 +5,7 @@ import Link from "next/link"; import { Carousel } from "@bitcoin-dev-project/bdp-ui"; import { ArrowLinkRight } from "@bitcoin-dev-project/bdp-ui/icons"; import { ExploreTranscriptCard } from "../TranscriptCard"; +import { countItemsAndSort } from "@/utils"; interface TagInfo { name: string; @@ -14,19 +15,12 @@ interface TagInfo { interface ExploreTranscriptClientProps { categories: { [category: string]: TagInfo[] }; - types: { - [key: string]: number; - }; + types: { [category: string]: TagInfo[] }; } const ExploreTranscriptClient = ({ categories, types }: ExploreTranscriptClientProps) => { - const sortedCategories = Object.fromEntries( - Object.entries(categories) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([key, value]) => [key, value.sort((a, b) => a.name.localeCompare(b.name))]) - ); - - const sortedTypes = Object.fromEntries(Object.entries(types).sort(([a], [b]) => a.localeCompare(b))); + const sortedCategories = countItemsAndSort(categories); + const sortedTypes = countItemsAndSort(types); return (
@@ -43,7 +37,7 @@ const ExploreTranscriptClient = ({ categories, types }: ExploreTranscriptClientP {Object.entries(sortedCategories).map(([key, value]) => ( - + ))} diff --git a/src/components/landing-page/explore-transcripts/ExploreTranscripts.tsx b/src/components/landing-page/explore-transcripts/ExploreTranscripts.tsx index de5bffc..440a328 100644 --- a/src/components/landing-page/explore-transcripts/ExploreTranscripts.tsx +++ b/src/components/landing-page/explore-transcripts/ExploreTranscripts.tsx @@ -10,7 +10,7 @@ function getTags() { return JSON.parse(fileContents); } -const getTypes = (): { [key: string]: number } => { +const getTypes = () => { const filePath = path.join(process.cwd(), "public", "types-data.json"); const fileContents = fs.readFileSync(filePath, "utf8"); return JSON.parse(fileContents); diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 9201de6..4aa1a26 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -189,7 +189,9 @@ const Header = () => {
- + + +