This commit is contained in:
babayaga 2025-12-27 08:30:30 +01:00
parent 70e6c1d37b
commit dbf2fb27cd
10 changed files with 338 additions and 79 deletions

View File

@ -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

View File

@ -62,7 +62,7 @@
"text": "Library"
},
{
"href": "/${LANG}/tutorials",
"href": "/${LANG}/howtos",
"text": "Tutorials"
},
{

View File

@ -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();

View File

@ -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 />

View File

@ -84,7 +84,6 @@ const Content_Debug = await createMarkdownComponent(str_debug);
hideNavigation={true}
{...rest}
>
<Navigation />
<Wrapper>
<!-- Header with Breadcrumb on its own line -->
<div

View File

@ -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">

View 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}/>
})()
)}

View File

@ -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}/>

View File

@ -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>