1 Commits

Author SHA1 Message Date
dredgy
89e9e0b7a7 Added a simple close modal feature. 2022-08-22 14:22:54 +10:00
11 changed files with 581 additions and 655 deletions

View File

@@ -7,11 +7,12 @@ let attr = Giraffe.ViewEngine.HtmlElements.attr
let getItemActionAttributes (itemCode: string) = let getItemActionAttributes (itemCode: string) =
let item = Entity.GetFirstByColumn<item> "code" (StringTrim itemCode) let item = Entity.GetFirstByColumn<item> "code" (StringTrim itemCode)
[item |> jsonEncode |> attr "data-item"] [(attr "data-item") <| jsonEncode item]
let getGridActionAttributes (gridId: int) = [(attr "data-grid") <| jsonEncode gridId] let getGridActionAttributes (gridId: int) = [(attr "data-grid") <| jsonEncode gridId]
let getActionAttributes (action: string) (actionValue: string) = let getActionAttributes (action: string) (actionValue: string) =
match action with match action with
| "item" -> getItemActionAttributes actionValue | "item" -> getItemActionAttributes actionValue
| "grid" -> actionValue |> int |> getGridActionAttributes | "grid" -> actionValue |> int |> getGridActionAttributes

View File

@@ -13,10 +13,10 @@ let decoratorItem (imageName, imageUrl) =
let decoratorRow decoratorItems = div [_class "decoratorRow"] [yield! decoratorItems] let decoratorRow decoratorItems = div [_class "decoratorRow"] [yield! decoratorItems]
let decorator (decorationRows: XmlNode[]) = let decorator (decorationRows: XmlNode[]) =
div [_id "decorator"] [ div [_id "decorator"; _class "modal"] [
div [_id "decoratorHeader"] [ div [_id "decoratorHeader"] [
h2 [] [lang "choose_decoration"] h2 [] [lang "choose_decoration"]
a [_class "posButton hideDecorator"] [str "×"] a [_class "posButton hideModals"] [str "×"]
] ]
div [_id "decoratorContent"] [ div [_id "decoratorContent"] [
yield! decorationRows yield! decorationRows

View File

@@ -4,10 +4,7 @@ open DredgePos
open Saturn open Saturn
open Giraffe open Giraffe
let installer = (warbler (fun _ -> htmlString (Controller.RunAllMigrations ())))
let router = router { let router = router {
pipe_through Ajax.Router.pipeline pipe_through Ajax.Router.pipeline
get "/" installer get "/" (warbler (fun _ -> htmlString (Controller.RunAllMigrations ())))
get "" installer
} }

View File

@@ -116,9 +116,6 @@ let itemButtonImage (button: button) =
_style $"background-image:url(\"/images/items/{button.image}\");" _style $"background-image:url(\"/images/items/{button.image}\");"
] [] ] []
let _data_primary_action = attr "data-primary-action"
let _data_secondary_action = attr "data-secondary-action"
let itemButton (button: button) = let itemButton (button: button) =
let extraClasses = let extraClasses =
if button.image.Length > 0 then button.extra_classes + " hasImage" if button.image.Length > 0 then button.extra_classes + " hasImage"
@@ -131,8 +128,8 @@ let itemButton (button: button) =
yield! primaryAttributes yield! primaryAttributes
yield! secondaryAttributes yield! secondaryAttributes
_style button.extra_styles _style button.extra_styles
_data_primary_action button.primary_action (attr "data-primary-action") button.primary_action
_data_secondary_action button.secondary_action (attr "data-secondary-action") button.secondary_action
] [ ] [
if button.image.Length > 0 then itemButtonImage button if button.image.Length > 0 then itemButtonImage button
span [_class "text "] [str button.text] span [_class "text "] [str button.text]

View File

@@ -17,10 +17,7 @@ module Program =
forward "/install" DredgePos.Installer.Router.router forward "/install" DredgePos.Installer.Router.router
} }
let app = let app = application {
let ipAddress = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[2];
printf $"DredgePOS is now running at http://{ipAddress}:5001\n"
application {
use_mime_types [(".woff", "application/font-woff")] use_mime_types [(".woff", "application/font-woff")]
use_static "wwwroot" use_static "wwwroot"
use_router router use_router router

670
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"removeComments":false, "removeComments":false,
"preserveConstEnums":true, "preserveConstEnums":true,
"outDir":"wwwroot/scripts", "outDir":"wwwroot/scripts",
"target":"ESNext", "target":"ES2016",
"sourceMap":true, "sourceMap":true,
"moduleResolution": "node" "moduleResolution": "node"
}, },

View File

@@ -23,9 +23,7 @@ const lang = (key: string, replacements?: string[] | string) => {
} }
/** Check if a variable is defined */ /** Check if a variable is defined */
const defined = (variable: any) => { const defined = (variable: any) => typeof variable !== 'undefined'
return typeof variable !== 'undefined'
}
/** Call an Ajax function asynchronously */ /** Call an Ajax function asynchronously */
const 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) => {
@@ -69,18 +67,17 @@ const ajaxSync = (endpoint: string, data?: any, method = 'POST') => {
/* Redirect to a specific URL */ /* Redirect to a specific URL */
const redirect = (url: string): void => location.assign(url) const redirect = (url: string): void => location.assign(url)
const resize = () => { const resize = () => $('#pageContainer').height(window.innerHeight + "px");
$('#pageContainer').height(window.innerHeight + "px");
}
const setupCore = (languageVars: Record<string, string>) => { const setupCore = (languageVars: Record<string, string>) => {
Application.languageVars = languageVars Application.languageVars = languageVars
const doc = $(document) $(document)
doc.on('click', '#alertNo, #alertOk', hideAlerts) .on('click', '#alertNo, #alertOk', hideAlerts)
doc.on('click', '.toggle', toggle) .on('click', '.toggle', toggle)
.on('click', '.hideModals', hideModals)
window.addEventListener('resize', resize) window.addEventListener('resize', resize)
resize() resize()
setElementVisibilityByMode() setElementVisibilityByMode()
} }
@@ -117,6 +114,7 @@ const confirmation = (message: string, data: any, title = 'Confirm', submitFunct
const hideAlerts = () => $('#alert').hide() const hideAlerts = () => $('#alert').hide()
const hideModals = () => $('.modal').hide()
const turnOnMode = (mode: PosMode) => { const turnOnMode = (mode: PosMode) => {
Application.mode.push(mode) Application.mode.push(mode)
@@ -184,46 +182,15 @@ const setElementVisibilityByMode = () => {
} }
const getPercentageOfPageContainerWidth = (pixels: number) => ( (pixels / $('#pageContainer').width()) * 100) + '%' const pulseElement = (element: JQuery) => element.addClass('pulse').on('animationend', () => element.removeClass('pulse'))
const pulseElement = (element: JQuery) => {
const orderItem: orderItem = element.data('order-item')
removeOrderItemsToPulse(orderItem.id)
element
.addClass('pulse')
.on('animationend', () => element.removeClass('pulse'))
}
Array.prototype.collect = function<x, y>(this: x[], func: (item: x) => y[]) {
return this.map(func).flat(1)
}
Array.prototype.where = function<x>(this: x[], property: string, value: any) { Array.prototype.where = function<x>(this: x[], property: string, value: any) {
return this.filter( item => (item as any)[property] === value)[0] || null return this.filter( item => (item as any)[property] === value)[0] || null
} }
Array.prototype.first = function<x>(this: x[]) {
return this[0]
}
Array.prototype.last = function<x>(this: x[]) {
return this[this.length-1]
}
Array.prototype.unique = function<x>(this: x[]) {
return [... new Set(this)]
}
const clone = <x>(obj: x) => JSON.parse(JSON.stringify(obj)) as x
const money = (amount: number, fromCents=true) => currency(amount, {fromCents: fromCents}) const money = (amount: number, fromCents=true) => currency(amount, {fromCents: fromCents})
const moneyFromString = (amount: string) => currency(amount) const moneyFromString = (amount: string) => currency(amount)
const array_push = <x>(array: x[], value: x) => {
let copy = array.slice()
return [...copy, value]
};
const array_remove = <x>(array: x[], valueToRemove: x) => array.filter(item => item != valueToRemove)
const array_pop = <x>(array:x[]) => array.slice(0,-1);
const array_unique = <x>(array:x[]) => [... new Set(array)]
const toggle = (e: JQuery.TriggeredEvent) => { const toggle = (e: JQuery.TriggeredEvent) => {
const button = $(e.target) const button = $(e.target)
const toggleGroup = button.closest('.toggleGroup') const toggleGroup = button.closest('.toggleGroup')

View File

@@ -7,34 +7,32 @@ type OrderScreenData = {
type OrderScreen = { type OrderScreen = {
order_screen_pages: order_screen_page[] order_screen_pages: order_screen_page[]
last_added_item: orderItem
order_items: orderItem[] order_items: orderItem[]
sales_categories: sales_category[] sales_categories: sales_category[]
print_groups: print_group[] print_groups: print_group[]
order_item_id_generator: Generator order_item_id_generator: Generator
selected_item_ids: number[] selected_item_ids: number[]
order_item_ids_to_pulse: number[]
qty_override: number qty_override: number
print_group_override: print_group print_group_override: print_group
custom_item: item, custom_item: item,
selected_cover: number selected_cover: number
table: floorplan_table, table: floorplan_table,
last_added_item_ids: number[]
} }
let OrderScreen : OrderScreen = { let OrderScreen : OrderScreen = {
order_screen_pages: null, order_screen_pages: null,
last_added_item: null,
order_items: [], order_items: [],
print_groups: [], print_groups: [],
sales_categories: [], sales_categories: [],
order_item_id_generator: newestId(), order_item_id_generator: newestId(),
selected_item_ids: [], selected_item_ids: [],
order_item_ids_to_pulse: [],
qty_override: 1, qty_override: 1,
print_group_override: null, print_group_override: null,
custom_item: null, custom_item: null,
selected_cover: 0, selected_cover: 0,
table: null, table: null,
last_added_item_ids: [],
} }
const loadPageGroup = (e: Event) => { const loadPageGroup = (e: Event) => {
@@ -59,30 +57,32 @@ const loadPageGroup = (e: Event) => {
} }
const setupOrderScreen = (data: OrderScreenData) => { const setupOrderScreen = (data: OrderScreenData) => {
OrderScreen.order_screen_pages = data.order_screen_pages OrderScreen.order_screen_pages = data.order_screen_pages
OrderScreen.sales_categories = data.sales_categories OrderScreen.sales_categories = data.sales_categories
OrderScreen.print_groups = data.print_groups OrderScreen.print_groups = data.print_groups
OrderScreen.custom_item = data.custom_item OrderScreen.custom_item = data.custom_item
updateOrderBoxTotals() updateOrderBoxTotals()
$(document) let doc = $(document)
.on('click', '.nextButton', goToNextPage) doc.on('click', '.nextButton', goToNextPage)
.on('click', '.prevButton', goToPrevPage) doc.on('click', '.prevButton', goToPrevPage)
.on('click', '.loadPageGroup', loadPageGroup) doc.on('click', '.loadPageGroup', loadPageGroup)
.on('click', getElementsByAction('item'), itemButtonClicked) doc.on('click', '[data-primary-action=item]', itemButtonClicked)
.on('click', getElementsByAction('grid'), gridButtonClicked) doc.on('click', '[data-primary-action=grid],[data-secondary-action=grid]', gridButtonClicked)
.on('click', '.closeGrid', hideGrids) doc.on('click', '.closeGrid', hideGrids)
.on('click', '.freetextButton', freetext) doc.on('click', '.freetextButton', freetext)
.on('click', '.openItemButton', customItem) doc.on('click', '.openItemButton', customItem)
.on('click', '.orderBoxTable tbody tr', itemRowClicked) doc.on('click', '.orderBoxTable tbody tr', itemRowClicked)
.on('click', '.voidButton', voidButtonClicked) doc.on('click', '.voidButton', voidButtonClicked)
.on('dblclick', '.voidButton', voidLastItem) doc.on('dblclick', '.voidButton', voidLastItem)
.on('click', '.numpadButton', overrideQty) doc.on('click', '.numpadButton', overrideQty)
.on('click', '.accumulateButton', () => toggleMode('accumulate')) doc.on('click', '.accumulateButton', () => toggleMode('accumulate'))
.on('click', '.changeCoverNumberButton', changeCoverNumberPrompt) doc.on('click', '.changeCoverNumberButton', changeCoverNumberPrompt)
.on('click', '.showCoverSelectorButton', showCoverSelector) doc.on('click', '.showCoverSelectorButton', showCoverSelector)
.on('click', '.coverSelectorButton', coverSelected) doc.on('click', '.coverSelectorButton', coverSelected)
.on('change', '[name=print_override]', printGroupOverride) doc.on('change', '[name=print_override]', printGroupOverride)
turnOnMode('accumulate') turnOnMode('accumulate')
@@ -98,8 +98,6 @@ const setupOrderScreen = (data: OrderScreenData) => {
} }
const getElementsByAction = (action: string) => `[data-primary-action=${action}], [data-secondary-action=${action}]`
/** /**
* @param direction 1 for forward, -1 for backwards. * @param direction 1 for forward, -1 for backwards.
* @param button * @param button
@@ -113,156 +111,88 @@ const navigatePage = (direction: number, button: JQuery) => {
grid.get()[0].scrollLeft += grid.width() * direction grid.get()[0].scrollLeft += grid.width() * direction
} }
const getOrderBox = () => $('.orderBoxTable tbody')
const goToNextPage = (e: JQuery.TriggeredEvent) => navigatePage(1, $(e.target)) const goToNextPage = (e: JQuery.TriggeredEvent) => navigatePage(1, $(e.target))
const goToPrevPage = (e: JQuery.TriggeredEvent) => navigatePage(-1, $(e.target)) const goToPrevPage = (e: JQuery.TriggeredEvent) => navigatePage(-1, $(e.target))
const setItemQty = (orderItem: orderItem, qty: number) => { const addItemToOrderBox = (orderItem:orderItem) => {
if(!orderItem || !OrderScreen.order_items.find(existingItem => orderItem.id == existingItem.id)) return; const orderBox = $('.orderBoxTable tbody')
let selectedRows = orderBox.find('tr.selected')
const instructionIds = getInstructionItems(orderItem.id).map(orderItem => orderItem.id) let lastRow : JQuery = selectedRows.length ? getLastInstructionRow(selectedRows.first()) : orderBox.find('tr').last()
const existingRow = orderBox
if(qty < 1){ .find('tr')
const newItems = OrderScreen.order_items.filter(existingOrderItem => .filterByData('item', orderItem.item)
(existingOrderItem.id != orderItem.id) && !instructionIds.includes(existingOrderItem.id) .filterByData('print_group', orderItem.print_group_id)
) .filterByData('cover', orderItem.cover)
OrderScreen.selected_item_ids = array_remove(OrderScreen.selected_item_ids, orderItem.id)
if(orderItem.item.item_type == "item") {
const lastItem = newItems.filter(orderItem => orderItem.item.item_type == "item")?.last()
OrderScreen.last_added_item_ids = newItems.length > 0 && lastItem ? [lastItem.id] : []
}
setOrderItems(newItems)
return;
}
const newItems = OrderScreen.order_items.map(existingOrderItem => {
if(existingOrderItem.id == orderItem.id){
existingOrderItem.qty = qty
}
return existingOrderItem
})
setOrderItems(newItems)
}
const incrementItemQty = (orderItem:orderItem) => setItemQty(orderItem, orderItem?.qty+1)
const decrementItemQty = (orderItem:orderItem) => setItemQty(orderItem, orderItem?.qty-1)
const deselectAllRows = () => {
OrderScreen.selected_item_ids = []
$('tr.selected').removeClass('selected')
}
const addOrderItemsToPulse = (id: number) => OrderScreen.order_item_ids_to_pulse = array_push(OrderScreen.order_item_ids_to_pulse, id)
const removeOrderItemsToPulse = (id: number) => OrderScreen.order_item_ids_to_pulse = array_remove(OrderScreen.order_item_ids_to_pulse, id)
const addItemToOrderBox = (newOrderItem:orderItem) => {
const existingItems = OrderScreen.order_items
.filter(existingOrderItem =>
existingOrderItem.item.id == newOrderItem.item.id
&& existingOrderItem.print_group.id == newOrderItem.print_group.id
&& existingOrderItem.cover == newOrderItem.cover
)
if(existingItems.length > 0 && isInMode('accumulate')) {
addOrderItemsToPulse(existingItems[0].id)
incrementItemQty(existingItems[0])
OrderScreen.last_added_item_ids = [existingItems[0].id]
} else {
addOrderItemsToPulse(newOrderItem.id)
if(!OrderScreen.selected_item_ids.length) {
const newItems = array_push(OrderScreen.order_items, newOrderItem)
setOrderItems(newItems)
} else {
const newItems =
OrderScreen.order_items
.collect(existingOrderItem => {
const firstSelectedItemId = getLastInstructionItem(OrderScreen.selected_item_ids.first()).id
return firstSelectedItemId == existingOrderItem.id
? [existingOrderItem, newOrderItem]
: [existingOrderItem]
})
setOrderItems(newItems)
}
OrderScreen.last_added_item_ids = [newOrderItem.id]
}
deselectAllRows()
}
const getParentItem = (orderItemId: number, itemList:orderItem[]) => {
const itemIndex = itemList.findIndex(orderItem => orderItem.id === orderItemId);
if(itemList[itemIndex].item.item_type == "item"){
return itemList[itemIndex]
}
return itemList
.filter((orderItem, index) => index < itemIndex && orderItem.item.item_type === 'item')
.last() .last()
}
const getInstructionItems = (orderItemId: number) => {
const itemIndex = OrderScreen.order_items.findIndex(orderItem => orderItem.id === orderItemId);
if(!OrderScreen.order_items[itemIndex+1]
|| OrderScreen.order_items[itemIndex+1].item.item_type == "item"
|| OrderScreen.order_items[itemIndex].item.item_type == "instruction"
)
return [OrderScreen.order_items[itemIndex]]
const nextItem = //If accumulating, just increase the quantity of the existing row.
OrderScreen.order_items if(existingRow.length > 0 && isInMode('accumulate')){
.filter((orderItem, index) => index > itemIndex && orderItem.item.item_type === 'item') incrementRowQty(existingRow, orderItem.qty)
?.first() scrollToElement(existingRow)
if(!nextItem) { existingRow.pulse()
return OrderScreen.order_items.filter((orderItem, index) => index > itemIndex) } else {
const newRow = createOrderRow(orderItem)
lastRow.length > 0
? lastRow.after(newRow)
: orderBox.append(newRow)
scrollToElement(newRow)
newRow.pulse()
} }
const nextItemIndex = OrderScreen.order_items.findIndex(orderItem => orderItem.id === nextItem.id);
return OrderScreen.order_items.slice(itemIndex, nextItemIndex)
deselectRow(orderBox.find('tr'))
} }
const getLastInstructionItem = (orderItemId: number) => getInstructionItems(orderItemId).last()
const addInstructionToOrderBox = (instruction: orderItem) => { const addInstructionToOrderBox = (instruction: orderItem) => {
const orderBox = $('.orderBoxTable tbody')
let selectedRows = orderBox.find('tr.selected')
const newRow = createOrderRow(instruction)
//If no items are added, then you can't add an instruction row. //If no items are added, then you can't add an instruction row.
if(!OrderScreen.order_items.length) return if(!orderBox.find('tr.itemRow').length) return
const addAfter = OrderScreen.selected_item_ids.length if(selectedRows.length > 0){
? OrderScreen.selected_item_ids selectedRows.each( (_, row) => {
.filter(selectedItemId => { const selectedRow = $(row)
const orderItem = OrderScreen.order_items.find(orderItem => orderItem.id == selectedItemId) const parentRow = getParentRow(selectedRow)
const parentItem = getParentItem(selectedItemId, OrderScreen.order_items) if(parentRow.is(selectedRow) || !parentRow.hasClass('selected')) {
return !(orderItem.item.item_type == "instruction" && OrderScreen.selected_item_ids.includes(parentItem.id)) const newRow = createOrderRow(instruction)
}) getLastInstructionRow(selectedRow).after(newRow)
.map(selectedItemId => getLastInstructionItem(selectedItemId).id) newRow
.unique() .setColumnValue( lang('printgroup_header'), selectedRow.getColumnValue(lang('printgroup_header')) )
: OrderScreen.last_added_item_ids.map(itemId => getLastInstructionItem(itemId).id)
const newItems = OrderScreen.order_items.collect(existingItem => {
const newInstruction = createNewOrderItem(instruction.item, instruction.qty, instruction.print_group)
addOrderItemsToPulse(newInstruction.id)
return addAfter.includes(existingItem.id)
? [existingItem, newInstruction]
: [existingItem]
})
setOrderItems(newItems)
}
const createNewOrderItem = (item: item, qty: number, printGroup: print_group) : orderItem => { if(parentRow.hasClass('selected')){
return { selectRow(newRow)
id: OrderScreen.order_item_id_generator.next().value, } else {
item: clone(item), newRow.pulse()
qty: qty,
print_group: printGroup,
cover: OrderScreen.selected_cover,
} }
scrollToElement(newRow)
}
})
}
const lastRow = orderBox.find('tr').last()
newRow
.setColumnValue(lang('printgroup_header'), lastRow.getColumnValue(lang('printgroup_header')))
.appendTo(orderBox)
.pulse()
scrollToElement(newRow)
} }
const addNewItem = (item: item, qty = 1) => { const addNewItem = (item: item, qty = 1) => {
const salesCategory = OrderScreen.sales_categories.where('id', item.sales_category_id) const salesCategory = OrderScreen.sales_categories.where('id', item.sales_category_id)
const printGroup = OrderScreen.print_group_override ?? OrderScreen.print_groups.where('id', salesCategory.print_group_id) const printGroup = OrderScreen.print_group_override ?? OrderScreen.print_groups.where('id', salesCategory.print_group_id)
const orderItem = createNewOrderItem(item, qty, printGroup) const orderItem : orderItem = {
id: OrderScreen.order_item_id_generator.next().value,
item: item,
qty: qty,
print_group_id: printGroup,
cover: OrderScreen.selected_cover,
}
switch(item.item_type){ switch(item.item_type){
case 'instruction': case 'instruction':
@@ -275,34 +205,52 @@ const addNewItem = (item: item, qty = 1) => {
} }
} }
const getLastInstructionRow = (row: JQuery) => {
let stopCounting = false
let finalRow = row
row.nextAll().each(function (index, activeRow){
if(!stopCounting){
if($(activeRow).hasClass('instructionRow')){
finalRow = $(activeRow)
} else {
stopCounting = true
}
}
})
return $(finalRow)
}
const getParentRow = (row: JQuery) =>
row.hasClass('instructionRow')
? row.prevAll('.itemRow').first()
: row
const incrementRowQty = (row: JQuery, qty: number) => {
const existingQty = Number(row.getColumnValue(lang('qty_header')))
const newQty = qty + existingQty
row.setColumnValue(lang('qty_header'), newQty)
calculateRowTotal(row)
}
const renderOrderBox = () => { const renderOrderBox = () => {
const orderBox = getOrderBox() const orderBox = $('.orderBoxTable')
const tbody = orderBox.children('tbody')
const newTbody = $('<tbody />') const newTbody = $('<tbody />')
OrderScreen.order_items.forEach(orderItem => { OrderScreen.order_items.forEach(orderItem => {
const newRow = createOrderRow(orderItem) const newRow = createOrderRow(orderItem)
newTbody.append(newRow) newTbody.append(newRow)
if(OrderScreen.selected_item_ids.includes(orderItem.id)){
newRow.addClass('selected')
}
if(OrderScreen.order_item_ids_to_pulse.includes(orderItem.id)){
newRow.pulse() newRow.pulse()
if(OrderScreen.selected_item_ids.includes(orderItem.id)){
selectRow(newRow)
} }
}) })
orderBox.replaceWith(newTbody) tbody.replaceWith(newTbody)
const element = newTbody.find('tr').last().get()[0] const element = orderBox.find('tbody tr').last().get()[0]
if(element) {
element.scrollIntoView() element.scrollIntoView()
} OrderScreen.last_added_item = null
updateOrderBoxTotals()
}
const setOrderItems = (orderItems: orderItem[]) => {
OrderScreen.order_items = orderItems
renderOrderBox()
} }
const createOrderRow = (orderItem: orderItem) => { const createOrderRow = (orderItem: orderItem) => {
@@ -315,10 +263,10 @@ const createOrderRow = (orderItem: orderItem) => {
.setColumnValue(lang('price_header'), price) .setColumnValue(lang('price_header'), price)
.setColumnValue(lang('id_header'), orderItem.item.id) .setColumnValue(lang('id_header'), orderItem.item.id)
.setColumnValue(lang('total_price_header'), price.multiply(orderItem.qty)) .setColumnValue(lang('total_price_header'), price.multiply(orderItem.qty))
.setColumnValue(lang('printgroup_header'), orderItem.print_group?.name) .setColumnValue(lang('printgroup_header'), orderItem.print_group_id?.name)
.data('order-item-id', orderItem.id) .data('order-item-id', orderItem.id)
.data('order-item', orderItem) .data('order-item-id', orderItem.id)
.data('print_group', orderItem.print_group) .data('print_group', orderItem.print_group_id)
.data('cover', orderItem.cover) .data('cover', orderItem.cover)
.data('item', orderItem.item) .data('item', orderItem.item)
.find('td.itemCell') .find('td.itemCell')
@@ -361,13 +309,14 @@ const hideGrids = () => $('.gridContainer').hide()
const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => { const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
const gridContainer = $('.gridContainer') const gridContainer = $('.gridContainer')
const cellDimensions = getGridCellDimensions() const gridCellWidth = getGridCellWidth()
const gridCellHeight = getGridCellHeight()
const grid = gridData.grid const grid = gridData.grid
const gridHtml = gridData.gridHtml const gridHtml = gridData.gridHtml
gridContainer gridContainer
.show() .show()
.width(cellDimensions.width * grid.cols) .width(gridCellWidth * grid.cols)
.children('.gridContainerHeader') .children('.gridContainerHeader')
.children('span') .children('span')
.text(grid.name) .text(grid.name)
@@ -377,97 +326,134 @@ const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
.html(gridHtml) .html(gridHtml)
.show() .show()
.parent() .parent()
.height(cellDimensions.height * grid.rows) .height(gridCellHeight * grid.rows)
.closest('.gridContainer') .closest('.gridContainer')
.find('.pageNavigation') .find('.pageNavigation')
.toggle(gridContainer.find('.gridPage').length > 1) .toggle(gridContainer.find('.gridPage').length > 1)
.height(cellDimensions.height) .height(gridCellHeight)
} }
const itemRowClicked = (e: JQuery.TriggeredEvent) => { const itemRowClicked = (e: JQuery.TriggeredEvent) => {
const row = $(e.target).closest('tr') const row = $(e.target).closest('tr')
const orderItem: orderItem = row.data('order-item')
if(isInMode('void')){ if(isInMode('void')){
voidOrderItems([orderItem.id]) voidRows(row)
turnOffMode('void') turnOffMode('void')
return return
} }
if(!row.hasClass('selected')) { if(!row.hasClass('selected')) selectRow(row)
OrderScreen.selected_item_ids = array_push(OrderScreen.selected_item_ids, orderItem.id) else deselectRow(row)
getInstructionItems(orderItem.id).forEach(instruction => OrderScreen.selected_item_ids = array_push(OrderScreen.selected_item_ids, instruction.id))
OrderScreen.selected_item_ids = OrderScreen.selected_item_ids.unique()
}
else {
OrderScreen.selected_item_ids = array_remove(OrderScreen.selected_item_ids, orderItem.id)
getInstructionItems(orderItem.id).forEach(instruction => OrderScreen.selected_item_ids = array_remove(OrderScreen.selected_item_ids, instruction.id))
}
renderOrderBox()
} }
const voidOrderItems = (orderItemIds: number[]) => { const selectRow = (row: JQuery) => {
const originalOrderItemList = clone(OrderScreen.order_items) row.addClass('selected')
const originalSelectedIds = clone(OrderScreen.selected_item_ids) const instructionRows = row.nextUntil('.itemRow')
orderItemIds.forEach(orderItemId => {
const orderItemToVoid = originalOrderItemList.find(item => item.id == orderItemId) if(row.hasClass('itemRow') && instructionRows.length){
if(orderItemToVoid.item.item_type == "instruction") { instructionRows.each((index, row) => {
const parentItem = getParentItem(orderItemToVoid.id, originalOrderItemList) selectRow($(row))
if(originalSelectedIds.includes(parentItem.id) && parentItem.qty-1 < 1 || !originalSelectedIds.includes(parentItem.id)){
decrementItemQty(orderItemToVoid)
}
} else decrementItemQty(orderItemToVoid)
}) })
}
} }
const deselectRow = (row: JQuery) => {
row.removeClass('selected')
const instructionRows = row.nextUntil('.itemRow')
if(row.hasClass('itemRow') && instructionRows.length){
deselectRow(instructionRows)
}
}
const deleteRow = (row: JQuery) => row.find('*:not(.hidden)').slideUp('fast', () => row.remove())
const voidInstructionRow = (row: JQuery) => {
if(!row.prevAll('.itemRow').first().hasClass('selected'))
deleteRow(row)
}
const voidItemRow = (row : JQuery) => decrementQty(row)
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 voidButtonClicked = () => {
const selectedRows = $('.orderBox tr.selected')
if(isInMode('void')){ if(isInMode('void')){
turnOffMode('void') turnOffMode('void')
} else if(OrderScreen.selected_item_ids.length){ } else if(selectedRows.length){
voidOrderItems(OrderScreen.selected_item_ids) voidRows(selectedRows)
} else { } else {
turnOnMode('void') turnOnMode('void')
} }
} }
const voidLastItem = () => { const voidLastItem = () => {
if (OrderScreen.order_items.length) const orderBox = $('.orderBoxTable tbody')
decrementItemQty(OrderScreen.order_items.last()) const allRows = orderBox.find('tr')
if(allRows.length < 1) return
voidRows(allRows.last())
} }
const getSelectedTotals = () =>
OrderScreen.selected_item_ids
.map(selectedId => OrderScreen.order_items.find(orderItem => orderItem.id == selectedId))
.reduce((resultSoFar, currentOrderItem) => {
return resultSoFar.add(currentOrderItem.item.price1 * currentOrderItem.qty)
}, money(0))
const getTotals = () =>
OrderScreen.order_items
.reduce((resultSoFar, currentOrderItem) => {
return resultSoFar.add(currentOrderItem.item.price1 * currentOrderItem.qty)
}, money(0))
const updateOrderBoxTotals = () => { const updateOrderBoxTotals = () => {
const completeTotal = lang('totalPrice', getTotals().format()) const allRows = $('.orderBoxTable tbody tr')
const selectedTotal = lang('selectedPrice', getSelectedTotals().format()) const selectedRows = $('.orderBoxTable tbody tr.selected')
const completeTotal = lang('totalPrice', getTotalOfRows(allRows))
const selectedTotal = lang('selectedPrice', getTotalOfRows(selectedRows))
$('.orderBoxTotal').text(completeTotal) $('.orderBoxTotal').text(completeTotal)
$('.orderBoxSelectedTotal').text(selectedTotal) $('.orderBoxSelectedTotal').text(selectedTotal)
} }
const getTotalOfRows = (rows: JQuery) => {
return money(rows
.find('td.totalPriceCell')
.get()
.map(cell => Number(cell.innerText))
.filter(number => !isNaN(number))
.reduce( (total, number) => total + number , 0), false)
.format()
}
const getQty = (row: JQuery) => Number(row.getColumnValue(lang('qty_header')))
const getUnitPrice = (row: JQuery) => moneyFromString(row.getColumnValue(lang('price_header')))
const calculateRowTotal = (row: JQuery) => {
let price = getUnitPrice(row)
let qty = getQty(row)
row.setColumnValue(lang('total_price_header'), price.multiply(qty))
}
const decrementQty = (row: JQuery, qty=1) => {
const existingQty = getQty(row)
if(existingQty <= 1){
const childRows = row.nextUntil('.itemRow')
deleteRow(row)
deleteRow(childRows)
return
}
row.setColumnValue(lang('qty_header'), existingQty - qty)
calculateRowTotal(row)
}
const scrollToElement = (JQueryElement: JQuery) => { const scrollToElement = (JQueryElement: JQuery) => {
const element = JQueryElement.get()[0] const element = JQueryElement.get()[0]
const container = JQueryElement.closest('.orderBox').get()[0] const container = JQueryElement.closest('.orderBox').get()[0]
const containerTop = $(container).scrollTop() const containerTop = $(container).scrollTop()
const containerBottom = containerTop + $(container).height() const containerBottom = containerTop + $(container).height();
const elemTop = element.offsetTop const elemTop = element.offsetTop
const elemBottom = elemTop + $(element).height() const elemBottom = elemTop + $(element).height();
if (elemTop < containerTop) { if (elemTop < containerTop) {
$(container).scrollTop(elemTop) $(container).scrollTop(elemTop);
} else if (elemBottom > containerBottom) { } else if (elemBottom > containerBottom) {
$(container).scrollTop(elemBottom - $(container).height()) $(container).scrollTop(elemBottom - $(container).height());
} }
} }
@@ -528,28 +514,21 @@ const customItemTextSubmitted = (text: string) => {
showVirtualNumpad(lang('enter_item_price'), 4, false, true, true, submitFunction) showVirtualNumpad(lang('enter_item_price'), 4, false, true, true, submitFunction)
} }
const getGridCellDimensions = () => { const getGridCellHeight = () => $('#pageGroupContainer').height()/8
const container = $('#pageGroupContainer') const getGridCellWidth = () => $('#pageGroupContainer').width()/6
return {
height: container.height()/8,
width: container.width()/6
}
}
const showCoverSelector = (event: JQuery.TriggeredEvent) => { const showCoverSelector = (event: JQuery.TriggeredEvent) => {
const button = $(event.target) const button = $(event.target)
const gridHeight = getGridCellDimensions().height const gridHeight = getGridCellHeight()
const coverSelector = $('.coverSelector') const coverSelector = $('.coverSelector')
const buttonPositionLeftPercent = getPercentageOfPageContainerWidth(button.offset().left)
const buttonWidthPercent = getPercentageOfPageContainerWidth(button.width())
coverSelector coverSelector
.toggle(!coverSelector.is(':visible')) .toggle(!coverSelector.is(':visible'))
.width(button.width())
.css({ .css({
width: buttonWidthPercent, left: button.offset().left + 'px',
left: buttonPositionLeftPercent, top: button.offset().top + button.height() + 'px',
top: (button.offset().top + button.height()) + 'px'
}) })
.find('.coverSelectorButton') .find('.coverSelectorButton')
.height(gridHeight) .height(gridHeight)
@@ -587,7 +566,7 @@ const changeCoverNumberPrompt = () =>
const changeCoverNumberPromptSubmitted = (value: string) => updateCoverNumbers(Number(value)) const changeCoverNumberPromptSubmitted = (value: string) => updateCoverNumbers(Number(value))
const updateCoverNumbers = (covers: number) => { const updateCoverNumbers = (covers: number) => {
let newTable = clone(OrderScreen.table) let newTable = Object.assign({}, OrderScreen.table)
newTable.default_covers = covers newTable.default_covers = covers
ajax('/order/updateCovers', newTable, 'post', coverNumbersUpdated, null, null) ajax('/order/updateCovers', newTable, 'post', coverNumbersUpdated, null, null)
} }

View File

@@ -25,9 +25,9 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
inputBox.text(''); inputBox.text('');
numpad.data('maxlength', maxlength) numpad.data('maxlength', maxlength)
numpad.data('submit-function', submitFunction) numpad.data('submitfunction', submitFunction)
numpad.data('password', isPassword); numpad.data('password', isPassword);
numpad.data('allow-decimals', allowDecimals); numpad.data('allowdecimals', allowDecimals);
$(document).off('keyup'); $(document).off('keyup');
$(document).on('keyup', e => { $(document).on('keyup', e => {
@@ -64,9 +64,9 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
let virtualNumpadInput = (input: string) => { let virtualNumpadInput = (input: string) => {
let inputBox = $('#virtualNumpadInput') let inputBox = $('#virtualNumpadInput')
let numpad = $('#virtualNumpad') let numpad = $('#virtualNumpad')
let maxlength : number = numpad.data('maxlength') let maxlength = numpad.data('maxlength')
let allowDecimals: boolean = numpad.data('allow-decimals') let allowDecimals = numpad.data('allowdecimals')
let submitFunction : Function = numpad.data('submit-function') let submitFunction = numpad.data('submitfunction')
let allowedValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'submit', 'clear'] let allowedValues = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'submit', 'clear']
let currentValue = numpad.data('value').toString() let currentValue = numpad.data('value').toString()
@@ -144,7 +144,7 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
inputBox.val('') inputBox.val('')
keyboard.data('maxlength', maxlength) keyboard.data('maxlength', maxlength)
keyboard.data('password', isPassword) keyboard.data('password', isPassword)
keyboard.data('submit-function', submitFunction) keyboard.data('submitfunction', submitFunction)
inputBox.attr('autofocus', 'autofocus'); inputBox.attr('autofocus', 'autofocus');
inputBox.trigger('focus') inputBox.trigger('focus')
inputBox.trigger('click') inputBox.trigger('click')
@@ -175,7 +175,7 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
break; break;
case 'submit': case 'submit':
hideVirtualKeyboard(); hideVirtualKeyboard();
let submitFunction = keyboard.data('submit-function') let submitFunction = keyboard.data('submitfunction')
submitFunction(inputBox.val()); submitFunction(inputBox.val());
break; break;
case 'shift': case 'shift':

View File

@@ -10,7 +10,7 @@ interface order {
interface orderItem { interface orderItem {
id: number id: number
qty: number qty: number
print_group: print_group print_group_id: print_group
item: item item: item
cover: number cover: number
} }
@@ -108,8 +108,4 @@ type sales_category = {
interface Array<T> { interface Array<T> {
where(property: string, value: any): T where(property: string, value: any): T
first(): T
last(): T
unique(): this
collect<y>(func: (item: T) => y[]): y[]
} }