Updates to order screen
This commit is contained in:
@@ -54,9 +54,9 @@ let getFloorplanData (id: int) =
|
|||||||
|> json
|
|> json
|
||||||
|
|
||||||
let getOrderScreenData (id: int) =
|
let getOrderScreenData (id: int) =
|
||||||
let pages = Entity.getAllInVenue<order_screen_page_group>
|
|
||||||
{|
|
{|
|
||||||
order_screen_pages = pages
|
order_screen_pages = Entity.getAllInVenue<order_screen_page_group>
|
||||||
|
sales_categories = Entity.getAllInVenue<sales_category>
|
||||||
|}
|
|}
|
||||||
|> ajaxSuccess
|
|> ajaxSuccess
|
||||||
|> json
|
|> json
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<Compile Include="Reservations.module.fs" />
|
<Compile Include="Reservations.module.fs" />
|
||||||
<Compile Include="Floorplan.module.fs" />
|
<Compile Include="Floorplan.module.fs" />
|
||||||
<Compile Include="Printer.module.fs" />
|
<Compile Include="Printer.module.fs" />
|
||||||
|
<Compile Include="Orders.module.fs" />
|
||||||
<Compile Include="OrderScreen.module.fs" />
|
<Compile Include="OrderScreen.module.fs" />
|
||||||
<Compile Include="Decorations.module.fs" />
|
<Compile Include="Decorations.module.fs" />
|
||||||
<Compile Include="Clerk.module.fs" />
|
<Compile Include="Clerk.module.fs" />
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ let get var =
|
|||||||
else
|
else
|
||||||
"Missing language variable: " + var
|
"Missing language variable: " + var
|
||||||
|
|
||||||
let getAndReplace languageVar replacements =
|
let getAndReplace languageVar (replacements: 'x list) =
|
||||||
let langString = get languageVar
|
let langString = get languageVar
|
||||||
replacements
|
replacements
|
||||||
|> List.mapi (fun index string
|
|> List.mapi (fun index replacement
|
||||||
-> index + 1, string)
|
-> index + 1, replacement.ToString())
|
||||||
|> List.fold (fun (result: string) (index, string)
|
|> List.fold (fun (result: string) (index, string)
|
||||||
-> result.Replace($"[{index}]", string)
|
-> result.Replace($"[{index}]", string)
|
||||||
) langString
|
) langString
|
||||||
@@ -11,7 +11,7 @@ open Theme
|
|||||||
let htmlAttributes (attributes: Map<string, string>) =
|
let htmlAttributes (attributes: Map<string, string>) =
|
||||||
" " + (attributes
|
" " + (attributes
|
||||||
|> Map.toArray
|
|> Map.toArray
|
||||||
|> Array.map (fun (attribute, value) -> attribute+"=\""+HttpUtility.HtmlEncode value + "\"")
|
|> Array.map (fun (attribute, value) -> attribute+"='"+HttpUtility.HtmlEncode value + "'")
|
||||||
|> String.concat " ")
|
|> String.concat " ")
|
||||||
|
|
||||||
let getAllPageGrids () = Entity.getAllInVenue<order_screen_page_group>
|
let getAllPageGrids () = Entity.getAllInVenue<order_screen_page_group>
|
||||||
@@ -46,9 +46,7 @@ let renderButton (buttonId: int) =
|
|||||||
then "invisible"
|
then "invisible"
|
||||||
else ""
|
else ""
|
||||||
|
|
||||||
let image = if button.image.Length > 0
|
let image = if button.image.Length > 0 then loadTemplateWithVars "orderScreen/button_image" (map ["image", button.image]) else ""
|
||||||
then loadTemplateWithVars "orderScreen/button_image" (map ["image", button.image])
|
|
||||||
else ""
|
|
||||||
|
|
||||||
let extraClasses = [|imageClass; spacerClass|] |> String.concat " "
|
let extraClasses = [|imageClass; spacerClass|] |> String.concat " "
|
||||||
|
|
||||||
@@ -63,7 +61,7 @@ let renderButton (buttonId: int) =
|
|||||||
"extra_styles", extra_styles
|
"extra_styles", extra_styles
|
||||||
"primary_action", button.primary_action
|
"primary_action", button.primary_action
|
||||||
"secondary_action", button.secondary_action
|
"secondary_action", button.secondary_action
|
||||||
"text", if button.text.Length >0 then button.text else action_data.text
|
"text", if button.text.Length > 0 then button.text else action_data.text
|
||||||
"image", image
|
"image", image
|
||||||
"extra_data", action_data.extra_data
|
"extra_data", action_data.extra_data
|
||||||
]
|
]
|
||||||
|
|||||||
3
Orders.module.fs
Normal file
3
Orders.module.fs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module Orders
|
||||||
|
|
||||||
|
let getHighestOrderNumber () = 6
|
||||||
@@ -36,12 +36,22 @@ let loadFloorplan (ctx: HttpContext) : HttpHandler =
|
|||||||
|
|
||||||
htmlString <| Theme.loadTemplateWithVarsArraysScriptsAndStyles "floorplan" variables arrays scripts styles
|
htmlString <| Theme.loadTemplateWithVarsArraysScriptsAndStyles "floorplan" variables arrays scripts styles
|
||||||
|
|
||||||
let loadOrderScreen (ctx: HttpContext) : HttpHandler =
|
let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
|
||||||
Session.RequireClerkAuthentication ctx
|
Session.RequireClerkAuthentication ctx
|
||||||
|
|
||||||
|
let covers = if tableNumber > 0 then (getTable tableNumber).default_covers else 0
|
||||||
|
let coverString = language.getAndReplace "covers" [covers]
|
||||||
|
|
||||||
|
let coverSelectorButton = if tableNumber > 0 then Theme.loadTemplateWithVars "orderScreen/cover_selector_button" (map ["covers", coverString]) else ""
|
||||||
|
|
||||||
|
let orderNumber =
|
||||||
|
if tableNumber > 0 then language.getAndReplace "active_table" [tableNumber]
|
||||||
|
else language.get "new_order"
|
||||||
|
|
||||||
let categoryList =
|
let categoryList =
|
||||||
Entity.getAll<order_screen_page_group>
|
Entity.getAllInVenue<order_screen_page_group>
|
||||||
|> Array.filter (fun category -> category.id <> 0)
|
|> Array.filter (fun page_group -> page_group.id <> 0)
|
||||||
|
|> Array.sortBy (fun {order=order} -> order)
|
||||||
|> Array.map (fun category ->
|
|> Array.map (fun category ->
|
||||||
let categoryMap = recordToMap category
|
let categoryMap = recordToMap category
|
||||||
let categoryArray = map ["page", categoryMap]
|
let categoryArray = map ["page", categoryMap]
|
||||||
@@ -54,15 +64,17 @@ let loadOrderScreen (ctx: HttpContext) : HttpHandler =
|
|||||||
|> Array.map OrderScreen.getPagesHTML
|
|> Array.map OrderScreen.getPagesHTML
|
||||||
|> String.concat "\n"
|
|> String.concat "\n"
|
||||||
|
|
||||||
|
|
||||||
let variables = map [
|
let variables = map [
|
||||||
"title", "Order"
|
"title", "Order"
|
||||||
"categoryList", categoryList
|
"categoryList", categoryList
|
||||||
"pageGroups", grids
|
"pageGroups", grids
|
||||||
|
"orderNumber", orderNumber
|
||||||
|
"coverSelectorButton", coverSelectorButton
|
||||||
|
"covers", coverString
|
||||||
]
|
]
|
||||||
|
|
||||||
let styles = ["dredgepos.orderScreen.css"]
|
let styles = ["dredgepos.orderScreen.css"]
|
||||||
let scripts = ["dredgepos.orderScreen.js"]
|
let scripts = ["dredgepos.tables.js";"../external/currency.min.js";"dredgepos.orderScreen.js"; ]
|
||||||
let currentClerk = recordToMap <| Session.getCurrentClerk ctx
|
let currentClerk = recordToMap <| Session.getCurrentClerk ctx
|
||||||
let arrays = map ["clerk", currentClerk]
|
let arrays = map ["clerk", currentClerk]
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ module Program =
|
|||||||
get "/" (redirectTo true "/login")
|
get "/" (redirectTo true "/login")
|
||||||
get "/login" (warbler (fun _ -> PageController.loadHomePage() ))
|
get "/login" (warbler (fun _ -> PageController.loadHomePage() ))
|
||||||
get "/floorplan" (warbler (fun ctx -> PageController.loadFloorplan (snd ctx)))
|
get "/floorplan" (warbler (fun ctx -> PageController.loadFloorplan (snd ctx)))
|
||||||
get "/order" (warbler (fun ctx -> PageController.loadOrderScreen (snd ctx)))
|
get "/order" (warbler (fun ctx -> PageController.loadOrderScreen (snd ctx) 0))
|
||||||
|
getf "/order/%i" (fun number -> (warbler (fun ctx -> PageController.loadOrderScreen (snd ctx) number)))
|
||||||
forward "/ajax" floorplanRouter
|
forward "/ajax" floorplanRouter
|
||||||
forward "/orderScreen" orderScreenRouter
|
forward "/orderScreen" orderScreenRouter
|
||||||
}
|
}
|
||||||
|
|||||||
31
Types.fs
31
Types.fs
@@ -29,11 +29,20 @@ type floorplan_table = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[<CLIMutable>]
|
[<CLIMutable>]
|
||||||
type category = {
|
type print_group = {
|
||||||
id: int
|
id: int
|
||||||
category_name: string
|
name: string
|
||||||
category_print_group: string
|
printer: int
|
||||||
category_department: string
|
venue_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type sales_category = {
|
||||||
|
id: int
|
||||||
|
parent: int
|
||||||
|
name: string
|
||||||
|
print_group: string
|
||||||
|
venue_id: int
|
||||||
}
|
}
|
||||||
|
|
||||||
[<CLIMutable>]
|
[<CLIMutable>]
|
||||||
@@ -64,7 +73,7 @@ type clerk = {id: int; clerk_name: string; clerk_login_code: int; clerk_usergrou
|
|||||||
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
|
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
|
||||||
|
|
||||||
[<CLIMutable>]
|
[<CLIMutable>]
|
||||||
type order_screen_page_group = {id: int; venue_id: int; label: string; grid_id: int}
|
type order_screen_page_group = {id: int; order: int; venue_id: int; label: string; grid_id: int}
|
||||||
|
|
||||||
[<CLIMutable>]
|
[<CLIMutable>]
|
||||||
type grid = {id: int; grid_name: string; grid_rows: int; grid_cols: int; grid_data: string}
|
type grid = {id: int; grid_name: string; grid_rows: int; grid_cols: int; grid_data: string}
|
||||||
@@ -89,9 +98,9 @@ type item = {
|
|||||||
item_category: int
|
item_category: int
|
||||||
item_name: string
|
item_name: string
|
||||||
item_type: string
|
item_type: string
|
||||||
price1: float
|
price1: int
|
||||||
price2: float
|
price2: int
|
||||||
price3: float
|
price3: int
|
||||||
price4: float
|
price4: int
|
||||||
price5: float
|
price5: int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@
|
|||||||
"merge_table":"Merge Table",
|
"merge_table":"Merge Table",
|
||||||
"unmerge_table":"Unmerge Table",
|
"unmerge_table":"Unmerge Table",
|
||||||
"order_table":"Place Order",
|
"order_table":"Place Order",
|
||||||
|
"order_number": "Order [1]",
|
||||||
|
"new_order": "New Order",
|
||||||
"view_table":"View Table",
|
"view_table":"View Table",
|
||||||
"reserve_table":"Reserve Table",
|
"reserve_table":"Reserve Table",
|
||||||
"unreserve_table":"Delete Reservation",
|
"unreserve_table":"Delete Reservation",
|
||||||
|
|||||||
12
wwwroot/scripts/external/currency.min.js
vendored
Normal file
12
wwwroot/scripts/external/currency.min.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
currency.js - v2.0.4
|
||||||
|
http://scurker.github.io/currency.js
|
||||||
|
|
||||||
|
Copyright (c) 2021 Jason Wilson
|
||||||
|
Released under MIT license
|
||||||
|
*/
|
||||||
|
(function(e,g){"object"===typeof exports&&"undefined"!==typeof module?module.exports=g():"function"===typeof define&&define.amd?define(g):(e=e||self,e.currency=g())})(this,function(){function e(b,a){if(!(this instanceof e))return new e(b,a);a=Object.assign({},m,a);var d=Math.pow(10,a.precision);this.intValue=b=g(b,a);this.value=b/d;a.increment=a.increment||1/d;a.groups=a.useVedic?n:p;this.s=a;this.p=d}function g(b,a){var d=2<arguments.length&&void 0!==arguments[2]?arguments[2]:!0;var c=a.decimal;
|
||||||
|
var h=a.errorOnInvalid,k=a.fromCents,l=Math.pow(10,a.precision),f=b instanceof e;if(f&&k)return b.intValue;if("number"===typeof b||f)c=f?b.value:b;else if("string"===typeof b)h=new RegExp("[^-\\d"+c+"]","g"),c=new RegExp("\\"+c,"g"),c=(c=b.replace(/\((.*)\)/,"-$1").replace(h,"").replace(c,"."))||0;else{if(h)throw Error("Invalid Input");c=0}k||(c=(c*l).toFixed(4));return d?Math.round(c):c}var m={symbol:"$",separator:",",decimal:".",errorOnInvalid:!1,precision:2,pattern:"!#",negativePattern:"-!#",format:function(b,
|
||||||
|
a){var d=a.pattern,c=a.negativePattern,h=a.symbol,k=a.separator,l=a.decimal;a=a.groups;var f=(""+b).replace(/^-/,"").split("."),q=f[0];f=f[1];return(0<=b.value?d:c).replace("!",h).replace("#",q.replace(a,"$1"+k)+(f?l+f:""))},fromCents:!1},p=/(\d)(?=(\d{3})+\b)/g,n=/(\d)(?=(\d\d)+\d\b)/g;e.prototype={add:function(b){var a=this.s,d=this.p;return e((this.intValue+g(b,a))/(a.fromCents?1:d),a)},subtract:function(b){var a=this.s,d=this.p;return e((this.intValue-g(b,a))/(a.fromCents?1:d),a)},multiply:function(b){var a=
|
||||||
|
this.s;return e(this.intValue*b/(a.fromCents?1:Math.pow(10,a.precision)),a)},divide:function(b){var a=this.s;return e(this.intValue/g(b,a,!1),a)},distribute:function(b){var a=this.intValue,d=this.p,c=this.s,h=[],k=Math[0<=a?"floor":"ceil"](a/b),l=Math.abs(a-k*b);for(d=c.fromCents?1:d;0!==b;b--){var f=e(k/d,c);0<l--&&(f=f[0<=a?"add":"subtract"](1/d));h.push(f)}return h},dollars:function(){return~~this.value},cents:function(){return~~(this.intValue%this.p)},format:function(b){var a=this.s;return"function"===
|
||||||
|
typeof b?b(this,a):a.format(this,Object.assign({},a,b))},toString:function(){var b=this.s,a=b.increment;return(Math.round(this.intValue/this.p/a)*a).toFixed(b.precision)},toJSON:function(){return this.value}};return e});
|
||||||
@@ -1,186 +1,207 @@
|
|||||||
let Application : ApplicationState = {
|
/// <reference path="./typings/currency.d.ts" />
|
||||||
keyboard : null,
|
|
||||||
mode: [],
|
const Application: ApplicationState = {
|
||||||
languageVars: {}
|
keyboard: null,
|
||||||
}
|
mode: [],
|
||||||
|
languageVars: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Parses a language variable. */
|
/** Parses a language variable. */
|
||||||
let lang = (key: string, replacements?: string[] | string) => {
|
const lang = (key: string, replacements?: string[] | string) => {
|
||||||
let finalValue = Application.languageVars[key] || ''
|
let finalValue = Application.languageVars[key] || ''
|
||||||
|
|
||||||
if(!replacements) return finalValue
|
if (!replacements) return finalValue
|
||||||
if(typeof replacements === 'string') replacements = [replacements]
|
if (typeof replacements === 'string') replacements = [replacements]
|
||||||
|
|
||||||
replacements.forEach( (replacement, index) => {
|
replacements.forEach((replacement, index) => {
|
||||||
let correctIndex = index+1
|
const correctIndex = index + 1
|
||||||
finalValue = finalValue.replace(`[${correctIndex}]`, replacement)
|
finalValue = finalValue.replace(`[${correctIndex}]`, replacement)
|
||||||
})
|
})
|
||||||
|
|
||||||
return finalValue
|
return finalValue
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if a variable is defined */
|
/** Check if a variable is defined */
|
||||||
let defined = (variable: any) => {
|
const defined = (variable: any) => {
|
||||||
return typeof variable !== 'undefined'
|
return typeof variable !== 'undefined'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call an Ajax function asynchronously */
|
/** Call an Ajax function asynchronously */
|
||||||
let ajax = (endpoint : string, data: any, method = 'POST', successFunction : Function , errorFunction : Function, beforeFunction: any) => {
|
const ajax = (endpoint: string, data: any, method = 'POST', successFunction: Function, errorFunction: Function, beforeFunction: any) => {
|
||||||
data = (data == null) ? data : JSON.stringify(data)
|
data = (data == null) ? data : JSON.stringify(data)
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
method: method,
|
||||||
|
data: data,
|
||||||
|
success: (response: ajaxResult) => {
|
||||||
|
if (successFunction && response.status == 'success')
|
||||||
|
successFunction(JSON.parse(response.data))
|
||||||
|
else if (errorFunction && response.status != 'success') {
|
||||||
|
errorFunction(JSON.parse(response.data))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error) => console.log(error.statusCode),
|
||||||
|
beforeSend: beforeFunction
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
For the flow of the app, synchronous is commonly preferred
|
||||||
|
though trying to keep its usage as low as possible.
|
||||||
|
*/
|
||||||
|
const ajaxSync = (endpoint: string, data?: any, method = 'POST') => {
|
||||||
|
const response = JSON.parse(
|
||||||
|
$.ajax({
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
method: method,
|
method: method,
|
||||||
data: data,
|
data: JSON.stringify(data),
|
||||||
success: (response: ajaxResult) => {
|
async: false,
|
||||||
if(successFunction && response.status == 'success')
|
}).responseText)
|
||||||
successFunction(JSON.parse(response.data))
|
|
||||||
else if (errorFunction && response.status != 'success'){
|
if (response.data) {
|
||||||
errorFunction(JSON.parse(response.data))
|
response.data = JSON.parse(response.data)
|
||||||
}
|
return response.data
|
||||||
},
|
}
|
||||||
error: (error) => console.log(error.statusCode),
|
|
||||||
beforeSend: beforeFunction
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Redirect to a specific URL */
|
||||||
|
const redirect = (url: string): void => location.assign(url)
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
$('#pageContainer').height(window.innerHeight + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupCore = (languageVars: Record<string, string>) => {
|
||||||
|
Application.languageVars = languageVars
|
||||||
|
const doc = $(document)
|
||||||
|
doc.on('click', '#alertNo, #alertOk', hideAlerts)
|
||||||
|
window.addEventListener('resize', resize)
|
||||||
|
resize()
|
||||||
|
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const posAlert = (message: string, title = 'Message') => {
|
||||||
|
const alertBox = $('#alert')
|
||||||
|
alertBox.css('display', 'flex');
|
||||||
|
alertBox.data('value', '');
|
||||||
|
$('#alertHeading').text(title);
|
||||||
|
$('#alertMessage').text(message);
|
||||||
|
|
||||||
|
$('#alertOk').css('display', 'flex');
|
||||||
|
$('#alertYes').css('display', 'none');
|
||||||
|
$('#alertNo').css('display', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmation = (message: string, data: any, title = 'Confirm', submitFunction = (data: any) => {hideAlerts()}) => {
|
||||||
|
const alert = $('#alert')
|
||||||
|
|
||||||
|
$(document).on('click', '#alert #alertYes', () => {
|
||||||
|
hideAlerts()
|
||||||
|
submitFunction(data)
|
||||||
|
$(document).off('click', '#alert #alertYes')
|
||||||
|
})
|
||||||
|
|
||||||
|
alert.css('display', 'flex')
|
||||||
|
$('#alertHeading').html(title)
|
||||||
|
$('#alertMessage').html(message)
|
||||||
|
|
||||||
|
$('#alertOk').css('display', 'none')
|
||||||
|
$('#alertYes').css('display', 'flex')
|
||||||
|
$('#alertNo').css('display', 'flex')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const hideAlerts = () => $('#alert').hide()
|
||||||
|
|
||||||
|
const turnOnMode = (mode: PosMode) => {
|
||||||
|
Application.mode.push(mode)
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnOffMode = (mode: PosMode) => {
|
||||||
|
Application.mode = Application.mode.filter((value) => value != mode)
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleMode = (mode: PosMode) => {
|
||||||
|
if (!isInMode(mode))
|
||||||
|
turnOnMode(mode)
|
||||||
|
else
|
||||||
|
turnOffMode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearModes = () => {
|
||||||
|
Application.mode = []
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInMode = (mode: PosMode) => Application.mode.includes(mode)
|
||||||
|
|
||||||
|
const setElementVisibilityByMode = () => {
|
||||||
|
const mode = Application.mode
|
||||||
|
const elements = $('[data-visible-in-mode]')
|
||||||
|
|
||||||
|
elements.each((index, elem) => {
|
||||||
|
const element = $(elem)
|
||||||
|
const visibleInModes: PosModes = element.data('visible-in-mode')
|
||||||
|
|
||||||
|
const showElement = visibleInModes.every(visibleMode => {
|
||||||
|
return mode.includes(visibleMode)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (element.hasClass('useVisibility')) {
|
||||||
|
if (showElement) {
|
||||||
|
element.css('visibility', 'visible')
|
||||||
|
} else element.css('visibility', 'hidden')
|
||||||
|
} else element.toggle(showElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
const invisibleElements = $('[data-invisible-in-mode]')
|
||||||
|
invisibleElements.each((index, elem) => {
|
||||||
|
const element = $(elem)
|
||||||
|
const inVisibleInModes: PosModes = element.data('invisible-in-mode')
|
||||||
|
const hideElement = inVisibleInModes.some(invisibleMode => {
|
||||||
|
return mode.includes(invisibleMode)
|
||||||
})
|
})
|
||||||
|
element.toggle(!hideElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
$('[data-active-in-mode]').each((index, elem) => {
|
||||||
|
const button = $(elem)
|
||||||
|
const activeInMode: PosMode = button.data('active-in-mode')
|
||||||
|
|
||||||
|
mode.includes(activeInMode)
|
||||||
|
? button.addClass('active')
|
||||||
|
: button.removeClass('active')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const pulseElement = (element: JQuery) => element.addClass('pulse').on('animationend', () => element.removeClass('pulse'))
|
||||||
|
|
||||||
|
Array.prototype.where = function<x>(this: x[], property: string, value: any) {
|
||||||
|
return this.filter( item => (item as any)[property] === value)[0] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const money = (amount: number) => currency(amount, {fromCents: true})
|
||||||
|
const moneyFromString = (amount: string) => currency(amount)
|
||||||
|
|
||||||
|
//Id generator.
|
||||||
|
function* newestId(){
|
||||||
|
let id = 0
|
||||||
|
while(true){
|
||||||
|
id++
|
||||||
|
yield id
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
$(() => ajax('/ajax/languageVars', null, 'GET', setupCore, null, null))
|
||||||
For the flow of the app, synchronous is commonly preferred
|
|
||||||
though trying to keep its usage as low as possible.
|
|
||||||
*/
|
|
||||||
let ajaxSync = (endpoint : string, data?: any, method = 'POST') => {
|
|
||||||
let response = JSON.parse(
|
|
||||||
$.ajax({
|
|
||||||
url: endpoint,
|
|
||||||
method: method,
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
async:false,
|
|
||||||
}).responseText)
|
|
||||||
|
|
||||||
if(response.data) {
|
|
||||||
response.data = JSON.parse(response.data)
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Redirect to a specific URL */
|
|
||||||
let redirect = (url: string) : void => location.assign(url)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const resize = () => {
|
|
||||||
$('#pageContainer').height(window.innerHeight + "px");
|
|
||||||
}
|
|
||||||
|
|
||||||
let setupCore = (languageVars: Record<string, string>) => {
|
|
||||||
Application.languageVars = languageVars
|
|
||||||
const doc = $(document)
|
|
||||||
doc.on('click', '#alertNo, #alertOk', hideAlerts)
|
|
||||||
window.addEventListener('resize', resize)
|
|
||||||
resize()
|
|
||||||
|
|
||||||
setElementVisibilityByMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
let posAlert = (message: string, title='Message') => {
|
|
||||||
let alertBox = $('#alert')
|
|
||||||
alertBox.css('display', 'flex');
|
|
||||||
alertBox.data('value', '');
|
|
||||||
$('#alertHeading').text(title);
|
|
||||||
$('#alertMessage').text(message);
|
|
||||||
|
|
||||||
$('#alertOk').css('display', 'flex');
|
|
||||||
$('#alertYes').css('display', 'none');
|
|
||||||
$('#alertNo').css('display', 'none');
|
|
||||||
}
|
|
||||||
|
|
||||||
let confirmation = (message: string, data: any, title='Confirm', submitFunction = (data: any) => {hideAlerts()}) => {
|
|
||||||
let alert = $('#alert')
|
|
||||||
|
|
||||||
$(document).on('click', '#alert #alertYes', () => {
|
|
||||||
hideAlerts()
|
|
||||||
submitFunction(data)
|
|
||||||
$(document).off('click', '#alert #alertYes')
|
|
||||||
})
|
|
||||||
|
|
||||||
alert.css('display', 'flex')
|
|
||||||
$('#alertHeading').html(title)
|
|
||||||
$('#alertMessage').html(message)
|
|
||||||
|
|
||||||
$('#alertOk').css('display', 'none')
|
|
||||||
$('#alertYes').css('display', 'flex')
|
|
||||||
$('#alertNo').css('display', 'flex')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let hideAlerts = () => $('#alert').hide()
|
|
||||||
|
|
||||||
let turnOnMode = (mode : PosMode) => {
|
|
||||||
Application.mode.push(mode)
|
|
||||||
setElementVisibilityByMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
let turnOffMode = (mode : PosMode) => {
|
|
||||||
Application.mode = Application.mode.filter((value) => value != mode)
|
|
||||||
setElementVisibilityByMode()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let toggleMode = (mode: PosMode) => {
|
|
||||||
if(!isInMode(mode))
|
|
||||||
turnOnMode(mode)
|
|
||||||
else
|
|
||||||
turnOffMode(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
let clearModes = () => {Application.mode = []}
|
|
||||||
let isInMode = (mode: PosMode) => Application.mode.includes(mode)
|
|
||||||
|
|
||||||
let setElementVisibilityByMode = () => {
|
|
||||||
const mode = Application.mode
|
|
||||||
const elements = $('[data-visible-in-mode]')
|
|
||||||
|
|
||||||
elements.each((index, elem) => {
|
|
||||||
let element = $(elem)
|
|
||||||
let visibleInModes : PosModes = element.data('visible-in-mode')
|
|
||||||
|
|
||||||
let showElement = visibleInModes.every( visibleMode => {
|
|
||||||
return mode.includes(visibleMode)
|
|
||||||
});
|
|
||||||
|
|
||||||
if(element.hasClass('useVisibility')){
|
|
||||||
if(showElement) {
|
|
||||||
element.css('visibility', 'visible')
|
|
||||||
} else element.css('visibility', 'hidden')
|
|
||||||
} else element.toggle(showElement)
|
|
||||||
})
|
|
||||||
|
|
||||||
const invisibleElements = $('[data-invisible-in-mode]')
|
|
||||||
invisibleElements.each((index, elem) => {
|
|
||||||
let element = $(elem)
|
|
||||||
let inVisibleInModes: PosModes = element.data('invisible-in-mode')
|
|
||||||
let hideElement = inVisibleInModes.some(invisibleMode => {
|
|
||||||
return mode.includes(invisibleMode)
|
|
||||||
})
|
|
||||||
element.toggle(!hideElement)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
$('[data-active-in-mode]').each((index, elem) =>{
|
|
||||||
const button = $(elem)
|
|
||||||
const activeInMode : PosMode = button.data('active-in-mode')
|
|
||||||
|
|
||||||
mode.includes(activeInMode)
|
|
||||||
? button.addClass('active')
|
|
||||||
: button.removeClass('active')
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$( () => ajax('/ajax/languageVars', null, 'GET', setupCore, null, null))
|
|
||||||
@@ -1,9 +1,25 @@
|
|||||||
interface OrderScreen{
|
type OrderScreenData = {
|
||||||
order_screen_pages: order_screen_page[]
|
order_screen_pages: order_screen_page[]
|
||||||
|
sales_categories: sales_category[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type OrderScreen = {
|
||||||
|
order_screen_pages: order_screen_page[]
|
||||||
|
last_added_item: orderItem
|
||||||
|
order_items: orderItem[]
|
||||||
|
sales_categories: sales_category[]
|
||||||
|
order_item_id_generator: Generator
|
||||||
|
selected_item_ids: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
let OrderScreen : OrderScreen = {
|
let OrderScreen : OrderScreen = {
|
||||||
order_screen_pages: null
|
order_screen_pages: null,
|
||||||
|
last_added_item: null,
|
||||||
|
order_items: [],
|
||||||
|
sales_categories: [],
|
||||||
|
order_item_id_generator: newestId(),
|
||||||
|
selected_item_ids: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadPageGroup = (e: Event) => {
|
const loadPageGroup = (e: Event) => {
|
||||||
@@ -14,6 +30,7 @@ const loadPageGroup = (e: Event) => {
|
|||||||
$('.pageGroup').hide()
|
$('.pageGroup').hide()
|
||||||
let activeGrid = $(`.pageGroup[data-page-group-id=${pageGroupId}]`)
|
let activeGrid = $(`.pageGroup[data-page-group-id=${pageGroupId}]`)
|
||||||
let navButtons = $('.pageNavigation')
|
let navButtons = $('.pageNavigation')
|
||||||
|
navButtons.css('display', 'flex')
|
||||||
|
|
||||||
activeGrid.find('.gridPage').length > 1
|
activeGrid.find('.gridPage').length > 1
|
||||||
? navButtons.show()
|
? navButtons.show()
|
||||||
@@ -22,12 +39,20 @@ const loadPageGroup = (e: Event) => {
|
|||||||
activeGrid.css('display', 'inline-flex')
|
activeGrid.css('display', 'inline-flex')
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupOrderScreen = (data: OrderScreen) => {
|
const setupOrderScreen = (data: OrderScreenData) => {
|
||||||
OrderScreen = data
|
OrderScreen.order_screen_pages = data.order_screen_pages
|
||||||
|
OrderScreen.sales_categories = data.sales_categories
|
||||||
let doc = $(document)
|
let doc = $(document)
|
||||||
doc.on('click', '.nextButton', goToNextPage)
|
doc.on('click', '.nextButton', goToNextPage)
|
||||||
doc.on('click', '.prevButton', goToPrevPage)
|
doc.on('click', '.prevButton', goToPrevPage)
|
||||||
doc.on('click', '.loadPageGroup', loadPageGroup)
|
doc.on('click', '.loadPageGroup', loadPageGroup)
|
||||||
|
doc.on('click', '[data-primary-action=item]', itemButtonClicked)
|
||||||
|
doc.on('click', 'tr', itemRowClicked)
|
||||||
|
doc.on('click', '.voidButton', voidButtonClicked)
|
||||||
|
doc.on('dblclick', '.voidButton', voidLastItem)
|
||||||
|
doc.on('click', '.accumulateButton', () => toggleMode('accumulate'))
|
||||||
|
|
||||||
|
turnOnMode('accumulate')
|
||||||
|
|
||||||
$('.loadPageGroup').first().trigger('click')
|
$('.loadPageGroup').first().trigger('click')
|
||||||
}
|
}
|
||||||
@@ -43,5 +68,204 @@ const navigatePage = (direction: number) => {
|
|||||||
const goToNextPage = () => navigatePage(1)
|
const goToNextPage = () => navigatePage(1)
|
||||||
const goToPrevPage = () => navigatePage(-1)
|
const goToPrevPage = () => navigatePage(-1)
|
||||||
|
|
||||||
|
const addNewItem = (item: item) => {
|
||||||
|
let salesCategory = OrderScreen.sales_categories.where('id', item.item_category)
|
||||||
|
const existingOrderItem = isInMode('accumulate') && item.item_type != 'instruction' ? OrderScreen.order_items.where('item', item) : null
|
||||||
|
let orderItem : orderItem = existingOrderItem || {
|
||||||
|
id: OrderScreen.order_item_id_generator.next().value,
|
||||||
|
item: item,
|
||||||
|
qty: 0,
|
||||||
|
sales_category: salesCategory,
|
||||||
|
}
|
||||||
|
|
||||||
|
saveOrderItem(orderItem)
|
||||||
|
renderOrderBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderOrderBox = () => {
|
||||||
|
const orderBox = $('.orderBoxTable')
|
||||||
|
const tbody = orderBox.children('tbody')
|
||||||
|
const newTbody = $('<tbody />')
|
||||||
|
OrderScreen.order_items.forEach(orderItem => {
|
||||||
|
const newRow = createOrderRow(orderItem)
|
||||||
|
newTbody.append(newRow)
|
||||||
|
if(orderItem.id == OrderScreen.last_added_item?.id){
|
||||||
|
pulseElement(newRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(OrderScreen.selected_item_ids.includes(orderItem.id)){
|
||||||
|
selectRow(newRow)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tbody.replaceWith(newTbody)
|
||||||
|
const element = orderBox.find('tbody tr').last().get()[0]
|
||||||
|
element.scrollIntoView()
|
||||||
|
OrderScreen.last_added_item = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOrderRow = (orderItem: orderItem) => {
|
||||||
|
const row = $('.orderBoxTable').EmptyRow()
|
||||||
|
const price = money(orderItem.item.price1)
|
||||||
|
row.data('order-item-id', orderItem.id)
|
||||||
|
row.addClass(`${orderItem.item.item_type}Row`)
|
||||||
|
row
|
||||||
|
.setColumnValue(lang('qty_header'), orderItem.qty)
|
||||||
|
.setColumnValue(lang('item_header'), orderItem.item.item_name)
|
||||||
|
.setColumnValue(lang('price_header'), price)
|
||||||
|
.setColumnValue(lang('total_price_header'), price)
|
||||||
|
.setColumnValue(lang('printgroup_header'), OrderScreen.sales_categories.where('id', orderItem.item.item_category)?.name)
|
||||||
|
.data('order-item-id', orderItem.id)
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveOrderItem = (orderItem: orderItem) => {
|
||||||
|
const selectedRows = $('.orderBoxTable tbody tr.selected').get()
|
||||||
|
const currentQty = orderItem.qty
|
||||||
|
orderItem.qty = currentQty + 1
|
||||||
|
if( isInMode('accumulate') && orderItem.qty > 1) {
|
||||||
|
OrderScreen.order_items = OrderScreen.order_items.map(
|
||||||
|
existingOrderItem => {
|
||||||
|
if (existingOrderItem == orderItem) return orderItem
|
||||||
|
else return existingOrderItem
|
||||||
|
})
|
||||||
|
} else if(orderItem.item.item_type == 'instruction' && selectedRows.length > 0){
|
||||||
|
const selectedOrderItemIds : number[] = selectedRows.map(row => {
|
||||||
|
const orderItem = OrderScreen.order_items.where('id', $(row).data('order-item-id'))
|
||||||
|
if (orderItem.item && orderItem.item.item_type != 'instruction') {
|
||||||
|
return orderItem.id
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}).filter(number => number)
|
||||||
|
|
||||||
|
selectedOrderItemIds.forEach(id => {
|
||||||
|
let item = OrderScreen.order_items.where('id', id)
|
||||||
|
let index = OrderScreen.order_items.indexOf(item) + 1
|
||||||
|
OrderScreen.order_items.splice(index, 0, orderItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
OrderScreen.order_items.push(orderItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderScreen.last_added_item = orderItem
|
||||||
|
|
||||||
|
return orderItem
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const itemButtonClicked = (e: JQuery.TriggeredEvent) => {
|
||||||
|
const existingItemRows = $('.itemRow')
|
||||||
|
const button = $(e.target).closest('.posButton')
|
||||||
|
const item : item = button.data('item')
|
||||||
|
|
||||||
|
if(item.item_type == 'instruction' && existingItemRows.length < 1) return
|
||||||
|
|
||||||
|
addNewItem(item)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const itemRowClicked = (e: JQuery.TriggeredEvent) => {
|
||||||
|
const row = $(e.target).closest('tr')
|
||||||
|
|
||||||
|
if(isInMode('void')){
|
||||||
|
voidRows(row)
|
||||||
|
turnOffMode('void')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!row.hasClass('selected')) selectRow(row)
|
||||||
|
else deselectRow(row)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectRow = (row: JQuery) => {
|
||||||
|
row.addClass('selected')
|
||||||
|
const id = row.data('order-item-id')
|
||||||
|
if(!OrderScreen.selected_item_ids.includes(id))
|
||||||
|
OrderScreen.selected_item_ids.push(id)
|
||||||
|
|
||||||
|
const instructionRows = row.nextUntil('.itemRow')
|
||||||
|
|
||||||
|
if(row.hasClass('itemRow') && instructionRows.length){
|
||||||
|
instructionRows.each((index, row) => {
|
||||||
|
selectRow($(row))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deselectRow = (row: JQuery) => {
|
||||||
|
row.removeClass('selected')
|
||||||
|
const instructionRows = row.nextUntil('.itemRow')
|
||||||
|
OrderScreen.selected_item_ids = OrderScreen.selected_item_ids.filter(id => id != row.data('order-item-id'))
|
||||||
|
|
||||||
|
if(row.hasClass('itemRow') && instructionRows.length){
|
||||||
|
deselectRow(instructionRows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRow = (row: JQuery) => row.find('*:not(.hidden)').slideUp('fast', () => {
|
||||||
|
OrderScreen.order_items = OrderScreen.order_items.filter(orderItem => orderItem.id != row.data('order-item-id'))
|
||||||
|
row.remove()
|
||||||
|
})
|
||||||
|
|
||||||
|
const voidInstructionRow = (row: JQuery) => {
|
||||||
|
const parentRow = row.prevAll('.itemRow').first()
|
||||||
|
const parentOrderItem = OrderScreen.order_items.where('id', parentRow.data('order-item-id'))
|
||||||
|
if(!parentRow.hasClass('selected') || (parentOrderItem && parentOrderItem?.qty == 0) || !parentOrderItem)
|
||||||
|
decrementQty(OrderScreen.order_items.where('id', row.data('order-item-id')))
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidItemRow = (row : JQuery) => {
|
||||||
|
const newQty = Number(row.getColumnValue(lang('qty_header'))) - 1
|
||||||
|
const orderItem = OrderScreen.order_items.where('id', row.data('order-item-id'))
|
||||||
|
const instructionRows = row.nextUntil('.itemRow')
|
||||||
|
|
||||||
|
if(newQty < 1)
|
||||||
|
voidRows(instructionRows)
|
||||||
|
|
||||||
|
decrementQty(orderItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidRow = (row: JQuery) => {
|
||||||
|
if(row.hasClass('itemRow')) voidItemRow(row)
|
||||||
|
else voidInstructionRow(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidRows = (rows: JQuery) => rows.each((index, row) => voidRow($(row)))
|
||||||
|
|
||||||
|
const voidButtonClicked = () => {
|
||||||
|
const selectedRows = $('.orderBox tr.selected')
|
||||||
|
if(isInMode('void')){
|
||||||
|
turnOffMode('void')
|
||||||
|
} else if(selectedRows.length){
|
||||||
|
voidRows(selectedRows)
|
||||||
|
} else {
|
||||||
|
turnOnMode('void')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidLastItem = () => {
|
||||||
|
if(OrderScreen.order_items.length < 1) return
|
||||||
|
let orderItem = OrderScreen.order_items[OrderScreen.order_items.length-1]
|
||||||
|
let row = getOrderItemRow(orderItem)
|
||||||
|
voidRows(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
const decrementQty = (orderItem: orderItem) => {
|
||||||
|
const row = getOrderItemRow(orderItem)
|
||||||
|
if(orderItem.qty <= 1){
|
||||||
|
OrderScreen.order_items = OrderScreen.order_items.filter(item => item != orderItem)
|
||||||
|
deleteRow(row)
|
||||||
|
} else {
|
||||||
|
orderItem.qty--
|
||||||
|
row.setColumnValue(lang('qty_header'), orderItem.qty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOrderItemRow = (orderItem: orderItem) => $('tr').filterByData('order-item-id', orderItem.id)
|
||||||
|
|
||||||
$(() => ajax('/orderScreen/getOrderScreenData/1', null, 'get', setupOrderScreen, null, null) )
|
$(() => ajax('/orderScreen/getOrderScreenData/1', null, 'get', setupOrderScreen, null, null) )
|
||||||
47
wwwroot/scripts/ts/dredgepos.tables.ts
Normal file
47
wwwroot/scripts/ts/dredgepos.tables.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
interface JQuery {
|
||||||
|
getColumnValue(columnHeading: string) : string
|
||||||
|
setColumnValue(columnHeading: string, value: any) : JQuery
|
||||||
|
getColumnIndex(columnHeading: string) : number
|
||||||
|
EmptyRow() : JQuery<HTMLTableRowElement>
|
||||||
|
filterByData(prop: string, value: any) : JQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.EmptyRow = function(this: JQuery) {
|
||||||
|
const headingRow = this.find('th').first().closest('tr')
|
||||||
|
const headingCells = headingRow.find('th')
|
||||||
|
const newRow = $('<tr/>')
|
||||||
|
headingCells.each( (index, cell) => {
|
||||||
|
const newCell = $('<td/>')
|
||||||
|
const attributes = Array.from(cell.attributes)
|
||||||
|
newCell.data('column', cell.innerText.trim())
|
||||||
|
newCell.data('column-index', index)
|
||||||
|
attributes.forEach(attribute => newCell.attr(attribute.name, attribute.value))
|
||||||
|
newRow.append(newCell)
|
||||||
|
})
|
||||||
|
|
||||||
|
return newRow as JQuery<HTMLTableRowElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.getColumnIndex = function(this: JQuery, columnHeading: string){
|
||||||
|
return this
|
||||||
|
.find('td')
|
||||||
|
.filterByData('column', columnHeading)
|
||||||
|
.data('column-index')
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.getColumnValue = function(this: JQuery, columnHeading: string){
|
||||||
|
return this.find('td').filterByData('column', columnHeading).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.setColumnValue = function(this: JQuery, columnHeading: string, value: any){
|
||||||
|
this.find('td').filterByData('column', columnHeading).text(value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.filterByData = function(prop: string, val: any) {
|
||||||
|
return this.filter(
|
||||||
|
function() {
|
||||||
|
return $(this).data(prop)==val
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,25 @@
|
|||||||
type PosMode = "edit" | "void" | "transfer" | "default" | "tableSelected" | "decorationSelected" | "activeTableSelected" | "merge" | "reservedTableSelected"
|
type PosMode = "edit" | "void" | "transfer" | "default" | "tableSelected" | "decorationSelected" | "activeTableSelected" | "merge" | "reservedTableSelected" | "accumulate"
|
||||||
type PosModes = PosMode[]
|
type PosModes = PosMode[]
|
||||||
|
|
||||||
|
interface order {
|
||||||
|
clerk: string
|
||||||
|
split: boolean
|
||||||
|
items: orderItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface orderItem {
|
||||||
|
id: number,
|
||||||
|
qty: number,
|
||||||
|
sales_category: sales_category
|
||||||
|
item: item
|
||||||
|
}
|
||||||
|
|
||||||
|
interface printGroup {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
printer: number,
|
||||||
|
venue_id: number,
|
||||||
|
}
|
||||||
|
|
||||||
interface ajaxResult {
|
interface ajaxResult {
|
||||||
status: string
|
status: string
|
||||||
@@ -69,3 +88,27 @@ interface keyboard {
|
|||||||
interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number}
|
interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number}
|
||||||
interface grid {id: number; grid_name: string; grid_rows: number; grid_cols: number; grid_data: string}
|
interface grid {id: number; grid_name: string; grid_rows: number; grid_cols: number; grid_data: string}
|
||||||
|
|
||||||
|
interface item {
|
||||||
|
id: number
|
||||||
|
item_code: string
|
||||||
|
item_category: number
|
||||||
|
item_name: string
|
||||||
|
item_type: string
|
||||||
|
price1: number
|
||||||
|
price2: number
|
||||||
|
price3: number
|
||||||
|
price4: number
|
||||||
|
price5: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type sales_category = {
|
||||||
|
id: number
|
||||||
|
parent: number
|
||||||
|
name: string
|
||||||
|
print_group: string
|
||||||
|
venue_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Array<T> {
|
||||||
|
where(property: string, value: any): T
|
||||||
|
}
|
||||||
36
wwwroot/scripts/ts/typings/currency.d.ts
vendored
36
wwwroot/scripts/ts/typings/currency.d.ts
vendored
@@ -1,26 +1,26 @@
|
|||||||
declare namespace currency {
|
declare namespace currency {
|
||||||
type Any = number | string | currency;
|
type Any = number | string | currency;
|
||||||
type Format = (currency?: currency, opts?: Options) => string;
|
type Format = (currency?: currency, opts?: Options) => string;
|
||||||
interface Constructor {
|
interface Constructor {
|
||||||
(value: currency.Any, opts?: currency.Options): currency,
|
(value: currency.Any, opts?: currency.Options): currency,
|
||||||
new(value: currency.Any, opts?: currency.Options): currency
|
new(value: currency.Any, opts?: currency.Options): currency
|
||||||
}
|
}
|
||||||
interface Options {
|
interface Options {
|
||||||
symbol?: string,
|
symbol?: string,
|
||||||
separator?: string,
|
separator?: string,
|
||||||
decimal?: string,
|
decimal?: string,
|
||||||
errorOnInvalid?: boolean,
|
errorOnInvalid?: boolean,
|
||||||
precision?: number,
|
precision?: number,
|
||||||
increment?: number,
|
increment?: number,
|
||||||
useVedic?: boolean,
|
useVedic?: boolean,
|
||||||
pattern?: string,
|
pattern?: string,
|
||||||
negativePattern?: string,
|
negativePattern?: string,
|
||||||
format?: currency.Format,
|
format?: currency.Format,
|
||||||
fromCents?: boolean
|
fromCents?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface currency {
|
interface currency {
|
||||||
add(number: currency.Any): currency;
|
add(number: currency.Any): currency;
|
||||||
subtract(number: currency.Any): currency;
|
subtract(number: currency.Any): currency;
|
||||||
multiply(number: currency.Any): currency;
|
multiply(number: currency.Any): currency;
|
||||||
@@ -33,6 +33,6 @@
|
|||||||
toJSON(): number;
|
toJSON(): number;
|
||||||
readonly intValue: number;
|
readonly intValue: number;
|
||||||
readonly value: number;
|
readonly value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const currency: currency.Constructor;
|
declare const currency: currency.Constructor;
|
||||||
|
|||||||
@@ -15,3 +15,12 @@
|
|||||||
--posbutton-background-active: #20282D -webkit-gradient(linear, left top, left bottom, color-stop(3%,#20282D), color-stop(51%,#252E34), color-stop(100%,#222A30)) 0 top
|
--posbutton-background-active: #20282D -webkit-gradient(linear, left top, left bottom, color-stop(3%,#20282D), color-stop(51%,#252E34), color-stop(100%,#222A30)) 0 top
|
||||||
--void-button-background: red -webkit-gradient(linear, left top, left bottom, color-stop(3%,darkred), color-stop(51%,darkred), color-stop(100%,red)) 0 top
|
--void-button-background: red -webkit-gradient(linear, left top, left bottom, color-stop(3%,darkred), color-stop(51%,darkred), color-stop(100%,red)) 0 top
|
||||||
|
|
||||||
|
/** Order Screen **/
|
||||||
|
--orderbox-header-background: #888
|
||||||
|
--orderbox-row-background: var(--pos-header-background)
|
||||||
|
--orderbox-selected-row-background: #dd7f37
|
||||||
|
|
||||||
|
/** Order Box/Payment Splitter Box **/
|
||||||
|
--pulse-first-color: #ffa93e
|
||||||
|
--pulse-second-color: #dd7f37
|
||||||
|
--pulse-final-color: var(--orderbox-row-background)
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
src: url("/fonts/OpenSans-Regular.ttf") format('truetype')
|
src: url("/fonts/OpenSans-Regular.ttf") format('truetype')
|
||||||
font-style: normal
|
font-style: normal
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: "manrope"
|
||||||
|
src: url("/fonts/OpenSans-SemiBold.ttf") format('truetype')
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: "manrope"
|
||||||
|
src: url("/fonts/OpenSans-Light.ttf") format('truetype')
|
||||||
|
font-weight: 100
|
||||||
|
|
||||||
*
|
*
|
||||||
margin: 0
|
margin: 0
|
||||||
@@ -10,10 +19,19 @@
|
|||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
font-family: 'manrope', sans-serif
|
font-family: 'manrope', sans-serif
|
||||||
scroll-behavior: smooth
|
scroll-behavior: smooth
|
||||||
|
cursor: pointer
|
||||||
|
-webkit-touch-callout: none
|
||||||
|
-webkit-user-select: none
|
||||||
|
-khtml-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
user-select: none
|
||||||
|
|
||||||
.rtl
|
.rtl
|
||||||
direction: rtl
|
direction: rtl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
input[type=text], select, textarea
|
input[type=text], select, textarea
|
||||||
padding-left: 1em
|
padding-left: 1em
|
||||||
padding-right: 1em
|
padding-right: 1em
|
||||||
@@ -85,4 +103,20 @@ body
|
|||||||
background: var(--void-button-background)
|
background: var(--void-button-background)
|
||||||
|
|
||||||
.invisible
|
.invisible
|
||||||
visibility: hidden
|
visibility: hidden
|
||||||
|
|
||||||
|
.hidden
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.pulse
|
||||||
|
animation-name: color
|
||||||
|
animation-duration: 300ms
|
||||||
|
animation-iteration-count: 1
|
||||||
|
|
||||||
|
@keyframes color
|
||||||
|
0%
|
||||||
|
background-color: var(--pulse-first-color)
|
||||||
|
50%
|
||||||
|
background-color: var(--pulse-second-color)
|
||||||
|
100%
|
||||||
|
background-color: var(--pulse-final-color)
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
.orderBox
|
.orderBox
|
||||||
flex-basis: 75%
|
flex-basis: 75%
|
||||||
background: var(--global-bgcolor)
|
background: var(--global-bgcolor)
|
||||||
|
overflow-y: auto
|
||||||
|
|
||||||
.orderBoxInfo
|
.orderBoxInfo
|
||||||
flex-basis: 5%
|
flex-basis: 5%
|
||||||
@@ -90,7 +91,7 @@
|
|||||||
.active
|
.active
|
||||||
border-bottom: none
|
border-bottom: none
|
||||||
|
|
||||||
#pageContainer
|
#pageGroupContainer
|
||||||
@include flex-column
|
@include flex-column
|
||||||
@include flex-column-item
|
@include flex-column-item
|
||||||
justify-content: flex-end
|
justify-content: flex-end
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
-ms-overflow-style: none
|
-ms-overflow-style: none
|
||||||
|
|
||||||
::-webkit-scrollbar
|
::-webkit-scrollbar
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
.pageGroup
|
.pageGroup
|
||||||
/*display: inline-flex*/
|
/*display: inline-flex*/
|
||||||
@@ -153,13 +154,20 @@
|
|||||||
|
|
||||||
.buttonImg
|
.buttonImg
|
||||||
padding: 0.6em
|
padding: 0.6em
|
||||||
flex-basis: 80%
|
flex-basis: 65%
|
||||||
width: 100%
|
width: 100%
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-grow: 0
|
||||||
|
|
||||||
.text
|
.text
|
||||||
@include flex
|
@include flex
|
||||||
flex-basis: 20%
|
align-items: flex-start
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-basis: 35%
|
||||||
width: 100%
|
width: 100%
|
||||||
|
overflow: hidden
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
.hasImage.doubleWidth
|
.hasImage.doubleWidth
|
||||||
flex-direction: row
|
flex-direction: row
|
||||||
@@ -189,11 +197,55 @@
|
|||||||
flex-basis: 50%
|
flex-basis: 50%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
|
||||||
|
|
||||||
.pageNavigation
|
.pageNavigation
|
||||||
@include flex
|
@include flex
|
||||||
@include flex-column-item
|
@include flex-column-item
|
||||||
display: none
|
display: none
|
||||||
flex-basis: 15%
|
|
||||||
|
|
||||||
> *
|
> *
|
||||||
@include flex-item
|
@include flex-item
|
||||||
|
|
||||||
|
|
||||||
|
.orderBoxTable
|
||||||
|
width: 100%
|
||||||
|
border-collapse: collapse
|
||||||
|
|
||||||
|
tr
|
||||||
|
background: var(--orderbox-row-background)
|
||||||
|
|
||||||
|
|
||||||
|
.selected
|
||||||
|
background: var(--orderbox-selected-row-background)
|
||||||
|
|
||||||
|
thead tr
|
||||||
|
background: var(--orderbox-header-background)
|
||||||
|
|
||||||
|
th
|
||||||
|
font-weight: normal
|
||||||
|
text-align: center
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
|
||||||
|
tr
|
||||||
|
td, th
|
||||||
|
text-align: center
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
|
td
|
||||||
|
padding: 1em 0.5em
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.itemCell
|
||||||
|
text-align: center
|
||||||
|
width: 70%
|
||||||
|
|
||||||
|
td.itemCell
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
tr.instructionRow
|
||||||
|
td.itemCell
|
||||||
|
padding-left: 2em
|
||||||
|
font-weight: 100
|
||||||
|
|
||||||
|
.qtyCell, .totalPriceCell, .printGroupCell
|
||||||
|
font-size: 0
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>DredgePOS</title>
|
<title>DredgePOS</title>
|
||||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://unpkg.com/current-device/umd/current-device.min.js"></script>
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name = "viewport" content = "user-scalable = no ,shrink-to-fit=yes" />
|
<meta name = "viewport" content = "user-scalable = no ,shrink-to-fit=yes" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest">
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
@@ -12,13 +10,24 @@
|
|||||||
|
|
||||||
<div id="pageContainer">
|
<div id="pageContainer">
|
||||||
<div id="leftColumn">
|
<div id="leftColumn">
|
||||||
<h1 class="tableHeading"><!--[lang:active_table]--></h1>
|
<h1 class="tableHeading"><!--[var: orderNumber]--></h1>
|
||||||
<div class="tableInfo">
|
<div class="tableInfo">
|
||||||
<a href="#" class="posButton"><!--[lang:covers]--></a>
|
<!--[var: coverSelectorButton]-->
|
||||||
<a class="posHeader">Logged in as <!--[arr:clerk|clerk_name]--></a></a>
|
<a class="posButton">Logged in as <!--[arr:clerk|clerk_name]--></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="orderBox">
|
<div class="orderBox">
|
||||||
|
<table class="orderBoxTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="orderBoxCell qtyCell"><!--[lang:qty_header]--></th>
|
||||||
|
<th class="orderBoxCell itemCell"><!--[lang:item_header]--></th>
|
||||||
|
<th class="orderBoxCell unitPriceCell hidden"><!--[lang:price_header]--></th>
|
||||||
|
<th class="orderBoxCell totalPriceCell"><!--[lang:total_price_header]--></th>
|
||||||
|
<th class="orderBoxCell printGroupCell"><!--[lang:printgroup_header]--></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="orderBoxInfo"></div>
|
<div class="orderBoxInfo"></div>
|
||||||
<div class="orderBoxFooter">
|
<div class="orderBoxFooter">
|
||||||
@@ -44,8 +53,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="functionColumn">
|
<div class="functionColumn">
|
||||||
<a class="posButton"></a>
|
<a class="posButton"></a>
|
||||||
<a class="posButton"></a>
|
<a class="posButton accumulateButton" data-active-in-mode="accumulate"><!--[lang:accumulate_function]--></a>
|
||||||
<a class="posButton voidButton"><!--[lang:void]--></a>
|
<a class="posButton voidButton" data-active-in-mode="void"><!--[lang:void]--></a>
|
||||||
<a class="posButton"></a>
|
<a class="posButton"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="functionColumn"></div>
|
<div class="functionColumn"></div>
|
||||||
@@ -55,8 +64,8 @@
|
|||||||
<div id="pageList">
|
<div id="pageList">
|
||||||
<!--[var:categoryList]-->
|
<!--[var:categoryList]-->
|
||||||
</div>
|
</div>
|
||||||
<div id="pageContainer">
|
<div id="pageGroupContainer">
|
||||||
<!--[var:pageGroups]-->
|
<!--[var:pageGroups]-->
|
||||||
</div>
|
</div>
|
||||||
<div class="pageNavigation">
|
<div class="pageNavigation">
|
||||||
<a class="posButton prevButton"><!--[lang:prev_page]--></a>
|
<a class="posButton prevButton"><!--[lang:prev_page]--></a>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<span class="buttonImg" style="background-image:url(images/items/<!--[var:image]-->);"></span>
|
<span class="buttonImg" style="background-image:url(/images/items/<!--[var:image]-->);"></span>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<a href="#" class="posButton coverSelector"><!--[var:covers]--></a>
|
||||||
Reference in New Issue
Block a user