code from amapei
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package react.store;
|
||||
import react.ReactComponent;
|
||||
import react.ReactMacro.jsx;
|
||||
import Common;
|
||||
|
||||
typedef CartProps = {
|
||||
var order:OrderSimple;
|
||||
var addToCart:ProductInfo -> Int -> Void;
|
||||
var removeFromCart:ProductInfo -> ?Int -> Void;
|
||||
var submitOrder:OrderSimple -> Void;
|
||||
};
|
||||
|
||||
class Cart extends react.ReactComponentOfProps<CartProps>
|
||||
{
|
||||
|
||||
function addToCart(product:ProductInfo, quantity:Int):Void {
|
||||
props.addToCart(product, quantity);
|
||||
}
|
||||
|
||||
function removeFromCart(product:ProductInfo, quantity:Int):Void {
|
||||
props.removeFromCart(product, quantity);
|
||||
}
|
||||
|
||||
function removeAllFromCart(product:ProductInfo):Void {
|
||||
props.removeFromCart(product);
|
||||
}
|
||||
|
||||
function submitOrder():Void {
|
||||
props.submitOrder(props.order);
|
||||
}
|
||||
|
||||
override public function render(){
|
||||
return jsx('
|
||||
<div className="cart">
|
||||
<h3>Ma Commande</h3>
|
||||
${renderProducts()}
|
||||
${renderFooter()}
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderProducts() {
|
||||
var productsToOrder = props.order.products.map(function(product:ProductWithQuantity) {
|
||||
var quantity = product.quantity;
|
||||
var product = product.product;
|
||||
|
||||
return jsx('
|
||||
<div className="product-to-order" key=${product.name}>
|
||||
<div>${product.name}</div>
|
||||
<div>$quantity</div>
|
||||
<div className="cart-action-buttons">
|
||||
<div onClick=${function(){
|
||||
this.addToCart(product, 1);
|
||||
}}>
|
||||
+
|
||||
</div>
|
||||
<div onClick=${function(){
|
||||
this.removeFromCart(product, 1);
|
||||
}}>
|
||||
-
|
||||
</div>
|
||||
<div onClick=${function(){
|
||||
this.removeAllFromCart(product);
|
||||
}}>
|
||||
x
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
');
|
||||
});
|
||||
|
||||
return jsx('
|
||||
<div className="products-to-order">
|
||||
${productsToOrder}
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderFooter() {
|
||||
var buttonClasses = ["order-button"];
|
||||
var submit = submitOrder;
|
||||
|
||||
if (props.order.products.length == 0) {
|
||||
buttonClasses.push("order-button--disabled");
|
||||
submit = null;
|
||||
}
|
||||
|
||||
return jsx('
|
||||
<div className="cart-footer">
|
||||
<div className="total">
|
||||
Total
|
||||
<div>${props.order.total} €</div>
|
||||
</div>
|
||||
<div className=${buttonClasses.join(" ")} onClick=$submit>Commander</div>
|
||||
</div>
|
||||
');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package react.store;
|
||||
import react.ReactComponent;
|
||||
import react.ReactMacro.jsx;
|
||||
import Common;
|
||||
|
||||
using Lambda;
|
||||
|
||||
typedef FiltersProps = {
|
||||
var categories:Array<CategoryInfo>;
|
||||
var filters:Array<String>;
|
||||
var toggleFilter:String -> Void;
|
||||
};
|
||||
|
||||
class Filters extends react.ReactComponentOfProps<FiltersProps>
|
||||
{
|
||||
override public function render(){
|
||||
return jsx('
|
||||
<div className="filters">
|
||||
<h3>Filtres</h3>
|
||||
${renderFilters()}
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderFilters() {
|
||||
return props.categories.map(function(category) {
|
||||
var classNames = ["filter"];
|
||||
if (props.filters.has(category.name))
|
||||
classNames.push("active");
|
||||
|
||||
return jsx('
|
||||
<div
|
||||
className=${classNames.join(" ")}
|
||||
key=${category.id}
|
||||
onClick=${function(){
|
||||
props.toggleFilter(category.name);
|
||||
}}
|
||||
>
|
||||
${category.name}
|
||||
</div>
|
||||
');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package react.store;
|
||||
|
||||
import js.Browser.window;
|
||||
import react.ReactComponent;
|
||||
import react.ReactMacro.jsx;
|
||||
import Common;
|
||||
|
||||
typedef ProductProps = {
|
||||
var product:ProductInfo;
|
||||
var addToCart:ProductInfo -> Int -> Void;
|
||||
};
|
||||
|
||||
typedef ProductState = {
|
||||
var quantity:Int;
|
||||
};
|
||||
|
||||
class Product extends react.ReactComponentOfPropsAndState<ProductProps, ProductState>
|
||||
{
|
||||
static inline var OVERLAY_URL = '/shop/productInfo';
|
||||
static inline var IMAGE_WIDTH = 120;
|
||||
static inline var IMAGE_HEIGHT = 120;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
state = {
|
||||
quantity: 1
|
||||
};
|
||||
}
|
||||
|
||||
function openOverlay() {
|
||||
untyped window._.overlay('$OVERLAY_URL/${props.product.id}', props.product.name);
|
||||
}
|
||||
|
||||
function updateQuantity(event:Dynamic) {
|
||||
var quantity = Std.parseInt(event.target.value);
|
||||
|
||||
if (Std.is(quantity, Int) && quantity > 0)
|
||||
setState({
|
||||
quantity: Std.int(quantity)
|
||||
});
|
||||
}
|
||||
|
||||
function addToCart() {
|
||||
props.addToCart(props.product, state.quantity);
|
||||
}
|
||||
|
||||
override public function render(){
|
||||
var product = props.product;
|
||||
|
||||
return jsx('
|
||||
<div className="product">
|
||||
<img src=${product.image} width=${IMAGE_WIDTH+'px'} height=${IMAGE_HEIGHT+'px'} alt={product.name} />
|
||||
<div className="body">
|
||||
<a onClick=$openOverlay>
|
||||
${product.name}
|
||||
</a>
|
||||
<div>${product.price} €</div>
|
||||
<input type="number" value=${state.quantity} onChange=$updateQuantity />
|
||||
<div className="button" onClick=$addToCart>Ajouter</div>
|
||||
</div>
|
||||
</div>
|
||||
');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package react.store;
|
||||
import react.ReactComponent;
|
||||
import react.ReactMacro.jsx;
|
||||
import Common;
|
||||
|
||||
using Lambda;
|
||||
|
||||
typedef ProductListProps = {
|
||||
var categories:Array<CategoryInfo>;
|
||||
var productsBySubcategoryIdMap:Map<Int, Array<ProductInfo>>;
|
||||
var filters:Array<String>;
|
||||
var addToCart:ProductInfo -> Int -> Void;
|
||||
};
|
||||
|
||||
class ProductList extends react.ReactComponentOfProps<ProductListProps>
|
||||
{
|
||||
override public function render(){
|
||||
return jsx('
|
||||
<div className="categories">
|
||||
${renderCategories()}
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderCategories() {
|
||||
return props.categories.map(function(category) {
|
||||
if (!props.filters.has(category.name))
|
||||
return null;
|
||||
|
||||
return jsx('
|
||||
<div className="category" key=${category.name}>
|
||||
<h2>${category.name}</h2>
|
||||
<div className="subCategories">
|
||||
${renderSubCategories(category)}
|
||||
</div>
|
||||
</div>
|
||||
');
|
||||
});
|
||||
}
|
||||
|
||||
function renderSubCategories(category) {
|
||||
var subCategories = category.subcategories.map(function(category) {
|
||||
if (!props.productsBySubcategoryIdMap.exists(category.id))
|
||||
return jsx('<div key=${category.name}>Loading...</div>');
|
||||
|
||||
var products = props.productsBySubcategoryIdMap.get(category.id);
|
||||
|
||||
return jsx('
|
||||
<div className="sub-category" key=${category.name}>
|
||||
<h3>${category.name}</h3>
|
||||
<div className="products">
|
||||
${renderProducts(products)}
|
||||
</div>
|
||||
</div>
|
||||
');
|
||||
});
|
||||
|
||||
return jsx('
|
||||
<div className="sub-categories">
|
||||
$subCategories
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderProducts(products) {
|
||||
return products.map(function(product) {
|
||||
return jsx('
|
||||
<Product product=${product} key=${product.id} addToCart=${props.addToCart}/>
|
||||
');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package react.store;
|
||||
import react.ReactComponent;
|
||||
import react.ReactMacro.jsx;
|
||||
import haxe.Json;
|
||||
|
||||
using Lambda;
|
||||
|
||||
import Common;
|
||||
import utils.CartUtils;
|
||||
import utils.HttpUtil;
|
||||
|
||||
typedef StoreProps = {
|
||||
var place:Int;
|
||||
var date:String;
|
||||
};
|
||||
|
||||
typedef StoreState = {
|
||||
var place:PlaceInfos;
|
||||
var orderByEndDates:Array<OrderByEndDate>;
|
||||
var categories:Array<CategoryInfo>;
|
||||
var productsBySubcategoryIdMap:Map<Int, Array<ProductInfo>>;
|
||||
var order:OrderSimple;
|
||||
var filters:Array<String>;
|
||||
};
|
||||
|
||||
class Store extends react.ReactComponentOfPropsAndState<StoreProps, StoreState>
|
||||
{
|
||||
static inline var CATEGORY_URL = '/api/shop/categories';
|
||||
static inline var PRODUCT_URL = '/api/shop/products';
|
||||
static inline var INIT_URL = '/api/shop/init';
|
||||
static inline var VIEW_URL = '/place/view';
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
state = {
|
||||
place: null,
|
||||
orderByEndDates: [],
|
||||
categories: [],
|
||||
filters: [],
|
||||
productsBySubcategoryIdMap: new Map(),
|
||||
order: {
|
||||
products: [],
|
||||
total: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
override function componentDidMount() {
|
||||
var categoriesRequest = HttpUtil.fetch(CATEGORY_URL, GET, {date: props.date, place: props.place}, JSON);
|
||||
var initRequest = HttpUtil.fetch(INIT_URL, GET, {date: props.date, place: props.place}, JSON);
|
||||
|
||||
initRequest.then(function(infos:Dynamic) {
|
||||
setState({
|
||||
place: infos.place,
|
||||
orderByEndDates: infos.orderEndDates
|
||||
});
|
||||
})
|
||||
.catchError(function(error) {
|
||||
trace("ERROR", error);
|
||||
});
|
||||
|
||||
categoriesRequest.then(function(categories:Dynamic) {
|
||||
var categories:Array<CategoryInfo> = categories.categories;
|
||||
var subCategories = [];
|
||||
|
||||
for (category in categories) {
|
||||
subCategories = subCategories.concat(category.subcategories);
|
||||
}
|
||||
|
||||
setState({
|
||||
categories: categories,
|
||||
filters: categories.map(function(category) {
|
||||
return category.name;
|
||||
})
|
||||
});
|
||||
|
||||
subCategories.map(function(category:CategoryInfo) {
|
||||
return HttpUtil.fetch(PRODUCT_URL, GET, {date: props.date, place: props.place, subcategory: category.id}, JSON)
|
||||
.then(function(result) {
|
||||
var productsBySubcategoryIdMapCopy = [
|
||||
for (key in state.productsBySubcategoryIdMap.keys())
|
||||
key => state.productsBySubcategoryIdMap.get(key)
|
||||
];
|
||||
productsBySubcategoryIdMapCopy.set(category.id, result.products);
|
||||
|
||||
setState({
|
||||
productsBySubcategoryIdMap: productsBySubcategoryIdMapCopy
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.catchError(function(error) {
|
||||
trace("ERROR", error);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleFilter(category:String) {
|
||||
var filters = state.filters.copy();
|
||||
|
||||
if (state.filters.find(function(categoryInFilter) {
|
||||
return category == categoryInFilter;
|
||||
}) != null)
|
||||
filters.remove(category);
|
||||
else
|
||||
filters.push(category);
|
||||
|
||||
if (filters.length == 0)
|
||||
filters = state.categories.map(function(category) {
|
||||
return category.name;
|
||||
});
|
||||
|
||||
setState({
|
||||
filters: filters
|
||||
});
|
||||
}
|
||||
|
||||
function addToCart(productToAdd:ProductInfo, quantity:Int):Void {
|
||||
setState({
|
||||
order: CartUtils.addToCart(state.order, productToAdd, quantity)
|
||||
});
|
||||
}
|
||||
|
||||
function removeFromCart(productToRemove:ProductInfo, ?quantity:Int):Void {
|
||||
setState({
|
||||
order: CartUtils.removeFromCart(state.order, productToRemove, quantity)
|
||||
});
|
||||
}
|
||||
|
||||
function submitOrder(order:OrderSimple) {
|
||||
var orderInSession = {
|
||||
total: order.total,
|
||||
products: order.products.map(function(p:ProductWithQuantity){
|
||||
return {
|
||||
productId: p.product.id,
|
||||
quantity: p.quantity
|
||||
};
|
||||
})
|
||||
}
|
||||
trace('Order', orderInSession);
|
||||
}
|
||||
|
||||
override public function render(){
|
||||
return jsx('
|
||||
<div className="shop">
|
||||
${renderHeader()}
|
||||
<ProductList
|
||||
categories=${state.categories}
|
||||
productsBySubcategoryIdMap=${state.productsBySubcategoryIdMap}
|
||||
filters=${state.filters}
|
||||
addToCart=$addToCart
|
||||
/>
|
||||
<Filters
|
||||
categories=${state.categories}
|
||||
filters=${state.filters}
|
||||
toggleFilter=$toggleFilter
|
||||
/>
|
||||
<Cart
|
||||
order=${state.order}
|
||||
addToCart=$addToCart
|
||||
removeFromCart=$removeFromCart
|
||||
submitOrder=$submitOrder
|
||||
/>
|
||||
</div>
|
||||
');
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
if (state.orderByEndDates == null || state.orderByEndDates.length == 0)
|
||||
return null;
|
||||
|
||||
var endDates;
|
||||
|
||||
if (state.orderByEndDates.length == 1) {
|
||||
var orderEndDate = state.orderByEndDates[0].date;
|
||||
endDates = [jsx('<div key=$orderEndDate>La commande fermera le $orderEndDate</div>')];
|
||||
}
|
||||
else {
|
||||
endDates = state.orderByEndDates.map(function(order) {
|
||||
if (order.contracts.length == 1)
|
||||
return jsx('
|
||||
<div key=${order.date}>
|
||||
La commande ${order.contracts[0]} fermera le: ${order.date}
|
||||
</div>
|
||||
');
|
||||
|
||||
return jsx('
|
||||
<div key=${order.date}>
|
||||
Les autres commandes fermeront: ${order.date}
|
||||
</div>
|
||||
');
|
||||
});
|
||||
}
|
||||
|
||||
var viewUrl = '$VIEW_URL/${props.place}';
|
||||
var addressBlock = Lambda.array([
|
||||
state.place.address1,
|
||||
state.place.address2,
|
||||
[state.place.zipCode, state.place.city].join(" "),
|
||||
].mapi(function(index, element) {
|
||||
if (element == null)
|
||||
return null;
|
||||
return jsx('<div className="address" key=$index>$element</div>');
|
||||
}));
|
||||
|
||||
return jsx('
|
||||
<div className="shop-header">
|
||||
<div>
|
||||
<div className="shop-distribution">
|
||||
Distribution le ${props.date}
|
||||
</div>
|
||||
|
||||
<div className="shop-order-ends">
|
||||
$endDates
|
||||
</div>
|
||||
</div>
|
||||
<div className="shop-place">
|
||||
<span className="info">
|
||||
<span className="glyphicon glyphicon-map-marker"></span>
|
||||
<a href=$viewUrl>${state.place.name}</a>
|
||||
</span>
|
||||
<div>
|
||||
$addressBlock
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user