19 Commits

Author SHA1 Message Date
9698671de7 Preparing for reactivity 2022-07-07 18:04:39 +10:00
dredgy
f133284309 Merge pull request #11 from dredgy/make_order_screen_reactive
Light fixes
2022-07-07 15:56:49 +10:00
dredgy
9558074c4f Slight refactoring to button action attributes 2022-07-07 15:42:40 +10:00
dredgy
ac30d8147e Few updates to cover selector width calculation 2022-07-07 14:48:32 +10:00
dredgy
b9a0b578e3 Updated typescript 2022-07-06 14:28:55 +10:00
dredgy
0a2ffa123a Merge pull request #10 from dredgy/test_data_update
instruction row now hides price if $0
2022-07-03 19:27:44 +10:00
510bd8cfb9 instruction row now hides price if $0 2022-07-03 19:09:31 +10:00
dredgy
b33db1987b Merge pull request #9 from dredgy/test_data_update
Migration now orders pages correctly, adds demo floorplan. Some small…
2022-07-02 23:29:21 +10:00
aa37b24293 Migration now orders pages correctly, adds demo floorplan. Some small fixes on order screen. 2022-07-02 23:28:42 +10:00
dredgy
627843f3a3 Merge pull request #8 from dredgy/orderscreen_view
Orderscreen view
2022-07-02 14:23:28 +10:00
6c7bb9eff4 Order Screen fully ported to Giraffe View Engine 2022-07-02 14:22:52 +10:00
905adcd7bd Migration test data complete 2022-07-01 15:25:10 +10:00
5e78701b0b Buttons now render entirely in Giraffe 2022-06-30 23:43:16 +10:00
420c6530e0 Lost source code, switching back to main branch 2022-06-29 22:03:45 +10:00
100a772297 Updates to Order Screen 2022-05-12 20:03:39 +10:00
a587423d3e view progression 2022-03-07 21:36:38 +10:00
cc7d06e78b Change to background image loader 2022-02-27 11:57:52 +10:00
05a1a71e6b CSS background in quotes 2022-02-26 22:31:59 +10:00
dredgy
b6aadc072f Merge pull request #7 from dredgy/installer
Migration system added.
2022-02-26 22:24:07 +10:00
22 changed files with 1459 additions and 327 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@
/wwwroot/scripts/*.js.map
/wwwroot/styles/*
/Folder.DotSettings.user
sql.log

View File

@@ -25,5 +25,4 @@ let setCookie name value (expiry: DateTimeOffset) (context: HttpContext) =
options.Expires <- expiry
context.Response.Cookies.Append(name, value, options);
let redirect url (context: HttpContext) =
context.Response.Redirect url
let redirect url (context: HttpContext) = context.Response.Redirect url

View File

@@ -20,6 +20,7 @@
<Compile Include="Entities\Floorplan_Decorations\View.fs" />
<Compile Include="Entities\Floorplan_Decorations\Controller.fs" />
<Compile Include="Entities\Floorplan_Decorations\Router.fs" />
<Compile Include="Entities\Buttons\Model.fs" />
<Compile Include="Authenticate\Model.fs" />
<Compile Include="Authenticate\View.fs" />
<Compile Include="Authenticate\Controller.fs" />
@@ -36,6 +37,7 @@
<Compile Include="Floorplan\Controller.fs" />
<Compile Include="Floorplan\Router.fs" />
<Compile Include="OrderScreen\Model.fs" />
<Compile Include="OrderScreen\View.fs" />
<Compile Include="OrderScreen\Controller.fs" />
<Compile Include="OrderScreen\Router.fs" />
<Compile Include="Reservations\Model.fs" />

18
Entities/Buttons/Model.fs Normal file
View File

@@ -0,0 +1,18 @@
module DredgePos.Entities.Buttons.Model
open DredgePos.Types
open DredgeFramework
let attr = Giraffe.ViewEngine.HtmlElements.attr
let getItemActionAttributes (itemCode: string) =
let item = Entity.GetFirstByColumn<item> "code" (StringTrim itemCode)
[item |> jsonEncode |> attr "data-item"]
let getGridActionAttributes (gridId: int) = [(attr "data-grid") <| jsonEncode gridId]
let getActionAttributes (action: string) (actionValue: string) =
match action with
| "item" -> getItemActionAttributes actionValue
| "grid" -> actionValue |> int |> getGridActionAttributes
| _ -> []

View File

@@ -87,14 +87,20 @@ let saveOrderToTable orderXML tableNumber =
File.WriteAllText(tableFile, tableXML)
let getTable (tableNumber : int) =
let getTableSafely (tableNumber: int) =
let query = select {
table "floorplan_tables"
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
}
let result = query |> Database.Select<floorplan_table>
result |> first
query
|> Database.Select<floorplan_table>
|> Array.tryItem 0
let getTable (tableNumber : int) =
match getTableSafely tableNumber with
| None -> failwith $"Table {tableNumber} not found in current venue"
| Some table -> table
let getTableById (id : int) =
select {
@@ -106,7 +112,7 @@ let getTableById (id : int) =
let getRoom (roomId: int) =
select {
table "floorplan_rooms"
table "rooms"
where (eq "id" roomId)
} |> Database.Select<room> |> first

View File

@@ -8,9 +8,7 @@ open Giraffe.ViewEngine
open DredgeFramework
let VisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-visible-in-mode")
let InvisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-invisible-in-mode")
let ActiveInMode (value: string) = value |> (attr "data-active-in-mode")
let pageContainer (clerk: clerk) roomMenu =

View File

@@ -8,4 +8,11 @@ let htmlViewWithContext func =
func (snd ctx)
|> htmlView
)
|> warbler
let htmlViewWithContextAndId (id: int) func =
(fun ctx ->
func (snd ctx) id
|> htmlView
)
|> warbler

View File

@@ -1,12 +1,20 @@
module DredgePos.Global.View
open Giraffe.ViewEngine
open DredgeFramework
open DredgePos.Types
open Giraffe.ViewEngine
let Value = attr "data-value"
let _table (value: floorplan_table) = value |> jsonEncode |> (attr "data-table")
let VisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-visible-in-mode")
let InvisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-invisible-in-mode")
let ActiveInMode (value: string) = value |> (attr "data-active-in-mode")
let innerText = str
let lang key = language.get key |> str
let template = tag "template"
let scriptToHTML (scriptFile: string) =
let scriptPath = $"/scripts/{scriptFile}"
match FileExists ("wwwroot" + scriptPath) with
@@ -89,6 +97,23 @@ let keyboards = [|
alert
|]
let posButton (extraClasses: string) attrs content =
let allAttrs = [_class $"posButton {extraClasses}"] |> List.append attrs
a allAttrs content
let PosButton classes (attrs: Map<string, 'x>) text =
let attrArray =
attrs
|> Map.map (fun key value ->
(attr key) (string value)
)
|> Map.values
|> Array.ofSeq
posButton classes [
yield! attrArray
] [str text]
let HtmlPage pageTitle scripts styles tags content =
html [] [
head [] [

View File

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

View File

@@ -4,13 +4,26 @@ open DredgeFramework
open DredgePos.Types
open System.IO
let spaceButton () = (Entity.GetFirstByColumn<button> "primary_action" "spacer").id
let getPageOrder pageName =
match pageName with
| "entrees" -> 1
| "mains" -> 2
| "dessert" -> 3
| "beer" -> 4
| "wine" -> 5
| _ -> 0
let CreatePageFromDirectory index (dir: string) =
let dirName = DirectoryInfo(dir).Name
let printGroup =
match dirName.ToLower() with
| "beer" | "wine" -> (Entity.GetFirstByColumn<print_group> "name" "Beverage").id
| _ -> (Entity.GetFirstByColumn<print_group> "name" "Food").id
| "beer" | "wine" -> (Entity.GetFirstByColumn<print_group> "name" "Drinks").id
| "entrees" | "dips" -> (Entity.GetFirstByColumn<print_group> "name" "Entrees").id
| "dessert" -> (Entity.GetFirstByColumn<print_group> "name" "Desserts").id
| _ -> (Entity.GetFirstByColumn<print_group> "name" "Mains").id
let parentName =
match dirName.ToLower() with
@@ -18,7 +31,7 @@ let CreatePageFromDirectory index (dir: string) =
| _ -> "Food"
let parentCategory = Entity.GetFirstByColumn<sales_category> "name" parentName
if dirName.ToLower() <> "dips" && dirName.ToLower() <> "Steak Temperatures" then
if dirName.ToLower() <> "dips" && dirName.ToLower() <> "steak temperatures" then
let NewGrid = Entity.Create {
id=0
name=dirName
@@ -29,11 +42,19 @@ let CreatePageFromDirectory index (dir: string) =
Entity.Create {
id=0
order=index
order= getPageOrder (dirName.ToLower())
venue_id=1
label=dirName
grid_id=NewGrid.id
} |> ignore
elif dirName.ToLower() = "steak temperatures" then
Entity.Create {
id=0
name=dirName
rows=4
cols=6
data=""
} |> ignore
else ()
Entity.Create {
@@ -49,38 +70,52 @@ let CreatePageFromDirectory index (dir: string) =
let CreateDefaultPrintGroups (path: string) =
Entity.Create {
id=0
name="Food"
name="Entrees"
printer_id=1
venue_id=1
} |> ignore
Entity.Create {
id=0
name="Beverage"
name="Mains"
printer_id=1
venue_id=1
} |> ignore
Entity.Create {
id=0
name="Desserts"
printer_id=1
venue_id=1
} |> ignore
Entity.Create {
id=0
name="Drinks"
printer_id=2
venue_id=1
} |> ignore
path
let CreateDefaultVenue (path: string) =
let venue: venue = {
{
id=0
name="Megalomania"
}
Entity.Create venue
|> Entity.Create
|>ignore
path
let CreateDefaultClerk (path: string) =
let venue: clerk = {
{
id=0
name="Josh"
login_code=1408
user_group_id=1
}
Entity.Create venue
|> Entity.Create
|>ignore
path
@@ -89,7 +124,7 @@ let CreateDefaultSalesCategories (path: string) =
id=0
parent=0
name="Food"
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Food").id
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Mains").id
venue_id=1
} |> ignore
@@ -97,12 +132,39 @@ let CreateDefaultSalesCategories (path: string) =
id=0
parent=0
name="Beverage"
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Beverage").id
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Drinks").id
venue_id=1
} |> ignore
path
let CreateDefaultButtons (path: string) =
Entity.Create {
id = 0
text = ""
primary_action = "spacer"
secondary_action = ""
primary_action_value = ""
secondary_action_value = ""
image = ""
extra_classes = "invisible"
extra_styles = ""
}
|> ignore
path
let CreateDefaultItems (path: string) =
Entity.Create {
id = 0
name = "Custom Item"
code = "OPEN000"
sales_category_id = (Entity.GetFirstByColumn<sales_category> "name" "Food").id
item_type = "item"
price1 = 0
}
|> ignore
path
let CreateRooms () =
"wwwroot/images/rooms"
|> Directory.GetFiles
@@ -122,23 +184,24 @@ let populateEntreeGrid () =
let DipSalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Dips"
let Entrees = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let Dips = Entity.GetAllByColumn<item> "sales_category_id" DipSalesCategory.id
let space = spaceButton()
let GridData =
[|
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
|]
|> Array.mapi (fun index current ->
let isFirstColumn = (index % 6) = 0
if not isFirstColumn then current else
let entree = Entrees |> Array.tryItem (index/6)
match entree with
| None -> 0
| None -> space
| Some x -> x.id
)
|> Array.mapi (fun index current ->
@@ -146,7 +209,7 @@ let populateEntreeGrid () =
if not isSecondRow then current else
let entree = Dips |> Array.tryItem (index-7)
match entree with
| None -> 0
| None -> space
| Some x -> x.id
)
@@ -162,20 +225,21 @@ let populateEntreeGrid () =
let populateMainGrid (category: string) () =
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" category
let Mains = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let space = spaceButton()
let getId index =
match Mains |> Array.tryItem index with
| None -> 0
| None -> space
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; getId 2; 0;
0; 0; 0; 0; 0; 0;
getId 3; 0; getId 4; 0; getId 5; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
getId 0; space; getId 1; space; getId 2; space;
space; space; space; space; space; space;
getId 3; space; getId 4; space; getId 5; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
|]
let grid =
@@ -186,22 +250,23 @@ let populateMainGrid (category: string) () =
Entity.Update newGrid |> ignore
let populateDessertGrid () =
let space = spaceButton()
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Dessert"
let Desserts = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let getId index =
match Desserts |> Array.tryItem index with
| None -> 0
| None -> space
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; 0 ; 0;
0; 0; 0; 0; 0; 0;
0; getId 2; 0; getId 4; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
getId 0; space; getId 1; space; space ; space;
space; space; space; space; space; space;
space; getId 2; space; getId 3; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
space; space; space; space; space; space;
|]
let grid =
@@ -212,58 +277,57 @@ let populateDessertGrid () =
Entity.Update newGrid |> ignore
let populateBeerGrid () =
let space = spaceButton()
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Beer"
let Beers = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let grid =
Entity.GetFirstByColumn<order_screen_page_group> "label" "Beer"
|> Entity.GetRelated<grid, order_screen_page_group>
let GridData =
Beers
|> Array.chunkBySize 24
|> Array.map (fun beerPage ->
let getId index =
match beerPage |> Array.tryItem index with
| None -> 0
| Some x -> x.id
[|
getId 0; getId 1; getId 2; getId 3; getId 4 ; getId 5;
0; 0; 0; 0; 0 ;0;
getId 6; getId 7; getId 8; getId 9; getId 10 ; getId 11;
0; 0; 0; 0; 0 ;0;
getId 12; getId 13; getId 14; getId 15; getId 16 ; getId 17;
0; 0; 0; 0; 0 ;0;
getId 18; getId 19; getId 20; getId 21; getId 22 ; getId 23;
0; 0; 0; 0; 0 ;0;
|]
)
|> Array.mapi (fun index beerpage -> (map [$"page{index+1}", beerpage]))
|> jsonEncode
let mutable buttonMap = Map.empty<string, int[]>
Beers
|> Array.chunkBySize 24
|> Array.map (fun beerPage ->
let getId index =
match beerPage |> Array.tryItem index with
| None -> space
| Some x -> x.id
[|
getId 0; getId 1; getId 2; getId 3; getId 4 ; getId 5;
space; space; space; space; space; space;
getId 6; getId 7; getId 8; getId 9; getId 10 ; getId 11;
space; space; space; space; space; space;
getId 12; getId 13; getId 14; getId 15; getId 16 ; getId 17;
space; space; space; space; space; space;
getId 18; getId 19; getId 20; getId 21; getId 22 ; getId 23;
space; space; space; space; space; space;
|]
)
|> Array.iteri (fun index buttonIds ->
buttonMap <- buttonMap |> Map.add $"page{index+1}" buttonIds
)
let GridData = buttonMap |> jsonEncode
let newGrid = {grid with data=GridData}
Entity.Update newGrid |> ignore
let populateSteakTemperaturesGrid () =
let space = spaceButton()
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Steak Temperatures"
let Temps = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let grid =
Entity.GetFirstByColumn<order_screen_page_group> "label" "Steak Temperatures"
|> Entity.GetRelated<grid, order_screen_page_group>
let grid = Entity.GetFirstByColumn<grid> "name" "Steak Temperatures"
let getId index =
match Temps |> Array.tryItem index with
| None -> 0
| None -> space
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; getId 2; 0;
0; 0; 0; 0; 0; 0;
getId 3; 0; getId 4; 0; getId 5; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
getId 0; space; getId 1; space; getId 2; space;
space; space; space; space; space; space;
getId 3; space; getId 4; space; getId 5; space;
space; space; space; space; space; space;
|]
let newGrid = {grid with data=(jsonEncode {|page1=GridData|}); rows=4; cols=6}
@@ -287,14 +351,13 @@ let CreateItemFromFileName (index: int) (dirName: string) (file: string) =
let fileName = Path.GetFileNameWithoutExtension file
let itemType =
match dirName.ToLower() with
| "dips" -> "instruction"
| "steak temperatures" -> "instruction"
| "dips" | "steak temperatures" -> "instruction"
| _ -> "item"
let categories = (Entity.GetAllByColumn<sales_category> "name" dirName)
let categoryID =
if categories.Length > 0 then categories[0].id
else 1
else (Entity.GetFirstByColumn<sales_category> "name" "Mains").id
let newItem = Entity.Create {
id = 0
@@ -302,7 +365,7 @@ let CreateItemFromFileName (index: int) (dirName: string) (file: string) =
sales_category_id=categoryID
name=fileName
item_type=itemType
price1=10
price1=1000
}
let classes =
@@ -310,7 +373,7 @@ let CreateItemFromFileName (index: int) (dirName: string) (file: string) =
| "beer" | "dessert" -> "doubleHeight"
| "mains" | "wine" | "steak temperatures" -> "doubleHeight doubleWidth"
| "entrees" -> "doubleWidth"
| _ -> ""
| _ -> "normal"
Entity.Create {
id=0
@@ -332,14 +395,91 @@ let CreateItemsAndButtons (dir: string) =
|> Array.filter (fun file -> Path.GetExtension file = ".png" || Path.GetExtension file = ".jpg")
|> Array.iteri (fun index -> CreateItemFromFileName index dirName)
let addFloorplanTable tableNumber x y shape width height rotation (room:room) =
Entity.Create {
id=0
table_number=tableNumber
pos_x=x
pos_y=y
shape=shape
width=width
height=height
rotation=rotation
room_id=room.id
venue_id=1
default_covers=2
merged_children=""
previous_state=""
status=0
} |> ignore
room
let PopulateRooms () =
Entity.GetFirstByColumn<room> "name" "Deck & Courtyard"
|> addFloorplanTable 1 1151 1145 "square" 115 115 45
|> addFloorplanTable 2 827 1152 "square" 115 115 0
|> addFloorplanTable 3 836 922 "square" 115 115 0
|> addFloorplanTable 4 956 712 "square" 115 115 0
|> addFloorplanTable 5 535 704 "square" 115 115 0
|> addFloorplanTable 6 265 861 "square" 115 115 0
|> addFloorplanTable 7 265 1031 "square" 115 115 0
|> addFloorplanTable 8 265 1197 "square" 115 115 0
|> addFloorplanTable 19 90 533 "square" 115 115 0
|> addFloorplanTable 20 90 378 "square" 115 115 0
|> addFloorplanTable 21 90 233 "square" 115 115 0
|> addFloorplanTable 22 90 77 "square" 115 115 0
|> addFloorplanTable 23 622 80 "square" 115 115 0
|> addFloorplanTable 24 613 296 "square" 115 115 -45
|> addFloorplanTable 25 498 539 "square" 115 115 0
|> addFloorplanTable 26 854 546 "square" 115 115 0
|> addFloorplanTable 27 932 191 "square" 115 115 45
|> addFloorplanTable 28 1136 79 "square" 115 115 0
|> addFloorplanTable 29 1145 317 "square" 115 115 0
|> addFloorplanTable 30 1145 522 "square" 115 115 0
|> ignore
Entity.GetFirstByColumn<room> "name" "Inside"
|> addFloorplanTable 31 943 1196 "square" 215 90 0
|> addFloorplanTable 32 943 952 "square" 215 90 0
|> addFloorplanTable 33 927 565 "circle" 150 150 0
|> addFloorplanTable 39 725 67 "square" 115 115 0
|> addFloorplanTable 40 685 222 "square" 115 115 0
|> addFloorplanTable 41 685 357 "square" 115 115 0
|> addFloorplanTable 42 725 531 "square" 115 115 0
|> addFloorplanTable 43 185 452 "square" 100 100 0
|> addFloorplanTable 44 185 326 "square" 100 100 0
|> addFloorplanTable 45 185 199 "square" 100 100 0
|> addFloorplanTable 46 185 69 "square" 100 100 0
|> addFloorplanTable 51 685 948 "square" 200 100 0
|> addFloorplanTable 52 685 1198 "square" 200 100 0
|> addFloorplanTable 61 415 762 "circle" 100 100 0
|> addFloorplanTable 62 415 901 "circle" 100 100 0
|> addFloorplanTable 63 415 1039 "circle" 100 100 0
|> addFloorplanTable 64 415 1176 "circle" 100 100 0
|> ignore
Entity.GetFirstByColumn<room> "name" "Function Room"
|> addFloorplanTable 71 1039 361 "square" 110 110 0
|> addFloorplanTable 72 1000 652 "square" 200 100 0
|> addFloorplanTable 73 976 966 "square" 130 130 0
|> addFloorplanTable 74 658 962 "square" 120 115 0
|> addFloorplanTable 75 567 629 "square" 110 200 0
|> addFloorplanTable 76 120 511 "square" 230 100 0
|> addFloorplanTable 77 120 847 "square" 230 100 0
let run () =
"wwwroot/images/items"
|> CreateDefaultVenue
|> CreateDefaultClerk
|> CreateDefaultPrintGroups
|> CreateDefaultSalesCategories
|> CreateDefaultItems
|> CreateDefaultButtons
|> Directory.GetDirectories
|> Array.mapi CreatePageFromDirectory
|> Array.iter CreateItemsAndButtons
|> CreateRooms
|> PopulateGrids
|> PopulateGrids
|> PopulateRooms

View File

@@ -3,87 +3,88 @@
open DredgePos
open DredgeFramework
open DredgePos.Types
open DredgePos.Global.Controller
open Saturn.CSRF
open Thoth.Json.Net
open Giraffe
open Microsoft.AspNetCore.Http
open FSharp.Collections
let getOrderScreenData (tableNumber: int) =
{|
order_screen_pages = Entity.GetAllInVenue<order_screen_page_group>
sales_categories = Entity.GetAllInVenue<sales_category>
print_groups = Entity.GetAllInVenue<print_group>
custom_item = Entity.GetAllByColumn<item> "item_code" "OPEN000" |> first
custom_item = Entity.GetFirstByColumn<item> "code" "OPEN000"
table = Floorplan.Model.getTable tableNumber
|}
|> ajaxSuccess
|> json
let renderGrid (grid: grid) =
let gridData = grid.data |> Decode.Auto.fromString<Map<string, int[]>>
match gridData with
| Error message -> failwith message
| Ok data ->
data
|> Map.toArray
|> Array.map snd
|> Array.map(
fun buttonIds ->
buttonIds
|> Array.map Entity.GetById<button>
|> Array.map View.itemButton
|> View.gridPage grid
)
let loadGrid (gridId: int) =
let grid = Entity.GetById<grid> gridId
let gridHtml = Model.loadGrid gridId
let gridNodes = (renderGrid grid) |> List.ofArray
let gridHtml = Giraffe.ViewEngine.RenderView.AsString.htmlNodes gridNodes
if gridHtml = "Error" then ajaxFail gridHtml
else ajaxSuccess {|grid=grid;gridHtml=gridHtml|}
|> json
let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
Authenticate.Model.RequireClerkAuthentication ctx
let loadOrderScreenView (ctx: HttpContext) (tableNumber: int) =
Authenticate.Model.RequireClerkAuthentication ctx
let tableOption = DredgePos.Floorplan.Model.getTableSafely tableNumber
let attr = Giraffe.ViewEngine.HtmlElements.attr
let table = Floorplan.Model.getTable tableNumber
match tableOption with
| None ->
Browser.redirect "/" ctx
View.posButtonTemplate
| Some table ->
let currentClerk = Authenticate.Model.getCurrentClerk ctx
let styles = [|"dredgepos.orderScreen.css"|] |> addDefaultStyles
let scripts = [|"dredgepos.tables.js";"./external/currency.min.js";"dredgepos.orderScreen.js"; |] |> addDefaultScripts
let metaTags = [|"viewport", "user-scalable = no, initial-scale=0.8,maximum-scale=0.8 ,shrink-to-fit=yes"|] |> addDefaultMetaTags
let covers = if tableNumber > 0 then table.default_covers else 0
let coverString = language.getAndReplace "covers" [covers]
let printGroupButtons =
Entity.GetAllInVenue<print_group>
|> Array.map View.printGroupButton
let changeCoverNumberButton = if tableNumber > 0 then Theme.loadTemplateWithVars "orderScreen/change_cover_number_button" (map ["covers", coverString]) else ""
let orderScreenPageGroupButtons =
Entity.GetAllInVenue<order_screen_page_group>
|> Array.filter (fun page_group -> page_group.id <> 0)
|> Array.sortBy (fun {order=order} -> order)
|> Array.map View.pageGroupButton
let orderNumber =
if tableNumber > 0 then language.getAndReplace "active_table" [tableNumber]
else language.get "new_order"
let grids = Model.getAllPageGridsInVenue ()
let pageGroupNodes =
grids
|> Array.map(fun (grid, page_group) ->
renderGrid grid
|> View.pageGroup page_group
)
let containerAttributes =
if tableNumber > 0 then
map ["data-table", jsonEncode table]
|> Theme.htmlAttributes
else ""
let coverSelectorButtons =
Array.init (table.default_covers + 1) id
|> Array.map(fun coverNumber ->
let text = if coverNumber > 0 then language.getAndReplace "selected_cover" [coverNumber]
else language.get "cover_zero"
Global.View.PosButton "coverSelectorButton" (map ["data-cover", coverNumber]) text
)
let categoryList =
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]
Theme.loadTemplateWithArrays "orderScreen/page_group_button" categoryArray
)
|> joinWithNewLine
let grids =
Model.getAllPageGrids ()
|> Array.map Model.getPagesHTML
|> joinWithNewLine
let coverSelectorButtons =
Array.init (covers+1) id
|> Array.map(fun coverNumber ->
let text = if coverNumber > 0 then language.getAndReplace "selected_cover" [coverNumber]
else language.get "cover_zero"
Theme.PosButton text "coverSelectorButton" $"""data-cover="{coverNumber}" """)
|> String.concat "\n"
let variables = map [
"title", "Order"
"containerAttributes", containerAttributes
"categoryList", categoryList
"pageGroups", grids
"orderNumber", orderNumber
"changeCoverNumberButton", changeCoverNumberButton
"covers", coverString
"salesCategoryOverrideButtons", Model.generateSalesCategoryOverrideButtons ()
"coverSelectorButtons", coverSelectorButtons
]
let styles = ["dredgepos.orderScreen.css"]
let scripts = ["dredgepos.tables.js";"./external/currency.min.js";"dredgepos.orderScreen.js"; ]
let currentClerk = recordToMap <| Authenticate.Model.getCurrentClerk ctx
let arrays = map ["clerk", currentClerk]
Theme.loadTemplateWithVarsArraysScriptsAndStyles "orderScreen" variables arrays scripts styles
|> htmlString
View.index tableNumber styles scripts metaTags currentClerk printGroupButtons orderScreenPageGroupButtons pageGroupNodes coverSelectorButtons

View File

@@ -8,129 +8,11 @@ open Thoth.Json.Net
open Theme
let getAllPageGrids () = Entity.GetAllInVenue<order_screen_page_group>
|> Array.filter(fun pageGroup -> pageGroup.grid_id <> 0)
|> Array.map(fun pageGroup -> (Entity.GetById<grid> pageGroup.grid_id), pageGroup)
let getImageButtonData (button: button) =
let itemCode =
if button.primary_action = "item" then button.primary_action_value
else button.secondary_action_value
let item = Entity.GetAllByColumn<item> "item_code" itemCode
|> first
let extraData =
map [
"data-item", jsonEncode item
] |> htmlAttributes
{|
extra_data = extraData
text = item.name
|}
let getGridButtonData (button: button) =
let gridId =
if button.primary_action = "grid" then button.primary_action_value
else button.secondary_action_value
|> int
let grid = Entity.GetById<grid> gridId
{|
extra_data = map ["data-grid", jsonEncode gridId] |> htmlAttributes
text = grid.name
|}
let getActionData (button: button) (action: string) =
let actionValue =
if action = "primary" then button.primary_action
else button.secondary_action
match actionValue with
| "item" -> getImageButtonData button
| "grid" -> getGridButtonData button
| "spacer" -> {|extra_data=""; text=""|}
| _ -> {|extra_data=""; text=""|}
let renderButton (buttonId: int) =
let button = Entity.GetById<button> buttonId
let extra_styles =
match button.extra_styles.Length with
| 0 -> ""
| _ -> $""" style="{button.extra_styles}" """
let imageClass = if button.image.Length > 0 then "hasImage" else ""
let spacerClass = if button.primary_action = "spacer" || button.secondary_action = "spacer"
then "invisible"
else ""
let image = if button.image.Length > 0 then loadTemplateWithVars "orderScreen/button_image" (map ["image", button.image]) else ""
let extraClasses = [|imageClass; spacerClass|] |> String.concat " "
let primary_action_data = getActionData button "primary"
let secondary_action_data = getActionData button "secondary"
let action_extra_data = primary_action_data.extra_data + " " + secondary_action_data.extra_data
let button_text =
if button.text.Length > 0 then button.text
else
if primary_action_data.text.Length > 0 then primary_action_data.text
else secondary_action_data.text
let vars = map [
"extra_classes", button.extra_classes + " " + extraClasses
"extra_styles", extra_styles
"primary_action", button.primary_action
"secondary_action", button.secondary_action
"text", button_text
"image", image
"extra_data", action_extra_data
]
loadTemplateWithVars "orderScreen/grid_button" vars
let renderPage (grid: grid) (buttonHTML: string) =
let vars = map ["pageButtons", buttonHTML; "rows", string grid.rows; "cols", string grid.cols]
loadTemplateWithVars "orderScreen/page" vars
let renderPageGroup (pageGroup: order_screen_page_group) (pageHTML: string) =
let vars = map [
"pages", pageHTML
"page_group_id", string pageGroup.id
]
loadTemplateWithVars "orderScreen/page_group" vars
let getAllPageGridsInVenue () =
Entity.GetAllInVenue<order_screen_page_group>
|> Array.filter(fun pageGroup -> pageGroup.grid_id <> 0)
|> Array.map(fun pageGroup -> (Entity.GetById<grid> pageGroup.grid_id), pageGroup)
let printGroupPosButton (printGroup: print_group) =
PosButton (language.getAndReplace "print_with" [printGroup.name]) "printGroupOverrideButton toggle" $"""data-value="{printGroup.id}" """
let generateSalesCategoryOverrideButtons () =
Entity.GetAllInVenue<print_group>
|> Array.map printGroupPosButton
|> Array.append [|PosButton (language.getAndReplace "print_with" ["default"]) "printGroupOverrideButton toggle default active" """data-value="0" """|]
|> joinWithNewLine
let renderGrid (grid: grid) =
let gridData = grid.data |> Decode.Auto.fromString<Map<string, int[]>>
match gridData with
| Error _ -> "Error"
| Ok pages ->
pages
|> Map.toArray
|> Array.map snd
|> Array.map(fun row -> row |> Array.map renderButton |> String.concat "\n")
|> Array.map (renderPage grid)
|> joinWithNewLine
let loadGrid gridId = renderGrid (Entity.GetById<grid> gridId)
let getPagesHTML (gridInfo: grid * order_screen_page_group) =
let grid, pageGroup = gridInfo
renderGrid grid
|> renderPageGroup pageGroup

View File

@@ -10,6 +10,5 @@ let router = router {
getf "/getOrderScreenData/%i" Controller.getOrderScreenData
getf "/getGridHtml/%i" Controller.loadGrid
post "/updateCovers" (bindJson<floorplan_table> (fun table -> Entity.Update table |> Array.head |> DredgeFramework.ajaxSuccess |> json))
get "/" (warbler (fun ctx -> Controller.loadOrderScreen (snd ctx) 0))
getf "/%i" (fun number -> (warbler (fun ctx -> Controller.loadOrderScreen (snd ctx) number)))
getf "/%i" (fun number -> (warbler (fun ctx -> htmlView <| Controller.loadOrderScreenView (snd ctx) number)))
}

167
OrderScreen/View.fs Normal file
View File

@@ -0,0 +1,167 @@
module DredgePos.OrderScreen.View
open DredgeFramework
open DredgePos.Types
open DredgePos.Global.View
open DredgePos.Entities.Buttons.Model
open Thoth.Json.Net
open Giraffe.ViewEngine
open language
let coverSelector buttons = div [_class "coverSelector"] [
yield! buttons
]
let pageContainer floorplanTable (clerk: clerk) printGroupButtons orderScreenPageGroupButtons pageGroups =
div [_id "pageContainer" ; _table floorplanTable] [
div [_id "leftColumn"] [
h1 [_class "tableHeading"] [str (getAndReplace "active_table" [floorplanTable.table_number])]
div [_class "tableInfo"] [
posButton "changeCoverNumberButton" [] [str (getAndReplace "covers" [floorplanTable.default_covers])]
posButton "" [] [str (getAndReplace "logged_in_as" [clerk.name])]
]
div [_class "orderBox"] [
table [_class "orderBoxTable"] [
thead [] [
tr [] [
th [_class "orderBoxCell qtyCell"] [str (get "qty_header")]
th [_class "orderBoxCell itemIdCell"] [str (get "id_header")]
th [_class "orderBoxCell itemCell"] [str (get "item_header")]
th [_class "orderBoxCell unitPriceCell"] [str (get "price_header")]
th [_class "orderBoxCell totalPriceCell"] [str (get "total_price_header")]
th [_class "orderBoxCell printGroupCell"] [str (get "printgroup_header")]
]
]
tbody [] []
]
]
div [_class "orderBoxInfo"] [
span [_class "voidModeWarning"; VisibleInMode ["void"]] [str (get "void_mode")]
]
div [_class "orderBoxFooter"] [
span [_class "orderBoxTotal"] [str (getAndReplace "totalPrice" ["0.00"])]
small [_class "orderBoxSelectedTotal"] [str (getAndReplace "selectedPrice" ["0.00"])]
]
]
div [_id "rightColumn"] [
div [_id "topHalf"] [
div [_class "functionButtons"] [
div [_class "printGroupButtons toggleGroup"] [
input [_type "hidden"; _class "value"; _name "print_override"]
posButton "printGroupOverrideButton toggle default" [
(attr "data-value") (string 0)
] [
["default"] |> getAndReplace "print_with" |> str
]
yield! printGroupButtons
]
div [_class "functionColumn"] [
posButton "accumulateButton" [ActiveInMode "accumulate"] [str (get "accumulate_function")]
posButton "showCoverSelectorButton" [] [str (get "select_covers")]
]
div [_class "functionColumn"] [
posButton "voidButton" [ActiveInMode "void"] [str (get "void")]
posButton "openItemButton" [] [str (get "custom_item_button")]
posButton "freetextButton" [] [str (get "freetext_button")]
posButton "numpadButton" [] [str (get "numpad_button")]
]
div [_class "functionColumn"] [
posButton "" [] ["pay_function" |> get |> str]
posButton "" [] ["print_function" |> get |> str]
]
]
]
div [_id "pageList"] [
yield! orderScreenPageGroupButtons
]
div [_id "pageGroupContainer"] [
yield! pageGroups
]
div [_class "pageNavigation"] [
posButton "prevButton" [] ["prev_page" |> get |> str]
posButton "nextButton" [] ["next_page" |> get |> str]
]
]
]
(* Grid Container, Cover Selector *)
let posButtonTemplate =
template [_id "posButtonTemplate"] [
posButton "" [] []
]
let gridContainer =
div [_class "gridContainer"] [
div [_class "gridContainerHeader"] [
span [] []
div [_class "posButton closeGrid"] [str "×"]
]
div [_class "gridContainerGrid"] [
div [_class "pageGroup"] []
]
div [_class "pageNavigation"] [
posButton "prevButton" [] ["prev_page" |> get |> str]
posButton "nextButton" [] ["next_page" |> get |> str]
]
]
let pageGroupButton (pageGroup: order_screen_page_group) = posButton "loadPageGroup" [(attr "data-page-group-id") (string pageGroup.id)] [str pageGroup.label]
let printGroupButton (printGroup: print_group) = posButton "toggle printGroupOverrideButton" [(attr "data-value") (string printGroup.id)] [ [printGroup.name] |> getAndReplace "print_with" |> str ]
let itemButtonImage (button: button) =
span [
_class "buttonImg"
_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 extraClasses =
if button.image.Length > 0 then button.extra_classes + " hasImage"
else button.extra_classes
let primaryAttributes = getActionAttributes button.primary_action button.primary_action_value
let secondaryAttributes = getActionAttributes button.secondary_action button.secondary_action_value
posButton extraClasses [
yield! primaryAttributes
yield! secondaryAttributes
_style button.extra_styles
_data_primary_action button.primary_action
_data_secondary_action button.secondary_action
] [
if button.image.Length > 0 then itemButtonImage button
span [_class "text "] [str button.text]
]
let _dataPageGroup = attr "data-page-group"
let _dataPageGroupId = attr "data-page-group-id"
let pageGroup (page_group: order_screen_page_group) gridNodes =
div [_class "pageGroup"; _dataPageGroupId (string page_group.id); ] [
yield! gridNodes
]
let gridPage (grid: grid) buttonNodes =
div [
_class "gridPage"
_style $"
grid-template-columns: repeat({grid.cols}, 1fr);
grid-template-rows: repeat({grid.rows}, 1fr);"
] [
yield! buttonNodes
]
let index orderNumber styles scripts tags clerk printGroupButtons orderScreenPageGroupButtons pageGroupNodes coverSelectorButtons =
[|
pageContainer (DredgePos.Floorplan.Model.getTable orderNumber) clerk printGroupButtons orderScreenPageGroupButtons pageGroupNodes
posButtonTemplate
gridContainer
coverSelector coverSelectorButtons
|]
|> HtmlPage "Order" (GetScripts scripts) (GetStyles styles) (GetMetaTags tags)

902
package-lock.json generated
View File

@@ -1,8 +1,874 @@
{
"name": "DredgePos",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "DredgePos",
"version": "1.0.0",
"dependencies": {
"@types/jquery": "^3.5.7",
"canvas": "^2.8.0",
"currency.js": "^2.0.4",
"konva": "^8.2.2",
"sass": "^1.43.4",
"tsc": "^2.0.4",
"typescript": "^4.7.4"
}
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz",
"integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==",
"dependencies": {
"detect-libc": "^1.0.3",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.1",
"nopt": "^5.0.0",
"npmlog": "^4.1.2",
"rimraf": "^3.0.2",
"semver": "^7.3.4",
"tar": "^6.1.0"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@types/jquery": {
"version": "3.5.7",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.7.tgz",
"integrity": "sha512-Why+9t1KuqWtIqYKtbk6wgWbE1PjyXJOyGkpmTUh0RX5p4HL7nnRuBkjAO9P2r9tGQP6bLWxl77jRLew3V5xXg==",
"dependencies": {
"@types/sizzle": "*"
}
},
"node_modules/@types/sizzle": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"node_modules/are-we-there-yet": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"engines": {
"node": ">=8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/canvas": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz",
"integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.14.0",
"simple-get": "^3.0.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/chokidar": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/currency.js": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/currency.js/-/currency.js-2.0.4.tgz",
"integrity": "sha512-6/OplJYgJ0RUlli74d93HJ/OsKVBi8lB1+Z6eJYS1YZzBuIp4qKKHpJ7ad+GvTlWmLR/hLJOWTykN5Nm8NJ7+w==",
"engines": {
"node": ">=4"
}
},
"node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dependencies": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"node_modules/https-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dependencies": {
"number-is-nan": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"node_modules/konva": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/konva/-/konva-8.2.2.tgz",
"integrity": "sha512-O6Ybw1V5x/PNX4Tc+h/U9LCuyoVZe+XaWc23XKxKs0xIbFyTkvdAJEsw3XQw+DsMDTO56UiTvspq5pfO75xEOg==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/lavrton"
},
{
"type": "opencollective",
"url": "https://opencollective.com/konva"
},
{
"type": "github",
"url": "https://github.com/sponsors/lavrton"
}
]
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minipass": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz",
"integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dependencies": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"node_modules/number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/sass": {
"version": "1.43.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.43.4.tgz",
"integrity": "sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"node_modules/signal-exit": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz",
"integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ=="
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dependencies": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tar": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^3.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"node_modules/tsc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==",
"bin": {
"tsc": "bin/tsc"
}
},
"node_modules/typescript": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
},
"dependencies": {
"@mapbox/node-pre-gyp": {
"version": "1.0.5",
@@ -386,9 +1252,9 @@
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
@@ -517,15 +1383,23 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@@ -536,14 +1410,6 @@
"strip-ansi": "^3.0.0"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -584,9 +1450,9 @@
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q=="
},
"typescript": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA=="
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ=="
},
"util-deprecate": {
"version": "1.0.2",

View File

@@ -8,7 +8,7 @@
"konva": "^8.2.2",
"sass": "^1.43.4",
"tsc": "^2.0.4",
"typescript": "^4.4.4"
"typescript": "^4.7.4"
},
"description": "",
"license": "",

View File

@@ -268,6 +268,9 @@
text-align: center
width: 60%
.itemIdCell
display: none
.qtyCell
width: 10%

View File

@@ -184,6 +184,8 @@ const setElementVisibilityByMode = () => {
}
const getPercentageOfPageContainerWidth = (pixels: number) => ( (pixels / $('#pageContainer').width()) * 100) + '%'
const pulseElement = (element: JQuery) => element.addClass('pulse').on('animationend', () => element.removeClass('pulse'))
Array.prototype.where = function<x>(this: x[], property: string, value: any) {

View File

@@ -615,8 +615,8 @@ const setRoomBackground = (roomToLoad: room) => {
const width = Floorplan.floorplanDiv.width()
const height = Floorplan.floorplanDiv.height()
if(roomToLoad.background_image) {
Floorplan.floorplanDiv.css("background-image", `url(/images/rooms/${roomToLoad.background_image})`)
if(roomToLoad.background_image != "") {
Floorplan.floorplanDiv.css("background-image", `url('/images/rooms/${roomToLoad.background_image}')`)
Floorplan.floorplanDiv.css("background-size", `${width}px ${height}px`)
} else {
Floorplan.floorplanDiv.css("background-image", "none")

View File

@@ -43,7 +43,6 @@ const loadPageGroup = (e: Event) => {
button.addClass('active')
let pageGroupId = button.data('page-group-id')
container.find('.pageGroup').hide()
let activeGrid = $(`.pageGroup[data-page-group-id=${pageGroupId}]`)
@@ -59,7 +58,6 @@ const loadPageGroup = (e: Event) => {
const setupOrderScreen = (data: OrderScreenData) => {
$('.coverSelector, .gridContainer').hide()
OrderScreen.order_screen_pages = data.order_screen_pages
OrderScreen.sales_categories = data.sales_categories
@@ -71,8 +69,8 @@ const setupOrderScreen = (data: OrderScreenData) => {
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', '[data-primary-action=grid],[data-secondary-action=grid]', gridButtonClicked)
doc.on('click', getElementsByAction('item'), itemButtonClicked)
doc.on('click', getElementsByAction('grid'), gridButtonClicked)
doc.on('click', '.closeGrid', hideGrids)
doc.on('click', '.freetextButton', freetext)
doc.on('click', '.openItemButton', customItem)
@@ -100,6 +98,8 @@ 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 button
@@ -186,7 +186,7 @@ const addInstructionToOrderBox = (instruction: orderItem) => {
const addNewItem = (item: item, qty = 1) => {
const salesCategory = OrderScreen.sales_categories.where('id', item.category)
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 orderItem : orderItem = {
id: OrderScreen.order_item_id_generator.next().value,
@@ -223,11 +223,11 @@ const getLastInstructionRow = (row: JQuery) => {
return $(finalRow)
}
const getParentRow = (row: JQuery) => {
return row.hasClass('instructionRow')
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')))
@@ -253,6 +253,12 @@ const renderOrderBox = () => {
const element = orderBox.find('tbody tr').last().get()[0]
element.scrollIntoView()
OrderScreen.last_added_item = null
updateOrderBoxTotals()
}
const setOrderItems = (orderItems: orderItem[]) => {
OrderScreen.order_items = orderItems
renderOrderBox()
}
const createOrderRow = (orderItem: orderItem) => {
@@ -278,7 +284,7 @@ const createOrderRow = (orderItem: orderItem) => {
if(orderItem.item.item_type == 'instruction' && price.value <= 0){
row
.find('.totalPriceCell')
.find('.totalPriceCell,.unitPriceCell')
.css('font-size', 0)
}
@@ -306,20 +312,18 @@ const gridButtonClicked = (e: JQuery.TriggeredEvent) => {
ajax(`/order/getGridHtml/${grid}`, null, null,gridHtmlGenerated, null, null)
}
const hideGrids = () => $('.gridContainer').hide()
const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
const gridContainer = $('.gridContainer')
const gridCellWidth = getGridCellWidth()
const gridCellHeight = getGridCellHeight()
const cellDimensions = getGridCellDimensions()
const grid = gridData.grid
const gridHtml = gridData.gridHtml
gridContainer
.show()
.width(gridCellWidth * grid.cols)
.width(cellDimensions.width * grid.cols)
.children('.gridContainerHeader')
.children('span')
.text(grid.name)
@@ -329,11 +333,11 @@ const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
.html(gridHtml)
.show()
.parent()
.height(gridCellHeight * grid.rows)
.height(cellDimensions.height * grid.rows)
.closest('.gridContainer')
.find('.pageNavigation')
.toggle(gridContainer.find('.gridPage').length > 1)
.height(gridCellHeight)
.height(cellDimensions.height)
}
const itemRowClicked = (e: JQuery.TriggeredEvent) => {
@@ -426,6 +430,7 @@ const getTotalOfRows = (rows: JQuery) => {
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)
@@ -498,7 +503,6 @@ const freetextSubmitted = (text: string) => {
item.name = text
addNewItem(item)
}
const customItem = () => showVirtualKeyboard(lang('enter_item_name'), 32,false, customItemTextSubmitted)
@@ -517,21 +521,28 @@ const customItemTextSubmitted = (text: string) => {
showVirtualNumpad(lang('enter_item_price'), 4, false, true, true, submitFunction)
}
const getGridCellHeight = () => $('#pageGroupContainer').height()/8
const getGridCellWidth = () => $('#pageGroupContainer').width()/6
const getGridCellDimensions = () => {
const container = $('#pageGroupContainer')
return {
height: container.height()/8,
width: container.width()/6
}
}
const showCoverSelector = (event: JQuery.TriggeredEvent) => {
const button = $(event.target)
const gridHeight = getGridCellHeight()
const gridHeight = getGridCellDimensions().height
const coverSelector = $('.coverSelector')
const buttonPositionLeftPercent = getPercentageOfPageContainerWidth(button.offset().left)
const buttonWidthPercent = getPercentageOfPageContainerWidth(button.width())
coverSelector
.toggle(!coverSelector.is(':visible'))
.width(button.width())
.css({
left: button.offset().left + 'px',
top: button.offset().top + button.height() + 'px',
width: buttonWidthPercent,
left: buttonPositionLeftPercent,
top: (button.offset().top + button.height()) + 'px'
})
.find('.coverSelectorButton')
.height(gridHeight)
@@ -600,5 +611,7 @@ const generateCoverSelector = () => {
$(() => {
OrderScreen.table = $('#pageContainer').data('table') || null
ajax('/order/getOrderScreenData/1', null, 'get', setupOrderScreen, null, null)
$('.coverSelector, .gridContainer').hide()
if(OrderScreen.table)
ajax(`/order/getOrderScreenData/${OrderScreen.table.table_number}`, null, 'get', setupOrderScreen, null, null)
})

View File

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

View File

@@ -92,7 +92,7 @@ interface grid {id: number; name: string; rows: number; cols: number; data: stri
interface item {
id: number
code: string
category: number
sales_category_id: number
name: string
item_type: string
price1: number