diff --git a/src/components/Article/Article.Block.tsx b/src/components/Article/Article.Block.tsx
index 6d1bd63..a13f0db 100644
--- a/src/components/Article/Article.Block.tsx
+++ b/src/components/Article/Article.Block.tsx
@@ -18,7 +18,7 @@ export const RenderArticleBlock = ({
}: {
block: LPE.Article.ContentBlock
activeId: string | null
- headingElementsRef: HeadingElementsRef
+ headingElementsRef?: HeadingElementsRef
hide?: boolean
}) => {
switch (block.type) {
@@ -70,7 +70,9 @@ export const RenderArticleBlock = ({
variant="body1"
component={block.tagName as any}
genericFontFamily="sans-serif"
- className={extractClassFromFirstTag(block.html) || ''}
+ className={`${extractClassFromFirstTag(
+ block.html,
+ )} ${block.classNames.join(' ')}`}
id={extractIdFromFirstTag(block.html) || `p-${block.order}`}
dangerouslySetInnerHTML={{ __html: extractInnerHtml(block.html) }}
/>
@@ -82,7 +84,9 @@ export const RenderArticleBlock = ({
variant="body1"
component={block.tagName as any}
genericFontFamily="sans-serif"
- className={extractClassFromFirstTag(block.html) || ''}
+ className={`${extractClassFromFirstTag(
+ block.html,
+ )} ${block.classNames.join(' ')}`}
id={extractIdFromFirstTag(block.html) || `p-${block.order}`}
dangerouslySetInnerHTML={{ __html: extractInnerHtml(block.html) }}
/>
diff --git a/src/components/Article/Article.Heading.tsx b/src/components/Article/Article.Heading.tsx
index b3950c8..30e223d 100644
--- a/src/components/Article/Article.Heading.tsx
+++ b/src/components/Article/Article.Heading.tsx
@@ -11,7 +11,7 @@ import { LPE } from '../../types/lpe.types'
type Props = PropsWithChildren<{
block: LPE.Article.TextBlock
- headingElementsRef: HeadingElementsRef
+ headingElementsRef?: HeadingElementsRef
typographyProps?: TypographyProps
}>
export const ArticleHeading = ({
@@ -24,7 +24,9 @@ export const ArticleHeading = ({
extractIdFromFirstTag(block.html) || `${block.tagName}-${block.order}`
const refProp = {
ref: (ref: any) => {
- headingElementsRef.current[id] = ref
+ if (headingElementsRef) {
+ headingElementsRef.current[id] = ref
+ }
},
}
diff --git a/src/components/Article/Header/Article.Header.tsx b/src/components/Article/Header/Article.Header.tsx
index a3a1727..203e7e1 100644
--- a/src/components/Article/Header/Article.Header.tsx
+++ b/src/components/Article/Header/Article.Header.tsx
@@ -48,7 +48,7 @@ const ArticleHeader = ({
{subtitle}
diff --git a/src/containers/StaticPage/StaticPage.tsx b/src/containers/StaticPage/StaticPage.tsx
new file mode 100644
index 0000000..cffb386
--- /dev/null
+++ b/src/containers/StaticPage/StaticPage.tsx
@@ -0,0 +1,62 @@
+import { Typography } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+import React from 'react'
+import { LPE } from '../../types/lpe.types'
+import { RenderArticleBlock } from '@/components/Article/Article.Block'
+
+export type StaticPageProps = React.DetailedHTMLProps<
+ React.HTMLAttributes,
+ HTMLDivElement
+> & {
+ data: {
+ page: LPE.StaticPage.Document
+ }
+}
+
+export const StaticPage: React.FC = ({
+ data,
+ data: { page },
+ ...props
+}) => {
+ const titleBlock = data.page.content.find((block) => {
+ return (
+ block.type === LPE.Post.ContentBlockTypes.Text &&
+ block.classNames &&
+ block.classNames.includes('title')
+ )
+ }) as LPE.Post.TextBlock | undefined
+
+ return (
+
+
+ {titleBlock && (
+
+ {titleBlock.text}
+
+ )}
+ {data.page.content.map((block, idx) => (
+
+ ))}
+
+
+ )
+}
+
+const Root = styled.div`
+ display: flex;
+ justify-content: center;
+ #p-2 {
+ font-size: var(--lsd-body1-fontSize) !important;
+ line-height: var(--lsd-body1-lineHeight) !important;
+ }
+ article {
+ width: 700px;
+ > * {
+ margin-bottom: 1rem;
+ }
+ }
+
+ .title {
+ display: none;
+ }
+`
diff --git a/src/containers/StaticPage/index.ts b/src/containers/StaticPage/index.ts
new file mode 100644
index 0000000..281220a
--- /dev/null
+++ b/src/containers/StaticPage/index.ts
@@ -0,0 +1 @@
+export * from './StaticPage'
diff --git a/src/pages/[slug].tsx b/src/pages/[slug].tsx
new file mode 100644
index 0000000..cf788f2
--- /dev/null
+++ b/src/pages/[slug].tsx
@@ -0,0 +1,87 @@
+import { CustomNextPage, GetStaticPaths, GetStaticProps } from 'next'
+import Error from 'next/error'
+import SEO from '../components/SEO/SEO'
+import { StaticPage, StaticPageProps } from '../containers/StaticPage'
+import unbodyApi from '../services/unbody/unbody.service'
+import NotFoundPage from './404'
+
+type PageProps = Partial> & {
+ error?: string
+ notFound?: boolean
+}
+
+const Page: CustomNextPage = ({
+ data,
+ error,
+ notFound,
+ ...props
+}) => {
+ if (!data) {
+ if (notFound) {
+ return
+ }
+
+ return
+ }
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export const getStaticPaths: GetStaticPaths = async () => {
+ const { data } = await unbodyApi.getStaticPages()
+ return {
+ paths: data.map((page) => ({
+ params: {
+ slug: page.slug,
+ },
+ })),
+ fallback: true,
+ }
+}
+
+export const getStaticProps: GetStaticProps = async (ctx) => {
+ const { data, errors } = await unbodyApi.getStaticPage({
+ slug: ctx.params!.slug as string,
+ })
+
+ if (!data) {
+ if (errors && typeof errors === 'string' && errors.includes('not found')) {
+ return {
+ notFound: true,
+ props: {
+ notFound: true,
+ },
+ }
+ }
+
+ console.error(errors)
+ return {
+ props: {
+ error: 'Something went wrong!',
+ },
+ notFound: false,
+ }
+ }
+
+ return {
+ props: {
+ data: {
+ page: data,
+ },
+ },
+ notFound: false,
+ }
+}
+
+export default Page
diff --git a/src/services/unbody/dataTypes/StaticPageDocument.dataType.ts b/src/services/unbody/dataTypes/StaticPageDocument.dataType.ts
new file mode 100644
index 0000000..f6f483b
--- /dev/null
+++ b/src/services/unbody/dataTypes/StaticPageDocument.dataType.ts
@@ -0,0 +1,42 @@
+import { LPE } from '../../../types/lpe.types'
+import { UnbodyResGoogleDocData } from '../unbody.types'
+import { UnbodyDataTypeConfig } from './types'
+
+export const StaticPageDataType: UnbodyDataTypeConfig<
+ UnbodyResGoogleDocData,
+ LPE.StaticPage.Document
+> = {
+ key: 'StaticPageDocument',
+ objectType: 'GoogleDoc',
+ classes: ['static-page', 'document'],
+
+ isMatch: (helpers, data) => data.pathString.includes('/Static pages/'),
+
+ transform: async (helpers, data) => {
+ const textBlock = helpers.dataTypes.get({
+ objectType: 'TextBlock',
+ })
+ const imageBlock = helpers.dataTypes.get({
+ objectType: 'ImageBlock',
+ })
+
+ const blocks =
+ await helpers.dataTypes.transformMany(
+ [...textBlock, ...imageBlock],
+ [...(data.blocks || [])].sort((a, b) => a.order - b.order),
+ data,
+ )
+
+ return {
+ id: data._additional.id,
+ slug: data.slug,
+ title: data.title,
+ subtitle: data.subtitle || '',
+ summary: data.summary || '',
+ createdAt: data.createdAt || null,
+ modifiedAt: data.modifiedAt || null,
+ content: blocks,
+ type: 'static_page',
+ }
+ },
+}
diff --git a/src/services/unbody/dataTypes/dataTypes.ts b/src/services/unbody/dataTypes/dataTypes.ts
index 9441145..9f1aeee 100644
--- a/src/services/unbody/dataTypes/dataTypes.ts
+++ b/src/services/unbody/dataTypes/dataTypes.ts
@@ -5,6 +5,7 @@ import { ArticleTextBlockDataType } from './ArticleTextBlock.dataType'
import { ImageBlockDataType } from './ImageBlock.dataType'
import { PodcastEpisodeDataType } from './PodcastEpisodeDocument.dataType'
import { PodcastShowDataType } from './PodcastShowDocument.dataType'
+import { StaticPageDataType } from './StaticPageDocument.dataType'
import { TextBlockDataType } from './TextBlock.dataType'
import { UnbodyDataTypes } from './UnbodyDataTypes'
@@ -17,4 +18,5 @@ export const unbodyDataTypes = new UnbodyDataTypes([
ArticleSearchResultItemDataType,
PodcastShowDataType,
PodcastEpisodeDataType,
+ StaticPageDataType,
])
diff --git a/src/services/unbody/dataTypes/types.ts b/src/services/unbody/dataTypes/types.ts
index e1f84df..bb89f96 100644
--- a/src/services/unbody/dataTypes/types.ts
+++ b/src/services/unbody/dataTypes/types.ts
@@ -49,6 +49,7 @@ export const UnbodyDataTypeKeys = {
ArticleSearchResultItem: 'ArticleSearchResultItem',
PodcastShowDocument: 'PodcastShowDocument',
PodcastEpisodeDocument: 'PodcastEpisodeDocument',
+ StaticPageDocument: 'StaticPageDocument',
} as const
export type UnbodyDataTypeKey =
@@ -61,6 +62,7 @@ export const UnbodyDataTypeClasses = {
Episode: 'episode',
Document: 'document',
Search: 'search',
+ StaticPage: 'static-page',
} as const
export type UnbodyDataTypeClass =
diff --git a/src/services/unbody/unbody.service.ts b/src/services/unbody/unbody.service.ts
index 37a1e58..e80ebfd 100644
--- a/src/services/unbody/unbody.service.ts
+++ b/src/services/unbody/unbody.service.ts
@@ -36,6 +36,10 @@ const podcastEpisodeDocument = unbodyDataTypes.get({
classes: ['podcast', 'document', 'episode'],
objectType: 'GoogleDoc',
})!
+const staticPageDocument = unbodyDataTypes.get({
+ classes: ['static-page', 'document'],
+ objectType: 'GoogleDoc',
+})!
const sortPosts = (a: LPE.Post.Document, b: LPE.Post.Document) =>
(a.type === 'podcast' ? new Date(a.publishedAt) : new Date(a.modifiedAt!)) >
@@ -52,7 +56,8 @@ export class UnbodyService {
initialData: {
posts: LPE.Post.Document[]
- } = { posts: [] }
+ staticPages: LPE.StaticPage.Document[]
+ } = { posts: [], staticPages: [] }
constructor(private apiKey: string, private projectId: string) {
const cache = new InMemoryCache({
@@ -103,6 +108,7 @@ export class UnbodyService {
private _loadInitialData = async () => {
const articles: LPE.Article.Data[] = await this.fetchAllArticles()
const episodes: LPE.Podcast.Document[] = await this.fetchAllEpisodes()
+ const staticPages = await this.fetchAllStaticPages()
const posts: LPE.Post.Document[] = [...articles, ...episodes].sort(
sortPosts,
@@ -110,9 +116,10 @@ export class UnbodyService {
this.initialData = {
posts,
+ staticPages,
}
- this.initialDataPromise.resolve({ posts })
+ this.initialDataPromise.resolve({ posts, staticPages })
}
loadInitialData = async (forced: boolean = false) => {
@@ -123,6 +130,31 @@ export class UnbodyService {
return this.initialDataPromise.promise
}
+ private fetchAllStaticPages = async () => {
+ const result: LPE.StaticPage.Document[] = []
+
+ let skip = 0
+ const limit = 50
+ while (true) {
+ const { data } = await this.findStaticPages({
+ skip,
+ limit,
+ parseContent: false,
+ textBlocks: false,
+ includeDrafts: false,
+ toc: false,
+ })
+
+ result.push(...data)
+
+ if (data.length === 0) break
+
+ skip += 50
+ }
+
+ return result
+ }
+
private fetchAllEpisodes = async () => {
const result: LPE.Podcast.Document[] = []
@@ -227,6 +259,112 @@ export class UnbodyService {
return GoogleDoc?.[0]?.meta?.count || 0
}, 0)
+ findStaticPages = ({
+ skip = 0,
+ limit = 10,
+ slug,
+ toc = false,
+ filter,
+ nearObject,
+ textBlocks = false,
+ includeDrafts = false,
+ }: {
+ slug?: string
+ skip?: number
+ limit?: number
+ toc?: boolean
+ filter?: GetPostsQueryVariables['filter']
+ nearObject?: string
+ textBlocks?: boolean
+ parseContent?: boolean
+ includeDrafts?: boolean
+ }) =>
+ this.handleRequest(async () => {
+ const {
+ data: {
+ Get: { GoogleDoc: docs },
+ },
+ } = await this.client.query({
+ query: GetPostsDocument,
+ variables: {
+ toc,
+ textBlocks,
+ mentions: true,
+ imageBlocks: true,
+ ...(nearObject
+ ? {
+ nearObject: {
+ id: nearObject,
+ },
+ }
+ : {}),
+ ...this.helpers.args.page(skip, limit),
+ filter: {
+ operator: 'And',
+ operands: [
+ this.helpers.args.wherePath([
+ 'Static pages',
+ includeDrafts ? 'published|draft' : 'published',
+ ]),
+ ...(slug
+ ? [
+ {
+ path: ['slug'],
+ operator: 'Equal',
+ valueString: slug,
+ } as GetObjectsGoogleDocWhereInpObj,
+ ]
+ : []),
+ ...(nearObject
+ ? [
+ {
+ path: ['id'],
+ operator: 'NotEqual',
+ valueString: nearObject,
+ } as GetObjectsGoogleDocWhereInpObj,
+ ]
+ : []),
+ ...(filter ? [filter] : []),
+ ],
+ },
+ },
+ })
+
+ return unbodyDataTypes.transformMany(
+ staticPageDocument,
+ docs,
+ undefined,
+ )
+ }, [])
+
+ getStaticPages = () =>
+ this.handleRequest(async () => {
+ const { staticPages } = await this.loadInitialData()
+
+ return staticPages
+ }, [])
+
+ getStaticPage = ({
+ slug,
+ includeDrafts = false,
+ }: {
+ slug: string
+ includeDrafts?: boolean
+ }) =>
+ this.handleRequest(async () => {
+ const { data } = await this.findStaticPages({
+ limit: 1,
+ slug,
+ includeDrafts,
+ textBlocks: true,
+ parseContent: true,
+ })
+
+ if (data.length === 0) throw 'Static page not found!'
+
+ return data[0]
+ }, null)
+
getArticles = ({
skip = 0,
limit = 10,
diff --git a/src/types/lpe.types.ts b/src/types/lpe.types.ts
index 04ec542..3b34fa1 100644
--- a/src/types/lpe.types.ts
+++ b/src/types/lpe.types.ts
@@ -52,7 +52,6 @@ export namespace LPE {
} as const
export type ContentBlockType = DictValues
-
export const ContentBlockLabels = {
Title: 'title',
Subtitle: 'subtitle',
@@ -92,6 +91,32 @@ export namespace LPE {
export type Document = LPE.Article.Data | LPE.Podcast.Document
}
+ export namespace StaticPage {
+ export type TextBlock = Post.TextBlock
+ export type ImageBlock = Post.ImageBlock
+ export type ContentBlock = Post.ContentBlock
+ export type Footnote = Post.Footnote
+ export type Footnotes = Post.Footnotes
+ export const ContentBlockLabels = Post.ContentBlockLabels
+ export type ContentBlockLabel = Post.ContentBlockLabel
+
+ export type Metadata = {
+ id: string
+ slug: string
+ title: string
+ summary: string
+ subtitle: string
+
+ createdAt: string | null
+ modifiedAt: string | null
+ type: 'static_page'
+ }
+
+ export type Document = Metadata & {
+ content: Array
+ }
+ }
+
export namespace Article {
export type Toc = Post.Toc
export type TocItem = Post.TocItem