253 lines
13 KiB
Plaintext
253 lines
13 KiB
Plaintext
---
|
|
import type { ContentsDto } from "../data/internals/ContentsDtoT";
|
|
import type { Localized } from "../data/internals/LocalizedT";
|
|
import type { Product } from "../data/models/multis/Product";
|
|
import { getAssetById, getBrandsByIds, getMarketplacesByIds, getOffersByListingId } from "../data/api-client";
|
|
import path from "node:path";
|
|
import * as core from "../data/core/client";
|
|
import { renderMarkdown } from "../lib/rendering";
|
|
import { SCHEMAS } from "../data/models/schemas";
|
|
import type { Listing } from "../data/models/multis/Listing";
|
|
import type { Offer } from "../data/models/multis/Offer";
|
|
import type { Marketplace } from "../data/models/multis/Marketplace";
|
|
import ImageCarousel from "./ImageCarousel.astro";
|
|
import type { AmazonMarketplaceConnection } from "../data/models/components/AmazonMarketplaceConnection";
|
|
import { getSellersByIds } from "../data/api-client";
|
|
import { DateTime } from "luxon";
|
|
import type { Multilingual, SquidexEditable } from "./SharedProperties";
|
|
|
|
interface Props extends Multilingual, SquidexEditable {
|
|
productDto: ContentsDto<Product>,
|
|
}
|
|
let category={ } as unknown as any;let site={ } as unknown as any
|
|
const formatAsCurrency = (amount: number) => amount.toLocaleString(locale, { style: 'currency', currency: 'USD' });
|
|
|
|
const { productDto, editToken, locale } = Astro.props;
|
|
const product = productDto.items[0].data!;
|
|
|
|
let amazonConnectorSchemaId = (await core.client.schemas.getSchema({ schema: 'product-marketplace-connection-amazon' })).id;
|
|
let brandDto = (await getBrandsByIds(product.brand.iv[0]));
|
|
let possibleAmazonConnectors = product.marketplaceConnections.iv.filter((connection) => connection.connection.schemaId === amazonConnectorSchemaId);
|
|
let amazonConnector = possibleAmazonConnectors.length > 0 ? possibleAmazonConnectors[0].connection as AmazonMarketplaceConnection : undefined;
|
|
|
|
const listingsDto = await core.getContentsUsingJsonQuery<Listing>(SCHEMAS.LISTINGS, JSON.stringify({
|
|
filter: {
|
|
path: "data.product.iv",
|
|
op: "eq",
|
|
value: productDto.items[0].id,
|
|
}
|
|
}));
|
|
const listingOffersDtos = listingsDto.items.map(async (listingDto) => await core.getContentsUsingJsonQuery<Offer>(SCHEMAS.OFFERS, JSON.stringify({
|
|
filter: {
|
|
path: "data.listing.iv",
|
|
op: "eq",
|
|
value: listingDto.id
|
|
}
|
|
})));
|
|
|
|
// listingsDto.items.forEach(async (listing) => {
|
|
// let marketplaceId = listing.data?.marketplace.iv[0]!;
|
|
// const marketplaceDto = await core.getContentsByIds<Marketplace>(SCHEMAS.MARKETPLACES, marketplaceId);
|
|
// const marketplace = marketplaceDto.items[0].data!;
|
|
// pushDisclaimer({ renderedText: renderMarkdown(marketplace.disclaimer[locale]), marketplaceEditToken: marketplaceDto.items[0].editToken! });
|
|
// });
|
|
|
|
let productListingImages: string[] = [];
|
|
for (let listingDto of listingsDto.items) {
|
|
for (let assetId of listingDto.data?.marketplaceImages?.iv||[]) {
|
|
let assetDto = await getAssetById(assetId);
|
|
let assetUrl = path.posix.join('/img', assetDto.links['content']
|
|
.href
|
|
.split('/')
|
|
.reverse()
|
|
.filter((_value, index, array) => index < (array.length - index - 2))
|
|
.reverse()
|
|
.join('/'));
|
|
productListingImages.push(assetUrl);
|
|
}
|
|
}
|
|
|
|
let i18n: { [key: string]: Localized<string> } = {
|
|
'Brand:': {
|
|
'en-US': "Brand:",
|
|
'es-US': "Marca :",
|
|
'fr-CA': "Marque :",
|
|
},
|
|
'New from': {
|
|
'en-US': "New from",
|
|
'es-US': "Nuevo desde",
|
|
'fr-CA': "Nouveau depuis",
|
|
},
|
|
'Used from': {
|
|
'en-US': "Used from",
|
|
'es-US': "Usado desde",
|
|
'fr-CA': "Utilisé depuis",
|
|
},
|
|
'on': {
|
|
'en-US': "on",
|
|
'es-US': "en",
|
|
'fr-CA': "sur",
|
|
},
|
|
'Price information updated as of': {
|
|
'en-US': "Price information updated as of",
|
|
'es-US': "Información de precio actualizada a partir de",
|
|
'fr-CA': "Information sur le prix mise à jour jusqu\'à",
|
|
},
|
|
'Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.': {
|
|
'en-US': "Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.",
|
|
'es-US': "Precios de los productos y disponibilidad son exactos a la fecha/hora indicada y están sujetos a cambios. Cualquier información sobre precios y disponibilidad que se muestre en Amazon.com al momento del pago se aplicará a la compra de este producto.",
|
|
'fr-CA': "Les prix des produits et la disponibilité sont exacts à la date/heure indiquée et peuvent varier. Toute information sur les prix et la disponibilité affichés sur Amazon.com au moment de l'achat s'appliqueront à l'achat de ce produit."
|
|
}
|
|
}
|
|
|
|
---
|
|
<div class="callout">
|
|
<!-- <Fragment content={product.callout||category.description||site.categoriesCallout} /> -->
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-12 col-md-6 col-lg-4">
|
|
{ productListingImages.length == 1 &&
|
|
<img src={productListingImages[0]} alt={product.productName[locale]} style="max-width: 100%;" />
|
|
}
|
|
{ productListingImages.length > 1 &&
|
|
<ImageCarousel showDots={true} images={productListingImages.map((productUrl: string) => { return { src: productUrl }; } )||[]} />
|
|
}
|
|
</div>
|
|
<div class="col-sm-12 col-md-6 col-lg-8">
|
|
<h3 class="card-title">
|
|
<a href={amazonConnector?.siteStripeUrl||''}>{product?.productName[locale]}</a>
|
|
</h3>
|
|
<p>
|
|
<!-- <StarRating value={product?.amazonProductDetails?.reviewRating||0} max={5} overlayColor="#13151a" /> {product?.amazonProductDetails?.reviewCount} Reviews -->
|
|
{ brandDto && brandDto.items.length &&
|
|
// <span>•</span>
|
|
<span class="item-metadata-key">{i18n['Brand:'][locale].toUpperCase()}</span> <span class="item-metadata-value"><a href={`/${brandDto.items[0].data!.slug[locale]}`}>{brandDto.items[0].data!.brandName[locale]}</a></span>
|
|
}
|
|
</p>
|
|
<div class="navbar">
|
|
{ listingsDto.items.map(async (listingDto) => {
|
|
let marketplacesDto = await getMarketplacesByIds(listingDto.data?.marketplace.iv[0]!);
|
|
let offersDto = await getOffersByListingId(listingDto.id);
|
|
return (
|
|
<Fragment>
|
|
{ offersDto.items.map(async (offerDto) => {
|
|
let sellersDto = await getSellersByIds(offerDto.data?.seller.iv[0]!);
|
|
return (
|
|
<span class="custom-btn-container">
|
|
{ offerDto.data?.newPrice.iv !== null &&
|
|
<a href={amazonConnector?.siteStripeUrl||''}>
|
|
{offerDto.data!.newPrice.iv ? formatAsCurrency(offerDto.data!.newPrice.iv||0) : 'See Price'} {i18n['New from'][locale]} {sellersDto.items[0].data?.sellerName[locale]} {i18n['on'][locale]} {marketplacesDto.items[0].data?.marketplaceName[locale]}
|
|
<span>→</span>
|
|
<br />
|
|
<small><small><i>{i18n['Price information updated as of'][locale]} {DateTime.fromISO(offerDto.data!.offerDate.iv).setLocale(locale).toFormat('D, t ZZZZ')}.</i></small></small>
|
|
</a>
|
|
}
|
|
{ offerDto.data?.usedPrice.iv &&
|
|
<a href={amazonConnector?.siteStripeUrl||''}>
|
|
{offerDto.data!.usedPrice.iv ? formatAsCurrency(offerDto.data!.usedPrice.iv||0) : 'See Price'} {i18n['Used from'][locale]} {sellersDto.items[0].data?.sellerName[locale]} {i18n['on'][locale]} {marketplacesDto.items[0].data?.marketplaceName[locale]}
|
|
<span>→</span><br />
|
|
<small><small><i>{i18n['Price information updated as of'][locale]} {DateTime.fromISO(offerDto.data!.offerDate.iv).setLocale(locale).toFormat('D, t ZZZZ')}.</i></small></small>
|
|
</a>
|
|
}
|
|
</span>
|
|
);
|
|
})}
|
|
{ marketplacesDto.items[0].data?.marketplaceName["en-US"] === 'Amazon' && locale === 'en-US' &&
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.']['en-US']}</i></small></small></p>
|
|
}
|
|
{ marketplacesDto.items[0].data?.marketplaceName["en-US"] === 'Amazon' && locale !== 'en-US' &&
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.']['en-US']}</i></small></small></p>
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.'][locale]}</i></small></small></p>
|
|
}
|
|
</Fragment>
|
|
)
|
|
})}
|
|
</div>
|
|
{ product?.description &&
|
|
<Fragment set:html={renderMarkdown(product.description[locale])} />
|
|
}
|
|
<!-- { !product?.description &&
|
|
<ul>
|
|
{product?.amazonProductDetails?.featureBullets?.map(featureBullet => (
|
|
<li>{featureBullet}</li>
|
|
))}
|
|
</ul>
|
|
<p>
|
|
{product?.amazonProductDetails?.description && product?.amazonProductDetails?.description}
|
|
</p>
|
|
} -->
|
|
<div class="navbar">
|
|
{ listingsDto.items.map(async (listingDto) => {
|
|
let marketplacesDto = await getMarketplacesByIds(listingDto.data?.marketplace.iv[0]!);
|
|
let offersDto = await getOffersByListingId(listingDto.id);
|
|
return (
|
|
<Fragment>
|
|
{ offersDto.items.map(async (offerDto) => {
|
|
let sellersDto = await getSellersByIds(offerDto.data?.seller.iv[0]!);
|
|
return (
|
|
<span class="custom-btn-container">
|
|
{ offerDto.data?.newPrice.iv !== null &&
|
|
<a href={amazonConnector?.siteStripeUrl||''}>
|
|
{offerDto.data!.newPrice.iv ? formatAsCurrency(offerDto.data!.newPrice.iv||0) : 'See Price'} {i18n['New from'][locale]} {sellersDto.items[0].data?.sellerName[locale]} {i18n['on'][locale]} {marketplacesDto.items[0].data?.marketplaceName[locale]}
|
|
<span>→</span><br />
|
|
<small>{i18n['Price information updated as of'][locale]} {DateTime.fromISO(offerDto.data!.offerDate.iv).setLocale(locale).toFormat('D, t ZZZZ')}.</small>
|
|
</a>
|
|
}
|
|
{ offerDto.data?.usedPrice.iv &&
|
|
<a href={amazonConnector?.siteStripeUrl||''}>
|
|
{offerDto.data!.usedPrice.iv ? formatAsCurrency(offerDto.data!.usedPrice.iv||0) : 'See Price'} {i18n['Used from'][locale]} {sellersDto.items[0].data?.sellerName[locale]} {i18n['on'][locale]} {marketplacesDto.items[0].data?.marketplaceName[locale]}
|
|
<span>→</span><br />
|
|
<small>{i18n['Price information updated as of'][locale]} {DateTime.fromISO(offerDto.data!.offerDate.iv).setLocale(locale).toFormat('D, t ZZZZ')}.</small>
|
|
</a>
|
|
}
|
|
</span>
|
|
);
|
|
})}
|
|
{ marketplacesDto.items[0].data?.marketplaceName["en-US"] === 'Amazon' && locale === 'en-US' &&
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.']['en-US']}</i></small></small></p>
|
|
}
|
|
{ marketplacesDto.items[0].data?.marketplaceName["en-US"] === 'Amazon' && locale !== 'en-US' &&
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.'][locale]}</i></small></small></p>
|
|
<p><small><small><i>{i18n['Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon.com at the time of purchase will apply to the purchase of this product.']['en-US']}</i></small></small></p>
|
|
}
|
|
</Fragment>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.custom-btn-container {
|
|
background-color: #23262d;
|
|
background-image: none;
|
|
background-size: 400%;
|
|
border-radius: 8px;
|
|
background-position: 100%;
|
|
padding: 1px;
|
|
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;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.custom-btn-container > a {
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
line-height: 1.4;
|
|
/* padding: calc(0.5rem - 1px); */
|
|
color: white;
|
|
padding: 1rem;
|
|
background-color: #23262d;
|
|
opacity: 0.8;
|
|
}
|
|
.custom-btn-container:is(:hover, :focus-within) {
|
|
background-position: 0;
|
|
background-image: var(--accent-gradient);
|
|
}
|
|
.navbar {
|
|
display: flex;
|
|
/* grid-template: auto; */
|
|
}
|
|
</style> |