You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
9.7 KiB
451 lines
9.7 KiB
import Common;
|
|
import js.JQuery;
|
|
/**
|
|
* JS Shopping Cart
|
|
*
|
|
* @author fbarbut<francois.barbut@gmail.com>
|
|
*/
|
|
class ShopCart
|
|
{
|
|
|
|
public var products : Map<Int,ProductInfo>; //product db
|
|
public var productsArray : Array<ProductInfo>; //to keep order of products
|
|
public var categories : Array<{name:String,pinned:Bool,categs:Array<CategoryInfo>}>; //categ db
|
|
public var pinnedCategories : Array<{name:String,pinned:Bool,categs:Array<CategoryInfo>}>; //categ db
|
|
public var order : OrderInSession;
|
|
|
|
var loader : JQuery; //ajax loader gif
|
|
|
|
//for scroll mgmt
|
|
var cartTop : Int;
|
|
var cartLeft : Int;
|
|
var cartWidth : Int;
|
|
var jWindow : JQuery;
|
|
var cartContainer : JQuery;
|
|
|
|
var date : String;
|
|
var place : Int;
|
|
|
|
|
|
public function new()
|
|
{
|
|
products = new Map();
|
|
productsArray = [];
|
|
|
|
order = cast { products:[] };
|
|
categories = [];
|
|
pinnedCategories = [];
|
|
}
|
|
|
|
|
|
|
|
public function add(pid:Int) {
|
|
loader.show();
|
|
|
|
var q = App.j('#productQt' + pid).val();
|
|
var qt = 0.0;
|
|
var p = this.products.get(pid);
|
|
if (p.hasFloatQt) {
|
|
q = StringTools.replace(q, ",", ".");
|
|
qt = Std.parseFloat(q);
|
|
}else {
|
|
qt = Std.parseInt(q);
|
|
}
|
|
|
|
if (qt == null) {
|
|
qt = 1;
|
|
}
|
|
//trace("qté : "+qt);
|
|
|
|
//add server side
|
|
var r = new haxe.Http('/shop/add/$pid/$qt');
|
|
|
|
r.onData = function(data:String) {
|
|
|
|
loader.hide();
|
|
|
|
var d = haxe.Json.parse(data);
|
|
if (!d.success) js.Browser.alert("Erreur : "+d);
|
|
|
|
//add locally
|
|
subAdd(pid, qt);
|
|
render();
|
|
|
|
|
|
}
|
|
r.request();
|
|
|
|
}
|
|
|
|
|
|
function subAdd(pid, qt:Float ) {
|
|
|
|
for ( p in order.products) {
|
|
if (p.productId == pid) {
|
|
|
|
p.quantity += qt;
|
|
render();
|
|
return;
|
|
}
|
|
}
|
|
|
|
order.products.push( { productId:pid, quantity:qt } );
|
|
}
|
|
|
|
/**
|
|
* Render the shopping cart and total
|
|
*/
|
|
function render() {
|
|
var c = App.j("#cart");
|
|
c.empty();
|
|
|
|
//render items in shopping cart
|
|
c.append( Lambda.map(order.products, function( x ) {
|
|
var p = this.products.get(x.productId);
|
|
if (p == null) {
|
|
//the product may have been disabled by an admin
|
|
return "";
|
|
}
|
|
|
|
var btn = "<a onClick='cart.remove(" + p.id + ")' class='btn btn-default btn-xs' data-toggle='tooltip' data-placement='top' title='Retirer de la commande'><span class='glyphicon glyphicon-remove'></span></a> ";
|
|
return "<div class='row'>
|
|
<div class = 'order col-md-9' > <b> " + x.quantity + " </b> x " + p.name+" </div>
|
|
<div class = 'col-md-3'> "+btn+"</div>
|
|
</div>";
|
|
}).join("\n") );
|
|
|
|
|
|
//compute total price
|
|
var total = 0.0;
|
|
for (p in order.products) {
|
|
var pinfo = products.get(p.productId);
|
|
if (pinfo == null) continue;
|
|
total += p.quantity * pinfo.price;
|
|
}
|
|
var ffilter = new sugoi.form.filters.FloatFilter();
|
|
|
|
var total = ffilter.filterString(Std.string(App.roundTo(total,2)));
|
|
c.append("<div class='total'>TOTAL : " + total + "</div>");
|
|
|
|
|
|
if (order.products.length > 0){
|
|
App.instance.setWarningOnUnload(true,"Vous avez une commande en cours. Si vous quittez cette page sans confirmer, votre commande sera perdue.");
|
|
}else{
|
|
App.instance.setWarningOnUnload(false);
|
|
}
|
|
}
|
|
|
|
|
|
function findCategoryName(cid:Int):String{
|
|
|
|
for ( cg in this.categories ){
|
|
for (c in cg.categs){
|
|
if (cid == c.id) {
|
|
return c.name;
|
|
}
|
|
}
|
|
}
|
|
for ( cg in this.pinnedCategories ){
|
|
for (c in cg.categs){
|
|
if (cid == c.id) {
|
|
return c.name;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Dynamically sort products by categories
|
|
*/
|
|
public function sortProductsBy(){
|
|
|
|
//store products by groups
|
|
var groups = new Map<Int,{name:String,products:Array<ProductInfo>}>();
|
|
var pinned = new Map<Int,{name:String,products:Array<ProductInfo>}>();
|
|
|
|
|
|
trace(this.categories);
|
|
var firstCategGroup = null;
|
|
if (this.categories.length > 0){
|
|
firstCategGroup = this.categories[0].categs;
|
|
}
|
|
|
|
|
|
//trace(firstCategGroup);
|
|
//trace(pinnedCategories);
|
|
|
|
var pList = this.productsArray.copy();
|
|
|
|
//for ( p in pList) trace(p.name+" : " + p.categories);
|
|
//trace("----------------");
|
|
|
|
//sort by categs
|
|
for ( p in pList.copy() ){
|
|
//trace(p.name+" : " + p.categories);
|
|
untyped p.element.remove();
|
|
|
|
for ( categ in p.categories){
|
|
|
|
if (firstCategGroup != null && Lambda.find(firstCategGroup, function(c) return c.id == categ) != null){
|
|
|
|
//is in this category group
|
|
var g = groups.get(categ);
|
|
if ( g == null){
|
|
var name = findCategoryName(categ);
|
|
g = {name:name,products:[]};
|
|
}
|
|
g.products.push(p);
|
|
//trace("remove " + p.name);
|
|
pList.remove(p);
|
|
groups.set(categ, g);
|
|
|
|
}
|
|
else{
|
|
// is in pinned group ?
|
|
var isInPinnedCateg = false;
|
|
for ( cg in pinnedCategories){
|
|
if (Lambda.find(cg.categs, function(c) return c.id == categ) != null){
|
|
isInPinnedCateg = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isInPinnedCateg){
|
|
|
|
var c = pinned.get(categ);
|
|
if ( c == null){
|
|
|
|
var name = findCategoryName(categ);
|
|
c = {name:name,products:[]};
|
|
}
|
|
c.products.push(p);
|
|
//trace( "add " + p.name+" in PINNED");
|
|
pList.remove(p);
|
|
pinned.set(categ, c);
|
|
|
|
|
|
}else{
|
|
//not in the selected categ nor in pinned groups
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//if some untagged products remain
|
|
if (pList.length > 0){
|
|
groups.set(0,{name:"Autres",products:pList});
|
|
}
|
|
//trace("----------------");
|
|
//render
|
|
var container = App.j(".shop .body");
|
|
//render firts "pinned" groups , then "groups"
|
|
for ( source in [pinned, groups]){
|
|
|
|
for (o in source){
|
|
|
|
if (o.products.length == 0) continue;
|
|
container.append("<div class='col-md-12 col-xs-12 col-sm-12 col-lg-12'><div class='catHeader'>" + o.name + "</div></div>");
|
|
for ( p in o.products){
|
|
//trace("GROUP "+o.name+" : "+p.name);
|
|
//if the element has already been inserted, we need to clone it
|
|
if (untyped p.element.parent().length == 0){
|
|
container.append( untyped p.element );
|
|
}else{
|
|
var clone = untyped p.element.clone();
|
|
container.append( clone );
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
App.j(".product").show();
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* is shopping cart empty ?
|
|
*/
|
|
public function isEmpty(){
|
|
return order.products.length == 0;
|
|
|
|
}
|
|
|
|
/**
|
|
* submit cart
|
|
*/
|
|
public function submit() {
|
|
|
|
var req = new haxe.Http("/shop/submit");
|
|
req.onData = function(d) {
|
|
App.instance.setWarningOnUnload(false);
|
|
js.Browser.location.href = "/shop/validate/"+place+"/"+date;
|
|
|
|
}
|
|
req.addParameter("data", haxe.Json.stringify(order));
|
|
req.request(true);
|
|
|
|
}
|
|
|
|
/**
|
|
* filter products by category
|
|
*/
|
|
public function filter(cat:Int) {
|
|
|
|
//icone sur bouton
|
|
App.j(".tag").removeClass("active").children().remove("span");//clean
|
|
|
|
var bt = App.j("#tag" + cat);
|
|
bt.addClass("active").prepend("<span class ='glyphicon glyphicon-ok'></span> ");
|
|
|
|
|
|
//affiche/masque produits
|
|
for (p in products) {
|
|
if (cat==0 || Lambda.has(p.categories, cat)) {
|
|
App.j(".shop .product" + p.id).fadeIn(300);
|
|
}else {
|
|
App.j(".shop .product" + p.id).fadeOut(300);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* remove a product from cart
|
|
* @param pid
|
|
*/
|
|
public function remove(pid:Int ) {
|
|
|
|
loader.show();
|
|
|
|
//add server side
|
|
var r = new haxe.Http('/shop/remove/$pid');
|
|
|
|
r.onData = function(data:String) {
|
|
|
|
loader.hide();
|
|
|
|
var d = haxe.Json.parse(data);
|
|
if (!d.success) js.Browser.alert("Erreur : "+d);
|
|
|
|
//remove locally
|
|
for ( p in order.products.copy()) {
|
|
if (p.productId == pid) {
|
|
order.products.remove(p);
|
|
render();
|
|
return;
|
|
}
|
|
}
|
|
render();
|
|
|
|
|
|
}
|
|
r.request();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* loads products DB and existing cart in ajax
|
|
*/
|
|
public function init(place:Int,date:String) {
|
|
|
|
this.place = place;
|
|
this.date = date;
|
|
|
|
loader = App.j("#cartContainer #loader");
|
|
|
|
var req = new haxe.Http("/shop/init/"+place+"/"+date);
|
|
req.onData = function(data) {
|
|
loader.hide();
|
|
|
|
var data : {
|
|
products:Array<ProductInfo>,
|
|
categories:Array<{name:String,pinned:Bool,categs:Array<CategoryInfo>}>,
|
|
order:OrderInSession } = haxe.Unserializer.run(data);
|
|
|
|
//populate local categories lists
|
|
for ( cg in data.categories){
|
|
if (cg.pinned){
|
|
pinnedCategories.push(cg);
|
|
}else{
|
|
categories.push(cg);
|
|
}
|
|
}
|
|
|
|
//product DB
|
|
for (p in data.products) {
|
|
//catch dom element for further usage
|
|
untyped p.element = App.j(".product"+p.id);
|
|
|
|
var id : Int = p.id;
|
|
//var id : Int = p.id;
|
|
//id = id + 1;
|
|
this.products.set(id, p);
|
|
this.productsArray.push(p);
|
|
//trace(p.name+" : " + p.categories);
|
|
}
|
|
|
|
//existing order
|
|
for ( p in data.order.products) {
|
|
subAdd(p.productId,p.quantity );
|
|
}
|
|
|
|
render();
|
|
|
|
sortProductsBy();
|
|
|
|
}
|
|
req.request();
|
|
|
|
//DISABLED : pb quand le panier est plus haut que l'ecran
|
|
//scroll mgmt, only for large screens. Otherwise let the cart on page bottom.
|
|
/*if (js.Browser.window.matchMedia("(min-width: 1024px)").matches) {
|
|
|
|
jWindow = App.j(js.Browser.window);
|
|
cartContainer = App.j("#cartContainer");
|
|
cartTop = cartContainer.position().top;
|
|
cartLeft = cartContainer.position().left;
|
|
cartWidth = cartContainer.width();
|
|
jWindow.scroll(onScroll);
|
|
|
|
}*/
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* keep the cart on top when scrolling
|
|
* @param e
|
|
*/
|
|
public function onScroll(e:Dynamic) {
|
|
|
|
//cart container top position
|
|
|
|
if (jWindow.scrollTop() > cartTop) {
|
|
//trace("absolute !");
|
|
cartContainer.addClass("scrolled");
|
|
cartContainer.css('left', Std.string(cartLeft) + "px");
|
|
cartContainer.css('top', Std.string(/*cartTop*/10) + "px");
|
|
cartContainer.css('width', Std.string(cartWidth) + "px");
|
|
|
|
}else {
|
|
cartContainer.removeClass("scrolled");
|
|
cartContainer.css('left',"");
|
|
cartContainer.css('top', "");
|
|
cartContainer.css('width', "");
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|