This commit is contained in:
Gary Kwok
2024-06-17 18:05:05 +08:00
commit 56f6d03385
105 changed files with 4350 additions and 0 deletions

20
src/pages/404.astro Normal file
View File

@@ -0,0 +1,20 @@
---
import Base from "@/layouts/Base.astro";
import { markdownify } from "@/lib/utils/textConverter";
import { getEntryBySlug } from "astro:content";
const entry = await getEntryBySlug("pages", "404");
const { Content } = await entry.render();
---
<Base title={entry.data.title}>
<section class="section">
<div class="container">
<div class="flex h-[40vh] items-center justify-center">
<div class="text-center">
<h1 class="mb-4" set:html={markdownify(entry.data.title)} />
<Content />
</div>
</div>
</div>
</section>
</Base>

42
src/pages/[regular].astro Normal file
View File

@@ -0,0 +1,42 @@
---
import Base from "@/layouts/Base.astro";
import Default from "@/layouts/Default.astro";
import PostSingle from "@/layouts/PostSingle.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import type { TPost } from "@/types";
const getPosts = (await getSinglePage("posts")) as TPost[];
const postsSlug = getPosts.map((item) => item.slug);
export async function getStaticPaths() {
const posts = await getSinglePage("posts");
const pages = await getSinglePage("pages");
const allPages = [...pages, ...posts];
const paths = allPages.map((page: any) => ({
params: {
regular: page.slug,
},
props: { page },
}));
return paths;
}
const { page } = Astro.props;
const { title, meta_title, description, image } = page.data;
---
<Base
title={title}
meta_title={meta_title}
description={description}
image={image}
>
{
postsSlug.includes(page.slug) ? (
<PostSingle post={page} />
) : (
<Default data={page} />
)
}
</Base>

66
src/pages/about.astro Normal file
View File

@@ -0,0 +1,66 @@
---
import { Image } from "astro:assets";
import Base from "@/layouts/Base.astro";
import { markdownify } from "@/lib/utils/textConverter";
import { getEntryBySlug } from "astro:content";
const entry = await getEntryBySlug("about", "index");
const { Content } = await entry.render();
const { title, description, meta_title, image, what_i_do } = entry.data;
---
<Base
title={title}
meta_title={meta_title}
description={description}
image={image}
>
<section class="section">
<div class="container">
<div class="row md:gx-4">
<div class="sm:col-5 md:col-4">
{
image && (
<div class="img-cover mb-8">
<Image
src={image}
width={295}
height={395}
alt={title}
class="rounded-lg w-full"
/>
</div>
)
}
</div>
<div class="sm:col-7 md:col-8">
<h1 set:html={markdownify(title)} class="h3 mb-8" />
<div class="content">
<Content />
</div>
<a href="/contact" class="btn btn-primary text-white py-2"
>Get In Touch</a
>
</div>
</div>
</div>
</section>
<section class="section pt-0">
<div class="container">
<h3 class="page-heading mb-20">{what_i_do.title}</h3>
<div class="row justify-center gy-4 text-center">
{
what_i_do.items.map((item: any) => (
<div class="lg:col-4 md:col-6">
<i class="{{.icon}} fa-3x text-primary mb-4" />
<h4 class="text-dark font-weight-700 mb-3">{item.title}</h4>
<p>{item.description}</p>
</div>
))
}
</div>
</div>
</section>
</Base>

View File

@@ -0,0 +1,110 @@
---
import { Image } from "astro:assets";
import AuthorSingle from "@/layouts/AuthorSingle.astro";
import Base from "@/layouts/Base.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import dateFormat from "@/lib/utils/dateFormat";
import { sortByDate } from "@/lib/utils/sortFunctions";
import { humanize, slugify } from "@/lib/utils/textConverter";
import { BiCalendarEdit, BiCategoryAlt } from "react-icons/bi";
export async function getStaticPaths() {
const authors = await getSinglePage("authors");
const paths = authors.map((author: any) => ({
params: {
single: author.slug,
},
props: { author },
}));
return paths;
}
const { author } = Astro.props;
const { title, meta_title, description, image } = author.data;
// Author Posts
const posts = await getSinglePage("posts");
const sortPostsByDate = sortByDate(posts);
const currentPosts = sortPostsByDate.filter((post) => {
return post.data.authors
.map((author: string) => slugify(author))
.includes(slugify(title));
});
---
<Base
title={title}
meta_title={meta_title}
description={description}
image={image}
>
<AuthorSingle author={author} />
{
currentPosts.length > 0 && (
<section class="section pt-0">
<div class="container">
<h2 class="mb-8 text-center h3">Recent Posts</h2>
<div
class={`row gy-4 ${currentPosts.length < 3 ? "justify-center" : ""}`}
>
{currentPosts.map((post: any, i: number) => (
<div class="col-12 sm:col-6 lg:col-4">
{post.data.image && (
<a
href={`/${post.slug}`}
class="rounded-lg block hover:text-primary overflow-hidden group"
>
<Image
class="group-hover:scale-[1.05] transition duration-300 w-full"
src={post.data.image}
alt={post.data.title}
width={445}
height={230}
/>
</a>
)}
<ul class="mt-4 text-text flex flex-wrap items-center text-sm">
<li class="mb-2 mr-4 flex items-center flex-wrap font-medium">
<BiCalendarEdit className="mr-1 h-[16px] w-[16px] text-gray-600" />
<>{dateFormat(post.data.date)}</>
</li>
<li class="mb-2 mr-4 flex items-center flex-wrap">
<BiCategoryAlt className="mr-1 h-[16px] w-[16px] text-gray-600" />
<>
<ul>
{post.data.categories.map(
(category: string, i: number) => (
<li class="inline-block">
<a
href={`/categories/${slugify(category)}`}
class="mr-2 hover:text-primary font-medium"
>
{humanize(category)}
{i !== post.data.categories.length - 1 && ","}
</a>
</li>
),
)}
</ul>
</>
</li>
</ul>
<h3 class="h5">
<a
href={`/${post.slug}`}
class="block hover:text-primary transition duration-300"
>
{post.data.title}
</a>
</h3>
</div>
))}
</div>
</div>
</section>
)
}
</Base>

View File

@@ -0,0 +1,24 @@
---
import config from "@/config/config.json";
import Authors from "@/layouts/Authors.astro";
import Base from "@/layouts/Base.astro";
import Pagination from "@/layouts/components/Pagination.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { sortByDate } from "@/lib/utils/sortFunctions";
import { markdownify } from "@/lib/utils/textConverter";
const authors = await getSinglePage("authors");
const sortedPosts = sortByDate(authors);
const totalPages = Math.ceil(authors.length / config.settings.pagination);
const currentPosts = sortedPosts.slice(0, config.settings.pagination);
---
<Base title={"Authors"}>
<section class="section">
<div class="container text-center">
<h1 set:text={markdownify("Authors")} class="page-heading h2" />
<Authors authors={currentPosts} />
<Pagination section={"authors"} currentPage={1} totalPages={totalPages} />
</div>
</section>
</Base>

View File

@@ -0,0 +1,47 @@
---
import config from "@/config/config.json";
import Authors from "@/layouts/Authors.astro";
import Base from "@/layouts/Base.astro";
import Pagination from "@/layouts/components/Pagination.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { sortByDate } from "@/lib/utils/sortFunctions";
import { markdownify } from "@/lib/utils/textConverter";
export async function getStaticPaths() {
const authors = await getSinglePage("authors");
const totalPages = Math.ceil(authors.length / config.settings.pagination);
const paths = [];
for (let i = 1; i < totalPages; i++) {
paths.push({
params: {
slug: (i + 1).toString(),
},
});
}
return paths;
}
const { slug } = Astro.params;
const authors = await getSinglePage("authors");
const sortedPosts = sortByDate(authors);
const totalPages = Math.ceil(authors.length / config.settings.pagination);
const currentPage = slug && !isNaN(Number(slug)) ? Number(slug) : 1;
const indexOfLastPost = currentPage * config.settings.pagination;
const indexOfFirstPost = indexOfLastPost - config.settings.pagination;
const currentPosts = sortedPosts.slice(indexOfFirstPost, indexOfLastPost);
---
<Base title={"Authors"}>
<section class="section">
<div class="container text-center">
<h1 set:text={markdownify("Authors")} class="page-heading h2" />
<Authors authors={currentPosts} />
<Pagination
section={"authors"}
currentPage={currentPage}
totalPages={totalPages}
/>
</div>
</section>
</Base>

View File

@@ -0,0 +1,35 @@
---
import Base from "@/layouts/Base.astro";
import Posts from "@/layouts/Posts.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { getTaxonomy } from "@/lib/taxonomyParser.astro";
import taxonomyFilter from "@/lib/utils/taxonomyFilter";
import { humanize } from "@/lib/utils/textConverter";
export async function getStaticPaths() {
const categories = await getTaxonomy("posts", "categories");
return categories.map((category) => {
return {
params: { category },
};
});
}
const { category } = Astro.params;
const posts = await getSinglePage("posts");
const filterByCategory = taxonomyFilter(posts, "categories", category);
const title = humanize(category || "");
---
<Base title={title || "Category"}>
<div class="section">
<div class="container">
<p class="text-center mb-4">Showing Posts From</p>
<h1 class="h2 mb-16 text-center text-primary">{title}</h1>
<Posts posts={filterByCategory} fluid={false} />
</div>
</div>
</Base>

View File

@@ -0,0 +1,32 @@
---
import Base from "@/layouts/Base.astro";
import { getTaxonomy } from "@/lib/taxonomyParser.astro";
import { humanize } from "@/lib/utils/textConverter";
const categories = await getTaxonomy("posts", "categories");
import { BiCategoryAlt } from "react-icons/bi";
---
<Base title={"Categories"}>
<section class="section">
<div class="container text-center">
<h1 class="h2 page-heading">Categories</h1>
<ul class="space-x-4">
{
categories.map((category) => (
<li class="inline-block">
<a
href={`/categories/${category}`}
class="rounded-lg bg-theme-light px-4 py-2 text-dark transition hover:bg-primary hover:text-white flex items-center group"
>
<BiCategoryAlt className="mr-1 text-primary group-hover:text-white transition h-6 w-6 scale-75" />
<>{humanize(category || "")}</>
</a>
</li>
))
}
</ul>
</div>
</section>
</Base>

80
src/pages/contact.astro Normal file
View File

@@ -0,0 +1,80 @@
---
import config from "@/config/config.json";
import Base from "@/layouts/Base.astro";
import { markdownify } from "@/lib/utils/textConverter";
import { getEntryBySlug } from "astro:content";
import { FaAddressCard, FaEnvelope, FaPhoneAlt } from "react-icons/fa";
const entry = await getEntryBySlug("pages", "contact");
const { contact_form_action } = config.params;
const { address, email, phone } = config.contactinfo;
const { title, description, meta_title, image } = entry.data;
---
<Base
title={title}
meta_title={meta_title}
description={description}
image={image}
>
<section class="section">
<div class="container">
<h1 set:html={markdownify(title)} class="h2 page-heading" />
<div class="row md:gx-5 gy-5">
<div class="sm:col-5 md:col-4">
<p class="mb-8 text-2xl font-bold text-theme-dark">Contact Info</p>
<ul class="flex flex-col space-y-8">
<li>
<div class="flex text-theme-dark items-center text-xl">
<FaAddressCard className="mr-3 text-primary" />
<p class="font-semibold">Address</p>
</div>
<p set:html={markdownify(address)} class="mt-2 leading-5 pl-8" />
</li>
<li>
<div class="flex text-theme-dark items-center text-xl">
<FaEnvelope className="mr-3 text-primary" />
<p class="font-semibold">Email</p>
</div>
<p set:html={markdownify(email)} class="mt-2 leading-5 pl-8 content" />
</li>
<li>
<div class="flex text-theme-dark items-center text-xl">
<FaPhoneAlt className="mr-3 text-primary" />
<p class="font-semibold">Phone</p>
</div>
<p set:html={markdownify(phone)} class="mt-2 leading-5 pl-8" />
</li>
</ul>
</div>
<div class="sm:col-7 md:col-8">
<form class="contact-form row gy-2 justify-center" method="POST" action={contact_form_action}>
<div class="lg:col-6">
<label class="mb-2 block" for="name">Name <span class="text-red-600">*</span></label>
<input class="form-input w-full" name="name" type="text" required />
</div>
<div class="lg:col-6">
<label class="mb-2 block" for="email">Email <span class="text-red-600">*</span></label>
<input class="form-input w-full" name="email" type="email" required />
</div>
<div class="col-12">
<label class="mb-2 block" for="subject">Subject</label>
<input
class="form-input w-full"
name="subject"
type="text"
/>
</div>
<div class="col-12">
<label class="mb-2 block" for="message">Message <span class="text-red-600">*</span></label>
<textarea class="form-textarea w-full" rows="4"></textarea>
</div>
<div class="col-12">
<button class="btn btn-primary mt-2">Submit Now</button>
</div>
</form>
</div>
</div>
</div>
</section>
</Base>

22
src/pages/index.astro Normal file
View File

@@ -0,0 +1,22 @@
---
import config from "@/config/config.json";
import Base from "@/layouts/Base.astro";
import Pagination from "@/layouts/components/Pagination.astro";
import Posts from "@/layouts/Posts.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { sortByDate } from "@/lib/utils/sortFunctions";
const posts = await getSinglePage("posts");
const sortedPosts = sortByDate(posts);
const totalPages = Math.ceil(posts.length / config.settings.pagination);
const currentPosts = sortedPosts.slice(0, config.settings.pagination);
---
<Base>
<section class="section">
<div class="container">
<Posts posts={currentPosts} className="mb-16" />
<Pagination currentPage={1} totalPages={totalPages} />
</div>
</section>
</Base>

View File

@@ -0,0 +1,41 @@
---
import config from "@/config/config.json";
import Base from "@/layouts/Base.astro";
import Pagination from "@/layouts/components/Pagination.astro";
import Posts from "@/layouts/Posts.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { sortByDate } from "@/lib/utils/sortFunctions";
export async function getStaticPaths() {
const posts = await getSinglePage("posts");
const totalPages = Math.ceil(posts.length / config.settings.pagination);
const paths = [];
for (let i = 1; i < totalPages; i++) {
paths.push({
params: {
slug: (i + 1).toString(),
},
});
}
return paths;
}
const { slug } = Astro.params;
const posts = await getSinglePage("posts");
const sortedPosts = sortByDate(posts);
const totalPages = Math.ceil(posts.length / config.settings.pagination);
const currentPage = slug && !isNaN(Number(slug)) ? Number(slug) : 1;
const indexOfLastPost = currentPage * config.settings.pagination;
const indexOfFirstPost = indexOfLastPost - config.settings.pagination;
const currentPosts = sortedPosts.slice(indexOfFirstPost, indexOfLastPost);
---
<Base>
<section class="section">
<div class="container">
<Posts className="mb-16" posts={currentPosts} />
<Pagination totalPages={totalPages} currentPage={currentPage} />
</div>
</section>
</Base>

23
src/pages/search.astro Normal file
View File

@@ -0,0 +1,23 @@
---
import Base from "@/layouts/Base.astro";
import SearchBar from "@/layouts/SearchBar";
import { getSinglePage } from "@/lib/contentParser.astro";
// Retrieve all articles
const posts = await getSinglePage("posts");
// List of items to search in
const searchList = posts.map((item: any) => ({
slug: item.slug,
data: item.data,
content: item.body,
}));
---
<Base title={`Search`}>
<section class="section">
<div class="container">
<SearchBar client:load searchList={searchList} />
</div>
</section>
</Base>

View File

@@ -0,0 +1,35 @@
---
import Base from "@/layouts/Base.astro";
import Posts from "@/layouts/Posts.astro";
import { getSinglePage } from "@/lib/contentParser.astro";
import { getTaxonomy } from "@/lib/taxonomyParser.astro";
import taxonomyFilter from "@/lib/utils/taxonomyFilter";
import { humanize } from "@/lib/utils/textConverter";
export async function getStaticPaths() {
const tags = await getTaxonomy("posts", "tags");
return tags.map((tag) => {
return {
params: { tag },
};
});
}
const { tag } = Astro.params;
const posts = await getSinglePage("posts");
const filterByTags = taxonomyFilter(posts, "tags", tag);
const title = humanize(tag || "");
---
<Base title={title || "Tag"}>
<div class="section">
<div class="container">
<p class="text-center mb-4">Showing Posts From</p>
<h1 class="h2 mb-16 text-center text-primary">{title}</h1>
<Posts posts={filterByTags} fluid={false} />
</div>
</div>
</Base>

View File

@@ -0,0 +1,32 @@
---
import Base from "@/layouts/Base.astro";
import { getTaxonomy } from "@/lib/taxonomyParser.astro";
import { humanize } from "@/lib/utils/textConverter";
import { FaHashtag } from "react-icons/fa";
const tags = await getTaxonomy("posts", "tags");
---
<Base title={"Tags"}>
<section class="section">
<div class="container text-center">
<h1 class="h2 page-heading">Tags</h1>
<ul class="space-x-4">
{
tags.map((tag) => (
<li class="inline-block">
<a
href={`/tags/${tag}`}
class="rounded-lg bg-theme-light px-4 py-2 text-dark transition hover:bg-primary hover:text-white flex items-center group"
>
<FaHashtag className="mr-1 text-primary group-hover:text-white transition" />
<>{humanize(tag || "")}</>
</a>
</li>
))
}
</ul>
</div>
</section>
</Base>