Added brand stores, updated about, and added First Aid Only first aid kit.

This commit is contained in:
David Ball 2024-07-13 00:13:32 -04:00
parent a74f4ed79d
commit 0392a5a79c
23 changed files with 521 additions and 62 deletions

BIN
public/assets/brands/coast.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/assets/brands/first-aid-only.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,14 @@
<svg width="239" height="95" viewBox="0 0 239 95" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M226.8 61.7C226 61.7 225.2 62 224.7 62.6C224.1 63.2 223.8 64 223.8 64.8C223.8 65.6 224.1 66.3 224.7 66.9C225.3 67.5 226.1 67.8 226.9 67.8C227.7 67.8 228.5 67.5 229.1 66.9C229.7 66.3 230 65.6 230 64.7C230 63.9 229.7 63.1 229.1 62.6C228.4 62 227.6 61.7 226.8 61.7ZM226.8 62.2C227.5 62.2 228.1 62.4 228.6 63C229.1 63.5 229.3 64.1 229.3 64.7C229.3 65.4 229.1 66 228.6 66.5C228.1 67 227.5 67.2 226.8 67.2C226.1 67.2 225.5 66.9 225 66.4C224.5 65.9 224.3 65.3 224.3 64.7C224.3 64 224.6 63.4 225.1 62.9C225.5 62.5 226.1 62.2 226.8 62.2ZM225.5 63V66.4H226.4V65H226.7L227.3 66.3H228.3L227.5 64.8C227.9 64.7 228.2 64.3 228.2 63.9C228.2 63.3 227.7 62.9 226.8 62.9L225.5 63ZM226.4 63.6H226.7C227.1 63.6 227.3 63.7 227.3 64.1C227.3 64.4 227.1 64.5 226.7 64.5H226.4V63.6Z" fill="black"/>
<path d="M221 25.3C173.7 13 117.4 13.5 117.3 13.5C117.2 13.5 60.9 13.1 13.6 25.6L13.7 68.1H13.9C61.2 80.5 117.3 80 117.4 80C117.5 80 173.8 80.4 221.1 67.9L221 25.3Z" fill="#CE0E2D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.6 42.9H57.4V48.9C57.4 51.9 57 53.5 54.9 53.5C52.6 53.5 52.2 52 52.2 49.8V42.9L47 43V50.7C47 54.2 47.9 57 52.8 57C55.2 57 56.2 56.3 57.4 54.1C57.4 54.9 57.5 55.7 57.5 56.5H62.5C62.4 55.6 62.4 54.6 62.4 53.7L62.6 42.9Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M124.6 56.4H129.8V51.3C129.8 48.6 131.6 47 134.4 47C135 47 135.4 47 135.8 47.1V42.7C132.4 42.7 131 43.2 129.7 45.8H129.6C129.6 44.8 129.6 43.9 129.5 42.9H124.5C124.6 43.8 124.6 44.8 124.6 45.7V56.4Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M137.7 56.3H142.9V49.8C142.9 47.3 143.6 45.7 145.4 45.7C147.7 45.7 148.2 47.3 148.2 49.4V56.3H153.4V49.1C153.4 47.1 154.2 45.7 155.9 45.7C158 45.7 158.8 47.2 158.8 49.2V56.3H164V48.6C164 43.3 160.5 42.3 157.6 42.3C154.9 42.3 153 43.5 151.9 45.3C151 43.2 149.1 42.3 147.1 42.3C144.5 42.3 143.4 43.9 142.9 45.2H142.8C142.8 44.4 142.8 42.8 142.8 42.8H137.7C137.8 43.7 137.8 44.7 137.8 45.6L137.7 56.3Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.3 40.8V45.3H35.1C37.7 45.3 38.5 44.5 38.5 43C38.5 41.1 37 40.8 34.3 40.8H31.3ZM25.7 56.5V37.1H36.9C41.6 37.1 44.2 38.8 44.2 42.2C44.2 44.7 42.2 46.4 39.3 46.9C40.9 47.1 42.5 47.4 43.1 48.6C44 50.4 44.1 54.5 45 56.5H39C38.4 54.6 38.5 51.8 37.9 50.5C37.6 49.6 36.7 49.2 34.4 49.2H31.3V56.6L25.7 56.5Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M78.6 49.6C78.6 47.2 77 45.5 74.7 45.6C72.4 45.6 70.7 47.2 70.7 49.8C70.7 52.2 72.4 53.8 74.7 53.8C77.4 53.8 78.6 51.8 78.6 49.6ZM65.6 56.4V37H70.8V44.7C71.9 43.6 73.6 42.4 76.6 42.4C81.1 42.4 83.6 45.1 83.6 49.6C83.6 54.2 81.1 56.9 76.8 56.9C73.4 56.9 71.9 55.6 70.7 54.2C70.7 54.9 70.7 56.4 70.7 56.4H65.6Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M110.4 48.1C110.6 46.7 111.3 45.2 113.7 45.2C115.5 45.2 117 45.9 117 48.1H110.4ZM122.6 50.9C122.6 45.6 120.3 42.4 113.7 42.4C109.1 42.4 105 44.7 105 49.9C105 55.2 109.1 56.9 113.8 56.9C117.5 56.9 121.2 56.5 122.3 52.3H116.8C116.5 53.2 116.1 54.1 113.9 54.1C111.7 54.1 110.5 53 110.4 50.9H122.6Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M177.4 50.4C177.4 51.8 176.6 53.8 173.3 53.8C171.9 53.8 171.1 53.2 171.1 52.4C171.1 51.3 171.8 50.4 175.3 50.4H177.4ZM171.8 46.8C171.8 45.9 172.4 45.1 174.3 45.1C176.3 45.1 177.3 45.6 177.3 47.8H173.5C168.4 47.8 165.4 49.3 165.4 52.5C165.4 55.3 167.7 56.9 171.5 56.9C173.8 56.9 176.1 56.2 177.3 54.2H177.4C177.4 54.9 177.5 55.9 177.5 56.4H182.5C182.4 55.4 182.4 54.4 182.4 53.4V48.2C182.4 43.6 179.4 42.4 174.1 42.4C170.5 42.4 166.6 43.4 166.3 46.9L171.8 46.8Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M184.5 56.3H189.7V42.8H184.5V56.3ZM189.9 36.9H184.3V40.7H189.9V36.9Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M98.8 49.6C98.8 47.2 97.2 45.5 94.9 45.6C92.6 45.6 90.9 47.2 90.9 49.8C90.9 52.2 92.6 53.8 94.9 53.8C97.6 53.8 98.8 51.7 98.8 49.6ZM85.8 56.4V37H91V44.7C92.1 43.6 93.8 42.4 96.7 42.4C101.2 42.4 103.7 45.1 103.7 49.6C103.7 54.2 101.1 56.9 96.9 56.9C93.5 56.9 92 55.6 90.8 54.2C90.8 55 90.8 56.4 90.8 56.4H85.8Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M196.4 49.4C196.4 47 198 45.3 200.3 45.3C202.6 45.3 204.3 46.9 204.3 49.5C204.3 51.9 202.6 53.5 200.3 53.5C197.6 53.7 196.4 51.6 196.4 49.4ZM209.4 56.2V36.8H204.2V44.5C203.2 43.4 201.4 42.2 198.4 42.2C193.9 42.2 191.5 44.9 191.5 49.4C191.5 54 194.1 56.7 198.3 56.7C201.7 56.7 203.2 55.4 204.4 53.9C204.4 54.7 204.4 56.1 204.4 56.1L209.4 56.2Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,59 @@
---
import type { Brand } from '../data/brands/brand';
interface Props {
brand: Brand
}
const { brand } = Astro.props;
---
<li class="link-card">
<a href={`/brand/${brand.slug}`}>
{brand.logoUrl !== undefined && <img src={brand.logoUrl} alt={brand.name} />}
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
text-align: center;
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(0.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
.link-card img {
max-width: 100%;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View File

@ -10,21 +10,18 @@ const { product } = Astro.props;
---
<div class="link-card">
<!-- <div class="card col-3"> -->
<a href={`/${product?.slug}`}>
<a href={`/${product?.slug}`}>
<h4 class="card-header">
{product?.name}
</h4>
{product?.productDetails?.imageUrls !== undefined && <img src={product!.productDetails?.imageUrls[0]} alt={product?.productDetails?.title} style="min-width: 45ch; max-width: 45ch;" />}
{product?.amazonProductDetails?.imageUrls !== undefined && <img src={product!.amazonProductDetails?.imageUrls[0]} alt={product?.amazonProductDetails?.title} style="min-width: 45ch; max-width: 45ch;" />}
<div class="card-body">
<StarRating value={product?.productDetails?.reviewRating||0} max={5} overlayColor="#23262d" /> {product?.productDetails?.reviewCount} Reviews
<StarRating value={product?.amazonProductDetails?.reviewRating||0} max={5} overlayColor="#23262d" /> {product?.amazonProductDetails?.reviewCount} Reviews
<h5 class="card-title">
{product?.productDetails?.title}
{product?.amazonProductDetails?.title}
</h5>
<!-- <a href={product?.amazonLink} class="btn btn-primary">${product?.productDetails?.price} On Amazon <span>&rarr;</span></a> -->
</div>
</a>
<!-- </div> -->
</a>
</div>
<style>

View File

@ -1,10 +0,0 @@
import { type Product } from '../products/product';
import { CoastStoreProducts } from './coast';
import { RubbermaidStoreProducts } from './rubbermaid';
import { VortextOpticsStoreProducts } from './vortex-optics';
export const AllStoreProducts: Product[] = [
...CoastStoreProducts,
...RubbermaidStoreProducts,
...VortextOpticsStoreProducts,
];

7
src/data/brands/brand.ts Normal file
View File

@ -0,0 +1,7 @@
export interface Brand {
name?: string;
slug: string;
shortDescription?: string;
description?: string;
logoUrl?: string;
}

View File

@ -1,11 +1,13 @@
import { type Product } from '../products/product';
import { getCategoryIdForSeoLink } from '../categories';
export const BRAND_STORE_SLUG = 'coast';
export const CoastStoreProducts: Product[] = [
{
slug: 'B00SJRDIN2',
tags: ['flashlight', 'safety'],
brand: 'Coast',
brandStoreSlug: BRAND_STORE_SLUG,
name: 'Durable 8" Spot/Flood LED flashlight',
callout: `House numbers can be tricky to locate late in the evening.`,
categoryId: getCategoryIdForSeoLink('safety-equipment')!,
@ -37,7 +39,7 @@ the Coast Polysteel 600 LED Flashlight has got your back. Its durable design and
DoorDash drivers like you.
`.trim(),
amazonLink: 'https://www.amazon.com/Polysteel-600-Waterproof-Flashlight-Stainless/dp/B00SJRDIN2?crid=29BV6TGKIV7U4&dib=eyJ2IjoiMSJ9.z_qqGdUikpKLO62rjeDuDoQDki7kToAVTM2kBLri4vs25y739Ll_nFVMziV7A5ZnYGQQYNujGdg5igViybnULLsVCa_T6qCk9HUVk7GuD30Jp0FrydoVV9zm-m-E9Zhi7vGbjJdDxUmYXypCL_GaGT6O6K4gf2P94QITVfbbBrjNT74VL9ZdRfs9ucPUSjkoTNLCMXcAXf4fXnJqniXk4PyFks_YYcZ9K8IDN4Fp-puEBc5lhdIp2hY4ugsmMD2v9zYNTvaTD1EaAnXVA_UXIrGwSTdg3Q2cWoqWF6sw6mo.z0JvreFTZ58D14a2IuwCSDybpR9x_CTUBSRrNlP9aZs&dib_tag=se&keywords=coast+flash+light&qid=1720695258&s=sporting-goods&sprefix=coast+flash+light%2Csporting%2C83&sr=1-27&linkCode=ll1&tag=dashersupply-20&linkId=9cfd6086ba43fac649f6884f72c7c844&language=en_US&ref_=as_li_ss_tl',
productDetails: {
amazonProductDetails: {
"title": "COAST POLYSTEEL 600 1000 Lumen LED Flashlight with Pure Beam Twist Focus, Stainless-Steel Core, Crushproof, Black",
"description": "",
"featureBullets": [

View File

@ -0,0 +1,85 @@
import { type Product } from '../products/product';
import { getCategoryIdForSeoLink } from '../categories';
export const BRAND_STORE_SLUG = 'first-aid-only';
export const FirstAidOnlyStoreProducts: Product[] = [
{
amazonLink: 'https://www.amazon.com/First-Aid-Only-Weatherproof-Plastic/dp/B001SG76MU?crid=17746AVZ2R4TK&dib=eyJ2IjoiMSJ9.nehq12VwBTB17Vyx1YODXq7JYQbnOM8xv6AZRadSceLpsk33o-ES3M7UnJMkq0usrVmB1uKgdw9rxtPf7wcS1fHI_DhXIkjp7ujnBf0xvt-SjW3Xw__yU6NvYnSUmSfQzcqj49ZMu893KSypCAIPiLZ0gHo9HbRPicFsuJVBOCv5aOQoBqlLRymArai_8k9lUwtCxAfhfiDjUGk6K3s_S6IFWUP88Ff8mbyU5lkVRtbE4dRTCp-wNjM6HpxqZPSZ0A3_-PPl75PlgjsmUXIkxArreEPatqaHwyJ13X-DCQU.CWOEqmjYxSJ7yRXLCgtz9iGOSGJD53MSoPw6jzAWx7Q&dib_tag=se&keywords=first+aid+kit&qid=1720746463&sprefix=first+aid+kit%2Caps%2C95&sr=8-3-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1&linkCode=ll1&tag=dashersupply-20&linkId=385f21e08641ef9ce7ad55aebe2d30cf&language=en_US&ref_=as_li_ss_tl',
slug: 'B001SG76MU',
categoryId: getCategoryIdForSeoLink('safety-equipment')!,
name: "57-pc First Aid Kit (small)",
tags: ['safety','first-aid'],
brandStoreSlug: BRAND_STORE_SLUG,
description: `
**Stay Prepared on the Go: The Ultimate Delivery Driver First Aid Kit**
This compact, lightweight kit is packed with everything you need to treat minor cuts, scrapes, and burns on the go.
With its easy-to-carry handle and durable plastic case, this portable first aid kit fits nicely in vehicle trunk
compartments.
This versatile kit features individual compartments, making it easy to access the supplies you need quickly.
**Stay Safe, Stay Prepared:**
You know that accidents can happen anytime. That's why it's crucial to have a reliable first aid kit by your side.
The 57-PC First Aid Kit (Small) is designed specifically for delivery drivers like you, providing everything you
need to treat minor injuries on the go.
**Features:**
- **Comprehensive Emergency Kit**: Includes adhesive fabric and plastic bandages, antibiotic ointments, BZK antiseptic towelettes, burn cream packets, gauze roll and pads, gloves, scissors, tweezers, and other multi-use first aid items.
- **Convenient Packaging**: An ideal worksite or office first aid kit, it features a durable plastic case complete with an easy-to-carry handle for transporting first aid supplies in an emergency.
- **Easy Access**: This convenient and versatile home, office, and jobsite first aid kit features individual compartments that make accessing first aid supplies quick and easy.
- **Compact Size**: Small enough to fit nicely into a backpack, vehicle compartment, or desk drawer, this travel-size first aid kit helps you stay prepared for potential emergencies when at home, in the office, or while on the go0
- **Personal and Professional First Aid Solutions**: First Aid Only offers a full line of first aid kits, cabinets, and stations, Emergency Response Care, individual first aid products, Spill Clean Up kits, CPR care and more.
`.trim(),
amazonProductDetails: {
"title": "First Aid Only 6060 10-Person Emergency First Aid Kit for Office, Home, and Worksites, 57 Pieces",
"description": "An ideal car, home, or job site first aid kit, this 57-piece first aid kit includes supplies for treating pain and swelling, as well as cuts, scrapes, and burns. This compact first aid is easy to carry and includes individual compartments designed to neatly store everything. Ideal for home, traveling, and taking on the go, our first aid kits for businesses, homes, and cars allow you to treat minor cuts and scrapes just about anywhere.",
"featureBullets": [
"Comprehensive Emergency Kit: Includes adhesive fabric and plastic bandages, antibiotic ointments, BZK antiseptic towelettes, burn cream packets, gauze roll and pads, gloves, scissors, tweezers, and other multi-use first aid items",
"Convenient Packaging: An ideal worksite or office first aid kit, it features a durable plastic case complete with an easy-to-carry handle for transporting first aid supplies in an emergency",
"Easy Access: This convenient and versatile home, office, and jobsite first aid kit features individual compartments that make accessing first aid supplies quick and easy",
"Compact Size: Small enough to fit nicely into a backpack, vehicle compartment, or desk drawer, this travel-size first aid kit helps you stay prepared for potential emergencies when at home, in the office, or while on the go",
"Personal and Professional First Aid Solutions: First Aid Only offers a full line of first aid kits, cabinets, and stations, Emergency Response Care, individual first aid products, Spill Clean Up kits, CPR care and more"
],
"price": 20.99,
"reviewCount": 11594,
"reviewRating": 4.7,
"imageUrls": [
// "https://m.media-amazon.com/images/I/413sfboF0mL._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/413sfboF0mL._SX466_.jpg",
"https://m.media-amazon.com/images/I/51PHxfhu09L._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/41DrWanEf2L._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/51vA72L9CkL._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/41UoP-S+XuL._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/51XP7Me5+IL._SX38_SY50_CR,0,0,38,50_.jpg",
"https://m.media-amazon.com/images/I/81jMWjiouFL._SX35_SY46._CR0,0,35,46_BG85,85,85_BR-120_PKdp-play-icon-overlay__.jpg"
],
"attributes": [
{
"label": "Brand",
"value": "First Aid Only"
},
{
"label": "Special Feature",
"value": "Portable, compact"
},
{
"label": "Number of Pieces",
"value": "57"
},
{
"label": "Recommended Uses For Product",
"value": "Emergency"
},
{
"label": "Color",
"value": "White"
}
]
}
},
];

88
src/data/brands/index.ts Normal file
View File

@ -0,0 +1,88 @@
import { type Brand } from './brand';
import { type Product } from '../products/product';
import { CoastStoreProducts, BRAND_STORE_SLUG as COAST_STORE_SLUG } from './coast';
import { FirstAidOnlyStoreProducts, BRAND_STORE_SLUG as FIRST_AID_ONLY_STORE_SLUG } from './first-aid-only';
import { RubbermaidStoreProducts, BRAND_STORE_SLUG as RUBBERMAID_STORE_SLUG } from './rubbermaid';
import { VortexOpticsStoreProducts, BRAND_STORE_SLUG as VORTEX_OPTICS_STORE_SLUG } from './vortex-optics';
export const ALL_BRAND_PRODUCTS: Product[] = [
...CoastStoreProducts,
...FirstAidOnlyStoreProducts,
...RubbermaidStoreProducts,
...VortexOpticsStoreProducts,
];
interface BrandSlugProductIndexDictionary {
[key: string]: Product[];
}
export const ALL_BRANDS: Brand[] = [
{
name: "COAST",
slug: COAST_STORE_SLUG,
shortDescription: `COAST flashlights are constructed with top-grade aluminum, stainless steel, and poly-nylon on the outside with custom-designed battery cartridges and heavy-duty O-rings on the inside.`,
logoUrl: '/assets/brands/coast.jpg',
},
{
name: "First Aid Only",
slug: FIRST_AID_ONLY_STORE_SLUG,
logoUrl: '/assets/brands/first-aid-only.png',
shortDescription: 'A trusted provider of cost effective first aid solutions for individuals and businesses.',
description: `
First Aid Only, an Acme United Corporation brand, is the trusted provider of cost effective first aid solutions for individuals and businesses in every industry no matter how big or small. Over the last 150 years, Acme United has acquired the first aid companies Pac-Kit, PhysiciansCare and First Aid Only, to provide the best first aid solutions to our customers.
First Aid Only has been helping businesses stay in compliance with federally mandated First Aid and safety regulations since 1988. Based on our years of experience, we make it easy for you to choose the first aid kit that best serves your business needs and number of employees. We offer solutions for single building sites, multi-sites or mobile operations.
First Aid Only offers a full line of first aid kits, cabinets, and stations, Emergency Response Care, individual first aid products, Spill Clean Up kits, CPR care and more for all your business and personal safety needs. Our products adhere to OSHA Regulations and ANSI Standards, so you know that what comes in your kit is what is needed. Refill options are also available for replenishing your kit contents, ensuring that your first aid offering is always stocked and ready in case of an emergency.
First Aid Only is proud to assemble its first aid kits in the USA. Our just-in-time manufacturing capabilities allow us to guarantee the longest possible shelf life for first aid items. We use quality products to ensure your employees get the proper medical care to maintain a safe work environment and minimize downtime.
Our history is rooted in innovative first-aid solutions, a tradition that continues today. The First Aid Only patented designs help businesses stay compliant with both ANSI Standards and federal OSHA Regulations.
First Aid Only...The Smart Choice.
`.trim(),
},
{
name: "Rubbermaid",
slug: RUBBERMAID_STORE_SLUG,
logoUrl: '/assets/brands/rubbermaid.svg',
shortDescription: "Rubbermaid is a leader in home and food storage.",
},
{
name: "Vortex Optics",
slug: VORTEX_OPTICS_STORE_SLUG,
shortDescription: "",
logoUrl: '/assets/brands/vortex-optics.svg',
shortDescription: `Vortex Optics is an American manufacturer of special optical equipments for hunting, wildlife watching, outdoor recreation, shooting sports and law enforcement and military. Vortex products include binoculars, spotting scopes, riflescopes, reflex sights, holographic sights and other accessories.`,
},
];
// export function getAllProductsByBrandSlug(brandSlug: string) {
// return allBrandProducts.filter((product) => product.slug === brandSlug);
// }
// export const allProductsByBrands = allBrands.map((brand: Brand) => {
// return {
// `${brand.slug}`: getAllProductsByBrandSlug(brand.slug)
// };
// )
// export function getAllProductsByBrandSlug(brandSlug: string): {[key: string]: Product[]} {
// // return ALL_BRAND_PRODUCTS.reduce((acc, product) => {
// // if (!acc[product.slug]) {
// // acc[product.slug] = [];
// // }
// // acc[product.slug].push(product);
// // return acc;
// // }, {});
// }
// export const ALL_PRODUCTS_BY_BRAND_SLUG = ALL_BRANDS.map((brand) => ALL_BRAND_PRODUCTS.filter((product) => product.slug === brand.slug));
// ALL_BRAND_PRODUCTS.reduce((acc, product) => {
// if (!acc[product.brandStoreSlug]) {
// acc[product.slug] = [];
// }
// acc[product.slug].push(product);
// return acc;
// });

View File

@ -1,6 +1,8 @@
import { type Product } from '../products/product';
import { getCategoryIdForSeoLink } from '../categories';
export const BRAND_STORE_SLUG = 'rubbermaid';
export const RubbermaidStoreProducts: Product[] = [
{
slug: 'B00006ICOT',
@ -9,7 +11,7 @@ export const RubbermaidStoreProducts: Product[] = [
tags: ['drink carrier'],
categoryId: getCategoryIdForSeoLink('delivery-gear')!,
name: 'Industrial Drink Carrier',
brand: 'Rubbermaid',
brandStoreSlug: BRAND_STORE_SLUG,
callout: 'Running out of cup holders? Spilling drinks? Juggling the struggle?',
description: `
If you're tired of juggling drinks, tipping drinks over in cardboard carriers, or running out of cup holders in your cab,
@ -30,7 +32,7 @@ all-purpose caddy is designed to keep your essentials organized and within reach
**Get Organized:** With its 8 rounded sections and durable design, this caddy is perfect for transporting frequently used sports drink bottles or even coffee cups. This Rubbermaid Commercial Deluxe Carry Caddy has got you covered.
`.trim(),
productDetails: {
amazonProductDetails: {
"title": "Rubbermaid Commercial Products Deluxe Carry Caddy for Take-Out Coffee/Soft Drinks, Postmates/Uber Eats/Food Delivery, Cleaning Products, Sports/Water Bottles, Black",
"description": "The Rubbermaid Commercial Deluxe Carry Cleaning Caddy is an all-purpose cleaning supply caddy with 8 rounded sections ideal for carrying and storing things such as tools, cleaning supplies, spray bottles, sports drink bottles, and other drinks. This products is ideal for those looking for a tool to transport their frequently used cleaning tools throughout their household or from job to job. This caddy is also ideal for Post mates or Uber Eats drivers who frequently need to carry multiple drinks such as coffee, smoothies, or large sodas. This caddy also works well as a holder for sports drinks and bottles and is therefore ideal for gyms, sports facilities, coaches, and others needing easy access and mobility to their drink bottles.",
"featureBullets": [

View File

@ -1,13 +1,15 @@
import { type Product } from '../products/product';
import { getCategoryIdForSeoLink } from '../categories';
export const VortextOpticsStoreProducts: Product[] = [
export const BRAND_STORE_SLUG = 'vortex-optics';
export const VortexOpticsStoreProducts: Product[] = [
{
slug: 'B07V3LB5DN',
name: 'Rugged Binocular HD Optical System',
callout: 'Out in the sticks? Am I in the right place or is this a wrong turn?',
amazonLink: 'https://www.amazon.com/dp/B07V3LB5DN?social_share=cm_sw_r_cso_cp_apin_dp_1S8QG7ATMWQXHEPZZJMA&starsLeft=1&fbclid=IwZXh0bgNhZW0CMTEAAR0r1pSlSIglwL42EFH5z3urFfzpT1EnEmxsTc589_C-QjkKpQYBl0m10wc_aem_tfAE9o8HXXadzB6BWVN-Sg&th=1&linkCode=ll1&tag=dashersupply-20&linkId=418648d02fea89d3cf2fad9645fe9f6e&language=en_US&ref_=as_li_ss_tl',
brand: 'Vortex Optics',
brandStoreSlug: BRAND_STORE_SLUG,
categoryId: getCategoryIdForSeoLink('safety-equipment')!,
tags: ['safety', 'outdoor'],
description: `
@ -31,7 +33,7 @@ clarity and confidence in a variety of environments.
The Crossfire HD binoculars bring HD optics, rugged performance and high end form-factor. Add in the included GlassPak binocular harness for quick optic deployment in the field and superior protection and comfort. The Crossfire HD truly is a rare find.
`.trim(),
productDetails: {
amazonProductDetails: {
"title": "Vortex Optics Crossfire HD 10x42 Binoculars - HD Optical System, Tripod Adaptable, Rubber Armor, Waterproof, Fogproof, Shockproof, Included GlassPak - Unlimited, Unconditional Warranty",
"description": "The Crossfire HD binoculars bring HD optics, rugged performance and high end form-factor. Add in the included GlassPak binocular harness for quick optic deployment in the field and superior protection and comfort - The Crossfire HD truly is a rare find.",
"featureBullets": [

View File

@ -45,7 +45,7 @@ export class StaticCategory {
/**
* A list of all the categories.
*/
export const allCategories: Category[] = [
export const ALL_CATEGORIES: Category[] = [
{
id: StaticCategory.nextId(),
category: "Vehicle Essentials",
@ -112,8 +112,8 @@ export const allCategories: Category[] = [
].sort((a, b) => a.seoLink.localeCompare(b.seoLink));
export function getCategoryIdForSeoLink(seoLink: string): number|null {
console.log('getCategoryIdForSeoLink looking for ', seoLink, 'in', allCategories);
for (const category of allCategories) {
console.log('getCategoryIdForSeoLink looking for ', seoLink, 'in', ALL_CATEGORIES);
for (const category of ALL_CATEGORIES) {
if (category.seoLink == seoLink) {
return category.id;
}

View File

@ -1,4 +1,4 @@
import { type ProductAttribute } from "../products/product-attribute";
import { type ProductAttribute } from "./product-attribute";
export interface ProductDetails {
title?: string;

View File

@ -1,12 +1,12 @@
import { getCategoryIdForSeoLink } from '../categories';
import { type Product } from './product';
import { AllStoreProducts } from '../brand_stores';
import { ALL_BRAND_PRODUCTS } from '../brands';
/**
* A list of all the products.
*/
export const allProducts: Product[] = [
...AllStoreProducts
export const ALL_PRODUCTS: Product[] = [
...ALL_BRAND_PRODUCTS
// {
// productDetails:
// }
@ -157,7 +157,7 @@ export const allProducts: Product[] = [
// for (let p = 0; p < allProducts.length; p++) {
// if (allProducts[p].amazonLink == url) {
// allProducts[p].productDetails = extractedProduct;
// allProducts[p].amazonProductDetails = extractedProduct;
// }
// }
// };
@ -168,10 +168,11 @@ export const allProducts: Product[] = [
// */
// const crawler = new CheerioCrawler({ requestHandler });
// // // await crawler.run(products.map((p) => p.amazonLink));
// // await crawler.run(products.map((p) => p.amazonLink));
// await crawler.run([
// // // 'https://www.amazon.com/Rubbermaid-Commercial-Deluxe-Cleaning-FG315488BLA/dp/B00006ICOT?crid=23IAS1CUMM6QG&dib=eyJ2IjoiMSJ9.WRH21whjlnubmVRL4HRNIccU9p3CC9B9pvd9LCCkzqxXQggwnV0UNwmgHs868sL9Jr_1cfUHxsHCU7sTT28EMZOCdxoGo-ylie7hWbrQ75ab9SFUJMawaE14LhyNFAQ69j45EtR9kd0njMvXY9WDrBWj61TMpe6K1vl0BC-kWFz8iQqZgrRsgLNN5jbuF83nWOddYMTMZFxQXuvyPUG13LwYmOe17iPUBa03FNecKl0.-fxaqjBgRSTfoIeqegQhb9rz9lE9LJTt475JTTi0J3A&dib_tag=se&keywords=drink+carrier&qid=1719716583&sprefix=drink+carrier,aps,162&sr=8-3&linkCode=sl1&tag=radspazzyspaz-20&linkId=4b1f972cd47168ab215cd7c8fecbefa8&language=en_US&ref_=as_li_ss_tl',
// // // 'https://www.amazon.com/dp/B07V3LB5DN?social_share=cm_sw_r_cso_cp_apin_dp_1S8QG7ATMWQXHEPZZJMA&starsLeft=1&fbclid=IwZXh0bgNhZW0CMTEAAR0r1pSlSIglwL42EFH5z3urFfzpT1EnEmxsTc589_C-QjkKpQYBl0m10wc_aem_tfAE9o8HXXadzB6BWVN-Sg&th=1&linkCode=ll1&tag=radspazzyspaz-20&linkId=983adc5be8c6bbb0c0f42676c76b4f6e&language=en_US&ref_=as_li_ss_tl',
// 'https://www.amazon.com/Polysteel-600-Waterproof-Flashlight-Stainless/dp/B00SJRDIN2?crid=29BV6TGKIV7U4&dib=eyJ2IjoiMSJ9.z_qqGdUikpKLO62rjeDuDoQDki7kToAVTM2kBLri4vs25y739Ll_nFVMziV7A5ZnYGQQYNujGdg5igViybnULLsVCa_T6qCk9HUVk7GuD30Jp0FrydoVV9zm-m-E9Zhi7vGbjJdDxUmYXypCL_GaGT6O6K4gf2P94QITVfbbBrjNT74VL9ZdRfs9ucPUSjkoTNLCMXcAXf4fXnJqniXk4PyFks_YYcZ9K8IDN4Fp-puEBc5lhdIp2hY4ugsmMD2v9zYNTvaTD1EaAnXVA_UXIrGwSTdg3Q2cWoqWF6sw6mo.z0JvreFTZ58D14a2IuwCSDybpR9x_CTUBSRrNlP9aZs&dib_tag=se&keywords=coast+flash+light&qid=1720695258&s=sporting-goods&sprefix=coast+flash+light%2Csporting%2C83&sr=1-27&linkCode=ll1&tag=dashersupply-20&linkId=9cfd6086ba43fac649f6884f72c7c844&language=en_US&ref_=as_li_ss_tl',
// // // // 'https://www.amazon.com/Rubbermaid-Commercial-Deluxe-Cleaning-FG315488BLA/dp/B00006ICOT?crid=23IAS1CUMM6QG&dib=eyJ2IjoiMSJ9.WRH21whjlnubmVRL4HRNIccU9p3CC9B9pvd9LCCkzqxXQggwnV0UNwmgHs868sL9Jr_1cfUHxsHCU7sTT28EMZOCdxoGo-ylie7hWbrQ75ab9SFUJMawaE14LhyNFAQ69j45EtR9kd0njMvXY9WDrBWj61TMpe6K1vl0BC-kWFz8iQqZgrRsgLNN5jbuF83nWOddYMTMZFxQXuvyPUG13LwYmOe17iPUBa03FNecKl0.-fxaqjBgRSTfoIeqegQhb9rz9lE9LJTt475JTTi0J3A&dib_tag=se&keywords=drink+carrier&qid=1719716583&sprefix=drink+carrier,aps,162&sr=8-3&linkCode=sl1&tag=radspazzyspaz-20&linkId=4b1f972cd47168ab215cd7c8fecbefa8&language=en_US&ref_=as_li_ss_tl',
// // // // 'https://www.amazon.com/dp/B07V3LB5DN?social_share=cm_sw_r_cso_cp_apin_dp_1S8QG7ATMWQXHEPZZJMA&starsLeft=1&fbclid=IwZXh0bgNhZW0CMTEAAR0r1pSlSIglwL42EFH5z3urFfzpT1EnEmxsTc589_C-QjkKpQYBl0m10wc_aem_tfAE9o8HXXadzB6BWVN-Sg&th=1&linkCode=ll1&tag=radspazzyspaz-20&linkId=983adc5be8c6bbb0c0f42676c76b4f6e&language=en_US&ref_=as_li_ss_tl',
// // 'https://www.amazon.com/Polysteel-600-Waterproof-Flashlight-Stainless/dp/B00SJRDIN2?crid=29BV6TGKIV7U4&dib=eyJ2IjoiMSJ9.z_qqGdUikpKLO62rjeDuDoQDki7kToAVTM2kBLri4vs25y739Ll_nFVMziV7A5ZnYGQQYNujGdg5igViybnULLsVCa_T6qCk9HUVk7GuD30Jp0FrydoVV9zm-m-E9Zhi7vGbjJdDxUmYXypCL_GaGT6O6K4gf2P94QITVfbbBrjNT74VL9ZdRfs9ucPUSjkoTNLCMXcAXf4fXnJqniXk4PyFks_YYcZ9K8IDN4Fp-puEBc5lhdIp2hY4ugsmMD2v9zYNTvaTD1EaAnXVA_UXIrGwSTdg3Q2cWoqWF6sw6mo.z0JvreFTZ58D14a2IuwCSDybpR9x_CTUBSRrNlP9aZs&dib_tag=se&keywords=coast+flash+light&qid=1720695258&s=sporting-goods&sprefix=coast+flash+light%2Csporting%2C83&sr=1-27&linkCode=ll1&tag=dashersupply-20&linkId=9cfd6086ba43fac649f6884f72c7c844&language=en_US&ref_=as_li_ss_tl',
// 'https://www.amazon.com/First-Aid-Only-Weatherproof-Plastic/dp/B001SG76MU?crid=17746AVZ2R4TK&dib=eyJ2IjoiMSJ9.nehq12VwBTB17Vyx1YODXq7JYQbnOM8xv6AZRadSceLpsk33o-ES3M7UnJMkq0usrVmB1uKgdw9rxtPf7wcS1fHI_DhXIkjp7ujnBf0xvt-SjW3Xw__yU6NvYnSUmSfQzcqj49ZMu893KSypCAIPiLZ0gHo9HbRPicFsuJVBOCv5aOQoBqlLRymArai_8k9lUwtCxAfhfiDjUGk6K3s_S6IFWUP88Ff8mbyU5lkVRtbE4dRTCp-wNjM6HpxqZPSZ0A3_-PPl75PlgjsmUXIkxArreEPatqaHwyJ13X-DCQU.CWOEqmjYxSJ7yRXLCgtz9iGOSGJD53MSoPw6jzAWx7Q&dib_tag=se&keywords=first+aid+kit&qid=1720746463&sprefix=first+aid+kit%2Caps%2C95&sr=8-3-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1&linkCode=ll1&tag=dashersupply-20&linkId=385f21e08641ef9ce7ad55aebe2d30cf&language=en_US&ref_=as_li_ss_tl',
// ]);

View File

@ -1,4 +1,4 @@
import { type ProductDetails } from './product-details';
import { type ProductDetails as AmazonProductDetails } from './amazon-product-details';
/**
* Product details.
@ -15,7 +15,7 @@ export interface Product {
/**
* Brand of the product.
*/
brand?: string;
brandStoreSlug?: string;
/**
* Call out for the product.
*/
@ -39,6 +39,6 @@ export interface Product {
/**
* Product Details.
*/
productDetails?: ProductDetails;
amazonProductDetails?: AmazonProductDetails;
}

View File

@ -1,7 +1,8 @@
---
import Layout from '../layouts/Layout.astro';
import { allCategories } from '../data/categories';
import { allProducts } from '../data/products';
import { type Category, ALL_CATEGORIES } from '../data/categories';
import { type Product, ALL_PRODUCTS } from '../data/products';
import { type Brand, ALL_BRANDS } from '../data/brands';
import StarRating from '../components/StarRating.astro';
import markdownIt from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs';
@ -21,7 +22,7 @@ const md = markdownIt({
type ProductStaticPath = { params: { productLookup: string }};
export function getStaticPaths() {
return allProducts.map<ProductStaticPath>((product) => { return {
return ALL_PRODUCTS.map<ProductStaticPath>((product) => { return {
params: {
productLookup: product.slug
}
@ -32,8 +33,9 @@ console.log(getStaticPaths());
const formatAsCurrency = (amount: number) => amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
const { productLookup } = Astro.params;
const product = allProducts.find(p => p.slug === productLookup)!;
const category = allCategories.find(c => c.id == product.categoryId)!;
const product: Product = ALL_PRODUCTS.find(p => p.slug === productLookup)!;
const category: Category = ALL_CATEGORIES.find(c => c.id === product.categoryId)!;
const brand: Brand = ALL_BRANDS.find(b => b.slug === product.brandStoreSlug)!;
---
<Layout title=`${product?.name} - Dasher Supply`>
@ -47,29 +49,32 @@ const category = allCategories.find(c => c.id == product.categoryId)!;
</h2>
<div class="row">
<div class="col-4">
{product?.productDetails?.imageUrls !== undefined && <img src={product!.productDetails?.imageUrls[0]} alt={product?.productDetails?.title} style="max-width: 100%;" />}
{product?.amazonProductDetails?.imageUrls !== undefined && <img src={product!.amazonProductDetails?.imageUrls[0]} alt={product?.amazonProductDetails?.title} style="max-width: 100%;" />}
</div>
<div class="col-8">
<h5 class="card-title">
<a href={product?.amazonLink}>{product?.productDetails?.title}</a>
<a href={product?.amazonLink}>{product?.amazonProductDetails?.title}</a>
</h5>
<p>
<StarRating value={product?.productDetails?.reviewRating||0} max={5} overlayColor="#13151a" /> {product?.productDetails?.reviewCount} Reviews
<StarRating value={product?.amazonProductDetails?.reviewRating||0} max={5} overlayColor="#13151a" /> {product?.amazonProductDetails?.reviewCount} Reviews
{ product?.brandStoreSlug &&
<span>&#x2022; <span class="item-metadata">Brand/<a href={`/brand/${brand?.slug}`}>{brand?.name}</a></span>
}
</p>
{ product?.description &&
<div set:html={md.render(product?.description||'')}></div>
}
{ !product?.description &&
<ul role="list">
{product?.productDetails?.featureBullets?.map(featureBullet => (
{product?.amazonProductDetails?.featureBullets?.map(featureBullet => (
<li>{featureBullet}</li>
))}
</ul>
<p>
{product?.productDetails?.description && product?.productDetails?.description}
{product?.amazonProductDetails?.description && product?.amazonProductDetails?.description}
</p>
}
<a href={product?.amazonLink} class="btn btn-primary">{formatAsCurrency(product?.productDetails?.price||0)} On Amazon <span>&rarr;</span></a>
<a href={product?.amazonLink} class="btn btn-primary">{formatAsCurrency(product?.amazonProductDetails?.price||0)} On Amazon <span>&rarr;</span></a>
</div>
</div>
<br />
@ -151,4 +156,10 @@ const category = allCategories.find(c => c.id == product.categoryId)!;
}
a, a:link, a:visited { text-decoration: none; color: #fff }
a:hover, a:active { text-decoration: underline; color: #fff }
.item-metadata {
text-transform: uppercase;
color: #ccc;
font-weight: 600;
font-size: 12pt;
}
</style>

View File

@ -2,7 +2,7 @@
import Layout from '../layouts/Layout.astro';
import CategoryCard from '../components/CategoryCard.astro';
// import { about } from '../data/about';
import { allCategories } from '../data/categories';
import { ALL_CATEGORIES } from '../data/categories';
---
<Layout title="Dasher Supply">
@ -21,11 +21,11 @@ import { allCategories } from '../data/categories';
</p>
<h2>Who I Am</h2>
<p>
My name is <a href="https://daball.me/">David A. Ball</a>. I'm also an expert at making web sites in my spare time. As such, I am uniquely qualified as an expert of both fields. My gift to you is a quick and simple, curated shopping list of things I believe are the most practical and best in-class products.
My name is <a href="https://daball.me/">David A. Ball</a>. I'm also an expert at making web sites in my spare time. As such, I am uniquely qualified as an expert of both fields. My gift to you is a thoughtful, curated shopping list of things I believe are the most practical and best in-class products.
</p>
<h2>Our Mission</h2>
<p>
Dasher Supply strives to document the latest and best quality accesories to help you along your route. I have hand-picked each of these products because I believe each one of these are indispensible tools of the trade. I've shopped ahead so you don't have to. Cut through the noise. <a href="/">Begin all of your shopping journies here.</a>
Dasher Supply strives to document the latest and best quality accesories to help you along your route. I have hand-picked each of these products because I believe each one of these are indispensible tools of the trade from brands you can trust. I've shopped ahead so you don't have to. Cut through the noise. <a href="/">Begin all of your shopping journies here.</a>
</p>
<h2>Who We Aren't</h2>
<p>

View File

@ -0,0 +1,170 @@
---
import Layout from '../../layouts/Layout.astro';
// import { useState, useEffect } from 'react';
import { type Brand, ALL_BRANDS } from '../../data/brands';
import { type Product, ALL_PRODUCTS } from '../../data/products';
import ProductCard from '../../components/ProductCard.astro';
import markdownIt from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs';
const md = markdownIt({
html: true,
linkify: true,
typographer: true,
}).use(
markdownItAttrs, {
// optional, these are default options
leftDelimiter: '{',
rightDelimiter: '}',
allowedAttributes: [] // empty array = all attributes are allowed
}
);
type BrandStaticPath = { params: { brandLookup: string }};
export function getStaticPaths() {
return ALL_BRANDS.map<BrandStaticPath>((brand: Brand) => { return {
params: {
brandLookup: brand.slug
}
}});
}
console.log(getStaticPaths());
const { brandLookup } = Astro.params;
const brand: Brand = ALL_BRANDS.find(b => b.slug === brandLookup)!;
const brandProducts: Product[] = ALL_PRODUCTS.filter(p => p.brandStoreSlug === brand.slug)||[];
---
<Layout title={`${brand.name} Store - Dasher Supply`}>
<main>
<h1 class="center"><a href="/"><span class="text-gradient">Dasher Supply</span></a> &gt; {brand.name} Store</h1>
<div class="instructions">
<div class="flex">
{brand.logoUrl && <div class="float-left"><img src={brand.logoUrl} /></div> }
<div class="flex-right">
{brand.shortDescription && <div class="short-desc" set:html={brand.shortDescription}></div> }
</div>
</div>
{brand.description && <div class="after-flex" set:html={md.render(brand.description)}></div> }
</div>
<!-- <div role="row"> -->
<ul role="list" class="link-card-grid">
{brandProducts.map((product, id) => (
<ProductCard
product={product}
/>
))}
</ul>
<!-- </div> -->
<br />
<div class="disclaimers">
<p>Dasher Supply is not endorsed by or affiliated with DoorDash. As an Amazon Associate I earn from qualifying purchases.</p>
<a href="/about">About this site.</a>
</div>
</main>
</Layout>
<style>
main {
margin: auto;
padding: 1rem;
min-width: 800px;
max-width: calc(100% - 2rem);
color: white;
font-size: 20px;
line-height: 1.6;
}
.center {
text-align: center;
}
h1 {
font-size: 4rem;
font-weight: 700;
line-height: 1;
margin-bottom: 1em;
font-family: "Holtwood One SC", sans-serif;
font-weight: 600;
font-style: bold;
/* text-shadow: -5px -5px 0 rgba(var(--accent-dark), 17%), 3px -3px 0 rgba(var(--accent-light), 10%), -2px 2px 0 rgba(var(--accent-light), 5%), 5px 5px 0 rgba(var(--accent-light), 10%); */
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.disclaimers {
margin-bottom: 2rem;
border: 1px solid rgba(var(--accent-light), 25%);
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
padding: 1.5rem;
border-radius: 8px;
font-family: "Urbanist", sans-serif;
font-weight: 400;
font-style: normal;
}
.instructions {
margin-bottom: 2rem;
border: 1px solid rgba(var(--accent-light), 25%);
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
padding: 0.5rem;
border-radius: 8px;
font-weight: 400;
font-style: normal;
font-size: 1rem;
text-align: left;
}
.instructions .flex {
display: flex;
gap: 2rem;
}
.instructions .after-flex p {
text-indent: 5em each-line;
color: pink;
}
.instructions .short-desc {
font-family: "Caveat", cursive;
font-size: 2rem;
}
.instructions .float-left {
justify-content: flex-start;
}
.instructions .flex-right {
justify-content: flex-start;
position: relative;
}
.instructions code {
font-size: 0.8em;
font-weight: bold;
background: rgba(var(--accent-light), 12%);
color: rgb(var(--accent-light));
border-radius: 4px;
padding: 0.3em 0.4em;
}
.instructions strong {
color: rgb(var(--accent-light));
/* font-weight: 800; */
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(48ch, 1fr));
/* grid-template-columns: repeat(3, 1fr); */
gap: 2rem;
padding: 0;
}
a, a:link, a:visited { text-decoration: none; color: #fff }
a:hover, a:active { text-decoration: underline; color: #fff }
.brand-store {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
</style>

View File

@ -1,14 +1,14 @@
---
import Layout from '../../layouts/Layout.astro';
// import { useState, useEffect } from 'react';
import { allCategories } from '../../data/categories';
import { allProducts } from '../../data/products';
import { ALL_CATEGORIES } from '../../data/categories';
import { ALL_PRODUCTS } from '../../data/products';
import ProductCard from '../../components/ProductCard.astro';
type CategoryStaticPath = { params: { categoryLookup: string }};
export function getStaticPaths() {
return allCategories.map<CategoryStaticPath>((category) => { return {
return ALL_CATEGORIES.map<CategoryStaticPath>((category) => { return {
params: {
categoryLookup: category.seoLink
}
@ -17,8 +17,8 @@ export function getStaticPaths() {
console.log(getStaticPaths());
const { categoryLookup } = Astro.params;
const category = allCategories.find(c => c.seoLink === categoryLookup)!;
const categoryProducts = allProducts.filter(p => p.categoryId === category.id)!;
const category = ALL_CATEGORIES.find(c => c.seoLink === categoryLookup)!;
const categoryProducts = ALL_PRODUCTS.filter(p => p.categoryId === category.id)!;
---
<Layout title={`${category.category} - Dasher Supply`}>

View File

@ -1,7 +1,9 @@
---
import Layout from '../layouts/Layout.astro';
import CategoryCard from '../components/CategoryCard.astro';
import { allCategories } from '../data/categories';
import BrandCard from '../components/BrandCard.astro';
import { ALL_CATEGORIES } from '../data/categories';
import { ALL_BRANDS } from '../data/brands';
---
<Layout title="Dasher Supply">
@ -11,12 +13,22 @@ import { allCategories } from '../data/categories';
Your one-stop shop for all your after-market Dasher supplies.
</p>
<ul role="list" class="link-card-grid">
{allCategories.map(category => (
{ALL_CATEGORIES.map(category => (
<CategoryCard
category={category}
/>
))}
</ul>
<p class="instructions">
We choose brand names you can trust.
</p>
<ul role="list" class="link-card-grid brands">
{ALL_BRANDS.map(brand => (
<BrandCard
brand={brand}
/>
))}
</ul>
<div class="disclaimers">
<p>Dasher Supply is not endorsed by or affiliated with DoorDash. As an Amazon Associate I earn from qualifying purchases.</p>
<a href="/about">About this site.</a>
@ -93,6 +105,10 @@ import { allCategories } from '../data/categories';
gap: 2rem;
padding: 0;
}
.link-card-grid.brands {
display: flex;
place-items: center;
}
a, a:link, a:visited { text-decoration: none; color: #fff }
a:hover { text-decoration: underline; color: #fff }
</style>

View File

@ -3,7 +3,7 @@
*/
import cheerio, { type CheerioAPI } from 'cheerio';
import { type ProductDetails } from '../data/products/product-details';
import { type ProductDetails } from '../data/products/amazon-product-details';
import { type ProductAttribute } from '../data/products/product-attribute';
import { parseNumberFromSelector } from './utils';