diff --git a/src/components/Hero/Hero.tsx b/src/components/Hero/Hero.tsx index b850152..fee5b29 100644 --- a/src/components/Hero/Hero.tsx +++ b/src/components/Hero/Hero.tsx @@ -7,11 +7,9 @@ import { uiConfigs } from '../../configs/ui.configs' import { useNavbarState } from '../../states/navbarState' import { lsdUtils } from '../../utils/lsd.utils' -export type HeroProps = Partial> & { - tags?: string[] -} +export type HeroProps = Partial> & {} -export const Hero: React.FC = ({ tags = [], ...props }) => { +export const Hero: React.FC = ({ ...props }) => { const ref = useRef(null) const scroll = useWindowScroll() const navbarState = useNavbarState() diff --git a/src/components/Section/Section.tsx b/src/components/Section/Section.tsx index d25651c..83b0406 100644 --- a/src/components/Section/Section.tsx +++ b/src/components/Section/Section.tsx @@ -1,73 +1,117 @@ import { Typography } from '@acid-info/lsd-react' import styled from '@emotion/styled' +import clsx from 'clsx' import React from 'react' import { lsdUtils } from '../../utils/lsd.utils' -export type SectionProps = Partial< - React.ComponentProps -> & { +export type SectionProps = Partial> & { title?: React.ReactNode subtitle?: string | React.ReactNode bordered?: boolean + size?: 'small' | 'large' } export const Section = ({ title, subtitle, bordered = true, + size = 'small', + className, children, ...props }: SectionProps) => { return ( - + {(title || subtitle) && ( - - +
+ {title} {subtitle && ( <> {typeof subtitle === 'string' ? ( - subtitle + + subtitle + ) : ( subtitle )} )} - +
)} - {children} -
+
{children}
+ ) } -const SectionContainer = styled.section<{ - bordered?: boolean -}>` +const Root = styled.section` width: 100%; box-sizing: border-box; - border-top: ${(props) => - props.bordered ? '1px solid rgb(var(--lsd-border-primary))' : 'none'}; - ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { - margin-top: var(--lsd-spacing-16); + &.section--small { + &.section--bordered { + border-top: 1px solid rgb(var(--lsd-border-primary)); + } + + ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { + margin-top: var(--lsd-spacing-16); + } + + .section__header { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + + padding: var(--lsd-spacing-24) 0; + + ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { + padding: var(--lsd-spacing-16) 0; + + & > .section__title { + ${lsdUtils.typography('subtitle3')} + } + } + } } -` -const Container = styled.div` - display: flex; - align-items: center; - gap: 8px; - width: 100%; + &.section--large { + & > .section__header { + padding-bottom: var(--lsd-spacing-24); - padding: var(--lsd-spacing-24) 0; + & > .section__title { + ${lsdUtils.typography('h2')} + } + } - ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { - padding: var(--lsd-spacing-16) 0; + &.section--bordered { + & > .section__header { + border-bottom: 1px solid rgb(var(--lsd-border-primary)); + } + } - & > h2 { - ${lsdUtils.typography('subtitle3')} + ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { + & > .section__header { + padding-bottom: var(--lsd-spacing-16); + + & > .section__title { + ${lsdUtils.typography('h3')} + } + } } } ` diff --git a/src/components/TagCard/TagCard.tsx b/src/components/TagCard/TagCard.tsx new file mode 100644 index 0000000..bea1b7b --- /dev/null +++ b/src/components/TagCard/TagCard.tsx @@ -0,0 +1,74 @@ +import { Button, FolderIcon, Typography } from '@acid-info/lsd-react' +import styled from '@emotion/styled' +import Link from 'next/link' +import React from 'react' + +export type TagCardProps = React.ComponentProps & { + name: string + count?: number +} + +export const TagCard: React.FC = ({ name, count, ...props }) => { + return ( + + }> + + + {name} + + {count && ( + + {count} posts + + )} + + + + ) +} + +const Root = styled(Link)` + text-decoration: none; +` + +const CustomButton = styled(Button)` + width: 100%; + + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: flex-end; + padding: var(--lsd-spacing-16) !important; + + .lsd-button { + overflow: hidden; + } + + .lsd-button__text { + flex: 1 1 auto; + } + + &:hover { + .lsd-button__text { + text-decoration: none !important; + } + } +` + +const Info = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + + & > p { + text-align: left; + text-decoration: underline; + height: var(--lsd-label1-lineHeight); + overflow: hidden; + text-overflow: ellipsis; + } + + & > span { + flex: 0 0 auto; + } +` diff --git a/src/components/TagCard/index.ts b/src/components/TagCard/index.ts new file mode 100644 index 0000000..c6ab0a0 --- /dev/null +++ b/src/components/TagCard/index.ts @@ -0,0 +1 @@ +export * from './TagCard' diff --git a/src/containers/HomePage/HomePage.tsx b/src/containers/HomePage/HomePage.tsx index cccd8fe..550947c 100644 --- a/src/containers/HomePage/HomePage.tsx +++ b/src/containers/HomePage/HomePage.tsx @@ -1,13 +1,17 @@ +import { Button, Typography } from '@acid-info/lsd-react' import styled from '@emotion/styled' import React from 'react' +import { Grid, GridItem } from '../../components/Grid/Grid' import { Hero } from '../../components/Hero' import { PostsGrid } from '../../components/PostsGrid' import { Section } from '../../components/Section/Section' +import { TagCard } from '../../components/TagCard' import { uiConfigs } from '../../configs/ui.configs' import { useRecentPosts } from '../../queries/useRecentPosts.query' import { ApiPaginatedPayload } from '../../types/data.types' import { LPE } from '../../types/lpe.types' import { lsdUtils } from '../../utils/lsd.utils' +import { formatTagText } from '../../utils/string.utils' import { PodcastShowsPreview } from '../PodcastShowsPreview' export type HomePageProps = React.DetailedHTMLProps< @@ -15,7 +19,7 @@ export type HomePageProps = React.DetailedHTMLProps< HTMLDivElement > & { data: { - tags: string[] + tags: LPE.Tag.Document[] shows: LPE.Podcast.Show[] latest: ApiPaginatedPayload highlighted: LPE.Post.Document[] @@ -32,74 +36,132 @@ export const HomePage: React.FC = ({ return ( - + - -
+
-
- - {/* {query.hasNextPage && ( -
- -
- )} */} +
+ !post.highlighted) + .slice(0, 8)} + /> +
+ + + +
+ + Tags + +
+ + {tags.map((tag) => ( + + + + ))} + + + + + + {query.hasNextPage && ( +
+ +
+ )} +
) @@ -113,27 +175,16 @@ const Root = styled('div')` .load-more { width: 100%; text-align: center; - - button { - width: 340px; - } - - ${(props) => lsdUtils.breakpoint(props.theme, 'md', 'down')} { - button { - width: 236px; - } - } + margin-top: var(--lsd-spacing-24); ${(props) => lsdUtils.breakpoint(props.theme, 'xs', 'exact')} { + margin-top: var(--lsd-spacing-16); + button { width: 100%; } } } - - .podcasts { - margin-top: 40px; - } ` const HeroContainer = styled.div` @@ -149,4 +200,28 @@ const Container = styled.div` @media (max-width: ${uiConfigs.maxContainerWidth}px) { padding: 0 var(--main-content-padding); } + + display: flex; + flex-direction: column; + gap: var(--lsd-spacing-120) 0; + + ${(props) => lsdUtils.breakpoint(props.theme, 'xs', 'exact')} { + gap: var(--lsd-spacing-80) 0; + } +` + +const BrowseAll = styled(Section)` + & > .section__content { + & > div:first-of-type { + padding: var(--lsd-spacing-24) 0; + } + } +` + +const AllPosts = styled(Section)` + margin-top: var(--lsd-spacing-64); + + ${(props) => lsdUtils.breakpoint(props.theme, 'xs', 'exact')} { + margin-top: var(--lsd-spacing-40); + } ` diff --git a/src/lib/unbody/unbody.generated.ts b/src/lib/unbody/unbody.generated.ts index aeaa391..0b30d98 100644 --- a/src/lib/unbody/unbody.generated.ts +++ b/src/lib/unbody/unbody.generated.ts @@ -9533,6 +9533,14 @@ export type GetAllTopicsQuery = { __typename?: 'AggregateGoogleDocGroupedByObj' value: string } + tags: { + __typename?: 'AggregateGoogleDoctagsObj' + topOccurrences: Array<{ + __typename?: 'AggregateGoogleDoctagsTopOccurrencesObj' + value: string + occurs: number + }> + } }> } } @@ -11252,6 +11260,32 @@ export const GetAllTopicsDocument = { ], }, }, + { + kind: 'Field', + name: { kind: 'Name', value: 'tags' }, + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'topOccurrences' }, + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'value' }, + }, + { + kind: 'Field', + name: { kind: 'Name', value: 'occurs' }, + }, + ], + }, + }, + ], + }, + }, ], }, }, diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 81e02a9..5e2bc63 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -31,7 +31,7 @@ export const getStaticProps: GetStaticProps = async () => { const { data: highlighted } = await unbodyApi.getHighlightedPosts() const { data: latest } = await unbodyApi.getRecentPosts({ skip: 0, - limit: 15, + limit: 12, }) const { data: _shows = [] } = await unbodyApi.getPodcastShows({ diff --git a/src/pages/search.tsx b/src/pages/search.tsx index eaa6714..aae20d7 100644 --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -126,7 +126,7 @@ export async function getStaticProps() { return { props: { - topics, + topics: topics.map((topic) => topic.value), shows, }, revalidate: 10, diff --git a/src/services/unbody/unbody.operators.ts b/src/services/unbody/unbody.operators.ts index ff0e1fc..a86a393 100644 --- a/src/services/unbody/unbody.operators.ts +++ b/src/services/unbody/unbody.operators.ts @@ -111,6 +111,12 @@ export const GET_ALL_TOPICS_QUERY = gql` groupedBy { value } + tags { + topOccurrences { + value + occurs + } + } } } } diff --git a/src/services/unbody/unbody.service.ts b/src/services/unbody/unbody.service.ts index d1f729a..cd22bb2 100644 --- a/src/services/unbody/unbody.service.ts +++ b/src/services/unbody/unbody.service.ts @@ -98,6 +98,7 @@ type Data = { episodes: LPE.Podcast.Document[] draftEpisodes: LPE.Podcast.Document[] highlightedEpisodes: LPE.Podcast.Document[] + publishedPosts: LPE.Post.Document[] staticPages: LPE.StaticPage.Document[] draftStaticPages: LPE.StaticPage.Document[] allRecords: PageRecord[] @@ -119,6 +120,7 @@ export class UnbodyService { allRecords: [], draftArticles: [], draftEpisodes: [], + publishedPosts: [], draftStaticPages: [], highlightedArticles: [], highlightedEpisodes: [], @@ -206,6 +208,7 @@ export class UnbodyService { allRecords: [], draftArticles: [], draftEpisodes: [], + publishedPosts: [], draftStaticPages: [], highlightedArticles: [], highlightedEpisodes: [], @@ -229,6 +232,11 @@ export class UnbodyService { } newData.posts = [...newData.articles, ...newData.episodes].sort(sortPosts) + newData.publishedPosts = [ + ...newData.posts, + ...newData.highlightedArticles, + ...newData.highlightedEpisodes, + ].sort(sortPosts) newData.allRecords = [...articles, ...episodes, ...staticPages] const oldData = { ...this.data } @@ -1171,13 +1179,13 @@ export class UnbodyService { this.handleRequest>( async () => { await this.fetchData() - const { posts } = this.data + const { publishedPosts } = this.data - const data = posts.slice(skip, skip + limit) + const data = publishedPosts.slice(skip, skip + limit) return { data, - hasMore: posts.length > skip + limit, + hasMore: publishedPosts.length > skip + limit, } }, { @@ -1596,7 +1604,13 @@ export class UnbodyService { }, }) - const topics = data.Aggregate.GoogleDoc.map((doc) => doc.groupedBy.value) + const topics = data.Aggregate.GoogleDoc.map((doc) => ({ + value: doc.groupedBy.value, + count: + (doc.tags.topOccurrences || []).find( + (t) => t.value === doc.groupedBy.value, + )?.occurs ?? 1, + })) return topics }, []) @@ -1753,7 +1767,7 @@ unbodyApi.onChange(async (oldData, data, changes, firstLoad) => { }, ]), ) - topics.forEach((topic) => feed.addCategory(formatTagText(topic))) + topics.forEach((topic) => feed.addCategory(formatTagText(topic.value))) feed.addCategory(articleCategory.name) Object.values(showCategories).forEach((cat) => feed.addCategory(cat.name)) diff --git a/src/types/lpe.types.ts b/src/types/lpe.types.ts index 4b77c7c..f54d47a 100644 --- a/src/types/lpe.types.ts +++ b/src/types/lpe.types.ts @@ -24,6 +24,13 @@ export namespace LPE { } } + export namespace Tag { + export type Document = { + value: string + count: number + } + } + export namespace Post { export type Footnote = { index: number