Packages

pify: some helpers to use gleam in your shopify project.

Current section

Files

Jump to
pify src storefront products.gleam
Raw

src/storefront/products.gleam

import gleam/dynamic/decode
import gleam/json
import gleam/list
import gleam/option.{None, Some}
import storefront/fragments.{product_fragment}
import storefront/utils.{
type Connection, type Image, type PriceRange, type ProductVariant, type Seo,
image_decoder, image_to_json, price_range_decoder, price_range_to_json,
product_variant_decoder, product_variant_to_json, remove_edges_and_nodes,
reshape_images, seo_decoder, seo_to_json,
}
pub type ProductOption {
ProductOption(id: String, name: String, values: List(String))
}
const hidden_product_tags = "gleam-frontend-hidden"
pub fn product_option_decoder() -> decode.Decoder(ProductOption) {
use id <- decode.field("id", decode.string)
use name <- decode.field("name", decode.string)
use values <- decode.field("values", decode.list(decode.string))
decode.success(ProductOption(id:, name:, values:))
}
pub type Products {
Products(products: List(Product))
}
pub fn products_decoder() -> decode.Decoder(Products) {
use products <- decode.field("products", decode.list(product_decoder()))
decode.success(Products(products:))
}
pub type Product {
Product(
id: String,
handle: String,
available_for_sale: Bool,
title: String,
description: String,
description_html: String,
price_range: PriceRange,
featured_image: Image,
seo: Seo,
tags: List(String),
updated_at: String,
variants: List(ProductVariant),
images: List(Image),
)
}
pub fn product_to_json(product: Product) -> json.Json {
let Product(
id:,
handle:,
available_for_sale:,
title:,
description:,
description_html:,
price_range:,
featured_image:,
seo:,
tags:,
updated_at:,
variants:,
images:,
) = product
json.object([
#("id", json.string(id)),
#("handle", json.string(handle)),
#("availableForSale", json.bool(available_for_sale)),
#("title", json.string(title)),
#("description", json.string(description)),
#("descriptionHtml", json.string(description_html)),
#("priceRange", price_range_to_json(price_range)),
#("featuredImage", image_to_json(featured_image)),
#("seo", seo_to_json(seo)),
#("tags", json.array(tags, json.string)),
#("updatedAt", json.string(updated_at)),
#("variants", json.array(variants, product_variant_to_json)),
#("images", json.array(images, image_to_json)),
])
}
pub fn product_decoder() -> decode.Decoder(Product) {
use id <- decode.field("id", decode.string)
use handle <- decode.field("handle", decode.string)
use available_for_sale <- decode.field("available_for_sale", decode.bool)
use title <- decode.field("title", decode.string)
use description <- decode.field("description", decode.string)
use description_html <- decode.field("description_html", decode.string)
use price_range <- decode.field("price_range", price_range_decoder())
use featured_image <- decode.field("featured_image", image_decoder())
use seo <- decode.field("seo", seo_decoder())
use tags <- decode.field("tags", decode.list(decode.string))
use updated_at <- decode.field("updated_at", decode.string)
use variants <- decode.field(
"variants",
decode.list(product_variant_decoder()),
)
use images <- decode.field("images", decode.list(image_decoder()))
decode.success(Product(
id:,
handle:,
available_for_sale:,
title:,
description:,
description_html:,
price_range:,
featured_image:,
seo:,
tags:,
updated_at:,
variants:,
images:,
))
}
pub type ShopifyProduct {
ShopifyProduct(
id: String,
handle: String,
available_for_sale: Bool,
title: String,
description: String,
description_html: String,
price_range: PriceRange,
variants: Connection(ProductVariant),
featured_image: Image,
images: Connection(Image),
seo: Seo,
tags: List(String),
updated_at: String,
)
}
// 2. Products
pub type ShopifyCollection {
ShopifyCollection(
handle: String,
title: String,
description: String,
seo: Seo,
updated_at: String,
)
}
pub fn reshape_product(
product: ShopifyProduct,
filter_hidden_products: Bool,
) -> Product {
let result = {
let is_hidden = list.contains(product.tags, hidden_product_tags)
case filter_hidden_products && is_hidden {
True -> None
False -> {
let reshaped_image = reshape_images(product.images, product.title)
let reshaped_variants = remove_edges_and_nodes(product.variants)
Some(Product(
id: product.id,
handle: product.handle,
available_for_sale: product.available_for_sale,
title: product.title,
description: product.description,
description_html: product.description_html,
price_range: product.price_range,
featured_image: product.featured_image,
seo: product.seo,
tags: product.tags,
updated_at: product.updated_at,
images: reshaped_image,
variants: reshaped_variants,
))
}
}
}
let reshaped_image = reshape_images(product.images, product.title)
let reshaped_variants = remove_edges_and_nodes(product.variants)
option.unwrap(
result,
Product(
id: product.id,
handle: product.handle,
available_for_sale: product.available_for_sale,
title: product.title,
description: product.description,
description_html: product.description_html,
price_range: product.price_range,
featured_image: product.featured_image,
seo: product.seo,
tags: product.tags,
updated_at: product.updated_at,
images: reshaped_image,
variants: reshaped_variants,
),
)
}
pub fn reshape_products(products: List(ShopifyProduct)) -> List(Product) {
list.map(products, fn(product) { reshape_product(product, True) })
}
/// Queries
///
pub const get_product_query = "
query getProduct($handle: String!) {
product(handle: $handle) {
...product
}
}
"
<> product_fragment
pub const get_products_query = "
query getProducts($sortKey: ProductSortKeys, $reverse: Boolean, $query: String) {
products(sortKey: $sortKey, reverse: $reverse, query: $query, first: 100) {
edges {
node {
...product
}
}
}
}
"
<> product_fragment
pub const get_product_recommendations_query = "
query getProductRecommendations($productId: ID!) {
productRecommendations(productId: $productId) {
...product
}
}
"
<> product_fragment