feat: astro template for front and fix login(#13)
All checks were successful
Build Docker Image Front / run (push) Successful in 25s
Build Docker Image Back / run (push) Successful in 25s

Reviewed-on: #13
Co-authored-by: Clement <c.boesmier@aptatio.com>
Co-committed-by: Clement <c.boesmier@aptatio.com>
This commit is contained in:
2024-05-21 10:07:56 +02:00
committed by Clement
parent 57a57c63ff
commit 1593fa3493
145 changed files with 14966 additions and 2182 deletions

View File

@ -0,0 +1,14 @@
---
import Item from 'components/blog/GridItem.astro';
import type { Post } from 'types';
export interface Props {
posts: Array<Post>;
}
const { posts } = Astro.props;
---
<div class="grid gap-6 row-gap-5 md:grid-cols-2 lg:grid-cols-4 -mb-6">
{posts.map((post) => <Item post={post} />)}
</div>

View File

@ -0,0 +1,69 @@
---
import { APP_BLOG } from 'astrowind:config';
import type { Post } from 'types';
import Image from 'components/common/Image.astro';
import { findImage } from 'utils/images';
import { getPermalink } from 'utils/permalinks';
export interface Props {
post: Post;
}
const { post } = Astro.props;
const image = await findImage(post.image);
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
---
<article class="mb-6 transition">
<div class="relative md:h-64 bg-gray-400 dark:bg-slate-700 rounded shadow-lg mb-6">
{
image &&
(link ? (
<a href={link}>
<Image
src={image}
class="w-full md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
width={400}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
layout="cover"
loading="lazy"
decoding="async"
/>
</a>
) : (
<Image
src={image}
class="w-full md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
width={400}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
layout="cover"
loading="lazy"
decoding="async"
/>
))
}
</div>
<h3 class="text-xl sm:text-2xl font-bold leading-tight mb-2 font-heading dark:text-slate-300">
{
link ? (
<a class="inline-block hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200" href={link}>
{post.title}
</a>
) : (
post.title
)
}
</h3>
<p class="text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>
</article>

View File

@ -0,0 +1,12 @@
---
const { title = await Astro.slots.render('default'), subtitle = await Astro.slots.render('subtitle') } = Astro.props;
---
<header class="mb-8 md:mb-16 text-center max-w-3xl mx-auto">
<h1 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading" set:html={title} />
{
subtitle && (
<div class="mt-2 md:mt-3 mx-auto text-xl text-gray-500 dark:text-slate-400 font-medium" set:html={subtitle} />
)
}
</header>

View File

@ -0,0 +1,20 @@
---
import Item from 'components/blog/ListItem.astro';
import type { Post } from 'types';
export interface Props {
posts: Array<Post>;
}
const { posts } = Astro.props;
---
<ul>
{
posts.map((post) => (
<li class="mb-12 md:mb-20">
<Item post={post} />
</li>
))
}
</ul>

View File

@ -0,0 +1,118 @@
---
import type { ImageMetadata } from 'astro';
import { Icon } from 'astro-icon/components';
import Image from 'components/common/Image.astro';
import PostTags from 'components/blog/Tags.astro';
import { APP_BLOG } from 'astrowind:config';
import type { Post } from 'types';
import { getPermalink } from 'utils/permalinks';
import { findImage } from 'utils/images';
import { getFormattedDate } from 'utils/utils';
export interface Props {
post: Post;
}
const { post } = Astro.props;
const image = (await findImage(post.image)) as ImageMetadata | undefined;
const link = APP_BLOG?.post?.isEnabled ? getPermalink(post.permalink, 'post') : '';
---
<article class={`max-w-md mx-auto md:max-w-none grid gap-6 md:gap-8 ${image ? 'md:grid-cols-2' : ''}`}>
{
image &&
(link ? (
<a class="relative block group" href={link ?? 'javascript:void(0)'}>
<div class="relative h-0 pb-[56.25%] md:pb-[75%] md:h-72 lg:pb-[56.25%] overflow-hidden bg-gray-400 dark:bg-slate-700 rounded shadow-lg">
{image && (
<Image
src={image}
class="absolute inset-0 object-cover w-full h-full mb-6 rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
width={900}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
loading="lazy"
decoding="async"
/>
)}
</div>
</a>
) : (
<div class="relative h-0 pb-[56.25%] md:pb-[75%] md:h-72 lg:pb-[56.25%] overflow-hidden bg-gray-400 dark:bg-slate-700 rounded shadow-lg">
{image && (
<Image
src={image}
class="absolute inset-0 object-cover w-full h-full mb-6 rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
width={900}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
loading="lazy"
decoding="async"
/>
)}
</div>
))
}
<div class="mt-2">
<header>
<div class="mb-1">
<span class="text-sm">
<Icon name="tabler:clock" class="w-3.5 h-3.5 inline-block -mt-0.5 dark:text-gray-400" />
<time datetime={String(post.publishDate)} class="inline-block">{getFormattedDate(post.publishDate)}</time>
{
post.author && (
<>
{' '}
· <Icon name="tabler:user" class="w-3.5 h-3.5 inline-block -mt-0.5 dark:text-gray-400" />
<span>{post.author.replaceAll('-', ' ')}</span>
</>
)
}
{
post.category && (
<>
{' '}
·{' '}
<a class="hover:underline" href={getPermalink(post.category.slug, 'category')}>
{post.category.title}
</a>
</>
)
}
</span>
</div>
<h2 class="text-xl sm:text-2xl font-bold leading-tight mb-2 font-heading dark:text-slate-300">
{
link ? (
<a
class="inline-block hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200"
href={link}
>
{post.title}
</a>
) : (
post.title
)
}
</h2>
</header>
{post.excerpt && <p class="flex-grow text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>}
{
post.tags && Array.isArray(post.tags) ? (
<footer class="mt-5">
<PostTags tags={post.tags} />
</footer>
) : (
<Fragment />
)
}
</div>
</article>

View File

@ -0,0 +1,36 @@
---
import { Icon } from 'astro-icon/components';
import { getPermalink } from 'utils/permalinks';
import Button from 'components/ui/Button.astro';
export interface Props {
prevUrl?: string;
nextUrl?: string;
prevText?: string;
nextText?: string;
}
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
---
{
(prevUrl || nextUrl) && (
<div class="container flex">
<div class="flex flex-row mx-auto container justify-between">
<Button
variant="tertiary"
class={`md:px-3 px-3 mr-2 ${!prevUrl ? 'invisible' : ''}`}
href={getPermalink(prevUrl)}
>
<Icon name="tabler:chevron-left" class="w-6 h-6" />
<p class="ml-2">{prevText}</p>
</Button>
<Button variant="tertiary" class={`md:px-3 px-3 ${!nextUrl ? 'invisible' : ''}`} href={getPermalink(nextUrl)}>
<span class="mr-2">{nextText}</span>
<Icon name="tabler:chevron-right" class="w-6 h-6" />
</Button>
</div>
</div>
)
}

View File

@ -0,0 +1,28 @@
---
import { APP_BLOG } from 'astrowind:config';
import { getRelatedPosts } from 'utils/blog';
import BlogHighlightedPosts from '../widgets/BlogHighlightedPosts.astro';
import type { Post } from 'types';
import { getBlogPermalink } from 'utils/permalinks';
export interface Props {
post: Post;
}
const { post } = Astro.props;
const relatedPosts = post.tags ? await getRelatedPosts(post, 4) : [];
---
{
APP_BLOG.isRelatedPostsEnabled ? (
<BlogHighlightedPosts
classes={{ container: 'pt-0 lg:pt-0 md:pt-0' }}
title="Related Posts"
linkText="View All Posts"
linkUrl={getBlogPermalink()}
postIds={relatedPosts.map((post) => post.id)}
/>
) : null
}

View File

@ -0,0 +1,99 @@
---
import { Icon } from 'astro-icon/components';
import Image from 'components/common/Image.astro';
import PostTags from 'components/blog/Tags.astro';
import SocialShare from 'components/common/SocialShare.astro';
import { getPermalink } from 'utils/permalinks';
import { getFormattedDate } from 'utils/utils';
import type { Post } from 'types';
export interface Props {
post: Post;
url: string | URL;
}
const { post, url } = Astro.props;
---
<section class="py-8 sm:py-16 lg:py-20 mx-auto">
<article>
<header class={post.image ? '' : ''}>
<div class="flex justify-between flex-col sm:flex-row max-w-3xl mx-auto mt-0 mb-2 px-4 sm:px-6 sm:items-center">
<p>
<Icon name="tabler:clock" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
<time datetime={String(post.publishDate)} class="inline-block">{getFormattedDate(post.publishDate)}</time>
{
post.author && (
<>
{' '}
· <Icon name="tabler:user" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
<span class="inline-block">{post.author}</span>
</>
)
}
{
post.category && (
<>
{' '}
·{' '}
<a class="hover:underline inline-block" href={getPermalink(post.category.slug, 'category')}>
{post.category.title}
</a>
</>
)
}
{
post.readingTime && (
<>
&nbsp;· <span>{post.readingTime}</span> min read
</>
)
}
</p>
</div>
<h1
class="px-4 sm:px-6 max-w-3xl mx-auto text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading"
>
{post.title}
</h1>
<p
class="max-w-3xl mx-auto mt-4 mb-8 px-4 sm:px-6 text-xl md:text-2xl text-muted dark:text-slate-400 text-justify"
>
{post.excerpt}
</p>
{
post.image ? (
<Image
src={post.image}
class="max-w-full lg:max-w-[900px] mx-auto mb-6 sm:rounded-md bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
sizes="(max-width: 900px) 400px, 900px"
alt={post?.excerpt || ''}
width={900}
height={506}
loading="eager"
decoding="async"
/>
) : (
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<div class="border-t dark:border-slate-700" />
</div>
)
}
</header>
<div
class="mx-auto px-6 sm:px-6 max-w-3xl prose prose-md lg:prose-xl dark:prose-invert dark:prose-headings:text-slate-300 prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary dark:prose-a:text-blue-400 prose-img:rounded-md prose-img:shadow-lg mt-8 prose-headings:scroll-mt-[80px] prose-li:my-0"
>
<slot />
</div>
<div class="mx-auto px-6 sm:px-6 max-w-3xl mt-8 flex justify-between flex-col sm:flex-row">
<PostTags tags={post.tags} class="mr-5 rtl:mr-0 rtl:ml-5" />
<SocialShare url={url} text={post.title} class="mt-5 sm:mt-1 align-middle text-gray-500 dark:text-slate-600" />
</div>
</article>
</section>

View File

@ -0,0 +1,45 @@
---
import { getPermalink } from 'utils/permalinks';
import { APP_BLOG } from 'astrowind:config';
import type { Post } from 'types';
export interface Props {
tags: Post['tags'];
class?: string;
title?: string | undefined;
isCategory?: boolean;
}
const { tags, class: className = 'text-sm', title = undefined, isCategory = false } = Astro.props;
---
{
tags && Array.isArray(tags) && (
<>
<>
{title !== undefined && (
<span class="align-super font-normal underline underline-offset-4 decoration-2 dark:text-slate-400">
{title}
</span>
)}
</>
<ul class={className}>
{tags.map((tag) => (
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 rtl:mr-0 rtl:ml-2 mb-2 py-0.5 px-2 lowercase font-medium">
{!APP_BLOG?.tag?.isEnabled ? (
tag.title
) : (
<a
href={getPermalink(tag.slug, isCategory ? 'category' : 'tag')}
class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200"
>
{tag.title}
</a>
)}
</li>
))}
</ul>
</>
)
}

View File

@ -0,0 +1,20 @@
---
import { Icon } from 'astro-icon/components';
import { getBlogPermalink } from 'utils/permalinks';
import { I18N } from 'astrowind:config';
import Button from 'components/ui/Button.astro';
const { textDirection } = I18N;
---
<div class="mx-auto px-6 sm:px-6 max-w-3xl pt-8 md:pt-4 pb-12 md:pb-20">
<Button variant="tertiary" class="px-3 md:px-3" href={getBlogPermalink()}>
{
textDirection === 'rtl' ? (
<Icon name="tabler:chevron-right" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
) : (
<Icon name="tabler:chevron-left" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
)
} Back to Blog
</Button>
</div>