Updates to order screen
This commit is contained in:
@@ -54,9 +54,9 @@ let getFloorplanData (id: int) =
|
||||
|> json
|
||||
|
||||
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
|
||||
|> json
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<Compile Include="Reservations.module.fs" />
|
||||
<Compile Include="Floorplan.module.fs" />
|
||||
<Compile Include="Printer.module.fs" />
|
||||
<Compile Include="Orders.module.fs" />
|
||||
<Compile Include="OrderScreen.module.fs" />
|
||||
<Compile Include="Decorations.module.fs" />
|
||||
<Compile Include="Clerk.module.fs" />
|
||||
|
||||
@@ -23,11 +23,11 @@ let get var =
|
||||
else
|
||||
"Missing language variable: " + var
|
||||
|
||||
let getAndReplace languageVar replacements =
|
||||
let getAndReplace languageVar (replacements: 'x list) =
|
||||
let langString = get languageVar
|
||||
replacements
|
||||
|> List.mapi (fun index string
|
||||
-> index + 1, string)
|
||||
|> List.mapi (fun index replacement
|
||||
-> index + 1, replacement.ToString())
|
||||
|> List.fold (fun (result: string) (index, string)
|
||||
-> result.Replace($"[{index}]", string)
|
||||
) langString
|
||||
@@ -11,7 +11,7 @@ open Theme
|
||||
let htmlAttributes (attributes: Map<string, string>) =
|
||||
" " + (attributes
|
||||
|> Map.toArray
|
||||
|> Array.map (fun (attribute, value) -> attribute+"=\""+HttpUtility.HtmlEncode value + "\"")
|
||||
|> Array.map (fun (attribute, value) -> attribute+"='"+HttpUtility.HtmlEncode value + "'")
|
||||
|> String.concat " ")
|
||||
|
||||
let getAllPageGrids () = Entity.getAllInVenue<order_screen_page_group>
|
||||
@@ -46,9 +46,7 @@ let renderButton (buttonId: int) =
|
||||
then "invisible"
|
||||
else ""
|
||||
|
||||
let image = if button.image.Length > 0
|
||||
then loadTemplateWithVars "orderScreen/button_image" (map ["image", button.image])
|
||||
else ""
|
||||
let image = if button.image.Length > 0 then loadTemplateWithVars "orderScreen/button_image" (map ["image", button.image]) else ""
|
||||
|
||||
let extraClasses = [|imageClass; spacerClass|] |> String.concat " "
|
||||
|
||||
@@ -63,7 +61,7 @@ let renderButton (buttonId: int) =
|
||||
"extra_styles", extra_styles
|
||||
"primary_action", button.primary_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
|
||||
"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
|
||||
|
||||
let loadOrderScreen (ctx: HttpContext) : HttpHandler =
|
||||
let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
|
||||
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 =
|
||||
Entity.getAll<order_screen_page_group>
|
||||
|> Array.filter (fun category -> category.id <> 0)
|
||||
Entity.getAllInVenue<order_screen_page_group>
|
||||
|> Array.filter (fun page_group -> page_group.id <> 0)
|
||||
|> Array.sortBy (fun {order=order} -> order)
|
||||
|> Array.map (fun category ->
|
||||
let categoryMap = recordToMap category
|
||||
let categoryArray = map ["page", categoryMap]
|
||||
@@ -54,15 +64,17 @@ let loadOrderScreen (ctx: HttpContext) : HttpHandler =
|
||||
|> Array.map OrderScreen.getPagesHTML
|
||||
|> String.concat "\n"
|
||||
|
||||
|
||||
let variables = map [
|
||||
"title", "Order"
|
||||
"categoryList", categoryList
|
||||
"pageGroups", grids
|
||||
"orderNumber", orderNumber
|
||||
"coverSelectorButton", coverSelectorButton
|
||||
"covers", coverString
|
||||
]
|
||||
|
||||
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 arrays = map ["clerk", currentClerk]
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ module Program =
|
||||
get "/" (redirectTo true "/login")
|
||||
get "/login" (warbler (fun _ -> PageController.loadHomePage() ))
|
||||
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 "/orderScreen" orderScreenRouter
|
||||
}
|
||||
|
||||
29
Types.fs
29
Types.fs
@@ -29,11 +29,20 @@ type floorplan_table = {
|
||||
}
|
||||
|
||||
[<CLIMutable>]
|
||||
type category = {
|
||||
type print_group = {
|
||||
id: int
|
||||
category_name: string
|
||||
category_print_group: string
|
||||
category_department: string
|
||||
name: string
|
||||
printer: int
|
||||
venue_id: int
|
||||
}
|
||||
|
||||
[<CLIMutable>]
|
||||
type sales_category = {
|
||||
id: int
|
||||
parent: int
|
||||
name: string
|
||||
print_group: string
|
||||
venue_id: int
|
||||
}
|
||||
|
||||
[<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}
|
||||
|
||||
[<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>]
|
||||
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_name: string
|
||||
item_type: string
|
||||
price1: float
|
||||
price2: float
|
||||
price3: float
|
||||
price4: float
|
||||
price5: float
|
||||
price1: int
|
||||
price2: int
|
||||
price3: int
|
||||
price4: int
|
||||
price5: int
|
||||
}
|
||||
@@ -51,6 +51,8 @@
|
||||
"merge_table":"Merge Table",
|
||||
"unmerge_table":"Unmerge Table",
|
||||
"order_table":"Place Order",
|
||||
"order_number": "Order [1]",
|
||||
"new_order": "New Order",
|
||||
"view_table":"View Table",
|
||||
"reserve_table":"Reserve Table",
|
||||
"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 = {
|
||||
keyboard : null,
|
||||
mode: [],
|
||||
languageVars: {}
|
||||
}
|
||||
/// <reference path="./typings/currency.d.ts" />
|
||||
|
||||
const Application: ApplicationState = {
|
||||
keyboard: null,
|
||||
mode: [],
|
||||
languageVars: {}
|
||||
}
|
||||
|
||||
|
||||
/** Parses a language variable. */
|
||||
let lang = (key: string, replacements?: string[] | string) => {
|
||||
let finalValue = Application.languageVars[key] || ''
|
||||
/** Parses a language variable. */
|
||||
const lang = (key: string, replacements?: string[] | string) => {
|
||||
let finalValue = Application.languageVars[key] || ''
|
||||
|
||||
if(!replacements) return finalValue
|
||||
if(typeof replacements === 'string') replacements = [replacements]
|
||||
if (!replacements) return finalValue
|
||||
if (typeof replacements === 'string') replacements = [replacements]
|
||||
|
||||
replacements.forEach( (replacement, index) => {
|
||||
let correctIndex = index+1
|
||||
finalValue = finalValue.replace(`[${correctIndex}]`, replacement)
|
||||
})
|
||||
replacements.forEach((replacement, index) => {
|
||||
const correctIndex = index + 1
|
||||
finalValue = finalValue.replace(`[${correctIndex}]`, replacement)
|
||||
})
|
||||
|
||||
return finalValue
|
||||
}
|
||||
return finalValue
|
||||
}
|
||||
|
||||
/** Check if a variable is defined */
|
||||
let defined = (variable: any) => {
|
||||
return typeof variable !== 'undefined'
|
||||
}
|
||||
/** Check if a variable is defined */
|
||||
const defined = (variable: any) => {
|
||||
return typeof variable !== 'undefined'
|
||||
}
|
||||
|
||||
/** Call an Ajax function asynchronously */
|
||||
let ajax = (endpoint : string, data: any, method = 'POST', successFunction : Function , errorFunction : Function, beforeFunction: any) => {
|
||||
data = (data == null) ? data : JSON.stringify(data)
|
||||
return $.ajax({
|
||||
/** Call an Ajax function asynchronously */
|
||||
const ajax = (endpoint: string, data: any, method = 'POST', successFunction: Function, errorFunction: Function, beforeFunction: any) => {
|
||||
data = (data == null) ? data : JSON.stringify(data)
|
||||
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,
|
||||
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
|
||||
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 */
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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))
|
||||
$(() => ajax('/ajax/languageVars', null, 'GET', setupCore, null, null))
|
||||
@@ -1,9 +1,25 @@
|
||||
interface OrderScreen{
|
||||
type OrderScreenData = {
|
||||
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 = {
|
||||
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) => {
|
||||
@@ -14,6 +30,7 @@ const loadPageGroup = (e: Event) => {
|
||||
$('.pageGroup').hide()
|
||||
let activeGrid = $(`.pageGroup[data-page-group-id=${pageGroupId}]`)
|
||||
let navButtons = $('.pageNavigation')
|
||||
navButtons.css('display', 'flex')
|
||||
|
||||
activeGrid.find('.gridPage').length > 1
|
||||
? navButtons.show()
|
||||
@@ -22,12 +39,20 @@ const loadPageGroup = (e: Event) => {
|
||||
activeGrid.css('display', 'inline-flex')
|
||||
}
|
||||
|
||||
const setupOrderScreen = (data: OrderScreen) => {
|
||||
OrderScreen = data
|
||||
const setupOrderScreen = (data: OrderScreenData) => {
|
||||
OrderScreen.order_screen_pages = data.order_screen_pages
|
||||
OrderScreen.sales_categories = data.sales_categories
|
||||
let doc = $(document)
|
||||
doc.on('click', '.nextButton', goToNextPage)
|
||||
doc.on('click', '.prevButton', goToPrevPage)
|
||||
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')
|
||||
}
|
||||
@@ -43,5 +68,204 @@ const navigatePage = (direction: number) => {
|
||||
const goToNextPage = () => 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) )
|
||||
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[]
|
||||
|
||||
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 {
|
||||
status: string
|
||||
@@ -69,3 +88,27 @@ interface keyboard {
|
||||
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 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 Format = (currency?: currency, opts?: Options) => string;
|
||||
interface Constructor {
|
||||
(value: currency.Any, opts?: currency.Options): currency,
|
||||
new(value: currency.Any, opts?: currency.Options): currency
|
||||
(value: currency.Any, opts?: currency.Options): currency,
|
||||
new(value: currency.Any, opts?: currency.Options): currency
|
||||
}
|
||||
interface Options {
|
||||
symbol?: string,
|
||||
separator?: string,
|
||||
decimal?: string,
|
||||
errorOnInvalid?: boolean,
|
||||
precision?: number,
|
||||
increment?: number,
|
||||
useVedic?: boolean,
|
||||
pattern?: string,
|
||||
negativePattern?: string,
|
||||
format?: currency.Format,
|
||||
fromCents?: boolean
|
||||
symbol?: string,
|
||||
separator?: string,
|
||||
decimal?: string,
|
||||
errorOnInvalid?: boolean,
|
||||
precision?: number,
|
||||
increment?: number,
|
||||
useVedic?: boolean,
|
||||
pattern?: string,
|
||||
negativePattern?: string,
|
||||
format?: currency.Format,
|
||||
fromCents?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface currency {
|
||||
interface currency {
|
||||
add(number: currency.Any): currency;
|
||||
subtract(number: currency.Any): currency;
|
||||
multiply(number: currency.Any): currency;
|
||||
@@ -33,6 +33,6 @@
|
||||
toJSON(): number;
|
||||
readonly intValue: 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
|
||||
--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')
|
||||
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
|
||||
@@ -10,10 +19,19 @@
|
||||
box-sizing: border-box
|
||||
font-family: 'manrope', sans-serif
|
||||
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
|
||||
direction: rtl
|
||||
|
||||
|
||||
|
||||
input[type=text], select, textarea
|
||||
padding-left: 1em
|
||||
padding-right: 1em
|
||||
@@ -86,3 +104,19 @@ body
|
||||
|
||||
.invisible
|
||||
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
|
||||
flex-basis: 75%
|
||||
background: var(--global-bgcolor)
|
||||
overflow-y: auto
|
||||
|
||||
.orderBoxInfo
|
||||
flex-basis: 5%
|
||||
@@ -90,7 +91,7 @@
|
||||
.active
|
||||
border-bottom: none
|
||||
|
||||
#pageContainer
|
||||
#pageGroupContainer
|
||||
@include flex-column
|
||||
@include flex-column-item
|
||||
justify-content: flex-end
|
||||
@@ -100,7 +101,7 @@
|
||||
-ms-overflow-style: none
|
||||
|
||||
::-webkit-scrollbar
|
||||
display: none
|
||||
display: none
|
||||
|
||||
.pageGroup
|
||||
/*display: inline-flex*/
|
||||
@@ -153,13 +154,20 @@
|
||||
|
||||
.buttonImg
|
||||
padding: 0.6em
|
||||
flex-basis: 80%
|
||||
flex-basis: 65%
|
||||
width: 100%
|
||||
flex-shrink: 0
|
||||
flex-grow: 0
|
||||
|
||||
.text
|
||||
@include flex
|
||||
flex-basis: 20%
|
||||
align-items: flex-start
|
||||
flex-grow: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: 35%
|
||||
width: 100%
|
||||
overflow: hidden
|
||||
font-size: 0.9em
|
||||
|
||||
.hasImage.doubleWidth
|
||||
flex-direction: row
|
||||
@@ -189,11 +197,55 @@
|
||||
flex-basis: 50%
|
||||
height: 100%
|
||||
|
||||
|
||||
.pageNavigation
|
||||
@include flex
|
||||
@include flex-column-item
|
||||
display: none
|
||||
flex-basis: 15%
|
||||
|
||||
> *
|
||||
@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>
|
||||
<html>
|
||||
<head>
|
||||
<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>
|
||||
<title>DredgePOS</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name = "viewport" content = "user-scalable = no ,shrink-to-fit=yes" />
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
@@ -12,13 +10,24 @@
|
||||
|
||||
<div id="pageContainer">
|
||||
<div id="leftColumn">
|
||||
<h1 class="tableHeading"><!--[lang:active_table]--></h1>
|
||||
<h1 class="tableHeading"><!--[var: orderNumber]--></h1>
|
||||
<div class="tableInfo">
|
||||
<a href="#" class="posButton"><!--[lang:covers]--></a>
|
||||
<a class="posHeader">Logged in as <!--[arr:clerk|clerk_name]--></a></a>
|
||||
<!--[var: coverSelectorButton]-->
|
||||
<a class="posButton">Logged in as <!--[arr:clerk|clerk_name]--></a>
|
||||
</div>
|
||||
<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 class="orderBoxInfo"></div>
|
||||
<div class="orderBoxFooter">
|
||||
@@ -44,8 +53,8 @@
|
||||
</div>
|
||||
<div class="functionColumn">
|
||||
<a class="posButton"></a>
|
||||
<a class="posButton"></a>
|
||||
<a class="posButton voidButton"><!--[lang:void]--></a>
|
||||
<a class="posButton accumulateButton" data-active-in-mode="accumulate"><!--[lang:accumulate_function]--></a>
|
||||
<a class="posButton voidButton" data-active-in-mode="void"><!--[lang:void]--></a>
|
||||
<a class="posButton"></a>
|
||||
</div>
|
||||
<div class="functionColumn"></div>
|
||||
@@ -55,8 +64,8 @@
|
||||
<div id="pageList">
|
||||
<!--[var:categoryList]-->
|
||||
</div>
|
||||
<div id="pageContainer">
|
||||
<!--[var:pageGroups]-->
|
||||
<div id="pageGroupContainer">
|
||||
<!--[var:pageGroups]-->
|
||||
</div>
|
||||
<div class="pageNavigation">
|
||||
<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