components:map
This commit is contained in:
parent
7de1df59fd
commit
5555c04387
File diff suppressed because one or more lines are too long
@ -1,12 +1,12 @@
|
||||
export default {
|
||||
"environment": "dev",
|
||||
"environment": "build",
|
||||
"isSsrBuild": false,
|
||||
"projectBase": "",
|
||||
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\public\\",
|
||||
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\",
|
||||
"mode": "dev",
|
||||
"outDir": "dist",
|
||||
"assetsDir": "/_astro",
|
||||
"mode": "production",
|
||||
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\dist\\",
|
||||
"assetsDir": "_astro",
|
||||
"sourcemap": false,
|
||||
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"
|
||||
}
|
||||
70
src/components/polymech/map-ex.md
Normal file
70
src/components/polymech/map-ex.md
Normal file
@ -0,0 +1,70 @@
|
||||
# map.astro Component Documentation
|
||||
|
||||
## Overview
|
||||
`map.astro` is an Astro component that renders a Google Maps static map displaying multiple locations with markers. The component centers the map based on the average coordinates of all locations.
|
||||
|
||||
## Component Structure
|
||||
- **File Location**: `C:\Users\zx\Desktop\polymech\polymech-site\src\components\polymech\map.astro`
|
||||
- **Dependencies**: Imports types from `map-types.ts`
|
||||
|
||||
## Props
|
||||
The component accepts the following props:
|
||||
|
||||
### `locations` (optional)
|
||||
An array of location objects with the following structure:
|
||||
```typescript
|
||||
interface Location {
|
||||
geo: {
|
||||
lat: number; // Latitude
|
||||
lon: number; // Longitude
|
||||
};
|
||||
title: string; // Location title/name
|
||||
}
|
||||
```
|
||||
|
||||
If not provided, it defaults to sample locations in Lamu, Kenya.
|
||||
|
||||
### `options` (optional)
|
||||
Configuration options for the map:
|
||||
```typescript
|
||||
interface Options {
|
||||
zoom?: number; // Map zoom level (default: 9)
|
||||
api_key?: string; // Google Maps API key (required for map to display)
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
1. **Automatic Map Centering**: Calculates the average of all location coordinates to center the map
|
||||
2. **Labeled Markers**: Adds alphabetical labels (A, B, C, etc.) to each marker
|
||||
3. **Location Legend**: Displays a list of locations with corresponding marker labels
|
||||
4. **Error Handling**: Shows an error message when no API key is provided
|
||||
|
||||
## Example Usage
|
||||
```astro
|
||||
---
|
||||
import Map from '../components/polymech/map.astro';
|
||||
---
|
||||
|
||||
<Map
|
||||
locations={[
|
||||
{
|
||||
geo: { lat: 37.7749, lon: -122.4194 },
|
||||
title: 'San Francisco'
|
||||
},
|
||||
{
|
||||
geo: { lat: 34.0522, lon: -118.2437 },
|
||||
title: 'Los Angeles'
|
||||
}
|
||||
]}
|
||||
options={{
|
||||
zoom: 7,
|
||||
api_key: 'YOUR_GOOGLE_MAPS_API_KEY'
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## Notes
|
||||
- **API Key Required**: A Google Maps API key is necessary for the map to function
|
||||
- **Responsive Design**: The map container uses responsive classes for proper display on various screen sizes
|
||||
- **Style Customization**: The component includes a style tag for additional CSS customization
|
||||
|
||||
@ -6,9 +6,13 @@ export interface GeoPos {
|
||||
export interface Location {
|
||||
geo: GeoPos;
|
||||
title: string;
|
||||
group?: string;
|
||||
website?: string;
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
zoom?: number;
|
||||
api_key?: string;
|
||||
}
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
@ -1,36 +1,200 @@
|
||||
---
|
||||
|
||||
import { Location, Options } from './map-types'
|
||||
import { Img } from "imagetools/components"
|
||||
|
||||
interface Props {
|
||||
locations?: Location[];
|
||||
options?: Options;
|
||||
}
|
||||
|
||||
const { locations = [
|
||||
// Historic Sites
|
||||
{
|
||||
geo: { lat: -2.269456, lon: 40.902096 },
|
||||
title: 'Lamu Old Town'
|
||||
title: 'Lamu Old Town',
|
||||
group: 'historic_sites',
|
||||
website: 'https://whc.unesco.org/en/list/1055/'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.267922, lon: 40.901592 },
|
||||
title: 'Lamu Fort',
|
||||
group: 'historic_sites'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.268811, lon: 40.902344 },
|
||||
title: 'Lamu Museum',
|
||||
group: 'historic_sites',
|
||||
website: 'https://museums.or.ke/lamu-museum/'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.267500, lon: 40.901850 },
|
||||
title: 'Riyadha Mosque',
|
||||
group: 'religious_sites',
|
||||
website: 'https://www.visitkenya.com/lamu/riyadha-mosque'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.268200, lon: 40.901100 },
|
||||
title: 'German Post Office Museum',
|
||||
group: 'historic_sites'
|
||||
},
|
||||
|
||||
// Beaches
|
||||
{
|
||||
geo: { lat: -2.272012, lon: 40.890586 },
|
||||
title: 'Shela Beach'
|
||||
title: 'Shela Beach',
|
||||
group: 'beaches',
|
||||
website: 'https://www.visitlamu.org/shela-beach'
|
||||
},
|
||||
{
|
||||
geo: { lat: -1.075012, lon: 40.890586 },
|
||||
title: 'Shela Beach2'
|
||||
geo: { lat: -2.336103, lon: 40.902733 },
|
||||
title: 'Manda Beach',
|
||||
group: 'beaches'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.408333, lon: 40.866667 },
|
||||
title: 'Kipungani Beach',
|
||||
group: 'beaches'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.243333, lon: 40.913333 },
|
||||
title: 'Matondoni Beach',
|
||||
group: 'beaches'
|
||||
},
|
||||
|
||||
// Hotels & Accommodations
|
||||
{
|
||||
geo: { lat: -2.274578, lon: 40.889988 },
|
||||
title: 'The Majlis Resort',
|
||||
group: 'hotels',
|
||||
website: 'https://themajlisresorts.com'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.267825, lon: 40.908644 },
|
||||
title: 'Lamu House Hotel',
|
||||
group: 'hotels'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.273205, lon: 40.889925 },
|
||||
title: 'Peponi Hotel',
|
||||
group: 'hotels',
|
||||
website: 'https://www.peponihotel.com'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.269872, lon: 40.904167 },
|
||||
title: 'Jannat House',
|
||||
group: 'hotels'
|
||||
},
|
||||
|
||||
// Restaurants
|
||||
{
|
||||
geo: { lat: -2.269925, lon: 40.901678 },
|
||||
title: 'Whispers Café',
|
||||
group: 'restaurants',
|
||||
website: 'https://www.tripadvisor.com/Restaurant_Review-Whispers-Cafe-Lamu'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.271564, lon: 40.902122 },
|
||||
title: 'Petley\'s Inn Restaurant',
|
||||
group: 'restaurants'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.273275, lon: 40.889950 },
|
||||
title: 'Peponi Restaurant',
|
||||
group: 'restaurants'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.270115, lon: 40.901988 },
|
||||
title: 'Seafront Café',
|
||||
group: 'restaurants'
|
||||
},
|
||||
|
||||
// Local Attractions
|
||||
{
|
||||
geo: { lat: -2.228056, lon: 40.839722 },
|
||||
title: 'Takwa Ruins',
|
||||
group: 'attractions',
|
||||
website: 'https://www.nationalmuseums.or.ke/takwa-ruins'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.303333, lon: 40.810000 },
|
||||
title: 'Dodori National Reserve',
|
||||
group: 'attractions'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.267778, lon: 40.902222 },
|
||||
title: 'Lamu Donkey Sanctuary',
|
||||
group: 'attractions',
|
||||
website: 'https://www.lamuconservationtrust.org/donkey-sanctuary'
|
||||
},
|
||||
|
||||
// Markets
|
||||
{
|
||||
geo: { lat: -2.269205, lon: 40.902015 },
|
||||
title: 'Lamu Market',
|
||||
group: 'markets',
|
||||
website: 'https://www.visitlamu.org/market'
|
||||
},
|
||||
{
|
||||
geo: { lat: -2.270250, lon: 40.901750 },
|
||||
title: 'Lamu Fish Market',
|
||||
group: 'markets'
|
||||
}
|
||||
], options = {} } = Astro.props;
|
||||
const { zoom = 9, api_key = '' } = options;
|
||||
// Generate markers query param
|
||||
const markersParam = locations.map((loc, index) => `markers=color:red%7Clabel:${String.fromCharCode(65 + index)}%7C${loc.geo.lat},${loc.geo.lon}`).join('&');
|
||||
const { zoom = 12, api_key = '', width = 1200, height = 600 } = options;
|
||||
|
||||
// Group locations
|
||||
const groupedLocations = locations.reduce((acc, loc) => {
|
||||
const group = loc.group || 'default';
|
||||
if (!acc[group]) {
|
||||
acc[group] = [];
|
||||
}
|
||||
acc[group].push(loc);
|
||||
return acc;
|
||||
}, {} as Record<string, Location[]>);
|
||||
|
||||
// Define colors for different groups
|
||||
const groupColors: Record<string, string> = {
|
||||
default: 'red',
|
||||
// Add more predefined colors for groups
|
||||
};
|
||||
|
||||
// Get a color for a group
|
||||
const getColorForGroup = (group: string): string => {
|
||||
if (groupColors[group]) {
|
||||
return groupColors[group];
|
||||
}
|
||||
|
||||
// Generate colors for groups that don't have predefined colors
|
||||
const colors = ['blue', 'green', 'purple', 'orange', 'yellow', 'pink', 'brown'];
|
||||
const index = Object.keys(groupColors).length % colors.length;
|
||||
groupColors[group] = colors[index];
|
||||
return groupColors[group];
|
||||
};
|
||||
|
||||
// Flatten grouped locations with indices for marker labels
|
||||
let allLocationsWithIndices: Array<{ location: Location, index: number }> = [];
|
||||
let currentIndex = 0;
|
||||
|
||||
Object.entries(groupedLocations).forEach(([group, locs]) => {
|
||||
locs.forEach(loc => {
|
||||
allLocationsWithIndices.push({
|
||||
location: loc,
|
||||
index: currentIndex++
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Generate markers query param with different colors for each group
|
||||
const markersParam = Object.entries(groupedLocations).map(([group, locs]) => {
|
||||
const color = getColorForGroup(group);
|
||||
return locs.map((loc, groupIndex) => {
|
||||
const index = allLocationsWithIndices.findIndex(item => item.location === loc);
|
||||
return `markers=color:${color}%7Clabel:${String.fromCharCode(65 + index)}%7C${loc.geo.lat},${loc.geo.lon}`;
|
||||
}).join('&');
|
||||
}).join('&');
|
||||
|
||||
const centerLat = locations.reduce((sum, loc) => sum + loc.geo.lat, 0) / locations.length;
|
||||
const centerLon = locations.reduce((sum, loc) => sum + loc.geo.lon, 0) / locations.length;
|
||||
|
||||
const googleMapsUrl = api_key ?
|
||||
`https://maps.googleapis.com/maps/api/staticmap?center=${centerLat},${centerLon}&size=1200x600&${markersParam}&key=${api_key}` : null;
|
||||
`https://maps.googleapis.com/maps/api/staticmap?center=${centerLat},${centerLon}&zoom=${zoom}&size=${width}x${height}&${markersParam}&key=${api_key}` : null;
|
||||
|
||||
const hasError = !api_key;
|
||||
---
|
||||
@ -41,20 +205,55 @@ const hasError = !api_key;
|
||||
<img
|
||||
src={googleMapsUrl}
|
||||
alt="Map showing locations"
|
||||
class="w-full h-auto" width="600"
|
||||
height="400"
|
||||
class="w-full h-auto" width={width}
|
||||
height={height}
|
||||
/>
|
||||
<div class="bottom-0 w-full bg-white/[70%] p-4 backdrop-blur-sm">
|
||||
<ul class="space-y-1">
|
||||
{locations.map((loc, index) => (
|
||||
<li key={index} class="flex items-center gap-2">
|
||||
<span class="flex w-6 h-6 rounded-full bg-red-500 text-white items-center justify-center text-sm font-bold">
|
||||
{String.fromCharCode(65 + index)}
|
||||
</span>
|
||||
<span class="font-medium">{loc.title}</span>
|
||||
</li>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-4">
|
||||
{Object.entries(groupedLocations).map(([group, locs]) => (
|
||||
<div key={group} class="space-y-1">
|
||||
{group !== 'default' && (
|
||||
<h3 class="font-bold text-gray-700 mb-2">{group.toUpperCase()}</h3>
|
||||
)}
|
||||
<ul class="space-y-1">
|
||||
{locs.map((loc) => {
|
||||
const index = allLocationsWithIndices.findIndex(item => item.location === loc);
|
||||
const color = getColorForGroup(group);
|
||||
return (
|
||||
<li key={index} class="flex items-center gap-2">
|
||||
<span
|
||||
class={`flex w-6 h-6 rounded-full bg-${color}-500 text-white items-center justify-center text-sm font-bold`}
|
||||
style={`background-color: ${color}`}
|
||||
>
|
||||
{String.fromCharCode(65 + index)}
|
||||
</span>
|
||||
<span class="font-medium">
|
||||
<a
|
||||
href={`https://www.google.com/maps?q=${loc.geo.lat},${loc.geo.lon}&z=12`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-600 hover:underline"
|
||||
>
|
||||
{loc.title}
|
||||
</a>
|
||||
{loc.website && (
|
||||
<a
|
||||
href={loc.website}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="ml-2 text-sm text-gray-600 hover:underline"
|
||||
>
|
||||
🌐
|
||||
</a>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@ -47,11 +47,7 @@ const pages = [
|
||||
{
|
||||
name: "Blog",
|
||||
links: [
|
||||
{ href: "/blog/home", text: "Blog home" },
|
||||
{ href: "/blog/posts/1", text: "Blog post" },
|
||||
{ href: "/blog/tags", text: "Tag index" },
|
||||
{ href: "/blog/tags/astro", text: "Tag category" },
|
||||
{ href: "/rss.xml", text: "RSS" },
|
||||
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -2,15 +2,3 @@
|
||||
page: About
|
||||
pubDate: 2024-01-01
|
||||
---
|
||||
|
||||
|
||||
|
||||
Welcome to Williamsburg, your one-stop destination for all your shopping needs. Discover a wide range of high-quality products, from fashion and electronics to home decor and more. With competitive prices and a user-friendly shopping experience, we make it easy for you to find exactly what you’re looking for.
|
||||
|
||||
At Williamsburg, we're committed to providing you with exceptional service and an unparalleled shopping experience. Our team is dedicated to curating the latest trends and top brands, ensuring that you have access to the best products on the market.
|
||||
|
||||
Whether you're shopping for yourself or searching for the perfect gift, Williamsburg has everything you need to elevate your shopping experience. Browse our extensive selection, enjoy hassle-free checkout, and experience the convenience of online shopping with Williamsburg.
|
||||
|
||||
Join us on our journey to redefine retail and discover the difference with Williamsburg. Start exploring now and elevate your shopping experience with us.
|
||||
|
||||
Welcome to Williamsburg – where shopping meets satisfaction.
|
||||
|
||||
@ -17,12 +17,6 @@ tags: ["community", "blogging", "c++"]
|
||||
|
||||
- [Article:Every Type of Plastic Used By LEGO](https://bricknerd.com/home/every-type-of-plastic-used-by-lego-5-20-22)
|
||||
|
||||
#### Products
|
||||
|
||||
- [Product Catalog - TheFlipFlopi @ Lamu - Africa](https://drive.google.com/file/d/1ulOZ1px8AxCHh-ksepDZE2mXQS0UoVU2/view)
|
||||
|
||||
- [Product Catalog - Ecollabo8](https://drive.google.com/drive/folders/1M-tvdT6jNTQHsh6UexctHvidKCav-1oF)
|
||||
|
||||
#### Finance & Economy
|
||||
|
||||
- [Panic At The Job Market](https://matt.sh/panic-at-the-job-market)
|
||||
@ -127,10 +121,6 @@ tags: ["community", "blogging", "c++"]
|
||||
|
||||
- https://283bc.com/product.html
|
||||
- https://www.instagram.com/wedoo_workshop_bali
|
||||
- https://bazar.preciousplastic.com/mecanizados-ingenieria-y-diseno/
|
||||
- https://www.instagram.com/insta_ventu/
|
||||
- https://community.preciousplastic.com/research/hfg-jaipur-knee-prosthesis-10#update_Rzirih7uOfaK402429VD
|
||||
|
||||
- https://ko-fi.com/sotop_recycling (https://www.instagram.com/sotop_recycling/)
|
||||
- https://www.youtube.com/c/AndysMachines
|
||||
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
---
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout>
|
||||
<section>
|
||||
<div
|
||||
class="grid grid-cols-1 md:grid-cols-1">
|
||||
<div class="flex flex-col ">
|
||||
|
||||
|
||||
<div class="prose-styles lg:col-span-2">
|
||||
<img
|
||||
class="w-full aspect-[4/2] rounded-2xl relative object-cover lg:-mt-2"
|
||||
src={frontmatter.image.url}
|
||||
alt={frontmatter.image.alt}
|
||||
/>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
@ -11,7 +11,6 @@ import Resources from "@/components/polymech/resources.astro"
|
||||
import Specs from "@/components/polymech/specs.astro"
|
||||
import TabButton from "@/components/polymech/tab-button.astro"
|
||||
import TabContent from "@/components/polymech/tab-content.astro"
|
||||
import Map from "@/components/polymech/map.astro"
|
||||
|
||||
import "flowbite"
|
||||
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import config from "config/config.json";
|
||||
|
||||
import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
import { LANGUAGES_PROD } from "config/config.js";
|
||||
import { getCollection } from "astro:content";
|
||||
import LatestEntries from "@/components/blog/LatestEntries.astro";
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro";
|
||||
import CtaOne from "@/components/cta/CtaOne.astro";
|
||||
|
||||
const allPosts = await getCollection("posts");
|
||||
const allProducts = await getCollection("store");
|
||||
const locale = Astro.currentLocale
|
||||
import { translate } from "@/base/i18n.js"
|
||||
import { I18N_SOURCE_LANGUAGE } from "config/config.js"
|
||||
const store = `/${locale}/store/`;
|
||||
const blog = `/blog/posts/`;
|
||||
import { slugify } from "@/base/strings.js"
|
||||
|
||||
const allProducts = await getCollection("store")
|
||||
const locale = Astro.currentLocale
|
||||
const store = `/${locale}/store/`;
|
||||
|
||||
export function getStaticPaths() {
|
||||
const all: unknown[] = [];
|
||||
LANGUAGES_PROD.forEach((lang) => {
|
||||
@ -45,79 +43,13 @@ const group = async (items) => {
|
||||
const items = await group(allProducts)
|
||||
|
||||
---
|
||||
|
||||
<BaseLayout>
|
||||
<Wrapper variant="standard" class="py-4">
|
||||
<CtaOne />
|
||||
<section>
|
||||
<div class="py-2 space-y-2">
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{
|
||||
config.pages.home.blog &&
|
||||
allPosts
|
||||
.slice(0, 3)
|
||||
.map((post) => (
|
||||
<LatestEntries
|
||||
url={blog + post.slug}
|
||||
title={post.data.title}
|
||||
description={post.data.description}
|
||||
alt={post.data.title}
|
||||
pubDate={post.data.pubDate.toString().slice(0, 10)}
|
||||
author={post.data.author}
|
||||
image={post.data.image.url}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{
|
||||
config.pages.home.blog && (
|
||||
<a
|
||||
href="/blog/home"
|
||||
title="link to your page"
|
||||
aria-label="your label"
|
||||
class="relative group overflow-hidden pl-4 justify-between text-xs text-orange-600 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 hover:text-orange-600 duration-300 rounded-xl"
|
||||
>
|
||||
<span class="relative uppercase text-xs">Read all articles</span>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-12 transition duration-300 -translate-y-7 group-hover:translate-y-7"
|
||||
>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{
|
||||
|
||||
@ -1,109 +0,0 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import EntriesOne from "@/components/blog/EntriesOne.astro";
|
||||
const allPosts = await getCollection("resources");
|
||||
|
||||
const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
|
||||
|
||||
---
|
||||
<BaseLayout>
|
||||
<section>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2 py-2">
|
||||
<div class="flex flex-col gap-12 h-full justify-between ">
|
||||
<div>
|
||||
<h1
|
||||
class="text-lg text-neutral-600 font-mono tracking-tight text-balance">
|
||||
Read articles about our work, projects, and more.
|
||||
</h1>
|
||||
<div
|
||||
class="relative flex flex-wrap mt-4 justify-start gap-2 max-w-sm">
|
||||
{
|
||||
tags.map((tag) => (
|
||||
<a
|
||||
class="text-xs text-neutral-500 font-medium hover:text-orange-600"
|
||||
href={`/blog/tags/${tag}`}>
|
||||
{tag}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<form class="mt-12">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="email-address"
|
||||
class="sr-only"
|
||||
>Email address</label
|
||||
>
|
||||
<input
|
||||
id="email-address"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
required
|
||||
class="min-w-0 flex-auto rounded-xl font-mono border-0 h-14 text-sm uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600"
|
||||
placeholder="Enter your email"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between">
|
||||
<span class="relative uppercase text-xs text-white"
|
||||
>Subscribe</span
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7">
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{
|
||||
allPosts.slice().map((post) => (
|
||||
<EntriesOne
|
||||
url={"/blog/posts/" + post.slug}
|
||||
title={post.data.title}
|
||||
description={post.data.description}
|
||||
alt={post.data.title}
|
||||
pubDate={post.data.pubDate.toString().slice(0, 10)}
|
||||
author={post.data.author}
|
||||
image={post.data.image.url}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</BaseLayout>
|
||||
@ -1,92 +1,30 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro"
|
||||
import { getCollection } from "astro:content"
|
||||
import LatestEntries from "@/components/blog/LatestEntries.astro"
|
||||
import StoreEntries from "@/components/store/StoreEntries.astro"
|
||||
import Hero from "@/components/landing/Hero.astro"
|
||||
import CtaOne from "@/components/cta/CtaOne.astro"
|
||||
import CtaThree from "@/components/cta/CtaThree.astro"
|
||||
import CtaTwo from "@/components/cta/CtaTwo.astro"
|
||||
import Locale from "./[locale].astro"
|
||||
const allPosts = await getCollection("posts")
|
||||
const allProducts = await getCollection("store")
|
||||
const locale = Astro.currentLocale || "en"
|
||||
|
||||
---
|
||||
<BaseLayout>
|
||||
<CtaTwo />
|
||||
<section>
|
||||
<div class="py-2 space-y-2">
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-2 gap-2 ">
|
||||
{
|
||||
allPosts.slice(0, 4).map((post) => (
|
||||
<LatestEntries
|
||||
url={locale + "/blog/posts/" + post.slug}
|
||||
title={post.data.title}
|
||||
description={post.data.description}
|
||||
alt={post.data.title}
|
||||
pubDate={post.data.pubDate.toString().slice(0, 10)}
|
||||
author={post.data.author}
|
||||
image={post.data.image.url}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<a
|
||||
href="/blog/home"
|
||||
title="link to your page"
|
||||
aria-label="your label"
|
||||
class="relative group overflow-hidden pl-4 justify-between text-xs text-orange-600 font-mono h-14 flex space-x-6 items-center bg-white hover:bg-neutral-200 hover:text-orange-600 duration-300 rounded-xl">
|
||||
<span class="relative uppercase text-xs">Read all articles</span>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-12 transition duration-300 -translate-y-7 group-hover:translate-y-7">
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<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((post) => (
|
||||
allProducts.map((item) => (
|
||||
<StoreEntries
|
||||
url={ locale + "/store/" + post.id}
|
||||
title={post.data.title}
|
||||
price={post.data.price}
|
||||
type={post.data.type}
|
||||
alt={post.data.title}
|
||||
model={post.data}
|
||||
url={ locale + "/store/" + item.id}
|
||||
title={item.data.title}
|
||||
price={item.data.price}
|
||||
type={item.data.type}
|
||||
alt={item.data.title}
|
||||
model={item.data}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
@ -3,14 +3,16 @@ import Wrapper from "@/components/containers/Wrapper.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import EntriesOne from "@/components/blog/EntriesOne.astro";
|
||||
const allPosts = await getCollection("resources")
|
||||
|
||||
const all = await getCollection("resources")
|
||||
|
||||
---
|
||||
<BaseLayout>
|
||||
<Wrapper>
|
||||
<section>
|
||||
<div class="grid sm:grid-cols-1 lg:grid-cols-3 xl:grid-cols-3 gap-2 py-2">
|
||||
{
|
||||
allPosts
|
||||
all
|
||||
.slice()
|
||||
.map((post) => (
|
||||
<EntriesOne
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
---
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
import EntriesOne from "@/components/blog/EntriesOne.astro";
|
||||
|
||||
import { getCollection } from "astro:content";
|
||||
export async function getStaticPaths() {
|
||||
const allPosts = await getCollection("posts");
|
||||
const uniqueTags = [
|
||||
...new Set(allPosts.map((post) => post.data.tags).flat()),
|
||||
];
|
||||
return uniqueTags.map((tag) => {
|
||||
const filteredPosts = allPosts.filter((post) =>
|
||||
post.data.tags.includes(tag)
|
||||
);
|
||||
return {
|
||||
params: { tag },
|
||||
props: { posts: filteredPosts },
|
||||
};
|
||||
});
|
||||
}
|
||||
const { tag } = Astro.params;
|
||||
const { posts } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout pageTitle={tag}>
|
||||
<section>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2 py-2">
|
||||
<div class="flex flex-col gap-12 h-full justify-between ">
|
||||
<div>
|
||||
<h1
|
||||
class="text-lg text-neutral-600 font-mono tracking-tight text-balance uppercase">
|
||||
Blog posts tagged with {tag}
|
||||
</h1>
|
||||
</div>
|
||||
<form class="mt-12">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label
|
||||
for="email-address"
|
||||
class="sr-only"
|
||||
>Email address</label
|
||||
>
|
||||
<input
|
||||
id="email-address"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
required
|
||||
class="min-w-0 flex-auto rounded-xl font-mono border-0 h-14 text-sm uppercase duration-300 px-3.5 py-2 text-neutral-500 ring-1 ring-inset ring-white placeholder:text-neutral-400 focus:ring-2 focus:ring-inset focus:ring-orange-600"
|
||||
placeholder="Enter your email"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between">
|
||||
<span class="relative uppercase text-xs text-white"
|
||||
>Subscribe</span
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7">
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{
|
||||
posts.map((post) => (
|
||||
<EntriesOne
|
||||
url={"/blog/posts/" + post.slug}
|
||||
title={post.data.title}
|
||||
description={post.data.description}
|
||||
alt={post.data.title}
|
||||
pubDate={post.data.pubDate.toString().slice(0, 10)}
|
||||
author={post.data.author}
|
||||
image={post.data.image.url}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||
|
||||
const allPosts = await getCollection("posts");
|
||||
const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
|
||||
const pageTitle = "Tag Index";
|
||||
---
|
||||
|
||||
<BaseLayout pageTitle={pageTitle}>
|
||||
<section>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 gap-2">
|
||||
<div>
|
||||
<h1
|
||||
class="text-lg text-neutral-600 font-mono tracking-tight text-balance uppercase">
|
||||
All tags
|
||||
</h1>
|
||||
<p class="text-sm text-balance text-neutral-500">
|
||||
All tags from our blog.
|
||||
</p>
|
||||
</div>
|
||||
<div class="items-center col-span-2">
|
||||
<div class="grid items-end gap-4 lg:grid-cols-3">
|
||||
<div class="col-span-full flex flex-col gap-2">
|
||||
{
|
||||
tags.sort().map((tag) => (
|
||||
<>
|
||||
<a
|
||||
href="/blog/home"
|
||||
class="relative group overflow-hidden pl-4 font-mono h-14 flex space-x-6 items-center bg-orange-500 hover:bg-black duration-300 rounded-xl w-full justify-between">
|
||||
<span class="relative uppercase text-xs text-white">
|
||||
{tag}
|
||||
</span>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="w-12 text-white transition duration-300 -translate-y-7 group-hover:translate-y-7">
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-14 flex">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 m-auto fill-white">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 8.25 21 12m0 0-3.75 3.75M21 12H3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
class="font-mono uppercase text-neutral-500 hover:text-orange-500 sr-only"
|
||||
href={`/blog/tags/${tag}`}>
|
||||
{tag}
|
||||
</a>
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
@ -5,7 +5,6 @@ import { getCollection } from "astro:content"
|
||||
import KBot from "@/components/polymech/kbot.astro"
|
||||
const locale = Astro.currentLocale
|
||||
const store = `${locale}/store/`
|
||||
const blog = `/blog/posts/`
|
||||
|
||||
import Map from "@/components/polymech/map.astro"
|
||||
|
||||
@ -19,8 +18,8 @@ const mapOptions = {
|
||||
<BaseLayout>
|
||||
<Wrapper variant="standard" class="py-4">
|
||||
<section>
|
||||
<Map options={mapOptions} />
|
||||
<div class="py-2 space-y-2">
|
||||
<Map options={mapOptions} />
|
||||
<div class="grid md:grid-cols-1 lg:grid-cols-1 gap-4" >
|
||||
<KBot router="openai" model="gpt-4.5-preview" mode="completion" template="code_simple" disabled={true}>
|
||||
</KBot>
|
||||
|
||||
Reference in New Issue
Block a user