PaperBag/plan/plan.js

475 lines
17 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*jshint sub:true, esversion: 6, -W083 */
class Store {
constructor(title, storeID) {
this.items = [];
let storeNum = $(".store").length+1;
this.title = title || "Store "+storeNum;
this.storeID = storeID || null;
let html = "";
html += "<div class='card store' style='width: 25rem; max-width: 90vw;'>";
html += " <div class='card-header'>"+this.title+"</div>";
html += " <div class='iconWrapper'>";
html += " <img src='../icon/pencil-square.svg' class='editStoreName' alt='edit name' data-toggle='tooltip' title='Edit store name' tabindex=0 role='button' />";
html += " <img src='../icon/x-circle.svg' class='removeStore' alt='remove store' data-toggle='tooltip' title='Remove store' tabindex=0 role='button' />";
html += " </div>";
html += " <div class='card-body'>";
html += " <div class='draggable' style='width: 100%; height: 10px; border-width: 1px;'></div> <ul class='list-group list-group-flush'>";
html += " <li class='list-group-item emptyList'>No items added</li>";
html += " </ul>";
html += " <hr>";
html += " <form action='#!' class='form-row input-group input-group-sm addItemForm'>";
// html += " <input type='text' class='form-control newItemName' placeholder='New Item Name' data-toggle='tooltip' title='New Item Name' aria-label='New Item Name'>";
// html += " <input type='number' class='form-control newItemPrice' value='0' min='0' step='.01' data-toggle='tooltip' title='Price' aria-label='Price'>";
html += " <div class='form-control form-floating'>";
html += " <input type='text' id='newItemName0' class='form-control newItemName' placeholder='New Item Name' aria-label='New Item Name'>";
html += " <label for='newItemName0'>New Item Name</label>";
html += " </div>";
html += " <div class='form-control form-floating'>";
html += " <input type='number' id='newItemPrice0' class='form-control newItemPrice' value='0' min='0' step='.01' aria-label='Price'>";
html += " <label for='newItemPrice0'>Price</label>";
html += " </div>";
html += " <div class='input-group-append'>";
html += " <input type='image' class='form-control addItem' src='../icon/plus.svg' alt='+'>";
html += " </div>";
html += " </form>";
// html += " <button class='save'>Save</button>";
html += " </div>";
html += " <div class='card-footer subtotal'>Subtotal: <span class='price'>0.00 kr</span></div>";
html += "</div>";
this.selector = $(html).appendTo("#stores");
this.selector.find(".addItemForm").on('submit', ev => {
ev.preventDefault();
this.addItem($(ev.target).find('.newItemName').val(), $(ev.target).find('.newItemPrice').val())
.done(json => {
$(this).find('.newItemPrice').val(0);
$(this).find('.newItemName').val("").focus();
});
});
this.selector.find('.editStoreName').one('click keyup', ev => { this.editNameFn(ev); });
this.selector.find('.removeStore').on('click keyup', ev => {
if(ev.type == 'click' || ev.keyCode == 13){
if(confirm("Are you sure you want to remove this store?")){ this.removeStore(); }
}
});
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
/** DEV **/
// this.selector.find('.save').click(ev => { this.save(); });
}
editNameFn(ev){
if(ev.type == 'click' || ev.keyCode == 13){
$(ev.target).parent().hide();
let headerElem = $(ev.target).parent().parent().find(".card-header");
let orgHtml = headerElem.html();
headerElem.html("<span style='display: none;'>"+orgHtml+"</span><form action='#' class='changeNameForm'><input type='text' class='newName' value='"+orgHtml+"'><input type='image' src='../icon/pencil-square.svg'></form>");
headerElem.find(".changeNameForm").on('submit keyup', ev2 => {
if(ev2.type == "submit"){
ev2.preventDefault();
let newName = $(ev2.target).find(".newName").val();
let that = this;
this.rename(newName).done(json => {
// success
headerElem.html(newName);
that.selector.find('.iconWrapper').show();
that.selector.find('.editStoreName').one('click keyup', ev3 => { this.editNameFn(ev3); });
});
}
});
headerElem.find(".newName").on('keyup', ev3 => { this.resetEditNameFn(ev3); });
// $("body").one('click', ev3 => { this.resetEditNameFn(ev3); });
}
}
resetEditNameFn(ev){
if(
(ev.type=="keyup" && ev.keyCode === 27) ||
ev.type != "keyup"
){
// cancel
let orgHtml = this.selector.find('.card-header span').html();
this.selector.find('.card-header').html(orgHtml);
this.selector.find('.iconWrapper').show();
this.selector.find('.editStoreName').one('click keyup', ev3 => { this.editNameFn(ev3); });
}
}
addItem(text, price){
if(text.length > 0){
if(this.storeID === null){
this.getStoreID().done(json => { this.addItem(text, price); });
return;
}
let that = this;
return ajaxReq({ plan: 'addItem', storeID: this.storeID, name: text, price: price })
.done(json => {
// console.log("addItem return:", json);
return that.addItemHtml(text, price, json['data']);
});
}
return false;
}
addItemHtml(text, price, itemID, amount){
amount = amount || 1;
try {
price = Number(price);
this.items.push({ text: text, price: price, itemID: itemID, amount: amount });
let html = "\n";
html += "<li class='list-group-item draggable' id='item_"+itemID+"' data-itemID='"+itemID+"' draggable='true'>";
html += " <span style='float: left;'>"+text+"</span>";
/*
html += " <br><span class='' style='float: left; padding: 0 10px;'>";
html += " <div class='input-group'>";
html += " <button class='btn btn-outline-danger itemAmountDown' type='button'>-</button>";
html += " <input class='form-control itemAmount' type='number' value='"+amount+"' min='0' max='99' aria-label='Amount of item' style='-webkit-appearance: none; -moz-appearance: textfield; width: 29px; padding: 0 5px;' >";
html += " <span class='input-group-text' style='padding-left: 3px; font-family: var(--bs-font-monospace);font-size: 12px;text-align: right;'>x"+price.toFixed(2)+" kr</span>";
html += " <button class='btn btn-outline-success itemAmountUp' type='button'>+</button>";
html += " </div>";
html += " </span>";
*/
html += " <span class='price'>"+price.toFixed(2)+" kr ";
// html += "<button class='remItem' data-price='"+price+"' data-toggle='tooltip' title='Remove item' style='background: url(../icon/dash.svg); height: 20px; width: 20px;'></button></span>";
html += "<img src='../icon/dash-circle.svg' alt='Remove item' class='remItem' data-itemID='"+itemID+"' data-price='"+price+"' style='height: 20px; width: 20px; cursor: pointer;'></span>";
html += "</li>";
this.selector.find("ul").append(html);
this.selector.find(".emptyList").hide();
this.verify();
return true;
}
catch(e){
alert("Something failed. Try again.");
console.error(e, e.stack);
}
return false;
}
remItem(pos, itemID, price){
let that = this;
return $.ajax({
method: "POST",
url: "do.php",
data: { plan: 'remItem', storeID: this.storeID, itemID: itemID, price: price },
dataType: 'JSON'
})
.done(json => {
if(handleJsonErrors(json)){
return false;
}
// console.log("remItem return:", json);
return that.remItemHtml(itemID);
})
.fail(handleAjaxErrors);
}
remItemHtml(itemID){
// this.items.splice(pos,1);
for(let i = 0; i < this.items.length; i++){
if(this.items[i].itemID == itemID){
this.items.splice(i,1);
break;
}
}
this.selector.find('#item_'+itemID).remove();
// $(this.selector).find(".remItem").unbind().each((key, val) => {
// if(key+1 == pos){
// $(val).parent().parent().remove();
// }
// });
this.verify();
return true;
}
setItemAmount(pos, amount){
this.items[pos].amount = amount;
this.verify();
return true;
// return ajaxReq({ plan: 'remItem', storeID: this.storeID, position: pos, price: price });
}
setItemPosition(itemID, afterID){
afterID = afterID || 0;
if(itemID != afterID){
return ajaxReq({ plan: 'moveItem', storeID: this.storeID, itemID: itemID, afterID: afterID });
}
return false;
}
verify(){
// UPDATE TOTAL PRICE
let total = 0;
this.items.forEach(item => {
total += item.price*item.amount;
// console.log("verify - item: "+item.price+"*"+item.amount);
});
$(this.selector).find(".subtotal .price").html(total.toFixed(2)+" kr");
// SHOW if-empty MESSAGE
if(this.selector.find("li").length <= 1){
this.selector.find(".emptyList").show();
}
// BIND add/remove item amount
this.selector.find('.itemAmountDown').each((key, val) => {
let that = this;
$(val).unbind().click(function(){
let newValue = Number($(this).parent().find('.itemAmount').val())-1;
if(newValue > 0){
$(this).parent().find('.itemAmount').val(newValue);
// UPDATE VALUE
that.setItemAmount(key+1, newValue);
}
});
});
this.selector.find('.itemAmountUp').each((key, val) => {
let that = this;
$(val).unbind().click(function(){
let newValue = Number($(this).parent().find('.itemAmount').val())+1;
if(newValue < 100){
$(this).parent().find('.itemAmount').val(newValue);
// UPDATE VALUE
that.setItemAmount(key+1, newValue);
}
});
});
this.selector.find('.itemAmount').each((key, val) => {
$(val).unbind().on('change', function(){
let newValue = Number( console.log($(this).val()) );
// UPDATE VALUE
that.setItemAmount(key+1, newValue);
});
});
// DRAGGABLE
let draggingObj = null, dragPos = null;
this.selector.find('.draggable').unbind().on('dragstart dragover dragenter dragleave dragend', ev => {
// this.selector.find('.draggable').hammer().bind('swipe', ev => {
if(ev.type === 'dragover' || ev.type === 'dragenter'){
ev.preventDefault();
// console.log(ev);
$(ev.currentTarget).addClass('drag-over');
dragPos = ev.currentTarget;
}
else if(ev.type === 'dragstart'){
// console.log('drag start');
draggingObj = $(ev.target);
draggingObj.css('opacity', 0.2);
}
else if(ev.type === 'dragleave'){
$('.drag-over').removeClass('drag-over');
}
else if(ev.type === 'dragend'){
// console.log(draggingObj);
if(draggingObj === null){
console.warn('no object to move');
return;
}
// console.log("move object", draggingObj, "to", dragPos);
draggingObj.css('opacity', 1);
if( this.setItemPosition(draggingObj.attr('data-itemID'), $(dragPos).attr('data-itemID')) !== false){
if($(dragPos).attr('data-itemID') == null){
draggingObj.detach().insertAfter('.emptyList');
}
else {
draggingObj.detach().insertAfter(dragPos);
}
draggingObj = null;
}
$('.drag-over').removeClass('drag-over');
}
});
// BIND remove-buttons
$(this.selector).find(".remItem").unbind().each((key, val) => {
let that = this;
$(val).click(function(){
if($(this).hasClass("confirm")){
that.remItem(key+1, $(this).attr('data-itemID'), $(this).attr("data-price"));
try {
$(this).tooltip('dispose');
}
catch(ignore){}
}
else {
$(".confirm").removeClass("confirm");
$(this).addClass("confirm");
let newThat = this;
setTimeout(function(){ $(newThat).removeClass("confirm"); }, 5000);
}
});
});
}
// STORE MANIPULATION
removeStore(){
this.selector.remove();
if(this.storeID != null){
$.ajax({
method: "POST",
url: "do.php",
data: { plan: 'deleteStore', storeID: this.storeID, storeName: this.title, itemsLength: this.items.length },
dataType: 'JSON'
})
.done(json => {
if(handleJsonErrors(json)){
return;
}
console.log("Delete store response:",json);
})
.fail(handleAjaxErrors);
}
}
getStoreID(){
let that = this;
return ajaxReq({ plan: 'saveStore', storeName: this.title })
.done(json => {
console.log("getStore:", json);
that.storeID = json['data'];
return that.storeID;
});
}
rename(newName){
this.title = newName;
if(this.storeID !== null){
return ajaxReq({ plan: 'renameStore', storeID: this.storeID, newName: newName });
}
// Return blank ajax as a false
return $.ajax();
}
}
var stores = [];
$("#addStore").click(ev => {
stores.push(new Store());
});
$("#refreshAll").click(ev => {
$(".tooltip").remove();
stores = [];
getStores();
});
$("body").on('click', function(ev){
if( $(".price").find( ev.target ).length === 0){
$(".confirm").removeClass("confirm");
}
});
// GET STORES
function getStores(){
$("#stores").html("");
$.getJSON('do.php', { plan: '' })
.done(json => {
if(handleJsonErrors(json)){
return;
}
// console.log(json);
for(const store in json.data){
storeID = stores.length;
stores.push(new Store(json.data[store].name, json.data[store].plan_store_id));
for(const item in json.data[store].items){
stores[storeID].addItemHtml(
json.data[store].items[item].name,
json.data[store].items[item].price,
json.data[store].items[item].plan_item_id,
json.data[store].items[item].amount);
}
}
})
.fail(handleAjaxErrors);
}
getStores();
function ajaxReq( data ){
return $.ajax({
method: "POST",
url: "do.php",
data: data,
dataType: 'JSON'
})
.done(json => {
if(handleJsonErrors(json)){
return false;
}
return true;
})
.fail(handleAjaxErrors);
}
function handleJsonErrors(json){
if(typeof json.status != "undefined" && json.status != 0){
alert(json.message);
return true;
}
return false;
}
function handleAjaxErrors(jqxhr, textStatus, error){
if(textStatus === "parsererror" && jqxhr.responseText != ""){
alert("An error occured:\n"+jqxhr.responseText);
}
else {
alert("An error occured:\n"+error);
}
}