From f94c11d1ead5bda72a29a36aa00ef5bc5441df5e Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Tue, 11 Feb 2025 11:54:07 +0000 Subject: [PATCH] Add guides and videos on dashboard --- src/components/Dashboard/DashboardPage.tsx | 8 +++ .../Dashboard/PersonalDashboard.tsx | 20 ++++++ src/components/FeaturedGuides.astro | 47 -------------- .../FeaturedGuides/FeaturedGuideList.tsx | 51 ++++++++++++++++ .../FeaturedGuides/GuideListItem.tsx | 57 +++++++++++++++++ src/components/FeaturedVideos.astro | 35 ----------- .../FeaturedVideos/FeaturedVideoList.tsx | 39 ++++++++++++ .../FeaturedVideos/VideoListItem.tsx | 38 ++++++++++++ src/components/GuideListItem.astro | 61 ------------------- src/components/HeroSection/HeroItemsGroup.tsx | 2 +- src/components/VideoListItem.astro | 40 ------------ src/lib/question-group.ts | 2 +- src/pages/authors/[authorId].astro | 19 +++--- src/pages/dashboard.astro | 6 ++ src/pages/guides/index.astro | 8 +-- src/pages/index.astro | 12 ++-- src/pages/videos/index.astro | 2 +- 17 files changed, 243 insertions(+), 204 deletions(-) delete mode 100644 src/components/FeaturedGuides.astro create mode 100644 src/components/FeaturedGuides/FeaturedGuideList.tsx create mode 100644 src/components/FeaturedGuides/GuideListItem.tsx delete mode 100644 src/components/FeaturedVideos.astro create mode 100644 src/components/FeaturedVideos/FeaturedVideoList.tsx create mode 100644 src/components/FeaturedVideos/VideoListItem.tsx delete mode 100644 src/components/GuideListItem.astro delete mode 100644 src/components/VideoListItem.astro diff --git a/src/components/Dashboard/DashboardPage.tsx b/src/components/Dashboard/DashboardPage.tsx index 6509bd1ef..4c2cfee20 100644 --- a/src/components/Dashboard/DashboardPage.tsx +++ b/src/components/Dashboard/DashboardPage.tsx @@ -11,6 +11,8 @@ import { DashboardTabButton } from './DashboardTabButton'; import { PersonalDashboard, type BuiltInRoadmap } from './PersonalDashboard'; import { TeamDashboard } from './TeamDashboard'; import type { QuestionGroupType } from '../../lib/question-group'; +import type { GuideFileType } from '../../lib/guide'; +import type { VideoFileType } from '../../lib/video'; type DashboardPageProps = { builtInRoleRoadmaps?: BuiltInRoadmap[]; @@ -18,6 +20,8 @@ type DashboardPageProps = { builtInBestPractices?: BuiltInRoadmap[]; isTeamPage?: boolean; questionGroups?: QuestionGroupType[]; + guides?: GuideFileType[]; + videos?: VideoFileType[]; }; export function DashboardPage(props: DashboardPageProps) { @@ -27,6 +31,8 @@ export function DashboardPage(props: DashboardPageProps) { builtInSkillRoadmaps, isTeamPage = false, questionGroups, + guides, + videos, } = props; const currentUser = getUser(); @@ -128,6 +134,8 @@ export function DashboardPage(props: DashboardPageProps) { builtInSkillRoadmaps={builtInSkillRoadmaps} builtInBestPractices={builtInBestPractices} questionGroups={questionGroups} + guides={guides} + videos={videos} /> )} diff --git a/src/components/Dashboard/PersonalDashboard.tsx b/src/components/Dashboard/PersonalDashboard.tsx index feb736142..bbd2c11bc 100644 --- a/src/components/Dashboard/PersonalDashboard.tsx +++ b/src/components/Dashboard/PersonalDashboard.tsx @@ -26,6 +26,11 @@ import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions'; import type { UserProgress } from '../TeamProgress/TeamProgressPage'; import { projectGroups } from '../../pages/index.astro'; import type { QuestionGroupType } from '../../lib/question-group'; +import { FeaturedGuideList } from '../FeaturedGuides/FeaturedGuideList'; +import { FeaturedVideoList } from '../FeaturedVideos/FeaturedVideoList'; +import type { GuideFileType } from '../../lib/guide'; +import type { VideoFileType } from '../../lib/video'; + type UserDashboardResponse = { name: string; email: string; @@ -56,6 +61,8 @@ type PersonalDashboardProps = { builtInSkillRoadmaps?: BuiltInRoadmap[]; builtInBestPractices?: BuiltInRoadmap[]; questionGroups?: QuestionGroupType[]; + guides?: GuideFileType[]; + videos?: VideoFileType[]; }; type DashboardStatItemProps = { @@ -203,6 +210,8 @@ export function PersonalDashboard(props: PersonalDashboardProps) { builtInBestPractices = [], builtInSkillRoadmaps = [], questionGroups = [], + guides = [], + videos = [], } = props; const toast = useToast(); @@ -529,6 +538,17 @@ export function PersonalDashboard(props: PersonalDashboardProps) { + +
+ questionGroup.frontmatter.authorId) + .slice(0, 7)} + /> + +
); } diff --git a/src/components/FeaturedGuides.astro b/src/components/FeaturedGuides.astro deleted file mode 100644 index 3d78a90b8..000000000 --- a/src/components/FeaturedGuides.astro +++ /dev/null @@ -1,47 +0,0 @@ ---- -import type { GuideFileType } from '../lib/guide'; -import GuideListItem from './GuideListItem.astro'; -import type { QuestionGroupType } from '../lib/question-group'; - -export interface Props { - heading: string; - guides: GuideFileType[]; - questions: QuestionGroupType[]; -} - -const { heading, guides, questions = [] } = Astro.props; - -const sortedGuides: (QuestionGroupType | GuideFileType)[] = [ - ...guides, - ...questions, -].sort((a, b) => { - const aDate = new Date(a.frontmatter.date as string); - const bDate = new Date(b.frontmatter.date as string); - - return bDate.getTime() - aDate.getTime(); -}); ---- - -
-

{heading}

- -
- {sortedGuides.map((guide) => )} -
- - - - -
diff --git a/src/components/FeaturedGuides/FeaturedGuideList.tsx b/src/components/FeaturedGuides/FeaturedGuideList.tsx new file mode 100644 index 000000000..15427f2d9 --- /dev/null +++ b/src/components/FeaturedGuides/FeaturedGuideList.tsx @@ -0,0 +1,51 @@ +import type { GuideFileType } from '../../lib/guide'; +import type { QuestionGroupType } from '../../lib/question-group'; +import { GuideListItem } from './GuideListItem'; + +export interface FeaturedGuidesProps { + heading: string; + guides: GuideFileType[]; + questions: QuestionGroupType[]; +} + +export function FeaturedGuideList(props: FeaturedGuidesProps) { + const { heading, guides, questions = [] } = props; + + const sortedGuides: (QuestionGroupType | GuideFileType)[] = [ + ...guides, + ...questions, + ].sort((a, b) => { + const aDate = new Date(a.frontmatter.date as string); + const bDate = new Date(b.frontmatter.date as string); + + return bDate.getTime() - aDate.getTime(); + }); + + return ( +
+

{heading}

+ +
+ {sortedGuides.map((guide) => ( + + ))} +
+ + + View All Guides → + + +
+ + View All Guides  → + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/FeaturedGuides/GuideListItem.tsx b/src/components/FeaturedGuides/GuideListItem.tsx new file mode 100644 index 000000000..70cafebce --- /dev/null +++ b/src/components/FeaturedGuides/GuideListItem.tsx @@ -0,0 +1,57 @@ +import type { GuideFileType, GuideFrontmatter } from '../../lib/guide'; +import { type QuestionGroupType } from '../../lib/question-group'; + +export interface GuideListItemProps { + guide: GuideFileType | QuestionGroupType; +} + +function isQuestionGroupType( + guide: GuideFileType | QuestionGroupType, +): guide is QuestionGroupType { + return (guide as QuestionGroupType).questions !== undefined; +} + +export function GuideListItem(props: GuideListItemProps) { + const { guide } = props; + const { frontmatter, id } = guide; + + let pageUrl = ''; + let guideType = ''; + + if (isQuestionGroupType(guide)) { + pageUrl = `/questions/${id}`; + guideType = 'Questions'; + } else { + const excludedBySlug = (frontmatter as GuideFrontmatter).excludedBySlug; + pageUrl = excludedBySlug ? excludedBySlug : `/guides/${id}`; + guideType = (frontmatter as GuideFrontmatter).type; + } + + return ( + + + {frontmatter.title} + + {frontmatter.isNew && ( + + New + +  ·  + {new Date(frontmatter.date || '').toLocaleString('default', { + month: 'long', + })} + + + )} + + + {guideType} + + + » + + ); +} diff --git a/src/components/FeaturedVideos.astro b/src/components/FeaturedVideos.astro deleted file mode 100644 index dbd70b792..000000000 --- a/src/components/FeaturedVideos.astro +++ /dev/null @@ -1,35 +0,0 @@ ---- -import type { VideoFileType } from '../lib/video'; -import VideoListItem from './VideoListItem.astro'; - -export interface Props { - heading: string; - videos: VideoFileType[]; -} - -const { heading, videos } = Astro.props; ---- - -
-

{heading}

- -
- {videos.map((video) => )} -
- - - - -
\ No newline at end of file diff --git a/src/components/FeaturedVideos/FeaturedVideoList.tsx b/src/components/FeaturedVideos/FeaturedVideoList.tsx new file mode 100644 index 000000000..4001c8241 --- /dev/null +++ b/src/components/FeaturedVideos/FeaturedVideoList.tsx @@ -0,0 +1,39 @@ +import type { VideoFileType } from '../../lib/video'; +import { VideoListItem } from './VideoListItem'; + +export interface FeaturedVideoListProps { + heading: string; + videos: VideoFileType[]; +} + +export function FeaturedVideoList(props: FeaturedVideoListProps) { + const { heading, videos } = props; + + return ( +
+

{heading}

+ +
+ {videos.map((video) => ( + + ))} +
+ + + View All Videos → + + +
+ + View All Videos  → + +
+
+ ); +} diff --git a/src/components/FeaturedVideos/VideoListItem.tsx b/src/components/FeaturedVideos/VideoListItem.tsx new file mode 100644 index 000000000..31aa4bd67 --- /dev/null +++ b/src/components/FeaturedVideos/VideoListItem.tsx @@ -0,0 +1,38 @@ +import type { VideoFileType } from '../../lib/video'; + +export interface VideoListItemProps { + video: VideoFileType; +} + +export function VideoListItem(props: VideoListItemProps) { + const { video } = props; + const { frontmatter, id } = video; + + return ( + + + {frontmatter.title} + + {frontmatter.isNew && ( + + New + + · + {new Date(frontmatter.date).toLocaleString('default', { + month: 'long', + })} + + + )} + + + {frontmatter.duration} + + + » + + ); +} \ No newline at end of file diff --git a/src/components/GuideListItem.astro b/src/components/GuideListItem.astro deleted file mode 100644 index a862ad354..000000000 --- a/src/components/GuideListItem.astro +++ /dev/null @@ -1,61 +0,0 @@ ---- -import type { GuideFileType, GuideFrontmatter } from '../lib/guide'; -import { type QuestionGroupType } from '../lib/question-group'; - -export interface Props { - guide: GuideFileType | QuestionGroupType; -} - -function isQuestionGroupType( - guide: GuideFileType | QuestionGroupType, -): guide is QuestionGroupType { - return (guide as QuestionGroupType).questions !== undefined; -} - -const { guide } = Astro.props; -const { frontmatter, id } = guide; - -let pageUrl = ''; -let guideType = ''; - -if (isQuestionGroupType(guide)) { - pageUrl = `/questions/${id}`; - guideType = 'Questions'; -} else { - const excludedBySlug = (frontmatter as GuideFrontmatter).excludedBySlug; - pageUrl = excludedBySlug ? excludedBySlug : `/guides/${id}`; - guideType = (frontmatter as GuideFrontmatter).type; -} ---- - - - - {frontmatter.title} - - { - frontmatter.isNew && ( - - New - - - ) - } - - - - » - diff --git a/src/components/HeroSection/HeroItemsGroup.tsx b/src/components/HeroSection/HeroItemsGroup.tsx index a4840dc33..f4e9b46f5 100644 --- a/src/components/HeroSection/HeroItemsGroup.tsx +++ b/src/components/HeroSection/HeroItemsGroup.tsx @@ -46,7 +46,7 @@ export function HeroItemsGroup(props: HeroItemsGroupProps) { 'border-b border-gray-800/50', { 'py-4': !isLoadingOrCollapsedOrEmpty, - 'py-3': isLoadingOrCollapsedOrEmpty, + 'py-4 ': isLoadingOrCollapsedOrEmpty, 'opacity-50 transition-opacity hover:opacity-100': isCollapsed && !isLoading, }, diff --git a/src/components/VideoListItem.astro b/src/components/VideoListItem.astro deleted file mode 100644 index a95bf4905..000000000 --- a/src/components/VideoListItem.astro +++ /dev/null @@ -1,40 +0,0 @@ ---- -import type { VideoFileType } from '../lib/video'; - -export interface Props { - video: VideoFileType; -} - -const { video } = Astro.props; -const { frontmatter, id } = video; ---- - - - - {frontmatter.title} - - { - frontmatter.isNew && ( - - New - - - ) - } - - - - » - diff --git a/src/lib/question-group.ts b/src/lib/question-group.ts index 68950ed4c..57080cb11 100644 --- a/src/lib/question-group.ts +++ b/src/lib/question-group.ts @@ -3,7 +3,7 @@ import slugify from 'slugify'; import { getAllAuthors, type AuthorFileType } from './author.ts'; import { getAllGuides } from './guide.ts'; -interface RawQuestionGroupFrontmatter { +export interface RawQuestionGroupFrontmatter { order: number; briefTitle: string; briefDescription: string; diff --git a/src/pages/authors/[authorId].astro b/src/pages/authors/[authorId].astro index 0ed281319..2bbcd920d 100644 --- a/src/pages/authors/[authorId].astro +++ b/src/pages/authors/[authorId].astro @@ -1,12 +1,12 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; import AstroIcon from '../../components/AstroIcon.astro'; -import { getGuidesByAuthor } from '../../lib/guide'; -import { getVideosByAuthor } from '../../lib/video'; -import GuideListItem from '../../components/GuideListItem.astro'; +import { GuideListItem } from '../../components/FeaturedGuides/GuideListItem'; +import { VideoListItem } from '../../components/FeaturedVideos/VideoListItem'; +import BaseLayout from '../../layouts/BaseLayout.astro'; import { getAuthorById, getAuthorIds } from '../../lib/author'; -import VideoListItem from '../../components/VideoListItem.astro'; +import { getGuidesByAuthor } from '../../lib/guide'; import { getAllQuestionGroups } from '../../lib/question-group'; +import { getVideosByAuthor } from '../../lib/video'; interface Params extends Record {} @@ -136,9 +136,12 @@ const videos = await getVideosByAuthor(authorId); { [...guides, ...questionGuides] .sort((a, b) => { - const aDate = a.frontmatter.date || a.frontmatter.publishedAt; - const bDate = b.frontmatter.date || b.frontmatter.publishedAt; - return new Date(bDate) - new Date(aDate); + const aFrontmatter = a.frontmatter as any; + const bFrontmatter = b.frontmatter as any; + + const aDate = aFrontmatter.date || aFrontmatter.publishedAt; + const bDate = bFrontmatter.date || bFrontmatter.publishedAt; + return new Date(bDate).getTime() - new Date(aDate).getTime(); }) .map((guide) => ) } diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index 53c94ff86..618550470 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -4,11 +4,15 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import { getAllBestPractices } from '../lib/best-practice'; import { getAllQuestionGroups } from '../lib/question-group'; import { getRoadmapsByTag } from '../lib/roadmap'; +import { getAllGuides } from '../lib/guide'; +import { getAllVideos } from '../lib/video'; const roleRoadmaps = await getRoadmapsByTag('role-roadmap'); const skillRoadmaps = await getRoadmapsByTag('skill-roadmap'); const bestPractices = await getAllBestPractices(); const questionGroups = await getAllQuestionGroups(); +const guides = await getAllGuides(); +const videos = await getAllVideos(); const enrichedRoleRoadmaps = roleRoadmaps .filter((roadmapItem) => !roadmapItem.frontmatter.isHidden) @@ -66,6 +70,8 @@ const enrichedBestPractices = bestPractices.map((bestPractice) => { builtInSkillRoadmaps={enrichedSkillRoadmaps} builtInBestPractices={enrichedBestPractices} questionGroups={questionGroups} + guides={guides.slice(0, 7)} + videos={videos.slice(0, 7)} client:load />
diff --git a/src/pages/guides/index.astro b/src/pages/guides/index.astro index df27054ff..ac7eeb02e 100644 --- a/src/pages/guides/index.astro +++ b/src/pages/guides/index.astro @@ -1,5 +1,5 @@ --- -import GuideListItem from '../../components/GuideListItem.astro'; +import { GuideListItem } from '../../components/FeaturedGuides/GuideListItem'; import SimplePageHeader from '../../components/SimplePageHeader.astro'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { getAllGuides } from '../../lib/guide'; @@ -12,8 +12,8 @@ const questionGuides = (await getAllQuestionGroups()).filter( const allGuides = [...guides, ...questionGuides]; const sortedGuides = allGuides.sort((a, b) => { - const aDate = new Date(a.frontmatter.date); - const bDate = new Date(b.frontmatter.date); + const aDate = new Date(a.frontmatter.date as string); + const bDate = new Date(b.frontmatter.date as string); return bDate.getTime() - aDate.getTime(); }); @@ -36,5 +36,5 @@ const sortedGuides = allGuides.sort((a, b) => { -
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index c6ce071a2..bf92f689c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,15 +1,15 @@ --- -import FeaturedVideos from '../components/FeaturedVideos.astro'; -import FeaturedGuides from '../components/FeaturedGuides.astro'; +import ChangelogBanner from '../components/ChangelogBanner.astro'; +import { FeaturedGuideList } from '../components/FeaturedGuides/FeaturedGuideList'; import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro'; +import { FeaturedVideoList } from '../components/FeaturedVideos/FeaturedVideoList'; import HeroSection from '../components/HeroSection/HeroSection.astro'; import BaseLayout from '../layouts/BaseLayout.astro'; import { getAllBestPractices } from '../lib/best-practice'; import { getAllGuides } from '../lib/guide'; +import { getAllQuestionGroups } from '../lib/question-group'; import { getRoadmapsByTag } from '../lib/roadmap'; import { getAllVideos } from '../lib/video'; -import { getAllQuestionGroups } from '../lib/question-group'; -import ChangelogBanner from '../components/ChangelogBanner.astro'; const roleRoadmaps = await getRoadmapsByTag('role-roadmap'); const skillRoadmaps = await getRoadmapsByTag('skill-roadmap'); @@ -105,12 +105,12 @@ const videos = await getAllVideos(); />
- - +
diff --git a/src/pages/videos/index.astro b/src/pages/videos/index.astro index 4226e4444..5bfdd5b44 100644 --- a/src/pages/videos/index.astro +++ b/src/pages/videos/index.astro @@ -1,6 +1,6 @@ --- -import VideoListItem from '../../components/VideoListItem.astro'; import SimplePageHeader from '../../components/SimplePageHeader.astro'; +import { VideoListItem } from '../../components/FeaturedVideos/VideoListItem'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { getAllVideos } from '../../lib/video';