Merge pull request #1 from tfmurad/v2

V2
This commit is contained in:
Al Murad Uzzaman
2024-07-31 15:53:53 +06:00
committed by GitHub
57 changed files with 396 additions and 192 deletions
+26 -26
View File
@@ -15,47 +15,47 @@
"remove-multilang": "node scripts/removeMultilang.js && yarn format"
},
"dependencies": {
"@astrojs/mdx": "^2.3.0",
"@astrojs/react": "^3.3.0",
"@astrojs/rss": "^4.0.5",
"@astrojs/sitemap": "^3.1.2",
"@astrojs/mdx": "^3.1.3",
"@astrojs/react": "^3.6.1",
"@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6",
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.6.1",
"astro": "^4.12.3",
"astro-auto-import": "^0.4.2",
"astro-font": "^0.0.80",
"astro-font": "^0.0.81",
"date-fns": "^3.6.0",
"disqus-react": "^1.1.5",
"github-slugger": "^2.0.0",
"gray-matter": "^4.0.3",
"marked": "^12.0.1",
"prettier-plugin-astro": "^0.13.0",
"prettier-plugin-tailwindcss": "^0.5.13",
"marked": "^13.0.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.5",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"react-lite-youtube-embed": "^2.4.0",
"remark-collapse": "^0.1.2",
"remark-toc": "^9.0.0",
"swiper": "^11.1.1"
"swiper": "^11.1.8"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.12",
"@tailwindcss/typography": "^0.5.13",
"@types/marked": "^5.0.2",
"@types/node": "20.12.7",
"@types/react": "18.2.78",
"@types/react-dom": "18.2.25",
"@types/node": "22.0.0",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"autoprefixer": "^10.4.19",
"eslint": "^9.0.0",
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"prettier-plugin-astro": "^0.13.0",
"prettier-plugin-tailwindcss": "^0.5.13",
"sass": "^1.75.0",
"sharp": "0.33.1",
"eslint": "^9.8.0",
"postcss": "^8.4.40",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.5",
"sass": "^1.77.8",
"sharp": "0.33.4",
"tailwind-bootstrap-grid": "^5.1.0",
"tailwindcss": "^3.4.3",
"typescript": "5.4.5"
"tailwindcss": "^3.4.7",
"typescript": "5.5.4"
}
}
+5 -2
View File
@@ -26,6 +26,7 @@
## 📌 Key Features
- 👥 Multi-Authors
- 🌐 Multilingual
- 🎯 Similar Posts Suggestion
- 🔍 Search Functionality
- 🌑 Dark Mode
@@ -65,10 +66,10 @@
### 📦 Dependencies
- astro 4.0+
- astro v4.12+
- node v20.10+
- npm v10.2+
- tailwind v3.3+
- tailwind v3.4+
### 👉 Install Dependencies
@@ -109,12 +110,14 @@ docker run -it --rm astroplate ash
```
<!-- reporting issue -->
## 🐞 Reporting Issues
We use GitHub Issues as the official bug tracker for this Template. Please Search [existing issues](https://github.com/zeon-studio/astroplate/issues). Its possible someone has already reported the same problem.
If your problem or idea has not been addressed yet, feel free to [open a new issue](https://github.com/zeon-studio/astroplate/issues).
<!-- licence -->
## 📝 License
Copyright (c) 2023 - Present, Designed & Developed by [Zeon Studio](https://zeon.studio/)
+22 -16
View File
@@ -9,11 +9,11 @@ const CONTENT_DEPTH = 3;
const BLOG_FOLDER = "blog";
// get data from markdown
const getData = (folder, groupDepth) => {
// get paths
const getData = (folder, groupDepth, langIndex = 0) => {
const getPaths = languages
.map((lang) => {
const dir = path.join(CONTENT_ROOT, lang.contentDir, folder);
.map((lang, index) => {
const langFolder = lang.contentDir ? lang.contentDir : lang.languageCode;
const dir = path.join(CONTENT_ROOT, folder, langFolder);
return fs
.readdirSync(dir)
.filter(
@@ -21,29 +21,37 @@ const getData = (folder, groupDepth) => {
!filename.startsWith("-") &&
(filename.endsWith(".md") || filename.endsWith(".mdx")),
)
.map((filename) => {
.flatMap((filename) => {
const filepath = path.join(dir, filename);
const stats = fs.statSync(filepath);
const isFolder = stats.isDirectory();
if (isFolder) {
return getData(filepath, groupDepth);
return getData(filepath, groupDepth, index);
} else {
const file = fs.readFileSync(filepath, "utf-8");
const { data, content } = matter(file);
const pathParts = filepath.split(path.sep);
const slug =
data.slug ||
pathParts
let slug;
if (data.slug) {
const slugParts = data.slug.split("/");
slugParts[0] = BLOG_FOLDER;
slug = slugParts.join("/");
} else {
slug = pathParts
.slice(CONTENT_DEPTH)
.join("/")
.replace(/\.[^/.]+$/, "");
const group = pathParts[groupDepth];
slug = `${BLOG_FOLDER}/${slug.split("/").slice(1).join("/")}`;
}
data.slug = slug;
const group = "blog";
return {
lang: lang.languageCode,
lang: languages[index].languageCode, // Set the correct language code dynamically
group: group,
slug: slug,
slug: data.slug,
frontmatter: data,
content: content,
};
@@ -52,9 +60,7 @@ const getData = (folder, groupDepth) => {
})
.flat();
const publishedPages = getPaths.filter(
(page) => !page.frontmatter?.draft && page,
);
const publishedPages = getPaths.filter((page) => !page.frontmatter?.draft);
return publishedPages;
};
@@ -70,7 +76,7 @@ try {
JSON.stringify(getData(BLOG_FOLDER, 3)),
);
// merger json files for search
// merge json files for search
const posts = require(`../${JSON_FOLDER}/posts.json`);
const search = [...posts];
fs.writeFileSync(`${JSON_FOLDER}/search.json`, JSON.stringify(search));
+85 -55
View File
@@ -1,66 +1,96 @@
const fs = require("fs");
const path = require("path");
const rootDirs = ["src/pages", "src/hooks", "src/layouts", "src/styles"];
(function () {
const rootDirs = ["src/pages", "src/hooks", "src/layouts", "src/styles"];
const configFiles = [
{
filePath: "tailwind.config.js",
patterns: ["darkmode:\\s*{[^}]*},", 'darkMode:\\s*"class",'],
},
{ filePath: "src/config/theme.json", patterns: ["colors.darkmode"] },
];
const deleteAssetList = [
"public/images/logo-darkmode.png",
"src/layouts/components/ThemeSwitcher.astro",
];
rootDirs.forEach(removeDarkModeFromPages);
configFiles.forEach(removeDarkMode);
const configFiles = [
{
filePath: "tailwind.config.js",
patterns: ["darkmode:\\s*{[^}]*},", 'darkMode:\\s*"class",'],
},
{ filePath: "src/config/theme.json", patterns: ["colors.darkmode"] },
];
function removeDarkModeFromFiles(filePath, regexPatterns) {
const fileContent = fs.readFileSync(filePath, "utf8");
let updatedContent = fileContent;
regexPatterns.forEach((pattern) => {
const regex = new RegExp(pattern, "g");
updatedContent = updatedContent.replace(regex, "");
});
fs.writeFileSync(filePath, updatedContent, "utf8");
}
const filePaths = [
{
filePath: "src/layouts/partials/Header.astro",
patterns: [
"<ThemeSwitchers*(?:\\s+[^>]+)?\\s*(?:\\/\\>|>([\\s\\S]*?)<\\/ThemeSwitchers*>)",
],
},
];
function removeDarkModeFromPages(directoryPath) {
const files = fs.readdirSync(directoryPath);
files.forEach((file) => {
const filePath = path.join(directoryPath, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
removeDarkModeFromPages(filePath);
} else if (stats.isFile()) {
removeDarkModeFromFiles(filePath, [
'(?:(?!["])\\S)*dark:(?:(?![,;"])\\S)*',
]);
}
});
}
function removeDarkMode(configFile) {
const { filePath, patterns } = configFile;
if (filePath === "tailwind.config.js") {
filePaths.forEach(({ filePath, patterns }) => {
removeDarkModeFromFiles(filePath, patterns);
} else {
const contentFile = JSON.parse(fs.readFileSync(filePath, "utf8"));
patterns.forEach((pattern) => deleteNestedProperty(contentFile, pattern));
fs.writeFileSync(filePath, JSON.stringify(contentFile));
}
}
});
function deleteNestedProperty(obj, propertyPath) {
const properties = propertyPath.split(".");
let currentObj = obj;
for (let i = 0; i < properties.length - 1; i++) {
const property = properties[i];
if (currentObj.hasOwnProperty(property)) {
currentObj = currentObj[property];
} else {
return; // Property not found, no need to continue
deleteAssetList.forEach(deleteAsset);
function deleteAsset(asset) {
try {
fs.unlinkSync(asset);
console.log(`${path.basename(asset)} deleted successfully!`);
} catch (error) {
console.error(`${asset} not found`);
}
}
delete currentObj[properties[properties.length - 1]];
}
rootDirs.forEach(removeDarkModeFromPages);
configFiles.forEach(removeDarkMode);
function removeDarkModeFromFiles(filePath, regexPatterns) {
const fileContent = fs.readFileSync(filePath, "utf8");
let updatedContent = fileContent;
regexPatterns.forEach((pattern) => {
const regex = new RegExp(pattern, "g");
updatedContent = updatedContent.replace(regex, "");
});
fs.writeFileSync(filePath, updatedContent, "utf8");
}
function removeDarkModeFromPages(directoryPath) {
const files = fs.readdirSync(directoryPath);
files.forEach((file) => {
const filePath = path.join(directoryPath, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
removeDarkModeFromPages(filePath);
} else if (stats.isFile()) {
removeDarkModeFromFiles(filePath, [
'(?:(?!["])\\S)*dark:(?:(?![,;"])\\S)*',
]);
}
});
}
function removeDarkMode(configFile) {
const { filePath, patterns } = configFile;
if (filePath === "tailwind.config.js") {
removeDarkModeFromFiles(filePath, patterns);
} else {
const contentFile = JSON.parse(fs.readFileSync(filePath, "utf8"));
patterns.forEach((pattern) => deleteNestedProperty(contentFile, pattern));
fs.writeFileSync(filePath, JSON.stringify(contentFile));
}
}
function deleteNestedProperty(obj, propertyPath) {
const properties = propertyPath.split(".");
let currentObj = obj;
for (let i = 0; i < properties.length - 1; i++) {
const property = properties[i];
if (currentObj.hasOwnProperty(property)) {
currentObj = currentObj[property];
} else {
return; // Property not found, no need to continue
}
}
delete currentObj[properties[properties.length - 1]];
}
})();
+1 -1
View File
@@ -22,7 +22,7 @@
"blog_folder": "blog",
"default_language": "en",
"disable_languages": [],
"default_language_in_path": false
"default_language_in_subdir": false
},
"params": {
@@ -6,4 +6,4 @@ image: "/images/avatar.png"
draft: false
---
L'entreprise elle-même est une entreprise très prospère. Ils ne connaissent pas les bienfaits du corps, ou sauf qu'ils le recevront à d'autres moments, le tout, les temps de travail, qu'il déteste à partir de ce moment mais. Il fuit les plaisirs perçus pour être supposés n'être rien, tout ou, et la douleur est ce qui est la plus grande option, car cela lui est facile, et avec ce que cela s'ensuit, ils lui procurent de la douleur et ainsi de suite ! Car ledit expédient de la vérité repousse le plaisir et empêche la douleur, ils fournissent comme si
L'entreprise elle-même est une entreprise très prospère. Ils ne connaissent pas les bienfaits du corps, ou sauf qu'ils le recevront à d'autres moments, le tout, les temps de travail, qu'il déteste à partir de ce moment mais. Il fuit les plaisirs perçus pour être supposés n'être rien, tout ou, et la douleur est ce qui est la plus grande option, car cela lui est facile, et avec ce que cela s'ensuit, ils lui procurent de la douleur et ainsi de suite ! Car ledit expédient de la vérité repousse le plaisir et empêche la douleur, ils fournissent comme si
+88 -1
View File
@@ -1,6 +1,6 @@
import { defineCollection, z } from "astro:content";
// Post collection schema
// Blog collection schema
const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
@@ -49,9 +49,96 @@ const pagesCollection = defineCollection({
}),
});
// Contact collection schema
const contactCollection = defineCollection({
schema: z.object({
title: z.string(),
meta_title: z.string().optional(),
description: z.string(),
image: z.string().optional(),
draft: z.boolean().optional(),
}),
});
// About collection schema
const aboutCollection = defineCollection({
schema: z.object({
title: z.string(),
meta_title: z.string().optional(),
description: z.string().optional(),
image: z.string(),
draft: z.boolean().optional(),
}),
});
// Banner schema
const bannerSchema = z.object({
title: z.string(),
content: z.string(),
image: z.string(),
button: z.object({
enable: z.boolean(),
label: z.string(),
link: z.string(),
}),
});
// Features schema
const featureSchema = z.object({
title: z.string(),
image: z.string(),
content: z.string(),
bulletpoints: z.array(z.string()),
button: z.object({
enable: z.boolean(),
label: z.string().optional(),
link: z.string().optional(),
}),
});
// Content schema (for the main content structure with banner and features)
const contentSchema = z.object({
banner: bannerSchema,
features: z.array(featureSchema),
});
// Content collection schema
const contentCollection = defineCollection({
schema: contentSchema,
});
// Testimonial schema
const testimonialSchema = z.object({
name: z.string(),
designation: z.string(),
avatar: z.string(),
content: z.string(),
});
// Testimonials schema
const testimonialsSchema = z.array(testimonialSchema);
// Call to Action schema
const callToActionSchema = z.object({
enable: z.boolean(),
title: z.string(),
image: z.string(),
description: z.string(),
button: z.object({
enable: z.boolean(),
label: z.string(),
link: z.string().url(),
}),
});
// Export collections
export const collections = {
blog: blogCollection,
authors: authorsCollection,
pages: pagesCollection,
contact: contactCollection,
about: aboutCollection,
content: contentCollection,
testimonials: testimonialsSchema,
callToAction: callToActionSchema,
};
@@ -50,4 +50,4 @@ features:
enable: false
label: ""
link: ""
---
---
+9 -6
View File
@@ -9,6 +9,7 @@ import "@/styles/main.scss";
import { AstroFont } from "astro-font";
import { ViewTransitions } from "astro:transitions";
import SearchModal from "./helpers/SearchModal";
import { getLangFromUrl } from "@/lib/utils/languageParser";
// font families
const pf = theme.fonts.font_family.primary;
@@ -40,6 +41,8 @@ export interface Props {
// distructure frontmatters
const { title, meta_title, description, image, noindex, canonical, lang } =
Astro.props;
const language = lang || getLangFromUrl(Astro.url);
---
<!doctype html>
@@ -108,7 +111,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<meta
name="description"
content={plainify(
description ? description : config.metadata.meta_description,
description ? description : config.metadata.meta_description
)}
/>
@@ -121,7 +124,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<meta
property="og:title"
content={plainify(
meta_title ? meta_title : title ? title : config.site.title,
meta_title ? meta_title : title ? title : config.site.title
)}
/>
@@ -129,7 +132,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<meta
property="og:description"
content={plainify(
description ? description : config.metadata.meta_description,
description ? description : config.metadata.meta_description
)}
/>
<meta property="og:type" content="website" />
@@ -142,7 +145,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<meta
name="twitter:title"
content={plainify(
meta_title ? meta_title : title ? title : config.site.title,
meta_title ? meta_title : title ? title : config.site.title
)}
/>
@@ -150,7 +153,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<meta
name="twitter:description"
content={plainify(
description ? description : config.metadata.meta_description,
description ? description : config.metadata.meta_description
)}
/>
@@ -174,7 +177,7 @@ const { title, meta_title, description, image, noindex, canonical, lang } =
<body>
<TwSizeIndicator />
<Header />
<SearchModal client:load />
<SearchModal lang={language} client:load />
<main id="main-content">
<slot />
</main>
+5
View File
@@ -18,6 +18,11 @@ const { title, image, date, author, categories } = data.data;
const lang = getLangFromUrl(Astro.url);
const { read_more } = await getTranslations(lang as keyof ContentEntryMap);
const slugParts = data.slug.split("/");
slugParts[0] = "blog";
const modifiedSlug = slugParts.join("/");
data.slug = modifiedSlug;
---
<div class="bg-body dark:bg-darkmode-body">
+4 -3
View File
@@ -11,19 +11,20 @@ if (supportedLang.includes(paths[0])) {
lang = paths.shift()!;
}
let baseHref = lang ? `/${lang}` : "/";
let parts = [
{
label: "Home",
href: `/${lang}`,
href: baseHref,
"aria-label":
Astro.url.pathname === `/${lang}` || Astro.url.pathname === `/${lang}/`
Astro.url.pathname === baseHref || Astro.url.pathname === `${baseHref}/`
? "page"
: undefined,
},
];
paths.forEach((label: string, i: number) => {
const href = `/${lang}/${paths.slice(0, i + 1).join("/")}`;
const href = `${baseHref}${paths.slice(0, i + 1).join("/")}`;
label !== "page" &&
parts.push({
label: humanize(label.replace(".html", "").replace(/[-_]/g, " ")) || "",
+11 -4
View File
@@ -9,7 +9,7 @@ const LanguageSwitcher = ({
lang: string;
pathname: string;
}) => {
const { default_language } = config.settings;
const { default_language, default_language_in_subdir } = config.settings;
// Function to remove trailing slash if necessary
const removeTrailingSlash = (path: string) => {
@@ -21,22 +21,29 @@ const LanguageSwitcher = ({
// Sort languages by weight and filter out disabled languages
const sortedLanguages = languages
// @ts-ignore
.filter(language => !config.settings.disable_languages.includes(language.languageCode))
.sort((a, b) => a.weight - b.weight);
return (
<div className="mr-5">
<div className={`mr-5 ${sortedLanguages.length > 1 ? "block" : "hidden"}`}>
<select
className="border border-dark text-dark bg-transparent dark:border-darkmode-primary dark:text-white py-1 rounded-sm cursor-pointer focus:ring-0 focus:border-dark dark:focus:border-darkmode-primary"
onChange={(e) => {
const selectedLang = e.target.value;
let newPath;
const baseUrl = window.location.origin;
if (selectedLang === default_language) {
newPath = `${baseUrl}${removeTrailingSlash(pathname.replace(`/${lang}`, ""))}`;
if (default_language_in_subdir) {
newPath = `${baseUrl}/${default_language}${removeTrailingSlash(pathname.replace(`/${lang}`, ""))}`;
} else {
newPath = `${baseUrl}${removeTrailingSlash(pathname.replace(`/${lang}`, ""))}`;
}
} else {
newPath = `/${selectedLang}${removeTrailingSlash(pathname.replace(`/${lang}`, ""))}`;
}
window.location.href = newPath;
}}
value={lang}
@@ -55,4 +62,4 @@ const LanguageSwitcher = ({
);
};
export default LanguageSwitcher;
export default LanguageSwitcher;
+15 -3
View File
@@ -1,8 +1,11 @@
import searchData from ".json/search.json";
import React, { useEffect, useState } from "react";
import SearchResult, { type ISearchItem } from "./SearchResult";
import config from "@/config/config.json";
const { default_language } = config.settings;
const SearchModal = () => {
const SearchModal = ({ lang }: { lang: string | undefined }) => {
lang = lang || default_language;
const [searchString, setSearchString] = useState("");
// handle input change
@@ -39,9 +42,13 @@ const SearchModal = () => {
}
};
// filter language specific search data
const filterSearchData = searchData.filter((item) => item.lang === lang);
// get search result
const startTime = performance.now();
const searchResult = doSearch(searchData);
const searchResult = doSearch(filterSearchData);
const endTime = performance.now();
const totalTime = ((endTime - startTime) / 1000).toFixed(3);
@@ -169,10 +176,15 @@ const SearchModal = () => {
name="search"
value={searchString}
onChange={handleSearch}
autoFocus
autoComplete="off"
/>
</div>
<SearchResult searchResult={searchResult} searchString={searchString} />
<SearchResult
searchResult={searchResult}
searchString={searchString}
lang={lang}
/>
<div className="search-wrapper-footer">
<span className="flex items-center">
<kbd>
+12 -4
View File
@@ -1,7 +1,9 @@
import { slugSelector } from "@/lib/utils/languageParser";
import { plainify, titleify } from "@/lib/utils/textConverter";
import React from "react";
export interface ISearchItem {
lang: string;
group: string;
slug: string;
frontmatter: {
@@ -33,10 +35,13 @@ export interface ISearchGroup {
const SearchResult = ({
searchResult,
searchString,
lang
}: {
searchResult: ISearchItem[];
searchString: string;
lang: string;
}) => {
// generate search result group
const generateSearchGroup = (searchResult: ISearchItem[]) => {
const joinDataByGroup: ISearchGroup[] = searchResult.reduce(
@@ -83,6 +88,7 @@ const SearchResult = ({
);
};
// match underline
const matchUnderline = (text: string, substring: string) => {
const parts = text?.split(new RegExp(`(${substring})`, "gi"));
@@ -148,12 +154,14 @@ const SearchResult = ({
<img
src={item.frontmatter.image}
alt={item.frontmatter.title}
width={100}
height={100}
/>
</div>
)}
<div className="search-result-item-body">
<a
href={`/${item.slug}`}
href={`${slugSelector(item.slug, lang)}`}
className="search-result-item-title search-result-item-link"
>
{matchUnderline(item.frontmatter.title, searchString)}
@@ -188,8 +196,8 @@ const SearchResult = ({
{matchUnderline(category, searchString)}
{item.frontmatter.categories &&
index !==
item.frontmatter.categories.length -
1 && <>, </>}
item.frontmatter.categories.length -
1 && <>, </>}
</span>
),
)}
@@ -211,7 +219,7 @@ const SearchResult = ({
{matchUnderline(tag, searchString)}
{item.frontmatter.tags &&
index !==
item.frontmatter.tags.length - 1 && <>, </>}
item.frontmatter.tags.length - 1 && <>, </>}
</span>
))}
</div>
+8 -6
View File
@@ -4,7 +4,11 @@ import ThemeSwitcher from "@/components/ThemeSwitcher.astro";
import config from "@/config/config.json";
import languages from "@/config/language.json";
import LanguageSwitcher from "@/helpers/LanguageSwitcher";
import { getLangFromUrl, getTranslations, slugSelector } from "@/lib/utils/languageParser";
import {
getLangFromUrl,
getTranslations,
slugSelector,
} from "@/lib/utils/languageParser";
import type { ContentEntryMap } from "astro:content";
import { IoSearch } from "react-icons/io5";
@@ -133,12 +137,10 @@ if (disabledLanguages.includes(lang)) {
}
{/* Theme switcher */}
<ThemeSwitcher className="mr-5" />
{/* Language switcher */}
{
languages.length > 1 && (
<LanguageSwitcher client:load lang={lang} pathname={pathname} />
)
}
<LanguageSwitcher client:load lang={lang} pathname={pathname} />
{/* Navigation button */}
{
navigation_button.enable && (
+1 -1
View File
@@ -67,7 +67,7 @@ const { testimonial } = Astro.props;
</div>
</div>
</div>
),
)
)}
</div>
<div class="testimonial-slider-pagination mt-9 flex items-center justify-center text-center" />
+66 -2
View File
@@ -9,7 +9,7 @@ import {
import config from "@/config/config.json";
import languages from "@/config/language.json";
export const getSinglePage = async <C extends CollectionKey>(
export const getSP = async <C extends CollectionKey>(
collectionName: C,
lang: keyof ContentEntryMap | undefined
): Promise<CollectionEntry<C>[]> => {
@@ -40,7 +40,7 @@ export const getSinglePage = async <C extends CollectionKey>(
return removeDrafts;
};
export const getListPage = async <C extends CollectionKey>(
export const getLP = async <C extends CollectionKey>(
collectionName: C,
lang: keyof ContentEntryMap | undefined
): Promise<CollectionEntry<C>[]> => {
@@ -67,4 +67,68 @@ export const getListPage = async <C extends CollectionKey>(
return pages;
};
export const getSinglePage = async <C extends CollectionKey>(
collectionName: C,
lang: keyof ContentEntryMap | undefined,
subCollectionName?: string
): Promise<CollectionEntry<C>[]> => {
const { default_language } = config.settings;
const selectedLanguageCode = lang || default_language;
const language = languages.find(
(l: any) => l.languageCode === selectedLanguageCode
);
if (!language) {
throw new Error("Language not found");
}
const { contentDir } = language;
const path = subCollectionName
? `${contentDir}/${subCollectionName}`
: contentDir;
const pages: CollectionEntry<C>[] = (await getCollection(
collectionName as any,
({ id }: any) => {
return id.startsWith(path) && !id.endsWith("-index.md");
}
)) as CollectionEntry<C>[];
// @ts-ignore
const removeDrafts = pages.filter((data) => !data.data.draft);
return removeDrafts;
};
export const getListPage = async <C extends CollectionKey>(
collectionName: C,
lang: keyof ContentEntryMap | undefined
): Promise<CollectionEntry<C>[]> => {
const { default_language } = config.settings;
const selectedLanguageCode = lang || default_language;
const language = languages.find(
(l: any) => l.languageCode == selectedLanguageCode
);
if (!language) {
throw new Error("Language not found");
}
const { contentDir } = language;
const pages: CollectionEntry<C>[] = (await getCollection(
collectionName as any,
({ id }: any) => {
return id.startsWith(contentDir);
}
)) as CollectionEntry<C>[];
return pages;
};
---
+8 -8
View File
@@ -16,7 +16,7 @@ export const getTaxonomy = async (collection: string, name: string) => {
actualCollection = language.contentDir;
} else {
const defaultLanguageMatch = languages.find(
(l) => l.languageCode === default_language,
(l) => l.languageCode === default_language
);
if (defaultLanguageMatch) {
actualCollection = defaultLanguageMatch.contentDir;
@@ -24,10 +24,10 @@ export const getTaxonomy = async (collection: string, name: string) => {
}
const singlePages = await getCollection(
actualCollection as keyof ContentEntryMap,
"blog" as keyof ContentEntryMap,
({ id }: any) => {
return id.startsWith("blog") && !id.endsWith("-index.md");
},
return id.startsWith(actualCollection) && !id.endsWith("-index.md");
}
);
const taxonomyPages = singlePages.map((page: any) => page.data[name]);
let taxonomies: string[] = [];
@@ -52,7 +52,7 @@ export const getAllTaxonomy = async (collection: string, name: string) => {
actualCollection = language.contentDir;
} else {
const defaultLanguageMatch = languages.find(
(l) => l.languageCode === default_language,
(l) => l.languageCode === default_language
);
if (defaultLanguageMatch) {
actualCollection = defaultLanguageMatch.contentDir;
@@ -60,10 +60,10 @@ export const getAllTaxonomy = async (collection: string, name: string) => {
}
const singlePages = await getCollection(
actualCollection as keyof ContentEntryMap,
"blog" as keyof ContentEntryMap,
({ id }: any) => {
return id.startsWith("blog") && !id.endsWith("-index.md");
},
return id.startsWith(actualCollection) && !id.endsWith("-index.md");
}
);
const taxonomyPages = singlePages.map((page: any) => page.data[name]);
let taxonomies: string[] = [];
+7 -14
View File
@@ -76,7 +76,7 @@ const filteredSupportedLang = supportedLang.filter(
export { filteredSupportedLang as supportedLang };
export const slugSelector = (url: string, lang: string) => {
const { default_language, default_language_in_path } = config.settings;
const { default_language, default_language_in_subdir } = config.settings;
const { trailing_slash } = config.site;
let constructedUrl;
@@ -90,28 +90,21 @@ export const slugSelector = (url: string, lang: string) => {
});
}
// Add language path if necessary
if (lang === default_language && default_language_in_subdir) {
constructedUrl = `/${lang}${constructedUrl}`;
}
// Adjust for trailing slash
if (trailing_slash) {
if (!constructedUrl.endsWith("/")) {
constructedUrl += "/";
}
} else {
if (constructedUrl.endsWith("/")) {
if (constructedUrl.endsWith("/") && constructedUrl !== "/") {
constructedUrl = constructedUrl.slice(0, -1);
}
}
// Ensure home URL is absolute
if (constructedUrl === "") {
constructedUrl = "/";
}
// Add language path if necessary
if (lang === default_language) {
constructedUrl = default_language_in_path
? `/${lang}${constructedUrl}`
: constructedUrl;
}
return constructedUrl;
};
+1 -1
View File
@@ -10,7 +10,7 @@ export async function getStaticPaths() {
supportedLang.map(async (lang) => {
const pages = await getSinglePage("pages", lang as keyof ContentEntryMap);
return pages.map((page: any) => ({
return pages.map((page) => ({
params: {
lang: lang || undefined,
regular: page.slug.split("/").pop(),
+2 -5
View File
@@ -4,7 +4,7 @@ import Base from "@/layouts/Base.astro";
import { getListPage } from "@/lib/contentParser.astro";
import { supportedLang } from "@/lib/utils/languageParser";
import { markdownify } from "@/lib/utils/textConverter";
import type { ContentCollectionKey, ContentEntryMap } from "astro:content";
import type { ContentEntryMap } from "astro:content";
export function getStaticPaths() {
const paths = supportedLang.map((lang) => ({
@@ -14,10 +14,7 @@ export function getStaticPaths() {
}
const { lang } = Astro.params;
const about: any = await getListPage(
"about" as ContentCollectionKey,
lang as keyof ContentEntryMap
);
const about = await getListPage("about", lang as keyof ContentEntryMap);
const { Content } = await about[0].render();
const { title, description, meta_title, image } = about[0].data;
+3 -3
View File
@@ -14,12 +14,12 @@ export async function getStaticPaths() {
const paths = await Promise.all(
supportedLang.map(async (lang) => {
const authors: any = await getSinglePage(
const authors = await getSinglePage(
COLLECTION_FOLDER,
lang as keyof ContentEntryMap
);
return authors.map((author: any) => ({
return authors.map((author) => ({
params: {
lang: lang || undefined,
single: author.slug.split("/").pop(),
@@ -42,7 +42,7 @@ const { Content } = await author.render();
const BLOG_FOLDER = "blog";
const posts = await getSinglePage(BLOG_FOLDER, lang as keyof ContentEntryMap);
const postFilterByAuthor = posts.filter(
(post: any) => slugify(post.data.author) === slugify(title)
(post) => slugify(post.data.author) === slugify(title)
);
---
+1 -1
View File
@@ -16,7 +16,7 @@ export function getStaticPaths() {
}
const { lang } = Astro.params;
const authorIndex: any = await getListPage(
const authorIndex = await getListPage(
COLLECTION_FOLDER,
lang as keyof ContentEntryMap
);
+2 -2
View File
@@ -10,12 +10,12 @@ export async function getStaticPaths() {
const paths = await Promise.all(
supportedLang.map(async (lang) => {
const posts: any = await getSinglePage(
const posts = await getSinglePage(
BLOG_FOLDER,
lang as keyof ContentEntryMap
);
return posts.map((post: any) => ({
return posts.map((post) => ({
params: {
lang: lang || undefined,
single: post.slug.split("/").pop(),
+1 -1
View File
@@ -19,7 +19,7 @@ export function getStaticPaths() {
}
const { lang } = Astro.params;
const BLOG_FOLDER = "blog";
const postIndex: any = await getListPage(
const postIndex = await getListPage(
BLOG_FOLDER,
lang as keyof ContentEntryMap
);
+1 -5
View File
@@ -4,7 +4,6 @@ import Base from "@/layouts/Base.astro";
import { getListPage } from "@/lib/contentParser.astro";
import { getTranslations, supportedLang } from "@/lib/utils/languageParser";
import PageHeader from "@/partials/PageHeader.astro";
import type { CollectionKey } from "astro:content";
import { type ContentEntryMap } from "astro:content";
export function getStaticPaths() {
@@ -15,10 +14,7 @@ export function getStaticPaths() {
}
const { lang } = Astro.params;
const contact: any = await getListPage(
"contact" as CollectionKey,
lang as keyof ContentEntryMap
);
const contact = await getListPage("contact", lang as keyof ContentEntryMap);
const { contact_form_action }: { contact_form_action: string } = config.params;
const { title, description, meta_title, image } = contact[0].data;
+10 -20
View File
@@ -6,18 +6,9 @@ import { supportedLang } from "@/lib/utils/languageParser";
import { markdownify } from "@/lib/utils/textConverter";
import CallToAction from "@/partials/CallToAction.astro";
import Testimonial from "@/partials/Testimonial.astro";
import type { Button, Feature } from "@/types";
import type { ContentCollectionKey, ContentEntryMap } from "astro:content";
import type { Feature } from "@/types";
import type { ContentEntryMap } from "astro:content";
import { FaCheck } from "react-icons/fa";
interface Homepage {
banner: {
title: string;
content: string;
image: string;
button: Button;
};
features: Feature[];
}
export function getStaticPaths() {
const paths = supportedLang.map((lang) => ({
@@ -27,20 +18,19 @@ export function getStaticPaths() {
}
const { lang } = Astro.params;
const homepage: any = await getListPage(
"homepage" as ContentCollectionKey,
lang as keyof ContentEntryMap
);
const { banner, features }: Homepage = homepage[0].data;
const homepage = await getListPage("homepage", lang as keyof ContentEntryMap);
const { banner, features } = homepage[0].data;
const testimonial = await getSinglePage(
"sections/testimonial" as ContentCollectionKey,
lang as keyof ContentEntryMap
"sections",
lang as keyof ContentEntryMap,
"testimonial"
);
const call_to_action = await getSinglePage(
"sections/call-to-action" as ContentCollectionKey,
lang as keyof ContentEntryMap
"sections",
lang as keyof ContentEntryMap,
"call-to-action"
);
---