parent
d02f0e5d54
commit
f94c11d1ea
17 changed files with 243 additions and 204 deletions
@ -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(); |
||||
}); |
||||
--- |
||||
|
||||
<div class='container'> |
||||
<h2 class='block text-2xl font-bold sm:text-3xl'>{heading}</h2> |
||||
|
||||
<div class='mt-3 sm:my-5'> |
||||
{sortedGuides.map((guide) => <GuideListItem guide={guide} />)} |
||||
</div> |
||||
|
||||
<a |
||||
href='/guides' |
||||
class='hidden rounded-full bg-gradient-to-r from-slate-600 to-black px-3 py-2 text-xs font-medium text-white transition-colors hover:from-blue-600 hover:to-blue-800 sm:inline' |
||||
> |
||||
View All Guides → |
||||
</a> |
||||
|
||||
<div class='mt-3 block sm:hidden'> |
||||
<a |
||||
href='/guides' |
||||
class='font-regular block rounded-md border border-black p-2 text-center text-sm text-black hover:bg-black hover:text-gray-50' |
||||
> |
||||
View All Guides → |
||||
</a> |
||||
</div> |
||||
</div> |
@ -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 ( |
||||
<div className="container"> |
||||
<h2 className="block text-2xl font-bold sm:text-3xl">{heading}</h2> |
||||
|
||||
<div className="mt-3 sm:my-5"> |
||||
{sortedGuides.map((guide) => ( |
||||
<GuideListItem key={guide.id} guide={guide} /> |
||||
))} |
||||
</div> |
||||
|
||||
<a |
||||
href="/guides" |
||||
className="hidden rounded-full bg-gradient-to-r from-slate-600 to-black px-3 py-2 text-xs font-medium text-white transition-colors hover:from-blue-600 hover:to-blue-800 sm:inline" |
||||
> |
||||
View All Guides → |
||||
</a> |
||||
|
||||
<div className="mt-3 block sm:hidden"> |
||||
<a |
||||
href="/guides" |
||||
className="font-regular block rounded-md border border-black p-2 text-center text-sm text-black hover:bg-black hover:text-gray-50" |
||||
> |
||||
View All Guides → |
||||
</a> |
||||
</div> |
||||
</div> |
||||
); |
||||
}
|
@ -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 ( |
||||
<a |
||||
className="text-md group block flex items-center justify-between border-b py-2 text-gray-600 no-underline hover:text-blue-600" |
||||
href={pageUrl} |
||||
> |
||||
<span className="text-sm transition-transform group-hover:translate-x-2 md:text-base"> |
||||
{frontmatter.title} |
||||
|
||||
{frontmatter.isNew && ( |
||||
<span className="ml-2.5 rounded-sm bg-green-300 px-1.5 py-0.5 text-xs font-medium uppercase text-green-900"> |
||||
New |
||||
<span className="hidden sm:inline"> |
||||
· |
||||
{new Date(frontmatter.date || '').toLocaleString('default', { |
||||
month: 'long', |
||||
})} |
||||
</span> |
||||
</span> |
||||
)} |
||||
</span> |
||||
<span className="hidden text-xs capitalize text-gray-500 sm:block"> |
||||
{guideType} |
||||
</span> |
||||
|
||||
<span className="block text-xs text-gray-400 sm:hidden"> »</span> |
||||
</a> |
||||
); |
||||
} |
@ -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; |
||||
--- |
||||
|
||||
<div class='container'> |
||||
<h2 class='text-2xl sm:text-3xl font-bold block'>{heading}</h2> |
||||
|
||||
<div class='mt-3 sm:my-5'> |
||||
{videos.map((video) => <VideoListItem video={video} />)} |
||||
</div> |
||||
|
||||
<a |
||||
href='/videos' |
||||
class='hidden sm:inline transition-colors py-2 px-3 text-xs font-medium rounded-full bg-gradient-to-r from-slate-600 to-black hover:from-blue-600 hover:to-blue-800 text-white' |
||||
> |
||||
View All Videos → |
||||
</a> |
||||
|
||||
<div class='block sm:hidden mt-3'> |
||||
<a |
||||
href='/videos' |
||||
class='text-sm font-regular block p-2 border border-black text-black rounded-md text-center hover:bg-black hover:text-gray-50' |
||||
> |
||||
View All Videos → |
||||
</a> |
||||
</div> |
||||
</div> |
@ -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 ( |
||||
<div className="container"> |
||||
<h2 className="block text-2xl font-bold sm:text-3xl">{heading}</h2> |
||||
|
||||
<div className="mt-3 sm:my-5"> |
||||
{videos.map((video) => ( |
||||
<VideoListItem key={video.id} video={video} /> |
||||
))} |
||||
</div> |
||||
|
||||
<a |
||||
href="/videos" |
||||
className="hidden rounded-full bg-gradient-to-r from-slate-600 to-black px-3 py-2 text-xs font-medium text-white transition-colors hover:from-blue-600 hover:to-blue-800 sm:inline" |
||||
> |
||||
View All Videos → |
||||
</a> |
||||
|
||||
<div className="mt-3 block sm:hidden"> |
||||
<a |
||||
href="/videos" |
||||
className="font-regular block rounded-md border border-black p-2 text-center text-sm text-black hover:bg-black hover:text-gray-50" |
||||
> |
||||
View All Videos → |
||||
</a> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -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 ( |
||||
<a |
||||
className="block no-underline py-2 group text-md items-center text-gray-600 hover:text-blue-600 flex justify-between border-b" |
||||
href={`/videos/${id}`} |
||||
> |
||||
<span className="group-hover:translate-x-2 transition-transform"> |
||||
{frontmatter.title} |
||||
|
||||
{frontmatter.isNew && ( |
||||
<span className="bg-green-300 text-green-900 text-xs font-medium px-1.5 py-0.5 rounded-sm uppercase ml-1.5"> |
||||
New |
||||
<span className="hidden sm:inline"> |
||||
· |
||||
{new Date(frontmatter.date).toLocaleString('default', { |
||||
month: 'long', |
||||
})} |
||||
</span> |
||||
</span> |
||||
)} |
||||
</span> |
||||
<span className="capitalize text-gray-500 text-xs hidden sm:block"> |
||||
{frontmatter.duration} |
||||
</span> |
||||
|
||||
<span className="text-gray-400 text-xs block sm:hidden"> »</span> |
||||
</a> |
||||
); |
||||
}
|
@ -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; |
||||
} |
||||
--- |
||||
|
||||
<a |
||||
class:list={[ |
||||
'text-md group block flex items-center justify-between border-b py-2 text-gray-600 no-underline hover:text-blue-600', |
||||
]} |
||||
href={pageUrl} |
||||
> |
||||
<span |
||||
class='text-sm transition-transform group-hover:translate-x-2 md:text-base' |
||||
> |
||||
{frontmatter.title} |
||||
|
||||
{ |
||||
frontmatter.isNew && ( |
||||
<span class='ml-1.5 rounded-sm bg-green-300 px-1.5 py-0.5 text-xs font-medium uppercase text-green-900'> |
||||
New |
||||
<span class='hidden sm:inline'> |
||||
· |
||||
{new Date(frontmatter.date || '').toLocaleString('default', { |
||||
month: 'long', |
||||
})} |
||||
</span> |
||||
</span> |
||||
) |
||||
} |
||||
</span> |
||||
<span class='hidden text-xs capitalize text-gray-500 sm:block'> |
||||
{guideType} |
||||
</span> |
||||
|
||||
<span class='block text-xs text-gray-400 sm:hidden'> »</span> |
||||
</a> |
@ -1,40 +0,0 @@ |
||||
--- |
||||
import type { VideoFileType } from '../lib/video'; |
||||
|
||||
export interface Props { |
||||
video: VideoFileType; |
||||
} |
||||
|
||||
const { video } = Astro.props; |
||||
const { frontmatter, id } = video; |
||||
--- |
||||
|
||||
<a |
||||
class:list={[ |
||||
'block no-underline py-2 group text-md items-center text-gray-600 hover:text-blue-600 flex justify-between border-b', |
||||
]} |
||||
href={`/videos/${id}`} |
||||
> |
||||
<span class='group-hover:translate-x-2 transition-transform'> |
||||
{frontmatter.title} |
||||
|
||||
{ |
||||
frontmatter.isNew && ( |
||||
<span class='bg-green-300 text-green-900 text-xs font-medium px-1.5 py-0.5 rounded-sm uppercase ml-1.5'> |
||||
New |
||||
<span class='hidden sm:inline'> |
||||
· |
||||
{new Date(frontmatter.date).toLocaleString('default', { |
||||
month: 'long', |
||||
})} |
||||
</span> |
||||
</span> |
||||
) |
||||
} |
||||
</span> |
||||
<span class='capitalize text-gray-500 text-xs hidden sm:block'> |
||||
{frontmatter.duration} |
||||
</span> |
||||
|
||||
<span class='text-gray-400 text-xs block sm:hidden'> »</span> |
||||
</a> |
Loading…
Reference in new issue