Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 82 additions & 63 deletions demos/BookstoreApp.res
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,8 @@ let header = () => {
} else {
"hidden"
}
}>
}
>
{Component.textSignal(() => Int.toString(Signal.get(cartItemCount)))}
</span>,
],
Expand Down Expand Up @@ -374,7 +375,8 @@ let homePage = () => {
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12">
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700">
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700"
>
<div class="text-3xl mb-3"> {Component.text("📖")} </div>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-2">
{Component.text("12 Unique Titles")}
Expand All @@ -384,7 +386,8 @@ let homePage = () => {
</p>
</div>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700">
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700"
>
<div class="text-3xl mb-3"> {Component.text("🎨")} </div>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-2">
{Component.text("Fictional Authors")}
Expand All @@ -394,7 +397,8 @@ let homePage = () => {
</p>
</div>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700">
class="bg-white dark:bg-stone-800 rounded-xl p-6 border-2 border-stone-200 dark:border-stone-700"
>
<div class="text-3xl mb-3"> {Component.text("Ƒ")} </div>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-2">
{Component.text("Functor Currency")}
Expand All @@ -414,7 +418,8 @@ let aboutPage = () => {
{Component.text("About Functional Bookstore")}
</h1>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-8 border-2 border-stone-200 dark:border-stone-700 space-y-6">
class="bg-white dark:bg-stone-800 rounded-xl p-8 border-2 border-stone-200 dark:border-stone-700 space-y-6"
>
<p class="text-lg text-stone-700 dark:text-stone-300">
{Component.text(
"Welcome to the Functional Bookstore, your one-stop shop for the most absurd and delightful functional programming literature in the known universe.",
Expand Down Expand Up @@ -461,49 +466,52 @@ let aboutPage = () => {
</div>
}

/* ProductCard Component using JSX */
type productCardProps = {product: product}

let productCard = (props: productCardProps) => {
let inCart = Computed.make(() => {
let item = getCartItem(props.product.id)
Option.isSome(item)
})
module ProductCard = {
/* ProductCard Component using JSX */
@jsx.component
let make = (~product: product) => {
let inCart = Computed.make(() => {
let item = getCartItem(product.id)
Option.isSome(item)
})

<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow border-2 border-stone-200 dark:border-stone-700">
<div class="text-6xl text-center mb-4"> {Component.text(props.product.cover)} </div>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-2">
{Component.text(props.product.title)}
</h3>
<p class="text-sm text-stone-600 dark:text-stone-400 mb-2">
{Component.text("by " ++ props.product.author)}
</p>
<p class="text-stone-600 dark:text-stone-400 text-sm mb-4 line-clamp-3">
{Component.text(props.product.description)}
</p>
<div class="flex justify-between items-center">
<span class="text-2xl font-bold text-stone-700 dark:text-stone-300">
{Component.text(formatPrice(props.product.price))}
</span>
<button
class={() =>
if Signal.get(inCart) {
"px-4 py-2 bg-green-600 text-white rounded-lg font-semibold"
} else {
"px-4 py-2 bg-stone-900 hover:bg-stone-700 dark:bg-stone-700 dark:hover:bg-stone-600 text-white rounded-lg font-semibold transition-colors"
}}
onClick={_ => addToCart(props.product.id)}>
{Component.textSignal(() =>
if Signal.get(inCart) {
"✓ In Cart"
} else {
"Add to Cart"
}
)}
</button>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow border-2 border-stone-200 dark:border-stone-700"
>
<div class="text-6xl text-center mb-4"> {Component.text(product.cover)} </div>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-2">
{Component.text(product.title)}
</h3>
<p class="text-sm text-stone-600 dark:text-stone-400 mb-2">
{Component.text("by " ++ product.author)}
</p>
<p class="text-stone-600 dark:text-stone-400 text-sm mb-4 line-clamp-3">
{Component.text(product.description)}
</p>
<div class="flex justify-between items-center">
<span class="text-2xl font-bold text-stone-700 dark:text-stone-300">
{Component.text(formatPrice(product.price))}
</span>
<button
class={() =>
if Signal.get(inCart) {
"px-4 py-2 bg-green-600 text-white rounded-lg font-semibold"
} else {
"px-4 py-2 bg-stone-900 hover:bg-stone-700 dark:bg-stone-700 dark:hover:bg-stone-600 text-white rounded-lg font-semibold transition-colors"
}}
onClick={_ => addToCart(product.id)}
>
{Component.textSignal(() =>
if Signal.get(inCart) {
"✓ In Cart"
} else {
"Add to Cart"
}
)}
</button>
</div>
</div>
</div>
}
}

/* CatalogView Component using JSX */
Expand All @@ -513,7 +521,7 @@ let catalogView = () => {
{Component.text("Available Books")}
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{Component.fragment(products->Array.map(product => productCard({product: product})))}
{products->Array.map(product => <ProductCard product />)->JSX.array}
</div>
</div>
}
Expand All @@ -527,7 +535,8 @@ let cartItemRow = (props: cartItemRowProps) => {
switch product {
| Some(p) =>
<div
class="bg-white dark:bg-stone-800 rounded-lg p-4 shadow border-2 border-stone-200 dark:border-stone-700">
class="bg-white dark:bg-stone-800 rounded-lg p-4 shadow border-2 border-stone-200 dark:border-stone-700"
>
<div class="flex gap-4">
<div class="text-4xl"> {Component.text(p.cover)} </div>
<div class="flex-1">
Expand All @@ -543,16 +552,19 @@ let cartItemRow = (props: cartItemRowProps) => {
<div class="flex items-center gap-2">
<button
class="px-3 py-1 bg-stone-200 dark:bg-stone-700 rounded hover:bg-stone-300 dark:hover:bg-stone-600 font-bold"
onClick={_ => updateQuantity(p.id, props.item.quantity - 1)}>
onClick={_ => updateQuantity(p.id, props.item.quantity - 1)}
>
{Component.text("-")}
</button>
<span
class="px-4 py-1 bg-stone-100 dark:bg-stone-700 rounded font-semibold text-stone-900 dark:text-white">
class="px-4 py-1 bg-stone-100 dark:bg-stone-700 rounded font-semibold text-stone-900 dark:text-white"
>
{Component.text(Int.toString(props.item.quantity))}
</span>
<button
class="px-3 py-1 bg-stone-200 dark:bg-stone-700 rounded hover:bg-stone-300 dark:hover:bg-stone-600 font-bold"
onClick={_ => updateQuantity(p.id, props.item.quantity + 1)}>
onClick={_ => updateQuantity(p.id, props.item.quantity + 1)}
>
{Component.text("+")}
</button>
</div>
Expand All @@ -561,7 +573,8 @@ let cartItemRow = (props: cartItemRowProps) => {
</p>
<button
class="text-sm text-red-600 hover:text-red-700 font-semibold"
onClick={_ => removeFromCart(p.id)}>
onClick={_ => removeFromCart(p.id)}
>
{Component.text("Remove")}
</button>
</div>
Expand All @@ -585,7 +598,8 @@ let cartView = () => {
"block"
} else {
"hidden"
}}>
}}
>
<div class="text-center py-12">
<p class="text-xl text-stone-600 dark:text-stone-400 mb-4">
{Component.text("Your cart is empty")}
Expand All @@ -609,12 +623,12 @@ let cartView = () => {
"hidden"
} else {
"block"
}}>
<div class="space-y-4 mb-6">
{Component.list(cart, item => cartItemRow({item: item}))}
</div>
}}
>
<div class="space-y-4 mb-6"> {Component.list(cart, item => cartItemRow({item: item}))} </div>
<div
class="bg-stone-100 dark:bg-stone-800 rounded-lg p-6 border-2 border-stone-300 dark:border-stone-700">
class="bg-stone-100 dark:bg-stone-800 rounded-lg p-6 border-2 border-stone-300 dark:border-stone-700"
>
<div class="flex justify-between items-center mb-4">
<span class="text-2xl font-bold text-stone-900 dark:text-white">
{Component.text("Total:")}
Expand Down Expand Up @@ -664,7 +678,8 @@ let checkoutView = () => {
{Component.text("Checkout")}
</h2>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg border-2 border-stone-200 dark:border-stone-700 mb-6">
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg border-2 border-stone-200 dark:border-stone-700 mb-6"
>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-4">
{Component.text("Shipping Information")}
</h3>
Expand Down Expand Up @@ -716,7 +731,8 @@ let checkoutView = () => {
</div>
</div>
<div
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg border-2 border-stone-200 dark:border-stone-700 mb-6">
class="bg-white dark:bg-stone-800 rounded-xl p-6 shadow-lg border-2 border-stone-200 dark:border-stone-700 mb-6"
>
<h3 class="text-xl font-bold text-stone-900 dark:text-white mb-4">
{Component.text("Payment Information")}
</h3>
Expand All @@ -733,7 +749,8 @@ let checkoutView = () => {
</div>
</div>
<div
class="bg-stone-100 dark:bg-stone-800 rounded-lg p-6 border-2 border-stone-300 dark:border-stone-700 mb-6">
class="bg-stone-100 dark:bg-stone-800 rounded-lg p-6 border-2 border-stone-300 dark:border-stone-700 mb-6"
>
<div class="flex justify-between items-center mb-2">
<span class="text-stone-600 dark:text-stone-400">
{Component.textSignal(() => "Items (" ++ Int.toString(Signal.get(cartItemCount)) ++ ")")}
Expand Down Expand Up @@ -773,7 +790,8 @@ let checkoutView = () => {
)}
<button
class="flex-1 px-6 py-3 bg-stone-900 hover:bg-stone-700 dark:bg-stone-700 dark:hover:bg-stone-600 text-white rounded-lg font-semibold transition-colors"
onClick={_ => completeOrder()}>
onClick={_ => completeOrder()}
>
{Component.text("Complete Order")}
</button>
</div>
Expand All @@ -784,7 +802,8 @@ let checkoutView = () => {
let orderConfirmedView = () => {
<div class="max-w-2xl mx-auto p-6">
<div
class="bg-white dark:bg-stone-800 rounded-xl p-8 shadow-lg border-2 border-stone-300 dark:border-stone-700 text-center">
class="bg-white dark:bg-stone-800 rounded-xl p-8 shadow-lg border-2 border-stone-300 dark:border-stone-700 text-center"
>
<div class="text-6xl mb-4"> {Component.text("✅")} </div>
<h2 class="text-3xl font-bold text-stone-900 dark:text-white mb-4">
{Component.text("Order Confirmed!")}
Expand Down