generated from polymech/site-template
cleanup
This commit is contained in:
parent
70e6c1d37b
commit
dbf2fb27cd
@ -23,7 +23,7 @@
|
||||
"format": "unix-time"
|
||||
}
|
||||
],
|
||||
"default": "2025-12-26T17:53:03.149Z"
|
||||
"default": "2025-12-27T07:01:36.602Z"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -62,7 +62,7 @@
|
||||
"text": "Library"
|
||||
},
|
||||
{
|
||||
"href": "/${LANG}/tutorials",
|
||||
"href": "/${LANG}/howtos",
|
||||
"text": "Tutorials"
|
||||
},
|
||||
{
|
||||
|
||||
@ -6,7 +6,7 @@ interface Props {
|
||||
const { variant = "standard", class: extraClass = "" } = Astro.props;
|
||||
// Map each variant to its specific classes
|
||||
const variantClasses = {
|
||||
standard: "max-w-4xl 2xl:max-w-4xl mx-auto w-full",
|
||||
standard: "2xl:max-w-4xl mx-auto w-full",
|
||||
};
|
||||
// Combine the classes for the specified variant with any extra classes
|
||||
const classes = `${variantClasses[variant]} ${extraClass}`.trim();
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
---
|
||||
import BaseHead from "../components/BaseHead.astro";
|
||||
import Navigation from "../components/global/Navigation.astro";
|
||||
import Footer from "../components/global/Footer.astro";
|
||||
import { isRTL } from "config/config.js"
|
||||
|
||||
import Navigation from "@polymech/astro-base/components/global/Navigation.astro";
|
||||
import Footer from "@polymech/astro-base/components/global/Footer.astro";
|
||||
import { isRTL } from "config/config.js";
|
||||
const { item, additionalKeywords, ...rest } = Astro.props;
|
||||
const d = 2
|
||||
|
||||
---
|
||||
<html lang={Astro.currentLocale} class="scroll-smooth" dir={isRTL(Astro.currentLocale) ? "rtl" : "ltr"}>
|
||||
|
||||
<html
|
||||
lang={Astro.currentLocale}
|
||||
class="scroll-smooth"
|
||||
dir={isRTL(Astro.currentLocale) ? "rtl" : "ltr"}
|
||||
>
|
||||
<head>
|
||||
<BaseHead
|
||||
item={item}
|
||||
{...rest}
|
||||
/>
|
||||
<BaseHead item={item} {...rest} />
|
||||
</head>
|
||||
<body class="bg-neutral-100 mx-auto 2xl:max-w-7xl flex flex-col min-h-svh p-4 text-neutral-500">
|
||||
<body
|
||||
class="bg-neutral-100 mx-auto 2xl:max-w-7xl flex flex-col min-h-svh p-4 text-neutral-500"
|
||||
>
|
||||
<Navigation />
|
||||
<main class="grow">
|
||||
<slot />
|
||||
|
||||
@ -84,7 +84,6 @@ const Content_Debug = await createMarkdownComponent(str_debug);
|
||||
hideNavigation={true}
|
||||
{...rest}
|
||||
>
|
||||
<Navigation />
|
||||
<Wrapper>
|
||||
<!-- Header with Breadcrumb on its own line -->
|
||||
<div
|
||||
|
||||
@ -9,10 +9,10 @@ import StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
import CtaOne from "@/components/cta/CtaOne.astro";
|
||||
import { group_by_path } from "@/model/component/component.js";
|
||||
|
||||
const view = "store"
|
||||
const items = await getCollection(view)
|
||||
const view = "library";
|
||||
const items = await getCollection("store");
|
||||
const locale = Astro.currentLocale;
|
||||
const store = `/${locale}/${view}/`
|
||||
const store = `/${locale}/${view}/`;
|
||||
|
||||
export function getStaticPaths() {
|
||||
const all: unknown[] = [];
|
||||
@ -31,11 +31,11 @@ const frontmatter = {
|
||||
title: "Store",
|
||||
description: "Store",
|
||||
keywords: "Store",
|
||||
image:{
|
||||
image: {
|
||||
src: config.pages.home.hero,
|
||||
url: config.pages.home.hero,
|
||||
alt: config.metadata.keywords
|
||||
}
|
||||
alt: config.metadata.keywords,
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
@ -50,10 +50,7 @@ const frontmatter = {
|
||||
{
|
||||
Object.keys(groups).map((relKey) => (
|
||||
<section>
|
||||
<h3
|
||||
aria-hidden="true"
|
||||
class="p-4 text-xs space-x-8 text-neutral-600"
|
||||
>
|
||||
<h3 aria-hidden="true" class="p-4 text-xs space-x-8 text-neutral-600">
|
||||
{relKey}
|
||||
</h3>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-2">
|
||||
|
||||
301
src/pages/[locale]/library/[...path].astro
Normal file
301
src/pages/[locale]/library/[...path].astro
Normal file
@ -0,0 +1,301 @@
|
||||
---
|
||||
import StoreLayout from '@/layouts/StoreLayout.astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
import { LANGUAGES_PROD } from "config/config.js"
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import Wrapper from "@polymech/astro-base/components/containers/Wrapper.astro";
|
||||
import Navigation from "@polymech/astro-base/components/global/Navigation.astro";
|
||||
import Translate from "@polymech/astro-base/components/i18n.astro";
|
||||
import { slugify } from "@polymech/astro-base/base/strings.js"
|
||||
|
||||
const view = 'library'
|
||||
|
||||
export async function getStaticPaths()
|
||||
{
|
||||
const view = 'library'
|
||||
const allProducts = await getCollection('store')
|
||||
const all: unknown[] = []
|
||||
|
||||
LANGUAGES_PROD.forEach((lang) => {
|
||||
// Add individual product routes
|
||||
allProducts.forEach((product) => {
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: product.id,
|
||||
},
|
||||
props: {
|
||||
page: product,
|
||||
locale: lang,
|
||||
path: product.id,
|
||||
view,
|
||||
type: 'product'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Add folder routes for categories
|
||||
const folders = new Set<string>()
|
||||
allProducts.forEach(product => {
|
||||
const segments = product.id.split('/').filter(segment => segment !== '')
|
||||
if (segments.length > 1) {
|
||||
// Add all possible folder paths (e.g., products, products/sheetpress, etc.)
|
||||
for (let i = 1; i < segments.length; i++) {
|
||||
const folderPath = segments.slice(0, i).join('/')
|
||||
folders.add(folderPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Add folder paths
|
||||
Array.from(folders).forEach(folder => {
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: folder,
|
||||
},
|
||||
props: {
|
||||
folderPath: folder,
|
||||
products: allProducts.filter(product => {
|
||||
const productFolder = product.id.split('/').slice(0, -1).join('/')
|
||||
return productFolder === folder
|
||||
}),
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Add special case for store/ (root store) - shows all products
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: undefined,
|
||||
},
|
||||
props: {
|
||||
folderPath: '',
|
||||
products: allProducts, // All products
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder',
|
||||
isRootStore: true
|
||||
}
|
||||
})
|
||||
|
||||
// Add special case for store/products/ - shows all products
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: 'products',
|
||||
},
|
||||
props: {
|
||||
folderPath: 'products',
|
||||
products: allProducts, // All products
|
||||
locale: lang,
|
||||
view,
|
||||
type: 'folder',
|
||||
isProductsFolder: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
const { page, folderPath, products, locale, type, isRootStore, isProductsFolder, ...rest } = Astro.props as any
|
||||
---
|
||||
|
||||
{type === 'folder' ? (
|
||||
(() => {
|
||||
// Handle folder view - show all products in category
|
||||
let categoryTitle: string
|
||||
const categoryDescription = 'Browse our complete collection of products'
|
||||
if (isRootStore) {
|
||||
// Root store - all products
|
||||
categoryTitle = 'All Products'
|
||||
} else if (isProductsFolder) {
|
||||
// Products folder - all products
|
||||
categoryTitle = 'Products'
|
||||
} else {
|
||||
// Regular category folder
|
||||
const categoryName = folderPath.split('/').pop() || folderPath
|
||||
categoryTitle = categoryName
|
||||
}
|
||||
categoryTitle = categoryTitle.charAt(0).toUpperCase() + categoryTitle.slice(1)
|
||||
|
||||
// Group products by category for all products views
|
||||
let groupedProducts: any = {}
|
||||
if (isRootStore || isProductsFolder) {
|
||||
// Use the same grouping logic as homepage - simplified for now
|
||||
groupedProducts = products.reduce((acc, item: any) => {
|
||||
const id = item.id.split("/")[1]
|
||||
const key = slugify(id)
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ')
|
||||
if (!acc[key]) {
|
||||
acc[key] = []
|
||||
}
|
||||
acc[key].push(item)
|
||||
return acc
|
||||
}, {})
|
||||
} else {
|
||||
// For category views, just sort products by title
|
||||
groupedProducts = {
|
||||
[categoryTitle]: products.sort((a: any, b: any) =>
|
||||
a.data.title.localeCompare(b.data.title)
|
||||
)
|
||||
}
|
||||
}
|
||||
const store = `/${locale}/${view}/`;
|
||||
return (
|
||||
<BaseLayout hideNavigation={true}>
|
||||
<Wrapper variant="standard" class="py-4">
|
||||
<!-- Header with Breadcrumb and Navigation on same line -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<!-- Breadcrumb on left -->
|
||||
<div class="flex-1">
|
||||
<nav class="breadcrumb-nav" aria-label="Breadcrumb navigation">
|
||||
<ol class="breadcrumb-list flex flex-wrap items-center gap-1 m-0 p-0 list-none">
|
||||
<li class="breadcrumb-item flex items-center gap-1">
|
||||
<a href={`/${locale}/`} class="breadcrumb-link text-sm no-underline px-2 py-1 rounded hover:underline transition-all">
|
||||
Home
|
||||
</a>
|
||||
<span class="breadcrumb-separator text-sm text-gray-400" aria-hidden="true">/</span>
|
||||
</li>
|
||||
<li class="breadcrumb-item flex items-center gap-1">
|
||||
<a href={`/${locale}/library/`} class="breadcrumb-link text-sm no-underline px-2 py-1 rounded hover:underline transition-all">
|
||||
Store
|
||||
</a>
|
||||
{folderPath && <span class="breadcrumb-separator text-sm text-gray-400" aria-hidden="true">/</span>}
|
||||
</li>
|
||||
{folderPath && folderPath.split('/').map((segment, index, arr) => {
|
||||
const isLast = index === arr.length - 1;
|
||||
const segmentPath = arr.slice(0, index + 1).join('/');
|
||||
const label = segment.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
|
||||
return (
|
||||
<li class="breadcrumb-item flex items-center gap-1">
|
||||
{!isLast ? (
|
||||
<>
|
||||
<a href={`/${locale}/library/${segmentPath}/`} class="breadcrumb-link text-sm no-underline px-2 py-1 rounded hover:underline transition-all">
|
||||
{label}
|
||||
</a>
|
||||
<span class="breadcrumb-separator text-sm text-gray-400" aria-hidden="true">/</span>
|
||||
</>
|
||||
) : (
|
||||
<span class="breadcrumb-current font-medium text-sm px-2 py-1" aria-current="page">
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const themeToggleBtn = document.getElementById('theme-toggle');
|
||||
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
// Change the icons inside the button based on previous settings
|
||||
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
themeToggleLightIcon.classList.remove('hidden');
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
themeToggleDarkIcon.classList.remove('hidden');
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
|
||||
themeToggleBtn.addEventListener('click', function() {
|
||||
// toggle icons inside button
|
||||
themeToggleDarkIcon.classList.toggle('hidden');
|
||||
themeToggleLightIcon.classList.toggle('hidden');
|
||||
|
||||
// if set via local storage previously
|
||||
if (localStorage.getItem('color-theme')) {
|
||||
if (localStorage.getItem('color-theme') === 'light') {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.setItem('color-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.setItem('color-theme', 'light');
|
||||
}
|
||||
|
||||
// if NOT set via local storage previously
|
||||
} else {
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.setItem('color-theme', 'light');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.setItem('color-theme', 'dark');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="mb-12">
|
||||
<h1 class="mb-6 text-2xl font-semibold">
|
||||
<Translate>{categoryTitle}</Translate>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-600 mb-8">
|
||||
<Translate>{categoryDescription}</Translate>
|
||||
</p>
|
||||
{Object.keys(groupedProducts).length > 0 ? (
|
||||
Object.keys(groupedProducts).map((categoryKey) => (
|
||||
<section class="mb-12">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-6">
|
||||
{groupedProducts[categoryKey].map((product: any) => (
|
||||
<StoreEntries
|
||||
key={product.id}
|
||||
url={store + product.id}
|
||||
title={product.data.title}
|
||||
type={product.data.type}
|
||||
alt={product.data.title}
|
||||
model={product.data}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
) : (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-gray-400 mb-4">
|
||||
<svg class="w-16 h-16 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">
|
||||
<Translate>No products in this category</Translate>
|
||||
</h3>
|
||||
<p class="text-gray-600">
|
||||
<Translate>This category appears to be empty.</Translate>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div class="mt-12 text-center">
|
||||
<a
|
||||
href={`/${locale}/library/`}
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
||||
</svg>
|
||||
<Translate>Back to Store</Translate>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</Wrapper>
|
||||
</BaseLayout>
|
||||
)
|
||||
})()
|
||||
) : (
|
||||
(() => {
|
||||
const { data } = page
|
||||
return <StoreLayout frontmatter={data} {...rest}/>
|
||||
})()
|
||||
)}
|
||||
@ -1,32 +0,0 @@
|
||||
---
|
||||
import StoreLayout from '@/layouts/StoreLayout.astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
import { LANGUAGES_PROD as LANGUAGES } from "config/config.js"
|
||||
|
||||
export async function getStaticPaths()
|
||||
{
|
||||
const view = 'store'
|
||||
const allProducts = await getCollection(view)
|
||||
const all: unknown[] = []
|
||||
LANGUAGES.forEach((lang) => {
|
||||
allProducts.forEach((product) => {
|
||||
all.push({
|
||||
params: {
|
||||
locale: lang,
|
||||
path: product.id,
|
||||
},
|
||||
props: {
|
||||
page: product,
|
||||
locale: lang,
|
||||
path: product.id,
|
||||
view
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return all
|
||||
}
|
||||
const { page:page, ...rest } = Astro.props
|
||||
const { data } = page
|
||||
---
|
||||
<StoreLayout frontmatter={data} {...rest}/>
|
||||
@ -1,35 +1,29 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro"
|
||||
import { getCollection } from "astro:content"
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro"
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
|
||||
import Howtos from "@/components/cta/CtaHowtos.astro"
|
||||
import KB from "@/components/cta/CtaKB.astro"
|
||||
import Howtos from "@/components/cta/CtaHowtos.astro";
|
||||
import KB from "@/components/cta/CtaKB.astro";
|
||||
import Directory from "@/components/cta/CtaDirectory.astro";
|
||||
import Shop from "@/components/cta/CtaShop.astro";
|
||||
|
||||
import Directory from "@/components/cta/CtaDirectory.astro"
|
||||
import Shop from "@/components/cta/CtaShop.astro"
|
||||
|
||||
const allProducts = await getCollection("store")
|
||||
const locale = Astro.currentLocale || "en"
|
||||
const allProducts = await getCollection("store");
|
||||
const locale = Astro.currentLocale || "en";
|
||||
---
|
||||
<BaseLayout>
|
||||
|
||||
<BaseLayout>
|
||||
<Howtos />
|
||||
<KB />
|
||||
<Directory />
|
||||
<Shop />
|
||||
<section>
|
||||
<div class="py-2 space-y-2">
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-2 gap-2 ">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-2">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-2">
|
||||
{
|
||||
allProducts.map((item) => (
|
||||
<StoreEntries
|
||||
url={ locale + "/store/" + item.id}
|
||||
url={`${locale}/library/${item.id}`}
|
||||
title={item.data.title}
|
||||
price={item.data.price}
|
||||
type={item.data.type}
|
||||
@ -40,5 +34,4 @@ const locale = Astro.currentLocale || "en"
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</BaseLayout>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user