Compare commits
65 Commits
delete-com
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f4d6b650e | ||
| c146a7209d | |||
|
|
2e213d95ad | ||
|
|
89e9e0b7a7 | ||
|
|
dd74054930 | ||
|
|
5094bbaf2b | ||
|
|
f824dd3beb | ||
|
|
42e96b1769 | ||
| 334391eb38 | |||
|
|
76b7fdea6c | ||
|
|
9fa1f7c888 | ||
|
|
983b7ae909 | ||
|
|
0507fcff65 | ||
| caae3a4380 | |||
|
|
89b881e081 | ||
| 9698671de7 | |||
|
|
f133284309 | ||
|
|
9558074c4f | ||
|
|
ac30d8147e | ||
|
|
b9a0b578e3 | ||
|
|
0a2ffa123a | ||
| 510bd8cfb9 | |||
|
|
b33db1987b | ||
| aa37b24293 | |||
|
|
627843f3a3 | ||
| 6c7bb9eff4 | |||
| 905adcd7bd | |||
| 5e78701b0b | |||
| 420c6530e0 | |||
| 100a772297 | |||
| a587423d3e | |||
| cc7d06e78b | |||
| 05a1a71e6b | |||
|
|
b6aadc072f | ||
| 207edf0de3 | |||
|
|
6439b4326c | ||
| cb58262507 | |||
|
|
b5a2514495 | ||
| a1d3ad19c3 | |||
| 300fb23905 | |||
| 6892b3d34c | |||
| c73184808c | |||
|
|
c7f3d7d2fb | ||
| e3794fc222 | |||
| 39dbba45f7 | |||
|
|
6cbd670260 | ||
| 430ad87880 | |||
|
|
abfb2331ba | ||
| 445cbbabe5 | |||
| 75c129c5d4 | |||
| 56d42f1339 | |||
| f656c5ab40 | |||
| 198d609e62 | |||
| fc4a5d8624 | |||
| 30bc79ef98 | |||
| 85722fa692 | |||
| 4de8f20d00 | |||
| 3067bd1bfc | |||
| 21cd4b2018 | |||
| cbd157d2c9 | |||
| bb5e613382 | |||
| 969e6b1b87 | |||
| db1b620aec | |||
| a48c3a68e0 | |||
| dd1ed266f8 |
7
.gitignore
vendored
@@ -6,5 +6,8 @@
|
|||||||
/tables/*
|
/tables/*
|
||||||
/Properties/
|
/Properties/
|
||||||
/.idea/
|
/.idea/
|
||||||
/wwwroot/scripts/js/
|
/wwwroot/scripts/*.js
|
||||||
/wwwroot/styles/css/*
|
/wwwroot/scripts/*.js.map
|
||||||
|
/wwwroot/styles/*
|
||||||
|
/Folder.DotSettings.user
|
||||||
|
sql.log
|
||||||
|
|||||||
15
Ajax/Controller.fs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
module DredgePos.Ajax.Controller
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
open language
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let getLanguageVars = ajaxSuccess languageVars
|
||||||
|
|
||||||
|
let getKeyboardLayout (language: string) =
|
||||||
|
let layout = $"""wwwroot/languages/{language}/keyboardLayout.json"""
|
||||||
|
|> GetFileContents
|
||||||
|
map [
|
||||||
|
"status", "success"
|
||||||
|
"data", layout
|
||||||
|
] |> json
|
||||||
14
Ajax/Router.fs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module DredgePos.Ajax.Router
|
||||||
|
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let pipeline = pipeline {
|
||||||
|
use_warbler
|
||||||
|
}
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
getf "/getKeyboardLayout/%s" Controller.getKeyboardLayout
|
||||||
|
get "/languageVars" (json Controller.getLanguageVars)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
module AjaxController
|
|
||||||
|
|
||||||
open DredgeFramework
|
|
||||||
open DredgePos
|
|
||||||
open Floorplan
|
|
||||||
open Microsoft.AspNetCore.Http
|
|
||||||
open Reservations
|
|
||||||
open language
|
|
||||||
open Giraffe
|
|
||||||
open Types
|
|
||||||
|
|
||||||
let loginWithLoginCode (context: HttpContext) (login_code: int) =
|
|
||||||
if Session.clerkLogin login_code context then ajaxSuccess "success"
|
|
||||||
else ajaxFail "fail"
|
|
||||||
|
|
||||||
let getLanguageVars = ajaxSuccess languageVars
|
|
||||||
|
|
||||||
let getActiveTables venue = Floorplan.getActiveTables venue |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let getRoomData roomId = Floorplan.getRoom roomId |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let mergeTables (tables: floorplan_table[]) =
|
|
||||||
let status =
|
|
||||||
if mergeTables tables.[0].table_number tables.[1].table_number then
|
|
||||||
let outputTables = map [
|
|
||||||
"parent", tables.[0];
|
|
||||||
"child", tables.[1];
|
|
||||||
"merged", getTable tables.[0].table_number;
|
|
||||||
]
|
|
||||||
ajaxSuccess outputTables
|
|
||||||
else ajaxFail "Could Not Merge Tables"
|
|
||||||
status |> json
|
|
||||||
|
|
||||||
let unmergeTable tableNumber =
|
|
||||||
let unmerged = Floorplan.unmergeTable tableNumber
|
|
||||||
let unmergedTables =
|
|
||||||
match unmerged with
|
|
||||||
| Some (parent, child) ->
|
|
||||||
map["parent", parent; "child", child] |> ajaxSuccess
|
|
||||||
| None -> ajaxFail "Could not Unmerge Table"
|
|
||||||
|
|
||||||
unmergedTables |> json
|
|
||||||
|
|
||||||
|
|
||||||
let getFloorplanData venue =
|
|
||||||
let tableList = Floorplan.tableList venue
|
|
||||||
let reservationList = getReservationList tableList
|
|
||||||
{|
|
|
||||||
tables = tableList
|
|
||||||
decorations = Decorations.decorationList venue
|
|
||||||
activeTableNumbers = Floorplan.getActiveTables venue
|
|
||||||
rooms = Floorplan.getRoomList venue
|
|
||||||
reservations = reservationList
|
|
||||||
|}
|
|
||||||
|> ajaxSuccess
|
|
||||||
|> json
|
|
||||||
|
|
||||||
let getKeyboardLayout (language: string) =
|
|
||||||
let layout = $"""wwwroot/languages/{language}/keyboardLayout.json""" |> GetFileContents
|
|
||||||
map [
|
|
||||||
"status", "success"
|
|
||||||
"data", layout
|
|
||||||
] |> json
|
|
||||||
|
|
||||||
|
|
||||||
let getRoomTablesAndDecorations roomId =
|
|
||||||
let tables = Floorplan.tablesInRoom roomId
|
|
||||||
let decorations = Decorations.decorationsInRoom roomId
|
|
||||||
let data = {|
|
|
||||||
tables = tables
|
|
||||||
decorations = decorations
|
|
||||||
|}
|
|
||||||
|
|
||||||
data |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let getTableData tableNumber = json <| Floorplan.getTable tableNumber
|
|
||||||
|
|
||||||
let updateTableShape (table: floorplan_table) =
|
|
||||||
Floorplan.updateTableShape table |> ignore
|
|
||||||
getTableData table.table_number
|
|
||||||
|
|
||||||
let transformTable (table: floorplan_table) =
|
|
||||||
Floorplan.updateTablePosition table |> ignore
|
|
||||||
getTableData table.table_number
|
|
||||||
|
|
||||||
let createTable (tableData: floorplan_table) =
|
|
||||||
let result =
|
|
||||||
if tableExists tableData.table_number = "False" then
|
|
||||||
ajaxSuccess (addNewTable tableData)
|
|
||||||
else ajaxFail (tableExists tableData.table_number)
|
|
||||||
|
|
||||||
result |> json
|
|
||||||
|
|
||||||
let deleteTable (table: floorplan_table) =
|
|
||||||
Floorplan.deleteTable table.table_number
|
|
||||||
table |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let transferTable (origin, destination) =
|
|
||||||
Floorplan.transferTable origin destination
|
|
||||||
let data = map ["origin", getTable origin ; "destination", getTable destination]
|
|
||||||
ajaxSuccess data |> json
|
|
||||||
|
|
||||||
let AddDecoration (data: floorplan_decoration) =
|
|
||||||
let image = "wwwroot/images/decorations/" + data.decoration_image
|
|
||||||
let width, height = image |> GetImageSize
|
|
||||||
let aspectRatio = decimal width / decimal height
|
|
||||||
|
|
||||||
let decoration : floorplan_decoration = {
|
|
||||||
id = 0
|
|
||||||
decoration_height = (200m / aspectRatio) |> int
|
|
||||||
decoration_width = 200
|
|
||||||
decoration_rotation = 0
|
|
||||||
decoration_image = data.decoration_image
|
|
||||||
decoration_pos_x = data.decoration_pos_x
|
|
||||||
decoration_pos_y = data.decoration_pos_y
|
|
||||||
decoration_room = data.decoration_room
|
|
||||||
}
|
|
||||||
|
|
||||||
Decorations.CreateDecoration decoration |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let UpdateDecoration data =
|
|
||||||
Decorations.UpdateDecoration data |> ignore
|
|
||||||
ajaxSuccess "true" |> json
|
|
||||||
|
|
||||||
let DeleteDecoration id = ajaxSuccess (Decorations.DeleteDecoration id) |> json
|
|
||||||
|
|
||||||
let newEmptyReservation (reservation: reservation) =
|
|
||||||
let newReservation = {reservation with
|
|
||||||
reservation_created_at = CurrentTime()
|
|
||||||
reservation_time = CurrentTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
if reservation.reservation_table_id > 0 then
|
|
||||||
let table = {(getTableById reservation.reservation_table_id) with
|
|
||||||
status = 2
|
|
||||||
default_covers = reservation.reservation_covers}
|
|
||||||
updateTablePosition table |> ignore
|
|
||||||
|
|
||||||
let createdReservation = Floorplan.createEmptyReservation newReservation
|
|
||||||
ajaxSuccess createdReservation |> json
|
|
||||||
|
|
||||||
let updateReservation (reservation: reservation) = updateReservation reservation |> ajaxSuccess |> json
|
|
||||||
|
|
||||||
let unreserveTable (table: floorplan_table) =
|
|
||||||
let newTable = {table with status = 0}
|
|
||||||
updateTablePosition newTable |> ignore
|
|
||||||
DeleteReservation newTable.id
|
|
||||||
newTable |> ajaxSuccess |> json
|
|
||||||
16
Authenticate/Controller.fs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module DredgePos.Authenticate.Controller
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
open Microsoft.AspNetCore.Http
|
||||||
|
open DredgePos.Global.Controller
|
||||||
|
|
||||||
|
let loadAuthenticatePage =
|
||||||
|
let scripts = [|"dredgepos.authenticate.js"|] |> addDefaultScripts
|
||||||
|
let styles = [|"dredgepos.authenticate.css"|] |> addDefaultStyles
|
||||||
|
let metaTags = [|"viewport", "user-scalable = no, initial-scale=0.8,maximum-scale=0.8 ,shrink-to-fit=yes"|] |> addDefaultMetaTags
|
||||||
|
|
||||||
|
View.index scripts styles metaTags
|
||||||
|
|
||||||
|
let loginWithLoginCode (context: HttpContext) (login_code: int) =
|
||||||
|
if Model.clerkLogin login_code context then ajaxSuccess "success"
|
||||||
|
else ajaxFail "fail"
|
||||||
@@ -1,30 +1,44 @@
|
|||||||
module Session
|
module DredgePos.Authenticate.Model
|
||||||
|
|
||||||
open System
|
open System
|
||||||
open DredgeFramework
|
open DredgeFramework
|
||||||
open Dapper.FSharp
|
open Dapper.FSharp
|
||||||
open Clerk
|
|
||||||
open DredgePos
|
open DredgePos
|
||||||
open Thoth.Json.Net
|
open Thoth.Json.Net
|
||||||
open Types
|
open Types
|
||||||
|
|
||||||
|
let getClerkByLoginCode (loginCode: int) =
|
||||||
|
let clerk =
|
||||||
|
select {
|
||||||
|
table "clerks"
|
||||||
|
where (eq "login_code" loginCode)
|
||||||
|
take 1
|
||||||
|
}
|
||||||
|
|> Database.Select<clerk>
|
||||||
|
|> EnumerableToArray
|
||||||
|
|
||||||
|
if (clerk |> length) > 0 then
|
||||||
|
Some (first clerk)
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
let deleteSession sessionId context =
|
let deleteSession sessionId context =
|
||||||
delete {
|
delete {
|
||||||
table "sessions"
|
table "sessions"
|
||||||
where (eq "session_id" sessionId)
|
where (eq "session_id" sessionId)
|
||||||
} |> db.Delete |> ignore
|
} |> Database.Delete |> ignore
|
||||||
Browser.deleteCookie "dredgepos_clerk_logged_in" context
|
Browser.deleteCookie "dredgepos_clerk_logged_in" context
|
||||||
|
|
||||||
let deleteSessionByClerkId clerk_id context =
|
let deleteSessionByClerkId clerk_id context =
|
||||||
delete {
|
delete {
|
||||||
table "sessions"
|
table "sessions"
|
||||||
where (eq "clerk_id" clerk_id)
|
where (eq "clerk_id" clerk_id)
|
||||||
} |> db.Delete |> ignore
|
} |> Database.Delete |> ignore
|
||||||
|
|
||||||
Browser.deleteCookie "dredgepos_clerk_logged_in" context
|
Browser.deleteCookie "dredgepos_clerk_logged_in" context
|
||||||
|
|
||||||
let createNewSession (clerk: clerk) context =
|
let createNewSession (clerk: clerk) context =
|
||||||
if (getClerkByLoginCode clerk.clerk_login_code).IsSome then
|
if (getClerkByLoginCode clerk.login_code).IsSome then
|
||||||
deleteSessionByClerkId clerk.id context
|
deleteSessionByClerkId clerk.id context
|
||||||
let newSessionId = (Guid.NewGuid().ToString "N") + (Guid.NewGuid().ToString "N")
|
let newSessionId = (Guid.NewGuid().ToString "N") + (Guid.NewGuid().ToString "N")
|
||||||
|
|
||||||
@@ -39,18 +53,14 @@ let createNewSession (clerk: clerk) context =
|
|||||||
table "sessions"
|
table "sessions"
|
||||||
value newSession
|
value newSession
|
||||||
}
|
}
|
||||||
|> db.Insert
|
|> Database.Insert
|
||||||
|> ignore
|
|> ignore
|
||||||
|
|
||||||
Browser.setCookie "dredgepos_clerk_logged_in" newSessionId (DateTimeOffset.UtcNow.AddHours(24.0)) context
|
Browser.setCookie "dredgepos_clerk_logged_in" newSessionId (DateTimeOffset.UtcNow.AddHours(24.0)) context
|
||||||
|
|
||||||
|
|
||||||
let sessionExists (sessionId: string) context =
|
let sessionExists (sessionId: string) context =
|
||||||
let sessions =
|
let sessions = Entity.GetAllByColumn<session> "session_id" sessionId
|
||||||
select {
|
|
||||||
table "sessions"
|
|
||||||
where (eq "session_id" sessionId)
|
|
||||||
} |> db.Select<session>
|
|
||||||
|
|
||||||
match sessions |> length with
|
match sessions |> length with
|
||||||
| 0 -> false
|
| 0 -> false
|
||||||
@@ -64,13 +74,11 @@ let sessionExists (sessionId: string) context =
|
|||||||
false
|
false
|
||||||
|
|
||||||
let checkAuthentication clerk =
|
let checkAuthentication clerk =
|
||||||
let existingClerk = getClerkByLoginCode clerk.clerk_login_code
|
let existingClerk = getClerkByLoginCode clerk.login_code
|
||||||
if existingClerk.IsSome
|
existingClerk.IsSome
|
||||||
&& existingClerk.Value.id = clerk.id
|
&& existingClerk.Value.id = clerk.id
|
||||||
&& existingClerk.Value.clerk_name = clerk.clerk_name
|
&& existingClerk.Value.name = clerk.name
|
||||||
&& existingClerk.Value.clerk_login_code = clerk.clerk_login_code
|
&& existingClerk.Value.login_code = clerk.login_code
|
||||||
then true
|
|
||||||
else false
|
|
||||||
|
|
||||||
let getLoginCookie context = Browser.getCookie "dredgepos_clerk_logged_in" context
|
let getLoginCookie context = Browser.getCookie "dredgepos_clerk_logged_in" context
|
||||||
|
|
||||||
@@ -79,7 +87,7 @@ let getSession (sessionId: string) =
|
|||||||
select {
|
select {
|
||||||
table "sessions"
|
table "sessions"
|
||||||
where (eq "session_id" sessionId)
|
where (eq "session_id" sessionId)
|
||||||
} |> db.Select<session>
|
} |> Database.Select<session>
|
||||||
|
|
||||||
match sessions |> length with
|
match sessions |> length with
|
||||||
| 0 -> {session_id = ""; clerk_json = ""; clerk_id= 0; expires= 0; id=0}
|
| 0 -> {session_id = ""; clerk_json = ""; clerk_id= 0; expires= 0; id=0}
|
||||||
@@ -87,7 +95,7 @@ let getSession (sessionId: string) =
|
|||||||
|
|
||||||
let getCurrentClerk context =
|
let getCurrentClerk context =
|
||||||
let cookie = getLoginCookie context
|
let cookie = getLoginCookie context
|
||||||
let emptyClerk = {id=0; clerk_login_code=0; clerk_usergroup=0; clerk_name=""}
|
let emptyClerk = {id=0; login_code=0; user_group_id=0; name=""}
|
||||||
match cookie with
|
match cookie with
|
||||||
| "" ->
|
| "" ->
|
||||||
Browser.redirect "/login" context
|
Browser.redirect "/login" context
|
||||||
18
Authenticate/Router.fs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module DredgePos.Authenticate.Router
|
||||||
|
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let homepage = (warbler (fun _ -> htmlView Controller.loadAuthenticatePage ))
|
||||||
|
let handlePostRoute<'a> handlerFunction post next ctx = json (handlerFunction ctx post) next ctx
|
||||||
|
|
||||||
|
let pipeline = pipeline{
|
||||||
|
use_warbler
|
||||||
|
}
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
pipe_through pipeline
|
||||||
|
get "/" homepage
|
||||||
|
get "" homepage
|
||||||
|
post "/authenticateClerk" (bindJson<int> (handlePostRoute Controller.loginWithLoginCode) )
|
||||||
|
}
|
||||||
10
Authenticate/View.fs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module DredgePos.Authenticate.View
|
||||||
|
|
||||||
|
open DredgePos.Global.View
|
||||||
|
open Giraffe.ViewEngine
|
||||||
|
|
||||||
|
let content = div [_id "authenticator"] []
|
||||||
|
|
||||||
|
let index scripts styles metaTags = HtmlPage "Floorplan" (GetScripts scripts) (GetStyles styles) (GetMetaTags metaTags) [|content|]
|
||||||
|
|
||||||
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
module Browser
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.IO
|
|
||||||
open Microsoft.AspNetCore.Builder
|
|
||||||
open Microsoft.AspNetCore.Http
|
|
||||||
open Microsoft.AspNetCore.Routing
|
|
||||||
open Microsoft.Extensions.DependencyInjection
|
|
||||||
open Microsoft.Extensions.Hosting
|
|
||||||
open DredgeFramework
|
|
||||||
|
|
||||||
|
|
||||||
let cookieExists name (context: HttpContext) =
|
|
||||||
context.Request.Cookies.ContainsKey(name)
|
|
||||||
|
|
||||||
let deleteCookie name (context: HttpContext) =
|
|
||||||
if cookieExists name context then context.Response.Cookies.Delete(name)
|
|
||||||
|
|
||||||
let getCookie cookieName (context: HttpContext) =
|
|
||||||
context.Request.Cookies.[cookieName] |? ""
|
|
||||||
|
|
||||||
let setCookie name value (expiry: DateTimeOffset) (context: HttpContext) =
|
|
||||||
deleteCookie name context
|
|
||||||
let options = CookieOptions()
|
|
||||||
options.Expires <- expiry
|
|
||||||
context.Response.Cookies.Append(name, value, options);
|
|
||||||
|
|
||||||
let redirect url (context: HttpContext) =
|
|
||||||
context.Response.Redirect url
|
|
||||||
|
|
||||||
let addRoute path controller (endpoints: IEndpointRouteBuilder) =
|
|
||||||
endpoints.MapGet(path, fun context ->
|
|
||||||
context.Response.WriteAsync(controller())) |> ignore
|
|
||||||
endpoints
|
|
||||||
|
|
||||||
let addRouteWithParameter path controller param1 (endpoints: IEndpointRouteBuilder) =
|
|
||||||
endpoints.MapGet(path, fun context ->
|
|
||||||
let param1Name, param1Type = param1
|
|
||||||
let parameter1 = context.Request.RouteValues.[param1Name] |> string |> param1Type
|
|
||||||
context.Response.WriteAsync(controller parameter1)) |> ignore
|
|
||||||
endpoints
|
|
||||||
|
|
||||||
let addRouteWithParameters path controller param1 param2 (endpoints: IEndpointRouteBuilder) =
|
|
||||||
endpoints.MapGet(path, fun context ->
|
|
||||||
let param1Name, param1Type = param1
|
|
||||||
let param2Name, param2Type = param2
|
|
||||||
let parameter1 = context.Request.RouteValues.[param1Name] |> string |> param1Type
|
|
||||||
let parameter2 = context.Request.RouteValues.[param2Name] |> string |> param2Type
|
|
||||||
context.Response.WriteAsync(controller parameter1 parameter2)) |> ignore
|
|
||||||
endpoints
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
module Clerk
|
|
||||||
|
|
||||||
open Dapper.FSharp
|
|
||||||
open DredgeFramework
|
|
||||||
open DredgePos
|
|
||||||
open Thoth.Json.Net
|
|
||||||
open Types
|
|
||||||
|
|
||||||
let mutable loginCookie = ""
|
|
||||||
|
|
||||||
let clerk_decoder : Decoder<clerk> =
|
|
||||||
Decode.object
|
|
||||||
(fun get ->
|
|
||||||
{
|
|
||||||
id = get.Required.Field "clerk_id" Decode.int
|
|
||||||
clerk_name = get.Required.Field "clerk_name" Decode.string
|
|
||||||
clerk_login_code = get.Required.Field "clerk_login_code" Decode.int
|
|
||||||
clerk_usergroup = get.Required.Field "clerk_usergroup" Decode.int
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
type user = {clerk_name:string}
|
|
||||||
|
|
||||||
let getClerkByLoginCode (loginCode: int) =
|
|
||||||
let clerk =
|
|
||||||
select {
|
|
||||||
table "clerks"
|
|
||||||
where (eq "clerk_login_code" loginCode)
|
|
||||||
take 1
|
|
||||||
}
|
|
||||||
|> db.Select<clerk>
|
|
||||||
|> EnumerableToArray
|
|
||||||
|
|
||||||
if (clerk |> length) > 0 then
|
|
||||||
Some (first clerk)
|
|
||||||
else
|
|
||||||
None
|
|
||||||
28
Core/Browser.module.fs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module Browser
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.IO
|
||||||
|
open Microsoft.AspNetCore.Builder
|
||||||
|
open Microsoft.AspNetCore.Http
|
||||||
|
open Microsoft.AspNetCore.Routing
|
||||||
|
open Microsoft.Extensions.DependencyInjection
|
||||||
|
open Microsoft.Extensions.Hosting
|
||||||
|
open DredgeFramework
|
||||||
|
|
||||||
|
|
||||||
|
let cookieExists name (context: HttpContext) =
|
||||||
|
context.Request.Cookies.ContainsKey(name)
|
||||||
|
|
||||||
|
let deleteCookie name (context: HttpContext) =
|
||||||
|
if cookieExists name context then context.Response.Cookies.Delete(name)
|
||||||
|
|
||||||
|
let getCookie cookieName (context: HttpContext) =
|
||||||
|
context.Request.Cookies[cookieName] |? ""
|
||||||
|
|
||||||
|
let setCookie name value (expiry: DateTimeOffset) (context: HttpContext) =
|
||||||
|
deleteCookie name context
|
||||||
|
let options = CookieOptions()
|
||||||
|
options.Expires <- expiry
|
||||||
|
context.Response.Cookies.Append(name, value, options);
|
||||||
|
|
||||||
|
let redirect url (context: HttpContext) = context.Response.Redirect url
|
||||||
98
Core/Database.module.fs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
module Database
|
||||||
|
|
||||||
|
open Dapper
|
||||||
|
open Dapper.FSharp.PostgreSQL
|
||||||
|
open DredgeFramework
|
||||||
|
open DredgePos.Types
|
||||||
|
open Npgsql
|
||||||
|
|
||||||
|
|
||||||
|
let connect connectionString = new NpgsqlConnection(connectionString)
|
||||||
|
|
||||||
|
let getDatabaseSettings () = (getConfig ()).database
|
||||||
|
|
||||||
|
let getConnectionString () =
|
||||||
|
let db = getDatabaseSettings ()
|
||||||
|
$"Server={db.host};Port={db.port};User Id={db.username};Password={db.password};Database={db.db_name};Include Error Detail=true"
|
||||||
|
|
||||||
|
let connectToDatabase () = connect (getConnectionString ())
|
||||||
|
|
||||||
|
let closeAndReturn (connection: NpgsqlConnection) (result: 'a) =
|
||||||
|
connection.Dispose()
|
||||||
|
result
|
||||||
|
|
||||||
|
let Select<'a> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.SelectAsync<'a>
|
||||||
|
|> RunSynchronously
|
||||||
|
|> EnumerableToArray
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
let SelectJoin<'a, 'b> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.SelectAsync<'a, 'b>
|
||||||
|
|> RunSynchronously
|
||||||
|
|> EnumerableToArray
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
|
||||||
|
let Insert<'a> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.InsertAsync<'a>
|
||||||
|
|> RunSynchronously
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
|
||||||
|
let InsertOutput<'a> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.InsertOutputAsync<'a, 'a>
|
||||||
|
|> RunSynchronously
|
||||||
|
|> EnumerableToArray
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
let Update<'a> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.UpdateOutputAsync<'a, 'a>
|
||||||
|
|> RunSynchronously
|
||||||
|
|> EnumerableToArray
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
let Delete<'a> asyncQuery =
|
||||||
|
let connection = connectToDatabase ()
|
||||||
|
asyncQuery
|
||||||
|
|> connection.DeleteAsync
|
||||||
|
|> RunSynchronously
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
let NonDbSpecificQuery (sql: string) (connection: NpgsqlConnection) =
|
||||||
|
sql
|
||||||
|
|> fun str -> System.IO.File.WriteAllText("sql.log", str); str
|
||||||
|
|> connection.Execute
|
||||||
|
|> closeAndReturn connection
|
||||||
|
|
||||||
|
let rawQuery (sql: string) = connectToDatabase () |> NonDbSpecificQuery sql
|
||||||
|
|
||||||
|
let CreateTable (tableName: string) (columnList: (string * string) list) =
|
||||||
|
let columns =
|
||||||
|
columnList
|
||||||
|
|> List.filter (fun (columnName, _) -> columnName <> "id")
|
||||||
|
|> List.map (fun (columnName, columnType) -> $""" "{columnName}" {columnType} not null""")
|
||||||
|
|> String.concat ",\n\t\t\t"
|
||||||
|
|
||||||
|
$"""
|
||||||
|
create table if not exists {tableName}
|
||||||
|
(
|
||||||
|
id serial
|
||||||
|
constraint {tableName}_pk
|
||||||
|
primary key,
|
||||||
|
{columns}
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
|> fun str -> System.IO.File.WriteAllText("sql.log", str); str
|
||||||
|
|> rawQuery
|
||||||
|
|> ignore
|
||||||
@@ -2,20 +2,23 @@
|
|||||||
|
|
||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
open System.Globalization
|
open System.Globalization
|
||||||
open FSharp.Data.Sql
|
|
||||||
open System
|
open System
|
||||||
open System.Drawing
|
open System.Drawing
|
||||||
open System.IO
|
open System.IO
|
||||||
open System.Linq
|
open System.Linq
|
||||||
open System.Xml;
|
open System.Xml;
|
||||||
open System.Xml.XPath;
|
|
||||||
open System.Xml.Xsl
|
open System.Xml.Xsl
|
||||||
|
open DredgePos.Types
|
||||||
open FSharp.Reflection
|
open FSharp.Reflection
|
||||||
|
|
||||||
open Thoth.Json.Net
|
open Thoth.Json.Net
|
||||||
|
|
||||||
let (|?) lhs rhs = if lhs = null then rhs else lhs
|
let (|?) lhs rhs = if lhs = null then rhs else lhs
|
||||||
|
|
||||||
|
let joinWithNewLine (arr: string[]) = arr |> String.concat "\n"
|
||||||
|
|
||||||
|
let getCurrentVenue () = 1
|
||||||
|
|
||||||
let map list = list |> Map.ofList
|
let map list = list |> Map.ofList
|
||||||
|
|
||||||
let JoinArray (char: string) (array: 'a[]) = String.Join(char, array)
|
let JoinArray (char: string) (array: 'a[]) = String.Join(char, array)
|
||||||
@@ -31,13 +34,16 @@ let EnumerableToArray (enumerable: IEnumerable<'T>) = enumerable.ToArray()
|
|||||||
|
|
||||||
let getFileExtension (file: string) = Path.GetExtension file
|
let getFileExtension (file: string) = Path.GetExtension file
|
||||||
|
|
||||||
let GetFileContents (file: string) = File.ReadAllText file
|
let GetFileContents = File.ReadAllText
|
||||||
let GetFileName (file: string) = Path.GetFileName file
|
let GetFileName (file: string) = Path.GetFileName file
|
||||||
|
let FileExists = File.Exists
|
||||||
|
|
||||||
let length (variable: 'T[]) = variable.Length
|
let length (variable: 'T[]) = variable.Length
|
||||||
let first (array: 'a[]) = array.[0]
|
let first (array: 'a[]) = array[0]
|
||||||
let last (array: 'a[]) = array.[array.Length-1]
|
|
||||||
|
|
||||||
|
let last (array: 'a[]) = array[array.Length-1]
|
||||||
|
|
||||||
|
let filterFirst (array:'a[]) = if array.Length > 0 then [|array[0]|] else [||]
|
||||||
|
|
||||||
let removeFalseValues (variable: bool[]) = variable |> Array.filter id
|
let removeFalseValues (variable: bool[]) = variable |> Array.filter id
|
||||||
|
|
||||||
@@ -95,3 +101,13 @@ let GetImageSize image =
|
|||||||
loadedImage.Width, loadedImage.Height
|
loadedImage.Width, loadedImage.Height
|
||||||
|
|
||||||
let CurrentTime() = DateTimeOffset.Now.ToUnixTimeSeconds() |> int
|
let CurrentTime() = DateTimeOffset.Now.ToUnixTimeSeconds() |> int
|
||||||
|
|
||||||
|
let getConfig () =
|
||||||
|
"config.json"
|
||||||
|
|> GetFileContents
|
||||||
|
|> Decode.Auto.fromString<config>
|
||||||
|
|> (fun result ->
|
||||||
|
match result with
|
||||||
|
| Ok config -> config
|
||||||
|
| Error message -> failwith ("config.json is not valid :" + message)
|
||||||
|
)
|
||||||
79
Core/GenericEntities.module.fs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
module Entity
|
||||||
|
open Dapper
|
||||||
|
open Dapper.FSharp
|
||||||
|
open DredgeFramework
|
||||||
|
open Pluralize.NET.Core
|
||||||
|
open FSharp.Reflection
|
||||||
|
|
||||||
|
let GetDatabaseTable<'x> =
|
||||||
|
let typeName = typeof<'x>.Name
|
||||||
|
Pluralizer().Pluralize typeName
|
||||||
|
|
||||||
|
let Create (record: 'x)=
|
||||||
|
let tableName = GetDatabaseTable<'x>
|
||||||
|
insert {
|
||||||
|
table tableName
|
||||||
|
value record
|
||||||
|
excludeColumn "id"
|
||||||
|
}
|
||||||
|
|> Database.InsertOutput
|
||||||
|
|> first
|
||||||
|
|
||||||
|
|
||||||
|
let inline Update (record: ^x) =
|
||||||
|
let tableName = GetDatabaseTable<'x>
|
||||||
|
let id = ((^x) : (member id : int) record)
|
||||||
|
update {
|
||||||
|
table tableName
|
||||||
|
set record
|
||||||
|
where (eq "id" id)
|
||||||
|
excludeColumn "id"
|
||||||
|
}
|
||||||
|
|> Database.Update
|
||||||
|
|
||||||
|
let GetAll<'x> =
|
||||||
|
let tableName = GetDatabaseTable<'x>
|
||||||
|
|
||||||
|
select {
|
||||||
|
table tableName
|
||||||
|
}
|
||||||
|
|> Database.Select<'x>
|
||||||
|
|
||||||
|
let GetAllByColumn<'x> (column: string) (value: obj) =
|
||||||
|
let tableName = GetDatabaseTable<'x>
|
||||||
|
|
||||||
|
select {
|
||||||
|
table tableName
|
||||||
|
where (eq column value)
|
||||||
|
} |> Database.Select<'x>
|
||||||
|
|
||||||
|
let GetFirstByColumn<'x> (column: string) (value: obj) = (GetAllByColumn<'x> column value) |> first
|
||||||
|
|
||||||
|
let GetAllInVenue<'x> = GetAllByColumn<'x> "venue_id" (getCurrentVenue ())
|
||||||
|
let GetById<'x> (id: int) = GetAllByColumn<'x> "id" id |> first
|
||||||
|
|
||||||
|
let inline GetRelated<'x, .. > (entity: ^y) =
|
||||||
|
let columnName = typeof<'x>.Name + "_id"
|
||||||
|
let primaryKeyValue = typeof<'y>.GetProperty(columnName).GetValue(entity) :?> int
|
||||||
|
GetById<'x> primaryKeyValue
|
||||||
|
|
||||||
|
let inline GetAllRelated<'x, .. > (entity: ^y) =
|
||||||
|
let id = typeof<'y>.GetProperty("id").GetValue(entity) :?> int
|
||||||
|
let columnName = typeof<'y>.Name + "_id"
|
||||||
|
GetAllByColumn<'x> columnName id
|
||||||
|
|
||||||
|
let DeleteById<'x> id =
|
||||||
|
let typeName = typeof<'x>.Name
|
||||||
|
let tableName = Pluralizer().Pluralize typeName
|
||||||
|
let entity = GetById<'x> id
|
||||||
|
|
||||||
|
delete {
|
||||||
|
table tableName
|
||||||
|
where (eq "id" id)
|
||||||
|
} |> Database.Delete |> ignore
|
||||||
|
|
||||||
|
entity
|
||||||
|
|
||||||
|
let inline Delete< ^x when ^x: (member id: int) > (entity: ^x) =
|
||||||
|
typeof<'x>.GetProperty("id").GetValue(entity) :?> int
|
||||||
|
|> DeleteById<'x>
|
||||||
@@ -19,15 +19,15 @@ let languageVars =
|
|||||||
//Gets a value of a language variable
|
//Gets a value of a language variable
|
||||||
let get var =
|
let get var =
|
||||||
if languageVars.ContainsKey var then
|
if languageVars.ContainsKey var then
|
||||||
languageVars.[var]
|
languageVars[var]
|
||||||
else
|
else
|
||||||
"Missing language variable: " + var
|
"Missing language variable: " + var
|
||||||
|
|
||||||
let getAndReplace languageVar replacements =
|
let getAndReplace languageVar (replacements: 'x list) =
|
||||||
let langString = get languageVar
|
let langString = get languageVar
|
||||||
replacements
|
replacements
|
||||||
|> List.mapi (fun index string
|
|> List.mapi (fun index replacement
|
||||||
-> index + 1, string)
|
-> index + 1, replacement.ToString())
|
||||||
|> List.fold (fun (result: string) (index, string)
|
|> List.fold (fun (result: string) (index, string)
|
||||||
-> result.Replace($"[{index}]", string)
|
-> result.Replace($"[{index}]", string)
|
||||||
) langString
|
) langString
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module Theme
|
module Theme
|
||||||
|
|
||||||
open System
|
open System.Web
|
||||||
open System.IO
|
open System.IO
|
||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
open System.Text.RegularExpressions
|
open System.Text.RegularExpressions
|
||||||
@@ -10,14 +10,14 @@ open DredgeFramework
|
|||||||
let currentTheme = "restaurant"
|
let currentTheme = "restaurant"
|
||||||
|
|
||||||
let getHTMLForFile file =
|
let getHTMLForFile file =
|
||||||
let stylePath = $"/styles/css/{file}"
|
let stylePath = $"/styles/{file}"
|
||||||
let scriptPath = $"/scripts/js/{file}"
|
let scriptPath = $"/scripts/{file}"
|
||||||
let fileExtension = file |> getFileExtension
|
let fileExtension = file |> getFileExtension
|
||||||
let scriptFileExists = File.Exists ("wwwroot"+stylePath) || File.Exists("wwwroot"+scriptPath)
|
let scriptFileExists = File.Exists ("wwwroot"+stylePath) || File.Exists("wwwroot"+scriptPath)
|
||||||
match scriptFileExists with
|
match scriptFileExists with
|
||||||
| true ->
|
| true ->
|
||||||
match fileExtension with
|
match fileExtension with
|
||||||
| ".css" -> $"\t<link test rel=\"stylesheet\" href=\"{stylePath}\" />"
|
| ".css" -> $"\t<link rel=\"stylesheet\" href=\"{stylePath}\" />"
|
||||||
| ".js" ->
|
| ".js" ->
|
||||||
let snippet = $"\t<script src=\"{scriptPath}\"></script>"
|
let snippet = $"\t<script src=\"{scriptPath}\"></script>"
|
||||||
snippet
|
snippet
|
||||||
@@ -26,7 +26,7 @@ let getHTMLForFile file =
|
|||||||
|
|
||||||
|
|
||||||
let ParseScriptsAndStylesheets files html =
|
let ParseScriptsAndStylesheets files html =
|
||||||
let defaultScriptsAndStyles = ["dark.theme.css"; "external/jquery.js" ; "dredgepos.core.js"; "keyboards.js";]
|
let defaultScriptsAndStyles = ["dark.theme.css"; "./external/jquery.js" ; "dredgepos.core.js"; "keyboards.js";]
|
||||||
let scriptsAndStylesheets = defaultScriptsAndStyles @ files
|
let scriptsAndStylesheets = defaultScriptsAndStyles @ files
|
||||||
|
|
||||||
let scriptAndStylesheetHTML =
|
let scriptAndStylesheetHTML =
|
||||||
@@ -43,24 +43,24 @@ let ParseVariables (varArray: Map<string, string>) (html:string) =
|
|||||||
Regex.Replace(html, "<!--\[var\:(.*?)\]-->",
|
Regex.Replace(html, "<!--\[var\:(.*?)\]-->",
|
||||||
MatchEvaluator(
|
MatchEvaluator(
|
||||||
fun matchedVar ->
|
fun matchedVar ->
|
||||||
let varName = matchedVar.Groups.[1] |> string |> StringTrim
|
let varName = matchedVar.Groups[1] |> string |> StringTrim
|
||||||
|
|
||||||
if varArray.ContainsKey varName then
|
if varArray.ContainsKey varName then
|
||||||
if varName |> ToLowerCase = "title" then titlePrefix varArray.[varName]
|
if varName |> ToLowerCase = "title" then titlePrefix varArray[varName]
|
||||||
else varArray.[varName]
|
else varArray[varName]
|
||||||
else
|
else
|
||||||
"<!--[Undefined Variable: " + varName + "]-->"
|
""
|
||||||
))
|
))
|
||||||
|
|
||||||
let ParseArrays (arrayArray: Map<string, Map<string, string>>) (string:string) =
|
let ParseArrays (arrayArray: Map<string, Map<string, string>>) (string:string) =
|
||||||
Regex.Replace(string, "<!--\[arr\:(.*?)\|(.*?)\]-->",
|
Regex.Replace(string, "<!--\[arr\:(.*?)\|(.*?)\]-->",
|
||||||
MatchEvaluator(
|
MatchEvaluator(
|
||||||
fun matchedVar ->
|
fun matchedVar ->
|
||||||
let arrayName = matchedVar.Groups.[1].ToString() |> StringTrim
|
let arrayName = matchedVar.Groups[1].ToString() |> StringTrim
|
||||||
let keyName = matchedVar.Groups.[2].ToString()
|
let keyName = matchedVar.Groups[2].ToString()
|
||||||
|
|
||||||
if arrayArray.ContainsKey arrayName && arrayArray.[arrayName].ContainsKey keyName then
|
if arrayArray.ContainsKey arrayName && arrayArray[arrayName].ContainsKey keyName then
|
||||||
arrayArray.[arrayName].[keyName]
|
arrayArray[arrayName][keyName]
|
||||||
else
|
else
|
||||||
"<!--[Undefined Array: " + arrayName + "]-->"
|
"<!--[Undefined Array: " + arrayName + "]-->"
|
||||||
)
|
)
|
||||||
@@ -70,7 +70,7 @@ let ParseSimpleLanguageVariables (string:string) =
|
|||||||
Regex.Replace(string, "<!--\[lang\:(.*?)\]-->",
|
Regex.Replace(string, "<!--\[lang\:(.*?)\]-->",
|
||||||
new MatchEvaluator(
|
new MatchEvaluator(
|
||||||
fun matchedVar ->
|
fun matchedVar ->
|
||||||
let varName = matchedVar.Groups.[1].ToString()
|
let varName = matchedVar.Groups[1].ToString()
|
||||||
|> StringTrim
|
|> StringTrim
|
||||||
|
|
||||||
language.get varName
|
language.get varName
|
||||||
@@ -78,10 +78,10 @@ let ParseSimpleLanguageVariables (string:string) =
|
|||||||
|
|
||||||
let ParseLanguageVariablesWithReplacements (string: string) =
|
let ParseLanguageVariablesWithReplacements (string: string) =
|
||||||
Regex.Replace(string, "<!--\[lang\:(.*?)\|(.*?)\]-->",
|
Regex.Replace(string, "<!--\[lang\:(.*?)\|(.*?)\]-->",
|
||||||
new MatchEvaluator(
|
MatchEvaluator(
|
||||||
fun matchedVar ->
|
fun matchedVar ->
|
||||||
let varName = matchedVar.Groups.[1].ToString()
|
let varName = matchedVar.Groups[1].ToString()
|
||||||
let replacements = matchedVar.Groups.[2].ToString()
|
let replacements = matchedVar.Groups[2].ToString()
|
||||||
|> StringSplit ","
|
|> StringSplit ","
|
||||||
|> Array.toList
|
|> Array.toList
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ let ParseLanguageVariablesWithReplacements (string: string) =
|
|||||||
))
|
))
|
||||||
|
|
||||||
let getTemplateFilePath templateName =
|
let getTemplateFilePath templateName =
|
||||||
"wwwroot/themes/"+ currentTheme + "/" + templateName + ".tpl.htm"
|
"views/"+ currentTheme + "/" + templateName + ".tpl.htm"
|
||||||
|
|
||||||
let templateExists templateName =
|
let templateExists templateName =
|
||||||
templateName
|
templateName
|
||||||
@@ -115,13 +115,16 @@ let rec loadTemplateWithVarsArraysScriptsAndStyles templateName vars arrays scri
|
|||||||
and ParseTemplates vars arrays scripts styles (string: string) =
|
and ParseTemplates vars arrays scripts styles (string: string) =
|
||||||
Regex.Replace(string, "<!--\[template\:(.*?)\]-->",
|
Regex.Replace(string, "<!--\[template\:(.*?)\]-->",
|
||||||
new MatchEvaluator( fun template ->
|
new MatchEvaluator( fun template ->
|
||||||
let templateName = template.Groups.[1].ToString() |> StringTrim
|
let templateName = template.Groups[1].ToString() |> StringTrim
|
||||||
loadTemplateWithVarsArraysScriptsAndStyles templateName vars arrays scripts styles
|
loadTemplateWithVarsArraysScriptsAndStyles templateName vars arrays scripts styles
|
||||||
))
|
))
|
||||||
|
|
||||||
let loadTemplate templateName =
|
let loadTemplate templateName =
|
||||||
loadTemplateWithVarsArraysScriptsAndStyles templateName Map.empty<string, string> Map.empty<string, Map<string, string>> [] []
|
loadTemplateWithVarsArraysScriptsAndStyles templateName Map.empty<string, string> Map.empty<string, Map<string, string>> [] []
|
||||||
|
|
||||||
|
let loadTemplateWithArrays templateName arrays =
|
||||||
|
loadTemplateWithVarsArraysScriptsAndStyles templateName Map.empty arrays [] []
|
||||||
|
|
||||||
let loadTemplateWithVars templateName vars =
|
let loadTemplateWithVars templateName vars =
|
||||||
loadTemplateWithVarsArraysScriptsAndStyles templateName vars Map.empty<string, Map<string, string>> [] []
|
loadTemplateWithVarsArraysScriptsAndStyles templateName vars Map.empty<string, Map<string, string>> [] []
|
||||||
|
|
||||||
@@ -135,3 +138,18 @@ let loadTemplateWithVarsAndStyles = loadTemplateWithVarsAndScripts
|
|||||||
|
|
||||||
let loadTemplateWithVarsScriptsAndStyles templateName vars scripts styles =
|
let loadTemplateWithVarsScriptsAndStyles templateName vars scripts styles =
|
||||||
loadTemplateWithVarsArraysScriptsAndStyles templateName vars Map.empty<string, Map<string, string>> scripts styles
|
loadTemplateWithVarsArraysScriptsAndStyles templateName vars Map.empty<string, Map<string, string>> scripts styles
|
||||||
|
|
||||||
|
|
||||||
|
let htmlAttributes (attributes: Map<string, string>) =
|
||||||
|
" " + (attributes
|
||||||
|
|> Map.toArray
|
||||||
|
|> Array.map (fun (attribute, value) -> attribute+"='"+HttpUtility.HtmlEncode value + "'")
|
||||||
|
|> String.concat " ")
|
||||||
|
|
||||||
|
let PosButton (text: string) (classes: string) (attributes: string) =
|
||||||
|
let vars = map [
|
||||||
|
"text", text
|
||||||
|
"classes", classes
|
||||||
|
"attributes", attributes
|
||||||
|
]
|
||||||
|
loadTemplateWithVars "components/posButton" vars
|
||||||
134
Core/Types.fs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
module DredgePos.Types
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type reservation = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
time: int
|
||||||
|
covers: int
|
||||||
|
floorplan_table_id: int
|
||||||
|
created_at: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type venue = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type floorplan_table = {
|
||||||
|
table_number: int
|
||||||
|
room_id: int
|
||||||
|
venue_id: int
|
||||||
|
pos_x: int
|
||||||
|
pos_y: int
|
||||||
|
shape: string
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
default_covers: int
|
||||||
|
rotation: int
|
||||||
|
merged_children: string
|
||||||
|
previous_state: string
|
||||||
|
status: int
|
||||||
|
id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type print_group = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
printer_id: int
|
||||||
|
venue_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type sales_category = {
|
||||||
|
id: int
|
||||||
|
parent: int
|
||||||
|
name: string
|
||||||
|
print_group_id: int
|
||||||
|
venue_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type room = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
background_image: string
|
||||||
|
venue_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type floorplan_decoration = {
|
||||||
|
id: int
|
||||||
|
room_id: int
|
||||||
|
pos_x: int
|
||||||
|
pos_y: int
|
||||||
|
rotation: int
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
image: string
|
||||||
|
venue_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type clerk = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
login_code: int
|
||||||
|
user_group_id: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type order_screen_page_group = {id: int; order: int; venue_id: int; label: string; grid_id: int}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type grid = {id: int; name: string; rows: int; cols: int; data: string}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type button = {
|
||||||
|
id: int
|
||||||
|
text: string
|
||||||
|
primary_action: string
|
||||||
|
primary_action_value: string
|
||||||
|
secondary_action: string
|
||||||
|
secondary_action_value: string
|
||||||
|
image: string
|
||||||
|
extra_classes: string
|
||||||
|
extra_styles: string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type item = {
|
||||||
|
id: int
|
||||||
|
code: string
|
||||||
|
sales_category_id: int
|
||||||
|
name: string
|
||||||
|
item_type: string
|
||||||
|
price1: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type db_config = {
|
||||||
|
db_name: string
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
host: string
|
||||||
|
port: int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type config = {
|
||||||
|
database: db_config
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CLIMutable>]
|
||||||
|
type migration = {
|
||||||
|
id: int
|
||||||
|
name: string
|
||||||
|
timestamp: int
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
module db
|
|
||||||
|
|
||||||
open Dapper
|
|
||||||
open Dapper.FSharp
|
|
||||||
open Dapper.FSharp.MySQL
|
|
||||||
open MySql.Data.MySqlClient
|
|
||||||
open DredgeFramework
|
|
||||||
|
|
||||||
let connString = "server=localhost;uid=root;pwd=;database=dredgepos;table cache = false"
|
|
||||||
let connection = new MySqlConnection(connString)
|
|
||||||
|
|
||||||
let Select<'a> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.SelectAsync<'a>
|
|
||||||
|> RunSynchronously
|
|
||||||
|> EnumerableToArray
|
|
||||||
|
|
||||||
let SelectJoin<'a, 'b> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.SelectAsync<'a, 'b>
|
|
||||||
|> RunSynchronously
|
|
||||||
|> EnumerableToArray
|
|
||||||
|
|
||||||
let Insert<'a> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.InsertAsync<'a>
|
|
||||||
|> RunSynchronously
|
|
||||||
|
|
||||||
let InsertOutput<'a> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.InsertAsync<'a>
|
|
||||||
|> RunSynchronously
|
|
||||||
|> ignore
|
|
||||||
|
|
||||||
let table = asyncQuery.Table
|
|
||||||
connection.Query<'a>($"""Select * From {table} Where id = (select last_insert_id())""")
|
|
||||||
|> EnumerableToArray
|
|
||||||
|
|
||||||
let Update<'a> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.UpdateAsync<'a>
|
|
||||||
|> RunSynchronously
|
|
||||||
|
|
||||||
let Delete<'a> asyncQuery =
|
|
||||||
asyncQuery
|
|
||||||
|> connection.DeleteAsync
|
|
||||||
|> RunSynchronously
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
module Decorations
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.IO
|
|
||||||
open System.Text.RegularExpressions
|
|
||||||
open DredgeFramework
|
|
||||||
open Dapper.FSharp
|
|
||||||
open DredgePos
|
|
||||||
open Types
|
|
||||||
|
|
||||||
let decorationList venue =
|
|
||||||
select {
|
|
||||||
table "floorplan_decorations"
|
|
||||||
innerJoin "floorplan_rooms" "id" "decoration_room"
|
|
||||||
}
|
|
||||||
|> db.SelectJoin<floorplan_decoration, floorplan_room>
|
|
||||||
|> Array.filter (fun (_, room) -> room.venue_id = venue )
|
|
||||||
|> Array.map fst
|
|
||||||
|
|
||||||
let decorationsInRoom (roomId: int) =
|
|
||||||
select {
|
|
||||||
table "floorplan_decorations"
|
|
||||||
where (eq "decoration_room" roomId)
|
|
||||||
}
|
|
||||||
|> db.Select<floorplan_decoration>
|
|
||||||
|
|
||||||
|
|
||||||
let getImageName (image: string, path: string) =
|
|
||||||
let imageName =
|
|
||||||
image
|
|
||||||
|> StringReplace "-" " "
|
|
||||||
|> StringReplace "_" " "
|
|
||||||
|> ToTitleCase
|
|
||||||
|
|
||||||
imageName, path
|
|
||||||
|
|
||||||
let isImageFile (fileName: string) = Regex.IsMatch(fileName |> ToLowerCase, @"^.+\.(jpg|jpeg|png|gif)$")
|
|
||||||
|
|
||||||
let getImageHTML (imageName: string, imageUrl: string) =
|
|
||||||
let vars = map [
|
|
||||||
"image_name", imageName
|
|
||||||
"image_url", imageUrl
|
|
||||||
]
|
|
||||||
Theme.loadTemplateWithVars "decoratorItem" vars
|
|
||||||
|
|
||||||
let GetFileNameWithoutExtension (path: string) =
|
|
||||||
let name = Path.GetFileNameWithoutExtension path
|
|
||||||
name, path |> Path.GetFileName
|
|
||||||
|
|
||||||
let getImageRowHtml (imagesInRow: string[]) =
|
|
||||||
let vars = map ["decorations", String.Join("", imagesInRow)]
|
|
||||||
Theme.loadTemplateWithVars "decoratorRow" vars
|
|
||||||
|
|
||||||
let generateDecorator () =
|
|
||||||
"wwwroot/images/decorations"
|
|
||||||
|> Directory.GetFiles
|
|
||||||
|> Array.filter isImageFile
|
|
||||||
|> Array.map GetFileNameWithoutExtension
|
|
||||||
|> Array.map getImageName
|
|
||||||
|> Array.map getImageHTML
|
|
||||||
|> Array.chunkBySize 4
|
|
||||||
|> Array.map getImageRowHtml
|
|
||||||
|> JoinArray ""
|
|
||||||
|
|
||||||
let CreateDecoration (decoration: floorplan_decoration) =
|
|
||||||
insert {
|
|
||||||
table "floorplan_decorations"
|
|
||||||
value decoration
|
|
||||||
}
|
|
||||||
|> db.InsertOutput
|
|
||||||
|> first
|
|
||||||
|
|
||||||
|
|
||||||
let UpdateDecoration (decoration: floorplan_decoration) =
|
|
||||||
update {
|
|
||||||
table "floorplan_decorations"
|
|
||||||
set decoration
|
|
||||||
where (eq "id" decoration.id )
|
|
||||||
} |> db.Update
|
|
||||||
|
|
||||||
let DeleteDecoration (decoration: floorplan_decoration) =
|
|
||||||
delete {
|
|
||||||
table "floorplan_decorations"
|
|
||||||
where (eq "id" decoration.id)
|
|
||||||
} |> db.Delete |> ignore
|
|
||||||
decoration
|
|
||||||
139
DredgePos.fsproj
@@ -5,20 +5,44 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Types.fs" />
|
<Compile Include="Core\Types.fs" />
|
||||||
<Compile Include="DredgeFramework.module.fs" />
|
<Compile Include="Core\DredgeFramework.module.fs" />
|
||||||
<Compile Include="Browser.module.fs" />
|
<Compile Include="Core\Browser.module.fs" />
|
||||||
<Compile Include="Database.module.fs" />
|
<Compile Include="Core\Database.module.fs" />
|
||||||
<Compile Include="GenericEntities.module.fs" />
|
<Compile Include="Core\GenericEntities.module.fs" />
|
||||||
<Compile Include="Language.module.fs" />
|
<Compile Include="Core\Language.module.fs" />
|
||||||
<Compile Include="Theme.module.fs" />
|
<Compile Include="Core\Theme.module.fs" />
|
||||||
<Compile Include="Reservations.module.fs" />
|
<Compile Include="Printer.module.fs" />
|
||||||
<Compile Include="Floorplan.module.fs" />
|
<Compile Include="Global\View.fs" />
|
||||||
<Compile Include="Decorations.module.fs" />
|
<Compile Include="Global\Controller.fs" />
|
||||||
<Compile Include="Clerk.module.fs" />
|
<Compile Include="Global\Router.fs" />
|
||||||
<Compile Include="Session.module.fs" />
|
<Compile Include="Entities\Floorplan_Decorations\Model.fs" />
|
||||||
<Compile Include="PageController.fs" />
|
<Compile Include="Entities\Floorplan_Decorations\View.fs" />
|
||||||
<Compile Include="AjaxController.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" />
|
||||||
|
<Compile Include="Authenticate\Router.fs" />
|
||||||
|
<Compile Include="Ajax\Controller.fs" />
|
||||||
|
<Compile Include="Ajax\Router.fs" />
|
||||||
|
<Compile Include="Migrations\CreateDatabaseSchema.fs" />
|
||||||
|
<Compile Include="Migrations\PopulateTestData.fs" />
|
||||||
|
<Compile Include="Installer\Model.fs" />
|
||||||
|
<Compile Include="Installer\Controller.fs" />
|
||||||
|
<Compile Include="Installer\Router.fs" />
|
||||||
|
<Compile Include="Floorplan\Model.fs" />
|
||||||
|
<Compile Include="Floorplan\View.fs" />
|
||||||
|
<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" />
|
||||||
|
<Compile Include="Reservations\Controller.fs" />
|
||||||
|
<Compile Include="Reservations\Router.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -34,37 +58,87 @@
|
|||||||
</Content>
|
</Content>
|
||||||
<Content Remove="node_modules\**" />
|
<Content Remove="node_modules\**" />
|
||||||
<Content Remove="wwwroot\scripts\ts\test.ts" />
|
<Content Remove="wwwroot\scripts\ts\test.ts" />
|
||||||
|
<Content Remove="languages\**" />
|
||||||
|
<Content Include="sass\dark.theme.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="sass\dredgepos.authenticate.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="sass\dredgepos.core.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="sass\dredgepos.floorplan.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="sass\dredgepos.keyboards.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="sass\dredgepos.orderScreen.sass">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\dredgepos.authenticate.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\dredgepos.core.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\dredgepos.floorplan.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\dredgepos.orderScreen.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\dredgepos.tables.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\keyboards.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\types.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\typings\currency.d.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="typescript\typings\konva.d.ts">
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
<Folder Include="wwwroot\styles" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.0.78" />
|
<PackageReference Include="Dapper.FSharp" Version="2.4.1" />
|
||||||
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
|
<PackageReference Include="Giraffe" Version="6.0.0-alpha-2" />
|
||||||
<PackageReference Include="Dapper.FSharp" Version="1.16.0" />
|
<PackageReference Include="Npgsql" Version="6.0.0" />
|
||||||
<PackageReference Include="FSharp.Data" Version="4.0.1" />
|
<PackageReference Include="Pluralize.NET.Core" Version="1.0.0" />
|
||||||
<PackageReference Include="Saturn" Version="0.15.0-preview03" />
|
<PackageReference Include="Saturn" Version="0.15.0" />
|
||||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.0" />
|
|
||||||
<PackageReference Include="FSharp.Data.SqlClient" Version="2.1.0-beta1" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0-preview.2.21154.2" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-preview.2.21154.2">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.23" />
|
|
||||||
<PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.22" />
|
|
||||||
<PackageReference Include="SQLProvider" Version="1.2.1" />
|
|
||||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0-preview.5.21301.5" />
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0-preview.5.21301.5" />
|
||||||
<PackageReference Include="Thoth.Json.Net" Version="5.0.0" />
|
<PackageReference Include="Thoth.Json.Net" Version="5.0.0" />
|
||||||
|
<PackageReference Update="FSharp.Core" Version="6.0.2-beta.21631.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="languages\english" />
|
|
||||||
<Folder Include="tables\archived" />
|
<Folder Include="tables\archived" />
|
||||||
<Content Include="xslt\orderXMLtoHTML.xslt" />
|
<Content Include="xslt\orderXMLtoHTML.xslt" />
|
||||||
<Content Include="xslt\orderHtmltoXML.xslt" />
|
<Content Include="xslt\orderHtmltoXML.xslt" />
|
||||||
<Content Include="xslt\htmlToEscPos.xslt" />
|
<Content Include="xslt\htmlToEscPos.xslt" />
|
||||||
<Content Include=".gitignore" />
|
<Content Include=".gitignore" />
|
||||||
<Folder Include="wwwroot\scripts\js" />
|
|
||||||
<Folder Include="wwwroot\styles\css" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -375,6 +449,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Remove="node_modules\**" />
|
<EmbeddedResource Remove="node_modules\**" />
|
||||||
|
<EmbeddedResource Remove="languages\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
16
DredgePos.sln
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "DredgePos", "DredgePos.fsproj", "{3C52169C-8E40-472B-B87B-82F43F96BFB0}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{3C52169C-8E40-472B-B87B-82F43F96BFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3C52169C-8E40-472B-B87B-82F43F96BFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3C52169C-8E40-472B-B87B-82F43F96BFB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3C52169C-8E40-472B-B87B-82F43F96BFB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
18
Entities/Buttons/Model.fs
Normal 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
|
||||||
|
| _ -> []
|
||||||
13
Entities/Floorplan_Decorations/Controller.fs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module DredgePos.Entities.Floorplan_Decorations.Controller
|
||||||
|
|
||||||
|
open System.IO
|
||||||
|
|
||||||
|
let generateDecorator () =
|
||||||
|
"wwwroot/images/decorations"
|
||||||
|
|> Directory.GetFiles
|
||||||
|
|> Array.filter Model.isImageFile
|
||||||
|
|> Array.map Model.GetFileNameWithoutExtension
|
||||||
|
|> Array.map Model.getImageName
|
||||||
|
|> Array.map View.decoratorItem
|
||||||
|
|> Array.chunkBySize 4
|
||||||
|
|> Array.map View.decoratorRow
|
||||||
24
Entities/Floorplan_Decorations/Model.fs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
module DredgePos.Entities.Floorplan_Decorations.Model
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.IO
|
||||||
|
open System.Text.RegularExpressions
|
||||||
|
open DredgeFramework
|
||||||
|
|
||||||
|
let decorationsInRoom (roomId: int) = Entity.GetAllByColumn "decoration_room" roomId
|
||||||
|
|
||||||
|
let getImageName (image: string, path: string) =
|
||||||
|
let imageName =
|
||||||
|
image
|
||||||
|
|> StringReplace "-" " "
|
||||||
|
|> StringReplace "_" " "
|
||||||
|
|> ToTitleCase
|
||||||
|
|
||||||
|
imageName, path
|
||||||
|
|
||||||
|
let isImageFile (fileName: string) = Regex.IsMatch(fileName |> ToLowerCase, @"^.+\.(jpg|jpeg|png|gif)$")
|
||||||
|
|
||||||
|
let GetFileNameWithoutExtension (path: string) =
|
||||||
|
let name = Path.GetFileNameWithoutExtension path
|
||||||
|
name, path |> Path.GetFileName
|
||||||
|
|
||||||
2
Entities/Floorplan_Decorations/Router.fs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module DredgePos.Entities.Floorplan_Decorations.Router
|
||||||
|
|
||||||
24
Entities/Floorplan_Decorations/View.fs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
module DredgePos.Entities.Floorplan_Decorations.View
|
||||||
|
|
||||||
|
open Giraffe.ViewEngine
|
||||||
|
open DredgePos.Global.View
|
||||||
|
|
||||||
|
let decoratorItem (imageName, imageUrl) =
|
||||||
|
let image = attr "data-image"
|
||||||
|
div [_class "decoratorItem"; image imageUrl] [
|
||||||
|
a [_style $"background-image:url('/images/decorations/{imageUrl}')"] []
|
||||||
|
a [] [str imageName]
|
||||||
|
]
|
||||||
|
|
||||||
|
let decoratorRow decoratorItems = div [_class "decoratorRow"] [yield! decoratorItems]
|
||||||
|
|
||||||
|
let decorator (decorationRows: XmlNode[]) =
|
||||||
|
div [_id "decorator"; _class "modal"] [
|
||||||
|
div [_id "decoratorHeader"] [
|
||||||
|
h2 [] [lang "choose_decoration"]
|
||||||
|
a [_class "posButton hideModals"] [str "×"]
|
||||||
|
]
|
||||||
|
div [_id "decoratorContent"] [
|
||||||
|
yield! decorationRows
|
||||||
|
]
|
||||||
|
]
|
||||||
119
Floorplan/Controller.fs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
module DredgePos.Floorplan.Controller
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Global.Controller
|
||||||
|
open DredgePos.Entities
|
||||||
|
open DredgePos.Types
|
||||||
|
open Giraffe
|
||||||
|
open Microsoft.AspNetCore.Http
|
||||||
|
open Model
|
||||||
|
open System.IO
|
||||||
|
|
||||||
|
let makeRoomButton (room: room) =
|
||||||
|
let vars = map [
|
||||||
|
"roomId", room.id |> string
|
||||||
|
"roomName", room.name
|
||||||
|
]
|
||||||
|
|
||||||
|
Theme.loadTemplateWithVars "roomButton" vars
|
||||||
|
|
||||||
|
let getActiveTables venue = Model.getActiveTables venue |> ajaxSuccess |> json
|
||||||
|
|
||||||
|
let mergeTables (tables: floorplan_table[]) =
|
||||||
|
let status =
|
||||||
|
if mergeTables tables[0].table_number tables[1].table_number then
|
||||||
|
let outputTables = map [
|
||||||
|
"parent", tables[0];
|
||||||
|
"child", tables[1];
|
||||||
|
"merged", Model.getTable tables[0].table_number;
|
||||||
|
]
|
||||||
|
ajaxSuccess outputTables
|
||||||
|
else ajaxFail "Could Not Merge Tables"
|
||||||
|
status |> json
|
||||||
|
|
||||||
|
let unmergeTable tableNumber =
|
||||||
|
let unmerged = unmergeTable tableNumber
|
||||||
|
let unmergedTables =
|
||||||
|
match unmerged with
|
||||||
|
| Some (parent, child) ->
|
||||||
|
map ["parent", parent; "child", child] |> ajaxSuccess
|
||||||
|
| None -> ajaxFail "Could not Unmerge Table"
|
||||||
|
|
||||||
|
unmergedTables |> json
|
||||||
|
|
||||||
|
|
||||||
|
let getFloorplanData (id: int) =
|
||||||
|
let tableList = Entity.GetAllInVenue<floorplan_table>
|
||||||
|
let reservationList = getReservationList tableList
|
||||||
|
{|
|
||||||
|
tables = tableList
|
||||||
|
decorations = Entity.GetAllInVenue<floorplan_decoration>
|
||||||
|
activeTableNumbers = Model.getActiveTables (getCurrentVenue())
|
||||||
|
rooms = Entity.GetAllInVenue<room>
|
||||||
|
reservations = reservationList
|
||||||
|
|}
|
||||||
|
|> ajaxSuccess
|
||||||
|
|> json
|
||||||
|
|
||||||
|
let transformTable (table: floorplan_table) =
|
||||||
|
Entity.Update table
|
||||||
|
|> ajaxSuccess
|
||||||
|
|> json
|
||||||
|
|
||||||
|
let createTable (tableData: floorplan_table) =
|
||||||
|
if tableExists tableData.table_number = "False" then
|
||||||
|
ajaxSuccess (addNewTable tableData)
|
||||||
|
else ajaxFail (tableExists tableData.table_number)
|
||||||
|
|> json
|
||||||
|
|
||||||
|
let deleteTable (table: floorplan_table) =
|
||||||
|
Entity.DeleteById<floorplan_table> table.id
|
||||||
|
|> ignore
|
||||||
|
table |> ajaxSuccess |> json
|
||||||
|
|
||||||
|
let transferTable (origin, destination) =
|
||||||
|
transferTable origin destination
|
||||||
|
let data = map ["origin", getTable origin ; "destination", getTable destination]
|
||||||
|
ajaxSuccess data |> json
|
||||||
|
|
||||||
|
let AddDecoration (data: floorplan_decoration) =
|
||||||
|
let image = "wwwroot/images/decorations/" + data.image
|
||||||
|
let width, height = image |> GetImageSize
|
||||||
|
let aspectRatio = decimal width / decimal height
|
||||||
|
|
||||||
|
let decoration : floorplan_decoration = {
|
||||||
|
id = 0
|
||||||
|
height = (200m / aspectRatio) |> int
|
||||||
|
width = 200
|
||||||
|
rotation = 0
|
||||||
|
image = data.image
|
||||||
|
pos_x = data.pos_x
|
||||||
|
pos_y = data.pos_y
|
||||||
|
room_id = data.room_id
|
||||||
|
venue_id = data.venue_id
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity.Create decoration
|
||||||
|
|> ajaxSuccess
|
||||||
|
|> json
|
||||||
|
|
||||||
|
let UpdateDecoration (data: floorplan_decoration) =
|
||||||
|
Entity.Update data
|
||||||
|
|> ignore
|
||||||
|
ajaxSuccess "true" |> json
|
||||||
|
|
||||||
|
let DeleteDecoration (decorationToDelete: floorplan_decoration) =
|
||||||
|
Entity.DeleteById<floorplan_decoration> decorationToDelete.id
|
||||||
|
|> ajaxSuccess
|
||||||
|
|> json
|
||||||
|
|
||||||
|
let loadFloorplanView (ctx: HttpContext) =
|
||||||
|
Authenticate.Model.RequireClerkAuthentication ctx
|
||||||
|
let roomMenu = Entity.GetAllInVenue<room> |> Array.map View.roomButton
|
||||||
|
let currentClerk = Authenticate.Model.getCurrentClerk ctx
|
||||||
|
let styles = [|"dredgepos.floorplan.css"|] |> addDefaultStyles
|
||||||
|
let scripts = [|"./external/konva.min.js" ; "dredgepos.floorplan.js"|] |> addDefaultScripts
|
||||||
|
let metaTags = [|"viewport", "user-scalable = no, initial-scale=0.8,maximum-scale=0.8 ,shrink-to-fit=yes"|] |> addDefaultMetaTags
|
||||||
|
|
||||||
|
View.index styles scripts metaTags currentClerk (Floorplan_Decorations.Controller.generateDecorator ()) roomMenu
|
||||||
@@ -1,41 +1,12 @@
|
|||||||
module Floorplan
|
module DredgePos.Floorplan.Model
|
||||||
|
|
||||||
open DredgePos
|
|
||||||
open Reservations
|
|
||||||
|
|
||||||
let currentVenue = 1
|
|
||||||
|
|
||||||
|
open DredgePos.Types
|
||||||
open System
|
open System
|
||||||
open System.IO
|
open System.IO
|
||||||
open System.Xml.Linq
|
open System.Xml.Linq
|
||||||
open DredgeFramework
|
open DredgeFramework
|
||||||
open Dapper
|
|
||||||
open Dapper.FSharp
|
open Dapper.FSharp
|
||||||
open Thoth.Json.Net
|
open Thoth.Json.Net
|
||||||
open Types
|
|
||||||
|
|
||||||
let floorplan_table_decoder : Decoder<floorplan_table> =
|
|
||||||
Decode.object
|
|
||||||
(fun get ->
|
|
||||||
{
|
|
||||||
table_number = get.Required.Field "table_number" Decode.int
|
|
||||||
room_id = get.Required.Field "room_id" Decode.int
|
|
||||||
venue_id = get.Required.Field "venue_id" Decode.int
|
|
||||||
pos_x = get.Required.Field "pos_x" Decode.int
|
|
||||||
pos_y = get.Required.Field "pos_y" Decode.int
|
|
||||||
shape = get.Required.Field "shape" Decode.string
|
|
||||||
width = get.Required.Field "width" Decode.int
|
|
||||||
height = get.Required.Field "height" Decode.int
|
|
||||||
default_covers = get.Required.Field "default_covers" Decode.int
|
|
||||||
rotation = get.Required.Field "rotation" Decode.int
|
|
||||||
merged_children = get.Required.Field "merged_children" Decode.string
|
|
||||||
previous_state = get.Required.Field "previous_state" Decode.string
|
|
||||||
status = get.Required.Field "status" Decode.int
|
|
||||||
id = get.Required.Field "id" Decode.int
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let activeTablePath = "tables/active/"
|
let activeTablePath = "tables/active/"
|
||||||
|
|
||||||
let getTableFile (tableNumber: int) =
|
let getTableFile (tableNumber: int) =
|
||||||
@@ -50,11 +21,11 @@ let tableIsOpen (table: floorplan_table) = tableNumberIsOpen table.table_number
|
|||||||
|
|
||||||
let fileNameToTableNumber (fileName: string) = //Takes a file name for a floorplan table and returns the table number
|
let fileNameToTableNumber (fileName: string) = //Takes a file name for a floorplan table and returns the table number
|
||||||
if fileName.Contains ".table" then
|
if fileName.Contains ".table" then
|
||||||
let fileName = (fileName.Split ".").[0]
|
let fileName = (fileName.Split ".")[0]
|
||||||
(fileName.Split "/table").[1] |> int
|
(fileName.Split "/table")[1] |> int
|
||||||
else 0
|
else 0
|
||||||
|
|
||||||
let openTables = //Get a list of all open tables.
|
let openTables () = //Get a list of all open tables.
|
||||||
let tableList = Directory.GetFiles(activeTablePath)
|
let tableList = Directory.GetFiles(activeTablePath)
|
||||||
|
|
||||||
tableList
|
tableList
|
||||||
@@ -68,7 +39,7 @@ let tablesInRoom (roomId: int) = //Get a list of all tables in a particular room
|
|||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
where (eq "room_id" roomId)
|
where (eq "room_id" roomId)
|
||||||
}
|
}
|
||||||
|> db.Select<floorplan_table>
|
|> Database.Select<floorplan_table>
|
||||||
|
|
||||||
|
|
||||||
let getActiveTables (venueId: int) =
|
let getActiveTables (venueId: int) =
|
||||||
@@ -76,7 +47,7 @@ let getActiveTables (venueId: int) =
|
|||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
where (eq "venue_id" venueId)
|
where (eq "venue_id" venueId)
|
||||||
}
|
}
|
||||||
|> db.Select
|
|> Database.Select
|
||||||
|> Array.filter tableIsOpen
|
|> Array.filter tableIsOpen
|
||||||
|> Array.map (fun table -> table.table_number)
|
|> Array.map (fun table -> table.table_number)
|
||||||
|
|
||||||
@@ -116,70 +87,45 @@ let saveOrderToTable orderXML tableNumber =
|
|||||||
|
|
||||||
File.WriteAllText(tableFile, tableXML)
|
File.WriteAllText(tableFile, tableXML)
|
||||||
|
|
||||||
let getTable (tableNumber : int) =
|
let getTableSafely (tableNumber: int) =
|
||||||
select {
|
let query = select {
|
||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
where (eq "table_number" tableNumber + eq "venue_id" currentVenue)
|
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
|
||||||
}
|
}
|
||||||
|> db.Select<floorplan_table>
|
|
||||||
|> 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) =
|
let getTableById (id : int) =
|
||||||
select {
|
select {
|
||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
where (eq "id" id)
|
where (eq "id" id)
|
||||||
}
|
}
|
||||||
|> db.Select<floorplan_table>
|
|> Database.Select<floorplan_table>
|
||||||
|> first
|
|> first
|
||||||
|
|
||||||
let getRoom (roomId: int) =
|
let getRoom (roomId: int) =
|
||||||
select {
|
select {
|
||||||
table "floorplan_rooms"
|
table "rooms"
|
||||||
where (eq "id" roomId)
|
where (eq "id" roomId)
|
||||||
} |> db.Select<floorplan_room> |> first
|
} |> Database.Select<room> |> first
|
||||||
|
|
||||||
let getRoomList (venueId: int) =
|
let updateTablePosition (floorplanTable: floorplan_table) = Entity.Update floorplanTable
|
||||||
select {
|
|
||||||
table "floorplan_rooms"
|
|
||||||
where (eq "venue_id" venueId)
|
|
||||||
} |> db.Select<floorplan_room>
|
|
||||||
|
|
||||||
let updateFloorplanTable (tableNumber:int) (column: string) value =
|
|
||||||
//TODO: Make update query venue specific
|
|
||||||
let sql = "Update floorplan_tables Set @column = @value Where table_number = @tableNumber"
|
|
||||||
let parameters = [("column", box column); ("value", box value); ("tableNumber", box tableNumber)]
|
|
||||||
db.connection.Execute(sql, parameters) |> ignore
|
|
||||||
|
|
||||||
getTable tableNumber
|
|
||||||
|
|
||||||
let updateTableShape (floorplanTable: floorplan_table) =
|
|
||||||
update {
|
|
||||||
table "floorplan_tables"
|
|
||||||
set floorplanTable
|
|
||||||
where (eq "table_number" floorplanTable.table_number + eq "venue_id" currentVenue)
|
|
||||||
} |> db.Update
|
|
||||||
|
|
||||||
|
|
||||||
let updateTablePosition (floorplanTable: floorplan_table) =
|
|
||||||
update {
|
|
||||||
table "floorplan_tables"
|
|
||||||
set floorplanTable
|
|
||||||
where (eq "table_number" floorplanTable.table_number + eq "venue_id" currentVenue)
|
|
||||||
} |> db.Update
|
|
||||||
|
|
||||||
let createEmptyReservation (reservation: reservation) =
|
let createEmptyReservation (reservation: reservation) =
|
||||||
update {
|
update {
|
||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
set {| status = 2 |}
|
set {| status = 2 |}
|
||||||
where(eq "id" reservation.reservation_table_id)
|
where(eq "id" reservation.floorplan_table_id)
|
||||||
} |> db.Update |> ignore
|
} |> Database.Update |> ignore
|
||||||
|
|
||||||
insert{
|
|
||||||
table "reservations"
|
|
||||||
value reservation
|
|
||||||
} |> db.InsertOutput |> first
|
|
||||||
|
|
||||||
|
|
||||||
|
Entity.Create reservation
|
||||||
|
|
||||||
let getChildTables tableNumber =
|
let getChildTables tableNumber =
|
||||||
let table = getTable tableNumber
|
let table = getTable tableNumber
|
||||||
@@ -213,15 +159,13 @@ let tableExists (tableNumber: int) =
|
|||||||
let numberOfResults =
|
let numberOfResults =
|
||||||
select{
|
select{
|
||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
where (eq "table_number" tableNumber + eq "venue_id" currentVenue)
|
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
|
||||||
} |> db.Select<floorplan_table> |> length
|
} |> Database.Select<floorplan_table> |> length
|
||||||
|
|
||||||
match numberOfResults with
|
match numberOfResults with
|
||||||
| 0 ->
|
| 0 ->
|
||||||
let allTables =
|
let allTables =
|
||||||
select {
|
Entity.GetAllInVenue<floorplan_table>
|
||||||
table "floorplan_tables"
|
|
||||||
} |> db.Select<floorplan_table>
|
|
||||||
|> Array.map(findChildTable tableNumber)
|
|> Array.map(findChildTable tableNumber)
|
||||||
|> Array.filter(fun tableNumber -> tableNumber <> 0)
|
|> Array.filter(fun tableNumber -> tableNumber <> 0)
|
||||||
|
|
||||||
@@ -229,16 +173,16 @@ let tableExists (tableNumber: int) =
|
|||||||
match allTables.Length with
|
match allTables.Length with
|
||||||
| 0 -> false |> string //Table does not exist
|
| 0 -> false |> string //Table does not exist
|
||||||
| _ ->
|
| _ ->
|
||||||
let parentTableData = getTable allTables.[0]
|
let parentTableData = getTable allTables[0]
|
||||||
let parentRoom = getRoom parentTableData.room_id
|
let parentRoom = getRoom parentTableData.room_id
|
||||||
let parentRoomName = parentRoom.room_name
|
let parentRoomName = parentRoom.name
|
||||||
language.getAndReplace "error_table_exists_merged" [parentRoomName; parentTableData.table_number.ToString()]
|
language.getAndReplace "error_table_exists_merged" [parentRoomName; parentTableData.table_number.ToString()]
|
||||||
|
|
||||||
|
|
||||||
| _ ->
|
| _ ->
|
||||||
let tableData = getTable tableNumber
|
let tableData = getTable tableNumber
|
||||||
let room = getRoom tableData.room_id
|
let room = getRoom tableData.room_id
|
||||||
language.getAndReplace "error_table_exists" [room.room_name]
|
language.getAndReplace "error_table_exists" [room.name]
|
||||||
|
|
||||||
|
|
||||||
let addNewTableWithoutOutput (newTable: floorplan_table) =
|
let addNewTableWithoutOutput (newTable: floorplan_table) =
|
||||||
@@ -246,23 +190,9 @@ let addNewTableWithoutOutput (newTable: floorplan_table) =
|
|||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
value newTable
|
value newTable
|
||||||
}
|
}
|
||||||
|> db.Insert
|
|> Database.Insert
|
||||||
|
|
||||||
let addNewTable (newTable: floorplan_table) =
|
let addNewTable (newTable: floorplan_table) = Entity.Create newTable
|
||||||
let newTableList =
|
|
||||||
insert{
|
|
||||||
table "floorplan_tables"
|
|
||||||
value newTable
|
|
||||||
}
|
|
||||||
|> db.InsertOutput
|
|
||||||
|
|
||||||
newTableList |> first
|
|
||||||
|
|
||||||
let deleteTable (tableNumber: int) =
|
|
||||||
delete {
|
|
||||||
table "floorplan_tables"
|
|
||||||
where (eq "table_number" tableNumber + eq "venue_id" currentVenue)
|
|
||||||
} |> db.Delete |> ignore
|
|
||||||
|
|
||||||
let mergeTables parent child = //Merge two tables together
|
let mergeTables parent child = //Merge two tables together
|
||||||
if parent = child then false else
|
if parent = child then false else
|
||||||
@@ -292,13 +222,12 @@ let mergeTables parent child = //Merge two tables together
|
|||||||
|
|
||||||
let existingChildrenJson = parentTable.merged_children |> StringTrim
|
let existingChildrenJson = parentTable.merged_children |> StringTrim
|
||||||
let existingChildren =
|
let existingChildren =
|
||||||
existingChildrenJson
|
existingChildrenJson |> Decode.Auto.fromString<floorplan_table[]>
|
||||||
|> Decode.fromString(Decode.list floorplan_table_decoder)
|
|
||||||
|
|
||||||
let tableList =
|
let tableList =
|
||||||
match existingChildren with
|
match existingChildren with
|
||||||
| Error _ -> [newChildTable]
|
| Error _ -> [|newChildTable|]
|
||||||
| Ok tables -> tables @ [newChildTable]
|
| Ok tables -> [tables ; [|newChildTable|]] |> Array.concat
|
||||||
|
|
||||||
let newChildrenJson = tableList |> jsonEncode
|
let newChildrenJson = tableList |> jsonEncode
|
||||||
let parentPreviousState = parentTable |> jsonEncode
|
let parentPreviousState = parentTable |> jsonEncode
|
||||||
@@ -314,10 +243,11 @@ let mergeTables parent child = //Merge two tables together
|
|||||||
pos_y = newPosY
|
pos_y = newPosY
|
||||||
default_covers = parentTable.default_covers + childTable.default_covers
|
default_covers = parentTable.default_covers + childTable.default_covers
|
||||||
|}
|
|}
|
||||||
where (eq "table_number" parent + eq "venue_id" currentVenue)
|
where (eq "table_number" parent + eq "venue_id" (getCurrentVenue()))
|
||||||
} |> db.Update |> ignore
|
} |> Database.Update |> ignore
|
||||||
|
|
||||||
deleteTable child
|
Entity.DeleteById<floorplan_table> newChildTable.id
|
||||||
|
|> ignore
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
@@ -326,8 +256,8 @@ let updateUnmergedTables parentTable childTable =
|
|||||||
update {
|
update {
|
||||||
table "floorplan_tables"
|
table "floorplan_tables"
|
||||||
set parentTable
|
set parentTable
|
||||||
where(eq "table_number" parentTable.table_number + eq "venue_id" currentVenue)
|
where(eq "table_number" parentTable.table_number + eq "venue_id" (getCurrentVenue()))
|
||||||
} |> db.Update |> ignore
|
} |> Database.Update |> ignore
|
||||||
|
|
||||||
addNewTableWithoutOutput childTable |> ignore
|
addNewTableWithoutOutput childTable |> ignore
|
||||||
true
|
true
|
||||||
@@ -350,44 +280,22 @@ let unmergeTable tableNumber = //Separates a merged table into itself and the la
|
|||||||
Some (getTable currentTable.table_number, unmergedChild)
|
Some (getTable currentTable.table_number, unmergedChild)
|
||||||
| Error _ -> None
|
| Error _ -> None
|
||||||
|
|
||||||
let convertRoomListToLinks (room: floorplan_room) =
|
|
||||||
let vars = map [
|
|
||||||
"roomId", room.id |> string
|
|
||||||
"roomName", room.room_name
|
|
||||||
]
|
|
||||||
|
|
||||||
Theme.loadTemplateWithVars "roomButton" vars
|
|
||||||
|
|
||||||
let getReservationList (tableList: floorplan_table[]) =
|
let getReservationList (tableList: floorplan_table[]) =
|
||||||
let tableIds =
|
tableList |> Array.collect Entity.GetAllRelated<reservation, floorplan_table>
|
||||||
tableList
|
|
||||||
|> Array.map(fun table -> table.id)
|
|
||||||
|> JoinArray ","
|
|
||||||
|
|
||||||
db.connection.Query<reservation>($"""Select * From reservations Where reservation_table_id In ({tableIds})""")
|
|
||||||
|> EnumerableToArray
|
|
||||||
|
|
||||||
let newReservation name time covers =
|
let newReservation name time covers =
|
||||||
let reservation = {
|
let reservation = {
|
||||||
id = 0
|
id = 0
|
||||||
reservation_name = name
|
name = name
|
||||||
reservation_time = time
|
time = time
|
||||||
reservation_covers = covers
|
covers = covers
|
||||||
reservation_table_id = 0
|
floorplan_table_id = 0
|
||||||
reservation_created_at = CurrentTime()
|
created_at = CurrentTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
insert {
|
Entity.Create reservation
|
||||||
table "reservations"
|
|
||||||
value reservation
|
|
||||||
} |> db.Insert
|
|
||||||
|
|
||||||
|
|
||||||
let tableList venueId =
|
let tableList () = Entity.GetAllInVenue<floorplan_table>
|
||||||
select{
|
|
||||||
table "floorplan_tables"
|
|
||||||
innerJoin "floorplan_rooms" "id" "floorplan_tables.room_id"
|
|
||||||
}
|
|
||||||
|> db.SelectJoin<floorplan_table, floorplan_room>
|
|
||||||
|> Array.filter (fun (_, room) -> room.venue_id = venueId )
|
|
||||||
|> Array.map fst
|
|
||||||
25
Floorplan/Router.fs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module DredgePos.Floorplan.Router
|
||||||
|
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Global.Router
|
||||||
|
open DredgePos.Types
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let floorplan = (htmlViewWithContext Controller.loadFloorplanView)
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
pipe_through Ajax.Router.pipeline
|
||||||
|
get "" floorplan
|
||||||
|
get "/" floorplan
|
||||||
|
post "/mergeTables" (bindJson<floorplan_table[]> Controller.mergeTables)
|
||||||
|
post "/transformTable" (bindJson<floorplan_table> Controller.transformTable)
|
||||||
|
post "/createTable" (bindJson<floorplan_table> Controller.createTable)
|
||||||
|
post "/addDecoration" (bindJson<floorplan_decoration> Controller.AddDecoration)
|
||||||
|
post "/updateDecoration" (bindJson<floorplan_decoration> Controller.UpdateDecoration)
|
||||||
|
post "/deleteDecoration" (bindJson<floorplan_decoration> Controller.DeleteDecoration)
|
||||||
|
post "/deleteTable" (bindJson<floorplan_table> Controller.deleteTable)
|
||||||
|
getf "/getFloorplanData/%i" Controller.getFloorplanData
|
||||||
|
getf "/transferTable/%i/%i" Controller.transferTable
|
||||||
|
getf "/unmergeTable/%i" Controller.unmergeTable
|
||||||
|
}
|
||||||
86
Floorplan/View.fs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
module DredgePos.Floorplan.View
|
||||||
|
|
||||||
|
open DredgePos.Global
|
||||||
|
open DredgePos.Global.View
|
||||||
|
open DredgePos.Entities
|
||||||
|
open DredgePos.Types
|
||||||
|
open Giraffe.ViewEngine
|
||||||
|
open DredgeFramework
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let pageContainer (clerk: clerk) roomMenu =
|
||||||
|
let loggedInText = str (language.getAndReplace "logged_in_as" [clerk.name])
|
||||||
|
|
||||||
|
div [_id "pageContainer"] [
|
||||||
|
div [_id "floorplanLeftColumn"] [
|
||||||
|
div [_class "topCell"] [
|
||||||
|
a [_class "posHeader"] [loggedInText]
|
||||||
|
]
|
||||||
|
div [_class "middleCell"] []
|
||||||
|
div [_class "bottomCell"] []
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_id "floorplanCenterColumn"] [
|
||||||
|
div [_class "topCell"] [
|
||||||
|
yield! roomMenu
|
||||||
|
]
|
||||||
|
div [_class "middleCell"] [
|
||||||
|
div [_id "floorplanCanvas"] []
|
||||||
|
div [_id "floorplanCanvas"] []
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_class "bottomCell"] [
|
||||||
|
div [_class "editControls" ; VisibleInMode ["tableSelected"]] [
|
||||||
|
div [_class "posHeader currentTable"] [
|
||||||
|
b [_class "selectedTableNumber"] []
|
||||||
|
a [_class "reservationStatus"; VisibleInMode ["reservedTableSelected"]] []
|
||||||
|
small [_class "selectedTableCovers"] []
|
||||||
|
]
|
||||||
|
|
||||||
|
a [_class "posButton placeOrderButton"] [lang "order_table"]
|
||||||
|
a [_class "posButton reserveTableButton"; InvisibleInMode ["reservedTableSelected"; "activeTableSelected"]] [lang "reserve_table"]
|
||||||
|
a [_class "posButton unreserveTableButton"; VisibleInMode ["reservedTableSelected"]] [lang "unreserve_table"]
|
||||||
|
a [_class "posButton payTableButton"; VisibleInMode ["activeTableSelected"]] [lang "pay_table"]
|
||||||
|
a [_class "posButton viewTableButton"; VisibleInMode ["activeTableSelected"]] [lang "view_table"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_id "floorplanRightColumn"] [
|
||||||
|
div [_class "topCell"] [
|
||||||
|
a [_class "posButton logOut"] [str "×"]
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_class "middleCell"] [
|
||||||
|
a [_class "posButton editModeButton"] [lang "edit_floorplan"]
|
||||||
|
div [_class "floorplanControls useVisibility"; VisibleInMode ["edit"]] [
|
||||||
|
a [_class "posButton addTableButton"] [lang "add_table"]
|
||||||
|
a [_class "posButton addDecoration"] [lang "add_decoration"]
|
||||||
|
a [_class "posButton deleteDecoration useVisibility"; VisibleInMode ["decorationSelected"; "edit"] ] [lang "delete_decoration"]
|
||||||
|
a [_class "posButton deleteTableButton useVisibility"; VisibleInMode ["tableSelected"; "edit"]] [lang "delete_table"]
|
||||||
|
a [_class "posButton changeShapeButton useVisibility"; VisibleInMode ["tableSelected"; "edit"]] [lang "change_shape"]
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_class "mergeControls useVisibility"; VisibleInMode ["tableSelected"]] [
|
||||||
|
a [_class "posButton mergeButton"; ActiveInMode "merge"] [lang "merge_table"]
|
||||||
|
a [_class "posButton unmergeButton"; VisibleInMode ["tableSelected"]] [lang "unmerge_table"]
|
||||||
|
a [_class "posButton transferTableButton" ; ActiveInMode "transfer" ; VisibleInMode ["activeTableSelected"]] [lang "transfer_table"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
div [_class "bottomCell"] []
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
let roomButton (room: room) = a [_class "posButton roomButton"; Value (string room.id)] [str room.name ]
|
||||||
|
|
||||||
|
let index styles scripts tags clerk decoratorRows roomMenu =
|
||||||
|
[|
|
||||||
|
pageContainer clerk roomMenu
|
||||||
|
decoratorRows |> Floorplan_Decorations.View.decorator
|
||||||
|
|]
|
||||||
|
|> HtmlPage "Floorplan" (GetScripts scripts) (GetStyles styles) (GetMetaTags tags)
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
module Entity
|
|
||||||
open DredgePos
|
|
||||||
open Types
|
|
||||||
open Dapper.FSharp
|
|
||||||
open DredgeFramework
|
|
||||||
|
|
||||||
let getDatabaseTable (record: 'a) = record.GetType().ToString().ToLower() + "s"
|
|
||||||
|
|
||||||
let addToDatabase (record: 'x)=
|
|
||||||
let tableName = getDatabaseTable record
|
|
||||||
insert {
|
|
||||||
table tableName
|
|
||||||
value record
|
|
||||||
}
|
|
||||||
|> db.InsertOutput
|
|
||||||
|> first
|
|
||||||
|
|
||||||
let updateInDatabase (record: 'x) =
|
|
||||||
let tableName = getDatabaseTable record
|
|
||||||
(* Run an update query *)
|
|
||||||
update {
|
|
||||||
table tableName
|
|
||||||
set record
|
|
||||||
}
|
|
||||||
|> db.Update |> ignore
|
|
||||||
record
|
|
||||||
7
Global/Controller.fs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module DredgePos.Global.Controller
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
|
||||||
|
let addDefaultScripts scripts = scripts |> Array.append [|"./external/jquery.js" ; "dredgepos.core.js"; "keyboards.js";|]
|
||||||
|
let addDefaultStyles styles = styles |> Array.append [|"dark.theme.css";|]
|
||||||
|
let addDefaultMetaTags (tags: (string*string)[]) = tags |> Array.append [|"apple-mobile-web-app-capable", "yes"|]
|
||||||
18
Global/Router.fs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module DredgePos.Global.Router
|
||||||
|
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let htmlViewWithContext func =
|
||||||
|
(fun ctx ->
|
||||||
|
func (snd ctx)
|
||||||
|
|> htmlView
|
||||||
|
)
|
||||||
|
|> warbler
|
||||||
|
|
||||||
|
let htmlViewWithContextAndId (id: int) func =
|
||||||
|
(fun ctx ->
|
||||||
|
func (snd ctx) id
|
||||||
|
|> htmlView
|
||||||
|
)
|
||||||
|
|> warbler
|
||||||
134
Global/View.fs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
module DredgePos.Global.View
|
||||||
|
|
||||||
|
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
|
||||||
|
| true -> script [_src scriptPath] []
|
||||||
|
| false -> comment $"[Missing script: {scriptFile}]"
|
||||||
|
let GetScripts (scripts: string[]) = scripts |> Array.map scriptToHTML
|
||||||
|
|
||||||
|
let styleToHTML (stylesheet:string) =
|
||||||
|
let stylePath = $"/styles/{stylesheet}"
|
||||||
|
match FileExists ("wwwroot" + stylePath) with
|
||||||
|
| true -> link [_rel "stylesheet" ; _href stylePath]
|
||||||
|
| false -> comment $"[Missing style: {stylesheet}]"
|
||||||
|
|
||||||
|
let GetStyles (scripts: string[]) = scripts |> Array.map styleToHTML
|
||||||
|
|
||||||
|
let tagToHtml (tag, content) = meta [_name tag; _content content]
|
||||||
|
|
||||||
|
let GetMetaTags (tags: (string * string)[]) = tags |> Array.map tagToHtml
|
||||||
|
|
||||||
|
let VirtualKeyboardRow numberOfButtons =
|
||||||
|
let buttons = Array.init numberOfButtons (fun _ -> a [] [])
|
||||||
|
div [_class "virtualKeyboardRow"] [
|
||||||
|
yield! buttons
|
||||||
|
]
|
||||||
|
|
||||||
|
let VirtualKeyboard =
|
||||||
|
div [_id "virtualKeyboard"] [
|
||||||
|
div [_class "headingRow"] [
|
||||||
|
h3 [_id "virtualKeyboardHeading"] []
|
||||||
|
a [_class "posButton closeKeyboards"] [str "X"]
|
||||||
|
]
|
||||||
|
input [_type "text"; _name "virtualKeyboardInput"; _id "virtualKeyboardInput"]
|
||||||
|
div [_id "virtualKeyboardButtons"] [
|
||||||
|
VirtualKeyboardRow 13
|
||||||
|
VirtualKeyboardRow 14
|
||||||
|
VirtualKeyboardRow 13
|
||||||
|
VirtualKeyboardRow 11
|
||||||
|
VirtualKeyboardRow 1
|
||||||
|
]
|
||||||
|
span [_class "forceFocus"] []
|
||||||
|
]
|
||||||
|
|
||||||
|
let VirtualNumpadButton (text: string) =
|
||||||
|
a [_href "#"; Value (text.ToLower()); _class "posButton virtualNumpadButton"] [str text]
|
||||||
|
|
||||||
|
let VirtualNumpadRow (buttons:string[]) =
|
||||||
|
div [_class "virtualNumpadRow"] [
|
||||||
|
yield! Array.map VirtualNumpadButton buttons
|
||||||
|
]
|
||||||
|
|
||||||
|
let VirtualNumpad =
|
||||||
|
div [_id "virtualNumpad"] [
|
||||||
|
div [_class "headingRow"] [
|
||||||
|
h3 [_id "virtualNumpadHeading"] []
|
||||||
|
a [_class "posButton closeKeyboards"] [str "X"]
|
||||||
|
]
|
||||||
|
div [_id "virtualNumpadInput"] []
|
||||||
|
div [_id "virtualNumpadButtons"] [
|
||||||
|
VirtualNumpadRow [|"1"; "2"; "3"|]
|
||||||
|
VirtualNumpadRow [|"4"; "5"; "6"|]
|
||||||
|
VirtualNumpadRow [|"7"; "8"; "9"|]
|
||||||
|
VirtualNumpadRow [|"0"; "."; "Clear"|]
|
||||||
|
VirtualNumpadRow [|"Submit"|]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
let alert =
|
||||||
|
div [_id "alert"] [
|
||||||
|
div [_id "alertHeading"] []
|
||||||
|
div [_id "alertMessage"] []
|
||||||
|
div [_id "alertButtons"] [
|
||||||
|
a [_class "posButton"; _id "alertOk"] [lang "alert_ok"]
|
||||||
|
a [_class "posButton"; _id "alertYes"] [lang "alert_yes"]
|
||||||
|
a [_class "posButton"; _id "alertNo"] [lang "alert_no"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
let keyboards = [|
|
||||||
|
VirtualKeyboard
|
||||||
|
VirtualNumpad
|
||||||
|
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 [] [
|
||||||
|
title [] [innerText pageTitle]
|
||||||
|
link [_rel "manifest" ; _href "/manifest.webmanifest"]
|
||||||
|
yield! styles
|
||||||
|
yield! scripts
|
||||||
|
yield! tags
|
||||||
|
]
|
||||||
|
body [] [
|
||||||
|
yield! content
|
||||||
|
yield! keyboards
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
let HTMLPageWithScripts pageTitle scripts content = HtmlPage pageTitle scripts [||] content
|
||||||
|
let HTMLPageWithStyles pageTitle styles content = HtmlPage pageTitle [||] styles content
|
||||||
|
let HTMLPageWithNoScriptsOrStyles pageTitle content = HtmlPage pageTitle [||] [||] content
|
||||||
32
Installer/Controller.fs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
module DredgePos.Installer.Controller
|
||||||
|
|
||||||
|
open System.Reflection
|
||||||
|
open DredgePos.Types
|
||||||
|
open FSharp.Reflection
|
||||||
|
|
||||||
|
let RunMigration (fsModule: System.Type) =
|
||||||
|
fsModule
|
||||||
|
.GetMethod("run")
|
||||||
|
.Invoke(null, [||])
|
||||||
|
|> ignore
|
||||||
|
Entity.Create {name=fsModule.FullName; timestamp=DredgeFramework.CurrentTime(); id=0} |> ignore
|
||||||
|
fsModule.FullName + " ran Successfully"
|
||||||
|
|
||||||
|
let RunAllMigrations () =
|
||||||
|
let completedMigrations =
|
||||||
|
try
|
||||||
|
Entity.GetAll<migration>
|
||||||
|
with
|
||||||
|
| _ -> [||]
|
||||||
|
|> Array.map (fun migration -> migration.name)
|
||||||
|
|
||||||
|
Assembly
|
||||||
|
.GetExecutingAssembly()
|
||||||
|
.GetTypes()
|
||||||
|
|> Array.filter FSharpType.IsModule
|
||||||
|
|> Array.filter (fun fsModule -> fsModule.Namespace = "DredgePos.Migrations")
|
||||||
|
|> Array.filter (fun fsModule -> not (completedMigrations |> Array.contains fsModule.FullName))
|
||||||
|
|> Array.sortBy (fun fsModule -> fsModule.Name)
|
||||||
|
|> Array.map RunMigration
|
||||||
|
|> (fun arr -> if arr.Length > 0 then arr else [|"No Migrations Were Run"|])
|
||||||
|
|> String.concat "<br/><hr/>"
|
||||||
2
Installer/Model.fs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module DredgePos.Installer.Model
|
||||||
|
|
||||||
13
Installer/Router.fs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module DredgePos.Installer.Router
|
||||||
|
|
||||||
|
open DredgePos
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let installer = (warbler (fun _ -> htmlString (Controller.RunAllMigrations ())))
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
pipe_through Ajax.Router.pipeline
|
||||||
|
get "/" installer
|
||||||
|
get "" installer
|
||||||
|
}
|
||||||
131
Migrations/CreateDatabaseSchema.fs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
module DredgePos.Migrations.CreateDatabaseSchema
|
||||||
|
|
||||||
|
open DredgePos.Types
|
||||||
|
open Database
|
||||||
|
open Dapper.FSharp
|
||||||
|
open Dapper.FSharp.PostgreSQL
|
||||||
|
|
||||||
|
let CreateDatabase (db: db_config) connectionString =
|
||||||
|
let connection = connect connectionString
|
||||||
|
connection
|
||||||
|
|> NonDbSpecificQuery $"""
|
||||||
|
CREATE DATABASE {db.db_name};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let addTables () =
|
||||||
|
CreateTable "sessions" [
|
||||||
|
"session_id", "varchar(200)"
|
||||||
|
"clerk_json", "text"
|
||||||
|
"clerk_id", "int"
|
||||||
|
"expires", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "grids" [
|
||||||
|
"name", "varchar(60)"
|
||||||
|
"rows", "int"
|
||||||
|
"cols", "int"
|
||||||
|
"data", "text"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "reservations" [
|
||||||
|
"name", "varchar(100)"
|
||||||
|
"time", "int"
|
||||||
|
"covers", "int"
|
||||||
|
"floorplan_table_id", "int"
|
||||||
|
"created_at", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "venues" [
|
||||||
|
"name", "varchar(60)"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "floorplan_tables" [
|
||||||
|
"table_number", "int"
|
||||||
|
"room_id", "int"
|
||||||
|
"venue_id", "int"
|
||||||
|
"pos_x", "int"
|
||||||
|
"pos_y", "int"
|
||||||
|
"shape", "varchar(12)"
|
||||||
|
"width", "int"
|
||||||
|
"height", "int"
|
||||||
|
"default_covers", "int"
|
||||||
|
"rotation", "int"
|
||||||
|
"merged_children", "text"
|
||||||
|
"previous_state", "text"
|
||||||
|
"status", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "print_groups" [
|
||||||
|
"name", "varchar(20)"
|
||||||
|
"printer_id", "int"
|
||||||
|
"venue_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "sales_categories" [
|
||||||
|
"parent", "int"
|
||||||
|
"name", "varchar(20)"
|
||||||
|
"print_group_id", "int"
|
||||||
|
"venue_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "rooms" [
|
||||||
|
"name", "varchar(20)"
|
||||||
|
"background_image", "varchar(100)"
|
||||||
|
"venue_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "floorplan_decorations" [
|
||||||
|
"room_id", "int"
|
||||||
|
"pos_x", "int"
|
||||||
|
"pos_y", "int"
|
||||||
|
"rotation", "int"
|
||||||
|
"width", "int"
|
||||||
|
"height", "int"
|
||||||
|
"image", "varchar(100)"
|
||||||
|
"venue_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "clerks" [
|
||||||
|
"name", "varchar(20)"
|
||||||
|
"login_code", "int"
|
||||||
|
"user_group_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "order_screen_page_groups" [
|
||||||
|
"order", "int"
|
||||||
|
"venue_id", "int"
|
||||||
|
"label", "varchar(40)"
|
||||||
|
"grid_id", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "buttons" [
|
||||||
|
"text", "varchar(60)"
|
||||||
|
"primary_action", "varchar(15)"
|
||||||
|
"primary_action_value", "varchar(20)"
|
||||||
|
"secondary_action", "varchar(15)"
|
||||||
|
"secondary_action_value", "varchar(20)"
|
||||||
|
"image", "varchar(60)"
|
||||||
|
"extra_classes", "text"
|
||||||
|
"extra_styles", "text"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "items" [
|
||||||
|
"code", "varchar(40)"
|
||||||
|
"sales_category_id", "int"
|
||||||
|
"name", "varchar(60)"
|
||||||
|
"item_type", "varchar(12)"
|
||||||
|
"price1", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
CreateTable "migrations" [
|
||||||
|
"name", "varchar(100)"
|
||||||
|
"timestamp", "int"
|
||||||
|
]
|
||||||
|
|
||||||
|
let run () =
|
||||||
|
let db = getDatabaseSettings ()
|
||||||
|
|
||||||
|
$"Server={db.host};Port={db.port};User Id={db.username};Password={db.password};Include Error Detail=true"
|
||||||
|
|> CreateDatabase db
|
||||||
|
|> ignore
|
||||||
|
|> addTables
|
||||||
485
Migrations/PopulateTestData.fs
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
module DredgePos.Migrations.PopulateTestData
|
||||||
|
|
||||||
|
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" "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
|
||||||
|
| "beer" | "wine" -> "Beverage"
|
||||||
|
| _ -> "Food"
|
||||||
|
let parentCategory = Entity.GetFirstByColumn<sales_category> "name" parentName
|
||||||
|
|
||||||
|
if dirName.ToLower() <> "dips" && dirName.ToLower() <> "steak temperatures" then
|
||||||
|
let NewGrid = Entity.Create {
|
||||||
|
id=0
|
||||||
|
name=dirName
|
||||||
|
rows=8
|
||||||
|
cols=6
|
||||||
|
data=""
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
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 {
|
||||||
|
id=0
|
||||||
|
parent=parentCategory.id
|
||||||
|
name=dirName
|
||||||
|
print_group_id=printGroup
|
||||||
|
venue_id=1
|
||||||
|
} |> ignore
|
||||||
|
|
||||||
|
dir
|
||||||
|
|
||||||
|
let CreateDefaultPrintGroups (path: string) =
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
name="Entrees"
|
||||||
|
printer_id=1
|
||||||
|
venue_id=1
|
||||||
|
} |> ignore
|
||||||
|
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
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) =
|
||||||
|
{
|
||||||
|
id=0
|
||||||
|
name="Megalomania"
|
||||||
|
}
|
||||||
|
|> Entity.Create
|
||||||
|
|>ignore
|
||||||
|
path
|
||||||
|
|
||||||
|
let CreateDefaultClerk (path: string) =
|
||||||
|
{
|
||||||
|
id=0
|
||||||
|
name="Josh"
|
||||||
|
login_code=1408
|
||||||
|
user_group_id=1
|
||||||
|
}
|
||||||
|
|> Entity.Create
|
||||||
|
|>ignore
|
||||||
|
path
|
||||||
|
|
||||||
|
let CreateDefaultSalesCategories (path: string) =
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
parent=0
|
||||||
|
name="Food"
|
||||||
|
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Mains").id
|
||||||
|
venue_id=1
|
||||||
|
} |> ignore
|
||||||
|
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
parent=0
|
||||||
|
name="Beverage"
|
||||||
|
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
|
||||||
|
|> Array.filter (fun file -> Path.GetExtension file = ".png" || Path.GetExtension file = ".jpg")
|
||||||
|
|> Array.iter (fun image ->
|
||||||
|
let roomName = Path.GetFileNameWithoutExtension image
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
name=roomName
|
||||||
|
background_image= Path.GetFileName image
|
||||||
|
venue_id=1
|
||||||
|
} |> ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
let populateEntreeGrid () =
|
||||||
|
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Entrees"
|
||||||
|
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 =
|
||||||
|
[|
|
||||||
|
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 -> space
|
||||||
|
| Some x -> x.id
|
||||||
|
)
|
||||||
|
|> Array.mapi (fun index current ->
|
||||||
|
let isSecondRow = index > 6 && index < 12
|
||||||
|
if not isSecondRow then current else
|
||||||
|
let entree = Dips |> Array.tryItem (index-7)
|
||||||
|
match entree with
|
||||||
|
| None -> space
|
||||||
|
| Some x -> x.id
|
||||||
|
)
|
||||||
|
|
||||||
|
let grid =
|
||||||
|
Entity.GetFirstByColumn<order_screen_page_group> "label" "Entrees"
|
||||||
|
|> Entity.GetRelated<grid, order_screen_page_group>
|
||||||
|
|
||||||
|
let newGrid = {grid with data=(jsonEncode {|page1=GridData|})}
|
||||||
|
Entity.Update newGrid |> ignore
|
||||||
|
|
||||||
|
()
|
||||||
|
|
||||||
|
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 -> space
|
||||||
|
| Some x -> x.id
|
||||||
|
let GridData =
|
||||||
|
[|
|
||||||
|
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 =
|
||||||
|
Entity.GetFirstByColumn<order_screen_page_group> "label" category
|
||||||
|
|> Entity.GetRelated<grid, order_screen_page_group>
|
||||||
|
|
||||||
|
let newGrid = {grid with data=(jsonEncode {|page1=GridData|})}
|
||||||
|
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 -> space
|
||||||
|
| Some x -> x.id
|
||||||
|
let GridData =
|
||||||
|
[|
|
||||||
|
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 =
|
||||||
|
Entity.GetFirstByColumn<order_screen_page_group> "label" "Dessert"
|
||||||
|
|> Entity.GetRelated<grid, order_screen_page_group>
|
||||||
|
|
||||||
|
let newGrid = {grid with data=(jsonEncode {|page1=GridData|})}
|
||||||
|
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 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<grid> "name" "Steak Temperatures"
|
||||||
|
|
||||||
|
let getId index =
|
||||||
|
match Temps |> Array.tryItem index with
|
||||||
|
| None -> space
|
||||||
|
| Some x -> x.id
|
||||||
|
let GridData =
|
||||||
|
[|
|
||||||
|
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}
|
||||||
|
Entity.Update newGrid |> ignore
|
||||||
|
|
||||||
|
let steakButtons = Entity.GetAllByColumn<button> "text" "Venison Wellington"
|
||||||
|
steakButtons |> Array.iter (fun button ->
|
||||||
|
Entity.Update {button with secondary_action="grid"; secondary_action_value=newGrid.id.ToString()} |> ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
let PopulateGrids () =
|
||||||
|
populateEntreeGrid ()
|
||||||
|
|> populateMainGrid "Mains"
|
||||||
|
|> populateMainGrid "Wine"
|
||||||
|
|> populateDessertGrid
|
||||||
|
|> populateBeerGrid
|
||||||
|
|> populateSteakTemperaturesGrid
|
||||||
|
|
||||||
|
let CreateItemFromFileName (index: int) (dirName: string) (file: string) =
|
||||||
|
let extension = Path.GetExtension file
|
||||||
|
let fileName = Path.GetFileNameWithoutExtension file
|
||||||
|
let itemType =
|
||||||
|
match dirName.ToLower() with
|
||||||
|
| "dips" | "steak temperatures" -> "instruction"
|
||||||
|
| _ -> "item"
|
||||||
|
|
||||||
|
let categories = (Entity.GetAllByColumn<sales_category> "name" dirName)
|
||||||
|
let categoryID =
|
||||||
|
if categories.Length > 0 then categories[0].id
|
||||||
|
else (Entity.GetFirstByColumn<sales_category> "name" "Mains").id
|
||||||
|
|
||||||
|
let newItem = Entity.Create {
|
||||||
|
id = 0
|
||||||
|
code = $"{dirName}0{index+1}" |> StringReplace " " ""
|
||||||
|
sales_category_id=categoryID
|
||||||
|
name=fileName
|
||||||
|
item_type=itemType
|
||||||
|
price1=1000
|
||||||
|
}
|
||||||
|
|
||||||
|
let classes =
|
||||||
|
match dirName.ToLower() with
|
||||||
|
| "beer" | "dessert" -> "doubleHeight"
|
||||||
|
| "mains" | "wine" | "steak temperatures" -> "doubleHeight doubleWidth"
|
||||||
|
| "entrees" -> "doubleWidth"
|
||||||
|
| _ -> "normal"
|
||||||
|
|
||||||
|
Entity.Create {
|
||||||
|
id=0
|
||||||
|
text=fileName
|
||||||
|
primary_action="item"
|
||||||
|
primary_action_value=newItem.code
|
||||||
|
secondary_action="None"
|
||||||
|
secondary_action_value=""
|
||||||
|
image= $"{dirName}/{fileName}{extension}"
|
||||||
|
extra_classes=classes
|
||||||
|
extra_styles=""
|
||||||
|
} |> ignore
|
||||||
|
|
||||||
|
|
||||||
|
let CreateItemsAndButtons (dir: string) =
|
||||||
|
let dirName = DirectoryInfo(dir).Name
|
||||||
|
dir
|
||||||
|
|> Directory.GetFiles
|
||||||
|
|> 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
|
||||||
|
|> PopulateRooms
|
||||||
90
OrderScreen/Controller.fs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
module DredgePos.OrderScreen.Controller
|
||||||
|
|
||||||
|
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.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 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 loadOrderScreenView (ctx: HttpContext) (tableNumber: int) =
|
||||||
|
Authenticate.Model.RequireClerkAuthentication ctx
|
||||||
|
let tableOption = DredgePos.Floorplan.Model.getTableSafely tableNumber
|
||||||
|
let attr = Giraffe.ViewEngine.HtmlElements.attr
|
||||||
|
|
||||||
|
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 printGroupButtons =
|
||||||
|
Entity.GetAllInVenue<print_group>
|
||||||
|
|> Array.map View.printGroupButton
|
||||||
|
|
||||||
|
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 grids = Model.getAllPageGridsInVenue ()
|
||||||
|
let pageGroupNodes =
|
||||||
|
grids
|
||||||
|
|> Array.map(fun (grid, page_group) ->
|
||||||
|
renderGrid grid
|
||||||
|
|> View.pageGroup page_group
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
View.index tableNumber styles scripts metaTags currentClerk printGroupButtons orderScreenPageGroupButtons pageGroupNodes coverSelectorButtons
|
||||||
18
OrderScreen/Model.fs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module DredgePos.OrderScreen.Model
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Types
|
||||||
|
open FSharp.Collections
|
||||||
|
open Thoth.Json.Net
|
||||||
|
open Theme
|
||||||
|
|
||||||
|
|
||||||
|
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}" """
|
||||||
|
|
||||||
14
OrderScreen/Router.fs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module DredgePos.OrderScreen.Router
|
||||||
|
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Types
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
pipe_through Ajax.Router.pipeline
|
||||||
|
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))
|
||||||
|
getf "/%i" (fun number -> (warbler (fun ctx -> htmlView <| Controller.loadOrderScreenView (snd ctx) number)))
|
||||||
|
}
|
||||||
167
OrderScreen/View.fs
Normal 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)
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
module PageController
|
|
||||||
|
|
||||||
open Microsoft.AspNetCore.Http
|
|
||||||
open Floorplan
|
|
||||||
open Giraffe
|
|
||||||
open DredgeFramework
|
|
||||||
|
|
||||||
let loadHomePage(): HttpHandler =
|
|
||||||
let variables = map["title", "Log In"]
|
|
||||||
let scripts = ["dredgepos.authenticate.js"]
|
|
||||||
let styles = ["dredgepos.authenticate.css"]
|
|
||||||
|
|
||||||
htmlString <| Theme.loadTemplateWithVarsScriptsAndStyles "authenticate" variables scripts styles
|
|
||||||
|
|
||||||
let loadFloorplan (ctx: HttpContext) : HttpHandler =
|
|
||||||
Session.RequireClerkAuthentication ctx
|
|
||||||
|
|
||||||
let roomMenu =
|
|
||||||
getRoomList currentVenue
|
|
||||||
|> Array.map convertRoomListToLinks
|
|
||||||
|> String.concat "\n"
|
|
||||||
|
|
||||||
let variables = map [
|
|
||||||
"title", "Floorplan"
|
|
||||||
"roomMenu", roomMenu
|
|
||||||
"decorator", Decorations.generateDecorator()
|
|
||||||
]
|
|
||||||
let styles = ["dredgepos.floorplan.css"]
|
|
||||||
let scripts = ["external/konva.min.js" ; "dredgepos.floorplan.js"]
|
|
||||||
let currentClerk = recordToMap <| Session.getCurrentClerk ctx
|
|
||||||
|
|
||||||
let arrays = map["clerk", currentClerk]
|
|
||||||
|
|
||||||
htmlString <| Theme.loadTemplateWithVarsArraysScriptsAndStyles "floorplan" variables arrays scripts styles
|
|
||||||
|
|
||||||
let loadContactPage id =
|
|
||||||
Session.clerkLogin 1408 |> ignore
|
|
||||||
Theme.loadTemplate "index"
|
|
||||||
|
|
||||||
let getOpenTables() =
|
|
||||||
let rows = Floorplan.openTables
|
|
||||||
rows |> jsonEncode
|
|
||||||
|
|
||||||
let transferTables() =
|
|
||||||
|
|
||||||
Theme.loadTemplate "index"
|
|
||||||
|
|
||||||
let mergeTables parent child =
|
|
||||||
Floorplan.mergeTables parent child |> ignore
|
|
||||||
"done"
|
|
||||||
|
|
||||||
let unmergeTables table =
|
|
||||||
Floorplan.unmergeTable table |> ignore
|
|
||||||
"done"
|
|
||||||
2
Printer.module.fs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module Printer
|
||||||
|
|
||||||
63
Program.fs
@@ -1,62 +1,29 @@
|
|||||||
namespace WebApplication
|
namespace DredgePos
|
||||||
|
|
||||||
|
|
||||||
open DredgePos
|
open DredgePos
|
||||||
open Microsoft.AspNetCore.Server.Kestrel.Core
|
|
||||||
open Reservations
|
|
||||||
open Saturn
|
open Saturn
|
||||||
open Giraffe
|
open Giraffe
|
||||||
open Types
|
|
||||||
|
|
||||||
module Program =
|
module Program =
|
||||||
|
let router = router {
|
||||||
let handlePostRoute<'a> handlerFunction post next ctx =
|
pipe_through Ajax.Router.pipeline
|
||||||
json (handlerFunction ctx post) next ctx
|
|
||||||
|
|
||||||
let browser = pipeline {
|
|
||||||
use_warbler
|
|
||||||
}
|
|
||||||
|
|
||||||
let ajaxRouter = router {
|
|
||||||
pipe_through browser
|
|
||||||
post "/authenticateClerk" (bindJson<int> (handlePostRoute AjaxController.loginWithLoginCode) )
|
|
||||||
post "/getTableData" (bindJson<int> AjaxController.getTableData)
|
|
||||||
post "/transformTable" (bindJson<floorplan_table> AjaxController.transformTable)
|
|
||||||
post "/createTable" (bindJson<floorplan_table> AjaxController.createTable)
|
|
||||||
post "/addDecoration" (bindJson<floorplan_decoration> AjaxController.AddDecoration)
|
|
||||||
post "/updateDecoration" (bindJson<floorplan_decoration> AjaxController.UpdateDecoration)
|
|
||||||
post "/deleteDecoration" (bindJson<floorplan_decoration> AjaxController.DeleteDecoration)
|
|
||||||
post "/deleteTable" (bindJson<floorplan_table> AjaxController.deleteTable)
|
|
||||||
post "/mergeTables" (bindJson<floorplan_table[]> AjaxController.mergeTables)
|
|
||||||
post "/newEmptyReservation" (bindJson<reservation> AjaxController.newEmptyReservation)
|
|
||||||
post "/updateReservation" (bindJson<reservation> AjaxController.updateReservation)
|
|
||||||
post "/getReservation" (bindJson<int> (fun reservation -> json <| GetReservationById reservation) )
|
|
||||||
post "/unreserveTable" (bindJson<floorplan_table> AjaxController.unreserveTable )
|
|
||||||
getf "/getRoomData/%i" AjaxController.getRoomData
|
|
||||||
getf "/getKeyboardLayout/%s" AjaxController.getKeyboardLayout
|
|
||||||
getf "/getTablesAndDecorations/%i" AjaxController.getRoomTablesAndDecorations
|
|
||||||
get "/languageVars" (json <| AjaxController.getLanguageVars)
|
|
||||||
get "/getOpenTables" (json <| Floorplan.getActiveTables Floorplan.currentVenue)
|
|
||||||
getf "/getActiveTables/%i" AjaxController.getActiveTables
|
|
||||||
getf "/getFloorplanData/%i" AjaxController.getFloorplanData
|
|
||||||
getf "/tableIsOpen/%i" (fun tableNumber -> json <| Floorplan.tableNumberIsOpen tableNumber)
|
|
||||||
getf "/transferTable/%i/%i" AjaxController.transferTable
|
|
||||||
getf "/unmergeTable/%i" AjaxController.unmergeTable
|
|
||||||
getf "/tableExists/%i" (fun tableNumber -> json <| Floorplan.tableExists tableNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageRouter = router {
|
|
||||||
pipe_through browser
|
|
||||||
not_found_handler (setStatusCode 404 >=> text "404")
|
not_found_handler (setStatusCode 404 >=> text "404")
|
||||||
get "/" (redirectTo true "/login")
|
get "/" (redirectTo true "/login")
|
||||||
get "/login" (warbler (fun _ -> PageController.loadHomePage() ))
|
forward "/ajax" Ajax.Router.router
|
||||||
get "/floorplan" (warbler (fun ctx -> PageController.loadFloorplan (snd ctx)))
|
forward "/floorplan" DredgePos.Floorplan.Router.router
|
||||||
forward "/ajax" ajaxRouter
|
forward "/order" DredgePos.OrderScreen.Router.router
|
||||||
|
forward "/login" DredgePos.Authenticate.Router.router
|
||||||
|
forward "/reservations" DredgePos.Reservations.Router.router
|
||||||
|
forward "/install" DredgePos.Installer.Router.router
|
||||||
}
|
}
|
||||||
|
|
||||||
let app = application {
|
let app =
|
||||||
|
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_static "wwwroot"
|
use_static "wwwroot"
|
||||||
use_router pageRouter
|
use_router router
|
||||||
url "http://0.0.0.0:5001"
|
url "http://0.0.0.0:5001"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
Reservations/Controller.fs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
module DredgePos.Reservations.Controller
|
||||||
|
|
||||||
|
open DredgeFramework
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Types
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let newEmptyReservation (reservation: reservation) =
|
||||||
|
let newReservation = {reservation with
|
||||||
|
created_at = CurrentTime()
|
||||||
|
time = CurrentTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
if reservation.floorplan_table_id > 0 then
|
||||||
|
let table = {(Entity.GetById<floorplan_table> reservation.floorplan_table_id) with
|
||||||
|
status = 2
|
||||||
|
default_covers = reservation.covers}
|
||||||
|
Floorplan.Model.updateTablePosition table |> ignore
|
||||||
|
|
||||||
|
let createdReservation = Floorplan.Model.createEmptyReservation newReservation
|
||||||
|
ajaxSuccess createdReservation |> json
|
||||||
|
|
||||||
|
let updateReservation (reservation: reservation) = Model.updateReservation reservation |> ajaxSuccess |> json
|
||||||
|
|
||||||
|
let unreserveTable (table: floorplan_table) =
|
||||||
|
let newTable = {table with status = 0}
|
||||||
|
Floorplan.Model.updateTablePosition newTable |> ignore
|
||||||
|
Model.DeleteReservation newTable.id
|
||||||
|
newTable |> ajaxSuccess |> json
|
||||||
@@ -1,29 +1,20 @@
|
|||||||
module Reservations
|
module DredgePos.Reservations.Model
|
||||||
|
|
||||||
open System
|
|
||||||
open DredgeFramework
|
open DredgeFramework
|
||||||
open Dapper.FSharp
|
open Dapper.FSharp
|
||||||
open DredgePos
|
open DredgePos
|
||||||
open Types
|
open Types
|
||||||
|
|
||||||
let GetReservationById (id: int) =
|
|
||||||
select {
|
|
||||||
table "reservations"
|
|
||||||
where (eq "id" id)
|
|
||||||
}
|
|
||||||
|> db.Select<reservation>
|
|
||||||
|> first
|
|
||||||
|
|
||||||
let updateReservation (reservation: reservation) =
|
let updateReservation (reservation: reservation) =
|
||||||
update{
|
update{
|
||||||
table "reservations"
|
table "reservations"
|
||||||
set reservation
|
set reservation
|
||||||
where(eq "id" reservation.id)
|
where(eq "id" reservation.id)
|
||||||
} |> db.Update |> ignore
|
} |> Database.Update |> ignore
|
||||||
reservation
|
reservation
|
||||||
|
|
||||||
let DeleteReservation (tableId: int) =
|
let DeleteReservation (tableId: int) =
|
||||||
delete {
|
delete {
|
||||||
table "reservations"
|
table "reservations"
|
||||||
where (eq "reservation_table_id" tableId)
|
where (eq "floorplan_table_id" tableId)
|
||||||
} |> db.Delete |> ignore
|
} |> Database.Delete |> ignore
|
||||||
13
Reservations/Router.fs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module DredgePos.Reservations.Router
|
||||||
|
|
||||||
|
open DredgePos
|
||||||
|
open DredgePos.Types
|
||||||
|
open Saturn
|
||||||
|
open Giraffe
|
||||||
|
|
||||||
|
let router = router {
|
||||||
|
pipe_through Ajax.Router.pipeline
|
||||||
|
post "/newEmptyReservation" (bindJson<reservation> Controller.newEmptyReservation)
|
||||||
|
post "/updateReservation" (bindJson<reservation> Controller.updateReservation)
|
||||||
|
post "/unreserveTable" (bindJson<floorplan_table> Controller.unreserveTable )
|
||||||
|
}
|
||||||
55
Types.fs
@@ -1,55 +0,0 @@
|
|||||||
module DredgePos.Types
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type reservation = {
|
|
||||||
id: int
|
|
||||||
reservation_name: string
|
|
||||||
reservation_time: int
|
|
||||||
reservation_covers: int
|
|
||||||
reservation_table_id: int
|
|
||||||
reservation_created_at: int
|
|
||||||
}
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type floorplan_table = {
|
|
||||||
table_number: int
|
|
||||||
room_id: int
|
|
||||||
venue_id: int
|
|
||||||
pos_x: int
|
|
||||||
pos_y: int
|
|
||||||
shape: string
|
|
||||||
width: int
|
|
||||||
height: int
|
|
||||||
default_covers: int
|
|
||||||
rotation: int
|
|
||||||
merged_children: string
|
|
||||||
previous_state: string
|
|
||||||
status: int
|
|
||||||
id: int
|
|
||||||
}
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type floorplan_room = {
|
|
||||||
id: int
|
|
||||||
room_name: string
|
|
||||||
background_image: string
|
|
||||||
venue_id: int
|
|
||||||
}
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type floorplan_decoration = {
|
|
||||||
id: int
|
|
||||||
decoration_room: int
|
|
||||||
decoration_pos_x: int
|
|
||||||
decoration_pos_y: int
|
|
||||||
decoration_rotation: int
|
|
||||||
decoration_width: int
|
|
||||||
decoration_height: int
|
|
||||||
decoration_image: string
|
|
||||||
}
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type clerk = {id: int; clerk_name: string; clerk_login_code: int; clerk_usergroup: int}
|
|
||||||
|
|
||||||
[<CLIMutable>]
|
|
||||||
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
|
|
||||||
9
config.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"db_name": "dredgepos",
|
||||||
|
"username": "postgres",
|
||||||
|
"password": "root",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 5432
|
||||||
|
}
|
||||||
|
}
|
||||||
1217
package-lock.json
generated
10
package.json
@@ -7,9 +7,15 @@
|
|||||||
"currency.js": "^2.0.4",
|
"currency.js": "^2.0.4",
|
||||||
"konva": "^8.2.2",
|
"konva": "^8.2.2",
|
||||||
"sass": "^1.43.4",
|
"sass": "^1.43.4",
|
||||||
"typescript": "^4.4.4"
|
"tsc": "^2.0.4",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"license": "",
|
"license": "",
|
||||||
"repository": ""
|
"repository": "",
|
||||||
|
"scripts": {
|
||||||
|
"sass": "sass sass:wwwroot/styles",
|
||||||
|
"typescript": "tsc",
|
||||||
|
"build": "npm run sass && npm run typescript"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,14 @@
|
|||||||
--posbutton-text-color-active: #fff
|
--posbutton-text-color-active: #fff
|
||||||
--posbutton-background: #232B30 -webkit-linear-gradient(top, #3D4850 3%, #000 4%, #333 100%)
|
--posbutton-background: #232B30 -webkit-linear-gradient(top, #3D4850 3%, #000 4%, #333 100%)
|
||||||
--posbutton-background-active: #20282D -webkit-gradient(linear, left top, left bottom, color-stop(3%,#20282D), color-stop(51%,#252E34), color-stop(100%,#222A30)) 0 top
|
--posbutton-background-active: #20282D -webkit-gradient(linear, left top, left bottom, color-stop(3%,#20282D), color-stop(51%,#252E34), color-stop(100%,#222A30)) 0 top
|
||||||
|
--void-button-background: red -webkit-gradient(linear, left top, left bottom, color-stop(3%,darkred), color-stop(51%,darkred), color-stop(100%,red)) 0 top
|
||||||
|
|
||||||
|
/** Order Screen **/
|
||||||
|
--orderbox-header-background: #888
|
||||||
|
--orderbox-row-background: var(--pos-header-background)
|
||||||
|
--orderbox-selected-row-background: #dd7f37
|
||||||
|
|
||||||
|
/** Order Box/Payment Splitter Box **/
|
||||||
|
--pulse-first-color: #ffa93e
|
||||||
|
--pulse-second-color: #dd7f37
|
||||||
|
--pulse-final-color: var(--orderbox-row-background)
|
||||||
@@ -1,8 +1,37 @@
|
|||||||
|
@font-face
|
||||||
|
font-family: "manrope"
|
||||||
|
src: url("/fonts/OpenSans-Regular.ttf") format('truetype')
|
||||||
|
font-style: normal
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: "manrope"
|
||||||
|
src: url("/fonts/OpenSans-SemiBold.ttf") format('truetype')
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family: "manrope"
|
||||||
|
src: url("/fonts/OpenSans-Light.ttf") format('truetype')
|
||||||
|
font-weight: 100
|
||||||
|
|
||||||
*
|
*
|
||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
padding: 0
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
font-family: Arial, Helvetica, sans-serif
|
font-family: 'manrope', sans-serif
|
||||||
|
scroll-behavior: smooth
|
||||||
|
|
||||||
|
*:not(input, textarea)
|
||||||
|
-webkit-touch-callout: none
|
||||||
|
-webkit-user-select: none
|
||||||
|
-khtml-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
user-select: none
|
||||||
|
|
||||||
|
.rtl
|
||||||
|
direction: rtl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
input[type=text], select, textarea
|
input[type=text], select, textarea
|
||||||
padding-left: 1em
|
padding-left: 1em
|
||||||
@@ -18,6 +47,7 @@ input[type=text], select, textarea
|
|||||||
bottom: 0
|
bottom: 0
|
||||||
left: 0
|
left: 0
|
||||||
right: 0
|
right: 0
|
||||||
|
z-index: 999
|
||||||
|
|
||||||
@mixin mobile
|
@mixin mobile
|
||||||
@media screen and (max-width: 900px)
|
@media screen and (max-width: 900px)
|
||||||
@@ -50,17 +80,15 @@ body
|
|||||||
background: var(--posbutton-background)
|
background: var(--posbutton-background)
|
||||||
text-shadow: 1px 1px #1f272b
|
text-shadow: 1px 1px #1f272b
|
||||||
border: solid 1px var(--posbutton-border-color)
|
border: solid 1px var(--posbutton-border-color)
|
||||||
|
overflow: hidden
|
||||||
|
cursor: pointer
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
.posHeader
|
.posHeader
|
||||||
padding: 0.5em
|
padding: 0.5em
|
||||||
color: var(--pos-header-text-color)
|
color: var(--pos-header-text-color)
|
||||||
background: var(--pos-header-background)
|
background: var(--pos-header-background)
|
||||||
|
cursor: default
|
||||||
.posButton.active,.posButton:active, .posButton:focus
|
|
||||||
border-color: var(--posbutton-border-color-active)
|
|
||||||
color: var(--posbutton-text-color-active)
|
|
||||||
background: var(--posbutton-background-active) /* webkit */
|
|
||||||
box-shadow: 1px 1px 1px rgba(255,255,255,0.1) /* CSS3 */
|
|
||||||
|
|
||||||
#pageContainer
|
#pageContainer
|
||||||
@include flex
|
@include flex
|
||||||
@@ -69,9 +97,29 @@ body
|
|||||||
.posButton, .posHeader
|
.posButton, .posHeader
|
||||||
@include flex
|
@include flex
|
||||||
text-align: center
|
text-align: center
|
||||||
cursor: default
|
|
||||||
|
|
||||||
.posButton.active, .posButton:active
|
.posButton.active, .posButton:active
|
||||||
border: inset 2px
|
border: inset 2px
|
||||||
|
background: var(--posbutton-background-active)
|
||||||
|
|
||||||
|
.posButton.voidButton
|
||||||
|
background: var(--void-button-background)
|
||||||
|
|
||||||
|
.invisible
|
||||||
|
visibility: hidden
|
||||||
|
|
||||||
|
.hidden
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.pulse
|
||||||
|
animation-name: color
|
||||||
|
animation-duration: 300ms
|
||||||
|
animation-iteration-count: 1
|
||||||
|
|
||||||
|
@keyframes color
|
||||||
|
0%
|
||||||
|
background-color: var(--pulse-first-color)
|
||||||
|
50%
|
||||||
|
background-color: var(--pulse-second-color)
|
||||||
|
100%
|
||||||
|
background-color: var(--pulse-final-color)
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
#floorplanCanvas
|
#floorplanCanvas
|
||||||
aspect-ratio: 1/1
|
aspect-ratio: 1/1
|
||||||
background-repeat: no-repeat
|
background-repeat: no-repeat
|
||||||
|
background: var(--global-secondary-bgcolor)
|
||||||
|
|
||||||
> *:not(#floorplanCanvas)
|
> *:not(#floorplanCanvas)
|
||||||
@include flex-column-item
|
@include flex-column-item
|
||||||
346
sass/dredgepos.orderScreen.sass
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
@import dredgepos.keyboards
|
||||||
|
|
||||||
|
#leftColumn
|
||||||
|
@include flex-column
|
||||||
|
flex-basis: 30%
|
||||||
|
height: 100%
|
||||||
|
background-color: grey
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex-column-item
|
||||||
|
|
||||||
|
.tableHeading
|
||||||
|
@include flex
|
||||||
|
color: black
|
||||||
|
background-color: white
|
||||||
|
flex-basis: 5%
|
||||||
|
|
||||||
|
.tableInfo
|
||||||
|
@include flex
|
||||||
|
flex-basis: 5%
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex-item
|
||||||
|
@include flex
|
||||||
|
|
||||||
|
.orderBox
|
||||||
|
flex-basis: 75%
|
||||||
|
background: var(--global-bgcolor)
|
||||||
|
overflow-y: auto
|
||||||
|
|
||||||
|
.orderBoxInfo
|
||||||
|
@include flex
|
||||||
|
flex-basis: 5%
|
||||||
|
background-color: white
|
||||||
|
|
||||||
|
.voidModeWarning
|
||||||
|
@include flex
|
||||||
|
@include flex-item
|
||||||
|
color: red
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.orderBoxFooter
|
||||||
|
flex-basis: 10%
|
||||||
|
@include flex-column
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex
|
||||||
|
@include flex-column-item
|
||||||
|
justify-content: flex-end
|
||||||
|
padding: 0 0.7em
|
||||||
|
|
||||||
|
> .orderBoxTotal
|
||||||
|
align-items: flex-end
|
||||||
|
font-size: 1.3em
|
||||||
|
> .orderBoxSelectedTotal
|
||||||
|
align-items: flex-start
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
|
#rightColumn
|
||||||
|
@include flex-column
|
||||||
|
height: 100%
|
||||||
|
flex-basis: 70%
|
||||||
|
background-color: var(--global-bgcolor)
|
||||||
|
border-left: 2px solid var(--global-border-color)
|
||||||
|
|
||||||
|
#topHalf
|
||||||
|
@include flex-column
|
||||||
|
@include flex-column-item
|
||||||
|
flex-basis: 30%
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
|
||||||
|
.functionButtons
|
||||||
|
@include flex-column-item
|
||||||
|
@include flex
|
||||||
|
flex-basis: 100%
|
||||||
|
|
||||||
|
> .functionColumn
|
||||||
|
@include flex-item
|
||||||
|
@include flex-column
|
||||||
|
flex-basis: 25%
|
||||||
|
> *
|
||||||
|
@include flex-column-item
|
||||||
|
|
||||||
|
> .printGroupButtons
|
||||||
|
flex-basis: 25%
|
||||||
|
height: 100%
|
||||||
|
display: grid
|
||||||
|
grid-template-columns: repeat(2, 1fr)
|
||||||
|
grid-auto-rows: auto
|
||||||
|
|
||||||
|
> *
|
||||||
|
padding: 0.5em
|
||||||
|
|
||||||
|
|
||||||
|
#pageList
|
||||||
|
@include flex
|
||||||
|
@include flex-column-item
|
||||||
|
flex-basis: 10%
|
||||||
|
background-color: var(--global-secondary-bgcolor)
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex-item
|
||||||
|
border-bottom: solid 2px var(--global-border-color)
|
||||||
|
|
||||||
|
.active
|
||||||
|
border-bottom: none
|
||||||
|
|
||||||
|
#pageGroupContainer
|
||||||
|
@include flex-column
|
||||||
|
@include flex-column-item
|
||||||
|
justify-content: flex-end
|
||||||
|
flex-basis: 45%
|
||||||
|
height: 50%
|
||||||
|
scrollbar-width: none
|
||||||
|
-ms-overflow-style: none
|
||||||
|
|
||||||
|
::-webkit-scrollbar
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.pageGroup
|
||||||
|
/*display: inline-flex*/
|
||||||
|
@include flex-column-item
|
||||||
|
flex-basis: 100%
|
||||||
|
flex-grow: 0
|
||||||
|
overflow-x: auto
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.gridPage
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-grow: 0
|
||||||
|
display: grid
|
||||||
|
|
||||||
|
.doubleWidth
|
||||||
|
width: calc(200%)
|
||||||
|
z-index: 10
|
||||||
|
|
||||||
|
.doubleHeight
|
||||||
|
height: calc(200%)
|
||||||
|
z-index: 10
|
||||||
|
|
||||||
|
.hasImage
|
||||||
|
.buttonImg
|
||||||
|
background-repeat: no-repeat
|
||||||
|
background-size: contain
|
||||||
|
background-position: center
|
||||||
|
background-origin: content-box
|
||||||
|
|
||||||
|
.hasImage.normal
|
||||||
|
font-size: 0.8em
|
||||||
|
.buttonImg
|
||||||
|
flex-basis: 40%
|
||||||
|
height: 100%
|
||||||
|
padding: 0.7em
|
||||||
|
justify-content: flex-end
|
||||||
|
|
||||||
|
.text
|
||||||
|
@include flex
|
||||||
|
justify-content: flex-start
|
||||||
|
flex-basis: 60%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.hasImage.doubleHeight
|
||||||
|
@include flex-column
|
||||||
|
|
||||||
|
.buttonImg
|
||||||
|
padding: 0.6em
|
||||||
|
flex-basis: 65%
|
||||||
|
width: 100%
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-grow: 0
|
||||||
|
|
||||||
|
.text
|
||||||
|
@include flex
|
||||||
|
align-items: flex-start
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-basis: 35%
|
||||||
|
width: 100%
|
||||||
|
overflow: hidden
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
|
.hasImage.doubleWidth
|
||||||
|
flex-direction: row
|
||||||
|
.buttonImg
|
||||||
|
@include flex
|
||||||
|
flex-basis: 30%
|
||||||
|
height: 100%
|
||||||
|
padding-top: 2%
|
||||||
|
|
||||||
|
.text
|
||||||
|
@include flex
|
||||||
|
flex-basis: 70%
|
||||||
|
height: 100%
|
||||||
|
justify-content: flex-start
|
||||||
|
|
||||||
|
.hasImage.doubleHeight.doubleWidth
|
||||||
|
flex-direction: row
|
||||||
|
|
||||||
|
.buttonImg
|
||||||
|
@include flex
|
||||||
|
flex-basis: 50%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
|
||||||
|
.text
|
||||||
|
@include flex
|
||||||
|
flex-basis: 50%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
|
||||||
|
.pageNavigation
|
||||||
|
@include flex
|
||||||
|
@include flex-column-item
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex-item
|
||||||
|
|
||||||
|
|
||||||
|
.coverSelector
|
||||||
|
display: flex
|
||||||
|
flex-wrap: wrap
|
||||||
|
border: 2px solid var(--global-border-color)
|
||||||
|
background: var(--global-bgcolor)
|
||||||
|
position: absolute
|
||||||
|
z-index: 100
|
||||||
|
|
||||||
|
.coverSelectorButton
|
||||||
|
flex-basis: 50%
|
||||||
|
|
||||||
|
.coverSelectorButton:first-of-type
|
||||||
|
flex-basis: 100%
|
||||||
|
|
||||||
|
|
||||||
|
.orderBoxTable
|
||||||
|
width: 100%
|
||||||
|
border-collapse: collapse
|
||||||
|
|
||||||
|
tr
|
||||||
|
background: var(--orderbox-row-background)
|
||||||
|
|
||||||
|
|
||||||
|
.selected
|
||||||
|
background: var(--orderbox-selected-row-background)
|
||||||
|
|
||||||
|
thead tr
|
||||||
|
background: var(--orderbox-header-background)
|
||||||
|
|
||||||
|
th
|
||||||
|
font-weight: normal
|
||||||
|
text-align: center
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
|
||||||
|
tr
|
||||||
|
td, th
|
||||||
|
text-align: center
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
|
td
|
||||||
|
padding: 1em 0.5em
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.itemCell
|
||||||
|
text-align: center
|
||||||
|
width: 60%
|
||||||
|
|
||||||
|
.itemIdCell
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.qtyCell
|
||||||
|
width: 10%
|
||||||
|
|
||||||
|
.printGroupCell
|
||||||
|
width: 20%
|
||||||
|
|
||||||
|
.totalPriceCell
|
||||||
|
width: 10%
|
||||||
|
|
||||||
|
td.itemCell
|
||||||
|
text-align: left
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
|
||||||
|
tr.instructionRow
|
||||||
|
td
|
||||||
|
font-weight: 100
|
||||||
|
td.itemCell
|
||||||
|
padding-left: 2em
|
||||||
|
small
|
||||||
|
font-size: 0
|
||||||
|
|
||||||
|
.qtyCell, .printGroupCell
|
||||||
|
font-size: 0
|
||||||
|
|
||||||
|
.gridContainer
|
||||||
|
background: var(--global-bgcolor)
|
||||||
|
border: var(--global-border-color) solid 2px
|
||||||
|
z-index: 500
|
||||||
|
margin: auto
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
top: 50%
|
||||||
|
transform: translateY(-50%)
|
||||||
|
|
||||||
|
.gridContainerHeader
|
||||||
|
height: 3em
|
||||||
|
width: 100%
|
||||||
|
@include flex
|
||||||
|
background-color: var(--global-secondary-bgcolor)
|
||||||
|
border-bottom: solid 1px var(--global-border-color)
|
||||||
|
|
||||||
|
> *
|
||||||
|
@include flex
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
span
|
||||||
|
flex-basis: 90%
|
||||||
|
.closeGrid
|
||||||
|
flex-basis: 10%
|
||||||
|
|
||||||
|
.gridContainerGrid
|
||||||
|
width: 100%
|
||||||
|
@include flex-column
|
||||||
|
scrollbar-width: none
|
||||||
|
-ms-overflow-style: none
|
||||||
|
|
||||||
|
::-webkit-scrollbar
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.pageGroup
|
||||||
|
display: inline-flex
|
||||||
|
flex-basis: 100%
|
||||||
|
flex-grow: 0
|
||||||
|
overflow-x: auto
|
||||||
|
|
||||||
|
.gridPage
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
flex-shrink: 0
|
||||||
|
flex-grow: 0
|
||||||
|
display: grid
|
||||||
10
sql.log
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
create table if not exists migrations
|
||||||
|
(
|
||||||
|
id serial
|
||||||
|
constraint migrations_pk
|
||||||
|
primary key,
|
||||||
|
"name" varchar(100) not null,
|
||||||
|
"timestamp" int not null
|
||||||
|
);
|
||||||
|
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
"noImplicitAny":true,
|
"noImplicitAny":true,
|
||||||
"removeComments":false,
|
"removeComments":false,
|
||||||
"preserveConstEnums":true,
|
"preserveConstEnums":true,
|
||||||
"outDir":"../js",
|
"outDir":"wwwroot/scripts",
|
||||||
"target":"ES2016",
|
"target":"ESNext",
|
||||||
"sourceMap":true,
|
"sourceMap":true,
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "node"
|
||||||
},
|
},
|
||||||
"include":[
|
"include":[
|
||||||
"*"
|
"typescript"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
let showLoginBox = () => showVirtualNumpad('Enter Login Code', 6, true, false, false, authenticate)
|
let showLoginBox = () => showVirtualNumpad('Enter Login Code', 6, true, false, false, authenticate)
|
||||||
|
|
||||||
let authenticate = (input : string) => {
|
let authenticate = (input : string) => {
|
||||||
let login = ajaxSync('/ajax/authenticateClerk', input)
|
let login = ajaxSync('/login/authenticateClerk', input)
|
||||||
if(login === 'success'){
|
if(login === 'success'){
|
||||||
location.assign('/floorplan')
|
location.assign('/floorplan/')
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
showLoginBox()
|
showLoginBox()
|
||||||
268
typescript/dredgepos.core.ts
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/// <reference path="./typings/currency.d.ts" />
|
||||||
|
|
||||||
|
const Application: ApplicationState = {
|
||||||
|
keyboard: null,
|
||||||
|
mode: [],
|
||||||
|
languageVars: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Parses a language variable. */
|
||||||
|
const lang = (key: string, replacements?: string[] | string) => {
|
||||||
|
let finalValue = Application.languageVars[key] || ''
|
||||||
|
|
||||||
|
if (!replacements) return finalValue
|
||||||
|
if (typeof replacements === 'string') replacements = [replacements]
|
||||||
|
|
||||||
|
replacements.forEach((replacement, index) => {
|
||||||
|
const correctIndex = index + 1
|
||||||
|
finalValue = finalValue.replace(`[${correctIndex}]`, replacement)
|
||||||
|
})
|
||||||
|
|
||||||
|
return finalValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if a variable is defined */
|
||||||
|
const defined = (variable: any) => typeof variable !== 'undefined'
|
||||||
|
|
||||||
|
/** Call an Ajax function asynchronously */
|
||||||
|
const ajax = (endpoint: string, data: any, method = 'POST', successFunction: Function, errorFunction: Function, beforeFunction: any) => {
|
||||||
|
data = (data == null) ? data : JSON.stringify(data)
|
||||||
|
return $.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
method: method,
|
||||||
|
data: data,
|
||||||
|
success: (response: ajaxResult) => {
|
||||||
|
if (successFunction && response.status == 'success')
|
||||||
|
successFunction(JSON.parse(response.data))
|
||||||
|
else if (errorFunction && response.status != 'success') {
|
||||||
|
errorFunction(JSON.parse(response.data))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (error) => console.log(error.statusCode),
|
||||||
|
beforeSend: beforeFunction
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
For the flow of the app, synchronous is commonly preferred
|
||||||
|
though trying to keep its usage as low as possible.
|
||||||
|
*/
|
||||||
|
const ajaxSync = (endpoint: string, data?: any, method = 'POST') => {
|
||||||
|
const response = JSON.parse(
|
||||||
|
$.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
method: method,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
async: false,
|
||||||
|
}).responseText)
|
||||||
|
|
||||||
|
if (response.data) {
|
||||||
|
response.data = JSON.parse(response.data)
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Redirect to a specific URL */
|
||||||
|
const redirect = (url: string): void => location.assign(url)
|
||||||
|
|
||||||
|
const resize = () => $('#pageContainer').height(window.innerHeight + "px");
|
||||||
|
|
||||||
|
const setupCore = (languageVars: Record<string, string>) => {
|
||||||
|
Application.languageVars = languageVars
|
||||||
|
$(document)
|
||||||
|
.on('click', '#alertNo, #alertOk', hideAlerts)
|
||||||
|
.on('click', '.toggle', toggle)
|
||||||
|
.on('click', '.hideModals', hideModals)
|
||||||
|
|
||||||
|
window.addEventListener('resize', resize)
|
||||||
|
resize()
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const posAlert = (message: string, title = 'Message') => {
|
||||||
|
const alertBox = $('#alert')
|
||||||
|
alertBox.css('display', 'flex');
|
||||||
|
alertBox.data('value', '');
|
||||||
|
$('#alertHeading').text(title);
|
||||||
|
$('#alertMessage').text(message);
|
||||||
|
|
||||||
|
$('#alertOk').css('display', 'flex');
|
||||||
|
$('#alertYes').css('display', 'none');
|
||||||
|
$('#alertNo').css('display', 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmation = (message: string, data: any, title = 'Confirm', submitFunction = (data: any) => {hideAlerts()}) => {
|
||||||
|
const alert = $('#alert')
|
||||||
|
|
||||||
|
$(document).on('click', '#alert #alertYes', () => {
|
||||||
|
hideAlerts()
|
||||||
|
submitFunction(data)
|
||||||
|
$(document).off('click', '#alert #alertYes')
|
||||||
|
})
|
||||||
|
|
||||||
|
alert.css('display', 'flex')
|
||||||
|
$('#alertHeading').html(title)
|
||||||
|
$('#alertMessage').html(message)
|
||||||
|
|
||||||
|
$('#alertOk').css('display', 'none')
|
||||||
|
$('#alertYes').css('display', 'flex')
|
||||||
|
$('#alertNo').css('display', 'flex')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const hideAlerts = () => $('#alert').hide()
|
||||||
|
const hideModals = () => $('.modal').hide()
|
||||||
|
|
||||||
|
const turnOnMode = (mode: PosMode) => {
|
||||||
|
Application.mode.push(mode)
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnOffMode = (mode: PosMode) => {
|
||||||
|
Application.mode = Application.mode.filter((value) => value != mode)
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleMode = (mode: PosMode) => {
|
||||||
|
if (!isInMode(mode))
|
||||||
|
turnOnMode(mode)
|
||||||
|
else
|
||||||
|
turnOffMode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearModes = () => {
|
||||||
|
Application.mode = []
|
||||||
|
setElementVisibilityByMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInMode = (mode: PosMode) => Application.mode.includes(mode)
|
||||||
|
|
||||||
|
const setElementVisibilityByMode = () => {
|
||||||
|
const mode = Application.mode
|
||||||
|
const elements = $('[data-visible-in-mode]')
|
||||||
|
|
||||||
|
elements.each((index, elem) => {
|
||||||
|
const element = $(elem)
|
||||||
|
const visibleInModes: PosModes = element.data('visible-in-mode')
|
||||||
|
|
||||||
|
const showElement = visibleInModes.every(visibleMode => {
|
||||||
|
return mode.includes(visibleMode)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (element.hasClass('useVisibility')) {
|
||||||
|
if (showElement) {
|
||||||
|
element.css('visibility', 'visible')
|
||||||
|
} else element.css('visibility', 'hidden')
|
||||||
|
} else element.toggle(showElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
const invisibleElements = $('[data-invisible-in-mode]')
|
||||||
|
invisibleElements.each((index, elem) => {
|
||||||
|
const element = $(elem)
|
||||||
|
const inVisibleInModes: PosModes = element.data('invisible-in-mode')
|
||||||
|
const hideElement = inVisibleInModes.some(invisibleMode => {
|
||||||
|
return mode.includes(invisibleMode)
|
||||||
|
})
|
||||||
|
element.toggle(!hideElement)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
$('[data-active-in-mode]').each((index, elem) => {
|
||||||
|
const button = $(elem)
|
||||||
|
const activeInMode: PosMode = button.data('active-in-mode')
|
||||||
|
|
||||||
|
mode.includes(activeInMode)
|
||||||
|
? button.addClass('active')
|
||||||
|
: button.removeClass('active')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPercentageOfPageContainerWidth = (pixels: number) => ( (pixels / $('#pageContainer').width()) * 100) + '%'
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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 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 button = $(e.target)
|
||||||
|
const toggleGroup = button.closest('.toggleGroup')
|
||||||
|
const input = toggleGroup.find('input.value')
|
||||||
|
const isActive = button.hasClass('active')
|
||||||
|
toggleGroup
|
||||||
|
.find('.toggle')
|
||||||
|
.removeClass('active')
|
||||||
|
|
||||||
|
let value = !isActive
|
||||||
|
? button.addClass('active').data('value')
|
||||||
|
: toggleGroup
|
||||||
|
.find('.toggle.default')
|
||||||
|
.addClass('active')
|
||||||
|
.data('value')
|
||||||
|
|
||||||
|
input.val(value).trigger('change')
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetToggle = (input: JQuery) => {
|
||||||
|
input
|
||||||
|
.closest('.toggleGroup')
|
||||||
|
.find('.toggle.default')
|
||||||
|
.trigger('click')
|
||||||
|
}
|
||||||
|
//Id generator.
|
||||||
|
function* newestId(){
|
||||||
|
let id = 0
|
||||||
|
while(true){
|
||||||
|
id++
|
||||||
|
yield id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadTemplate = (templateSelector: string) => {
|
||||||
|
const content = $(templateSelector)
|
||||||
|
.clone()
|
||||||
|
.removeAttr('id')
|
||||||
|
.prop('content')
|
||||||
|
return $(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(() => ajax('/ajax/languageVars', null, 'GET', setupCore, null, null))
|
||||||
@@ -10,8 +10,8 @@ interface floorplan{
|
|||||||
transformer: Konva.Transformer
|
transformer: Konva.Transformer
|
||||||
tableLayer: Konva.Layer
|
tableLayer: Konva.Layer
|
||||||
rooms: room[]
|
rooms: room[]
|
||||||
tables: table[]
|
tables: floorplan_table[]
|
||||||
decorations: decoration[]
|
decorations: floorplan_decoration[]
|
||||||
activeTableNumbers: number[]
|
activeTableNumbers: number[]
|
||||||
selectedTableNumber: number
|
selectedTableNumber: number
|
||||||
selectedDecorationId: number
|
selectedDecorationId: number
|
||||||
@@ -24,8 +24,8 @@ interface floorplan{
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface floorplan_data{
|
interface floorplan_data{
|
||||||
tables: table[]
|
tables: floorplan_table[]
|
||||||
decorations: decoration[]
|
decorations: floorplan_decoration[]
|
||||||
activeTableNumbers: number[]
|
activeTableNumbers: number[]
|
||||||
rooms: room[]
|
rooms: room[]
|
||||||
reservations:reservation[]
|
reservations:reservation[]
|
||||||
@@ -50,7 +50,7 @@ const Floorplan: floorplan = {
|
|||||||
selectedDecorationId: 0
|
selectedDecorationId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
$(() => ajax('/ajax/getFloorplanData/1', null, 'get', setupFloorplan, null, null) )
|
$(() => ajax('/floorplan/getFloorplanData/1', null, 'get', setupFloorplan, null, null) )
|
||||||
|
|
||||||
|
|
||||||
const setupFloorplanEvents = () => {
|
const setupFloorplanEvents = () => {
|
||||||
@@ -68,6 +68,11 @@ const setupFloorplanEvents = () => {
|
|||||||
doc.on('click', '.transferTableButton', toggleTransferMode)
|
doc.on('click', '.transferTableButton', toggleTransferMode)
|
||||||
doc.on('click', '.reserveTableButton', reserveTable)
|
doc.on('click', '.reserveTableButton', reserveTable)
|
||||||
doc.on('click', '.unreserveTableButton', unreserveTable)
|
doc.on('click', '.unreserveTableButton', unreserveTable)
|
||||||
|
doc.on('click', '.placeOrderButton', placeOrderButtonClicked)
|
||||||
|
}
|
||||||
|
|
||||||
|
const placeOrderButtonClicked = () => {
|
||||||
|
redirect(`/order/${Floorplan.selectedTableNumber}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const roomButtonClicked = (e: Event) => {
|
const roomButtonClicked = (e: Event) => {
|
||||||
@@ -113,11 +118,11 @@ const loadRoom = (roomToLoad: room) => {
|
|||||||
setupKonva()
|
setupKonva()
|
||||||
|
|
||||||
$('.roomButton').removeClass('active')
|
$('.roomButton').removeClass('active')
|
||||||
let button = $(`.roomButton[data-value=${roomToLoad.id}]`)
|
let button = $(`.roomButton[data-value=${roomToLoad?.id}]`)
|
||||||
button.addClass('active')
|
button.addClass('active')
|
||||||
|
|
||||||
const tablesInRoom = Floorplan.tables.filter(table => table.room_id == roomToLoad.id)
|
const tablesInRoom = Floorplan.tables.filter(table => table.room_id == roomToLoad.id)
|
||||||
const decorationsInRoom = Floorplan.decorations.filter(decoration => decoration.decoration_room == roomToLoad.id)
|
const decorationsInRoom = Floorplan.decorations.filter(decoration => decoration.room_id == roomToLoad.id)
|
||||||
decorationsInRoom.forEach(decoration => createDecorationShape(decoration, false))
|
decorationsInRoom.forEach(decoration => createDecorationShape(decoration, false))
|
||||||
tablesInRoom.forEach(createTableShape)
|
tablesInRoom.forEach(createTableShape)
|
||||||
if(!isInMode('transfer')) {
|
if(!isInMode('transfer')) {
|
||||||
@@ -132,9 +137,9 @@ const getRoomById = (roomId: number) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableIsOpen = (table: table) => Floorplan.activeTableNumbers.includes(table.table_number)
|
const tableIsOpen = (table: floorplan_table) => Floorplan.activeTableNumbers.includes(table.table_number)
|
||||||
|
|
||||||
const createTableShape = (table: table) => {
|
const createTableShape = (table: floorplan_table) => {
|
||||||
const draggable = isInMode('edit')
|
const draggable = isInMode('edit')
|
||||||
|
|
||||||
const tableGroup = new Konva.Group({
|
const tableGroup = new Konva.Group({
|
||||||
@@ -168,7 +173,7 @@ const createTableShape = (table: table) => {
|
|||||||
stroke: "black",
|
stroke: "black",
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
draggable: false,
|
draggable: false,
|
||||||
listening: true
|
listening: true,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -216,6 +221,8 @@ const setupTableEvents = (tableGroup: Konva.Group) => {
|
|||||||
|
|
||||||
tableGroup.on('click', tableClicked)
|
tableGroup.on('click', tableClicked)
|
||||||
tableGroup.on('tap', tableClicked)
|
tableGroup.on('tap', tableClicked)
|
||||||
|
tableGroup.on('dbltap', tableDblClicked)
|
||||||
|
tableGroup.on('dblclick', tableDblClicked)
|
||||||
tableGroup.on('dragend', tableGroupTransformed)
|
tableGroup.on('dragend', tableGroupTransformed)
|
||||||
tableShape.on('transformend', tableShapeTransformed)
|
tableShape.on('transformend', tableShapeTransformed)
|
||||||
}
|
}
|
||||||
@@ -236,7 +243,7 @@ const saveTableTransformation = (tableGroup: Konva.Group) => {
|
|||||||
const originalTable = getTableDataFromGroup(tableGroup)
|
const originalTable = getTableDataFromGroup(tableGroup)
|
||||||
const tableShape = getTableShapeFromGroup(tableGroup)
|
const tableShape = getTableShapeFromGroup(tableGroup)
|
||||||
|
|
||||||
const newTableInfo : table = {
|
const newTableInfo : floorplan_table = {
|
||||||
table_number : originalTable.table_number,
|
table_number : originalTable.table_number,
|
||||||
previous_state : originalTable.previous_state,
|
previous_state : originalTable.previous_state,
|
||||||
merged_children : originalTable.merged_children,
|
merged_children : originalTable.merged_children,
|
||||||
@@ -258,7 +265,7 @@ const saveTableTransformation = (tableGroup: Konva.Group) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const saveTable = (tableToUpdate: table) => {
|
const saveTable = (tableToUpdate: floorplan_table) => {
|
||||||
const tables =
|
const tables =
|
||||||
Floorplan
|
Floorplan
|
||||||
.tables
|
.tables
|
||||||
@@ -269,7 +276,7 @@ const saveTable = (tableToUpdate: table) => {
|
|||||||
tables.push(tableToUpdate)
|
tables.push(tableToUpdate)
|
||||||
|
|
||||||
Floorplan.tables = tables
|
Floorplan.tables = tables
|
||||||
ajax("/ajax/transformTable", tableToUpdate, 'post', null,null,null)
|
ajax("/floorplan/transformTable", tableToUpdate, 'post', null,null,null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setTransformerNodes = (nodes: Konva.Shape[]) => {
|
const setTransformerNodes = (nodes: Konva.Shape[]) => {
|
||||||
@@ -302,20 +309,20 @@ const getTableGroupFromTableNumber = (tableNumber : number) => {
|
|||||||
return getTableGroupFromShape(tableShape)
|
return getTableGroupFromShape(tableShape)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setReservationStatus = (table: table) => {
|
const setReservationStatus = (table: floorplan_table) => {
|
||||||
const reservationText = $('.reservationStatus')
|
const reservationText = $('.reservationStatus')
|
||||||
const tableShape = getTableShapeFromTableNumber(table.table_number)
|
const tableShape = getTableShapeFromTableNumber(table.table_number)
|
||||||
reservationText.text('')
|
reservationText.text('')
|
||||||
|
|
||||||
if(table.status == 2) {
|
if(table.status == 2) {
|
||||||
tableShape.fill('lightgreen')
|
tableShape.fill('lightgreen')
|
||||||
const reservations = Floorplan.reservations.filter(reservation => reservation.reservation_table_id == table.id)
|
const reservations = Floorplan.reservations.filter(reservation => reservation.floorplan_table_id == table.id)
|
||||||
if (reservations.length) {
|
if (reservations.length) {
|
||||||
turnOnMode('reservedTableSelected')
|
turnOnMode('reservedTableSelected')
|
||||||
reservationText.text(lang('reserved'))
|
reservationText.text(lang('reserved'))
|
||||||
let reservation = reservations[0]
|
let reservation = reservations[0]
|
||||||
if (reservation.reservation_name != '') {
|
if (reservation.name != '') {
|
||||||
reservationText.text(lang('reserved_for', reservation.reservation_name))
|
reservationText.text(lang('reserved_for', reservation.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -333,21 +340,21 @@ const reserveTable = () => {
|
|||||||
const createEmptyReservation = (covers: number) => {
|
const createEmptyReservation = (covers: number) => {
|
||||||
const newReservation: reservation = {
|
const newReservation: reservation = {
|
||||||
id: 0,
|
id: 0,
|
||||||
reservation_covers: covers,
|
covers: covers,
|
||||||
reservation_created_at: 0,
|
created_at: 0,
|
||||||
reservation_table_id: getSelectedTableData().id,
|
floorplan_table_id: getSelectedTableData().id,
|
||||||
reservation_name: '',
|
name: '',
|
||||||
reservation_time: 0,
|
time: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
ajax('/ajax/newEmptyReservation', newReservation,'post', emptyReservationCreated, null, null )
|
ajax('/reservations/newEmptyReservation', newReservation,'post', emptyReservationCreated, null, null )
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyReservationCreated = (reservation: reservation) => {
|
const emptyReservationCreated = (reservation: reservation) => {
|
||||||
Floorplan.reservations.push(reservation)
|
Floorplan.reservations.push(reservation)
|
||||||
const selectedTable = getSelectedTableData()
|
const selectedTable = getSelectedTableData()
|
||||||
selectedTable.status = 2
|
selectedTable.status = 2
|
||||||
selectedTable.default_covers = reservation.reservation_covers
|
selectedTable.default_covers = reservation.covers
|
||||||
updateTableData(selectedTable)
|
updateTableData(selectedTable)
|
||||||
updateCoverText(selectedTable)
|
updateCoverText(selectedTable)
|
||||||
setReservationStatus(getSelectedTableData())
|
setReservationStatus(getSelectedTableData())
|
||||||
@@ -357,9 +364,9 @@ const emptyReservationCreated = (reservation: reservation) => {
|
|||||||
|
|
||||||
const addReservationName = (name: string) => {
|
const addReservationName = (name: string) => {
|
||||||
hideVirtualKeyboard()
|
hideVirtualKeyboard()
|
||||||
const reservation = Floorplan.reservations.filter(reservation => reservation.reservation_table_id == getSelectedTableData().id)[0]
|
const reservation = Floorplan.reservations.filter(reservation => reservation.floorplan_table_id == getSelectedTableData().id)[0]
|
||||||
reservation.reservation_name = name
|
reservation.name = name
|
||||||
ajax('/ajax/updateReservation', reservation, 'post', reservationNameAdded, null, null)
|
ajax('/reservations/updateReservation', reservation, 'post', reservationNameAdded, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reservationNameAdded = (updatedReservation: reservation) => {
|
const reservationNameAdded = (updatedReservation: reservation) => {
|
||||||
@@ -369,9 +376,9 @@ const reservationNameAdded = (updatedReservation: reservation) => {
|
|||||||
setReservationStatus(getSelectedTableData())
|
setReservationStatus(getSelectedTableData())
|
||||||
}
|
}
|
||||||
|
|
||||||
const getReservationsOnTable = (table: table) => Floorplan.reservations.filter(reservation => reservation.reservation_table_id == table.id)
|
const getReservationsOnTable = (table: floorplan_table) => Floorplan.reservations.filter(reservation => reservation.floorplan_table_id == table.id)
|
||||||
|
|
||||||
const updateTableData = (tableToRemove: table) => {
|
const updateTableData = (tableToRemove: floorplan_table) => {
|
||||||
Floorplan.tables = Floorplan.tables.filter(table => table.id != tableToRemove.id)
|
Floorplan.tables = Floorplan.tables.filter(table => table.id != tableToRemove.id)
|
||||||
Floorplan.tables.push(tableToRemove)
|
Floorplan.tables.push(tableToRemove)
|
||||||
}
|
}
|
||||||
@@ -379,11 +386,11 @@ const updateTableData = (tableToRemove: table) => {
|
|||||||
const unreserveTable = () => {
|
const unreserveTable = () => {
|
||||||
const selectedTable = getSelectedTableData()
|
const selectedTable = getSelectedTableData()
|
||||||
selectedTable.status = 0
|
selectedTable.status = 0
|
||||||
ajax('/ajax/unreserveTable', selectedTable, 'post', tableUnreserved, null, null)
|
ajax('/reservations/unreserveTable', selectedTable, 'post', tableUnreserved, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableUnreserved = (table: table) => {
|
const tableUnreserved = (table: floorplan_table) => {
|
||||||
Floorplan.reservations = Floorplan.reservations.filter(reservation => reservation.reservation_table_id != table.id)
|
Floorplan.reservations = Floorplan.reservations.filter(reservation => reservation.floorplan_table_id != table.id)
|
||||||
updateTableData(table)
|
updateTableData(table)
|
||||||
setReservationStatus(table)
|
setReservationStatus(table)
|
||||||
}
|
}
|
||||||
@@ -391,7 +398,7 @@ const tableUnreserved = (table: table) => {
|
|||||||
const getSelectedTableData = () => getTableDataFromTableNumber(Floorplan.selectedTableNumber)
|
const getSelectedTableData = () => getTableDataFromTableNumber(Floorplan.selectedTableNumber)
|
||||||
|
|
||||||
const deselectTables = () => {
|
const deselectTables = () => {
|
||||||
Floorplan.stage.find('Rect, Ellipse').forEach( (shape: Konva.Shape, index) => {
|
Floorplan.stage.find('Rect, Ellipse').forEach( (shape: Konva.Shape) => {
|
||||||
shape.stroke('black')
|
shape.stroke('black')
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -431,9 +438,16 @@ const selectTable = (tableShape: Konva.Shape) => {
|
|||||||
turnOnMode('tableSelected')
|
turnOnMode('tableSelected')
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCoverText = (table:table) => $('.selectedTableCovers').text(lang('covers', table.default_covers.toString()))
|
const updateCoverText = (table:floorplan_table) => $('.selectedTableCovers').text(lang('covers', table.default_covers.toString()))
|
||||||
|
|
||||||
const tableClicked = (event: Konva.KonvaEventObject<any>) => {
|
const tableDblClicked = (event: Konva.KonvaEventObject<any>) => {
|
||||||
|
let tableShape = getTableShapeFromGroup(event.currentTarget as Konva.Group)
|
||||||
|
const table = getTableDataFromShape(tableShape)
|
||||||
|
redirect(`/order/${table.table_number}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const tableClicked = (event: Konva.KonvaEventObject<any>) => {
|
||||||
let tableShape = getTableShapeFromGroup(event.currentTarget as Konva.Group)
|
let tableShape = getTableShapeFromGroup(event.currentTarget as Konva.Group)
|
||||||
const table = getTableDataFromShape(tableShape)
|
const table = getTableDataFromShape(tableShape)
|
||||||
|
|
||||||
@@ -455,21 +469,21 @@ const tableClicked = (event: Konva.KonvaEventObject<any>) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createDecorationShape = (decoration:decoration, select?: boolean) => {
|
const createDecorationShape = (decoration:floorplan_decoration, select?: boolean) => {
|
||||||
const draggable = isInMode('edit')
|
const draggable = isInMode('edit')
|
||||||
const decorationShape = new Image()
|
const decorationShape = new Image()
|
||||||
|
|
||||||
decorationShape.onload = () => {
|
decorationShape.onload = () => {
|
||||||
const decorationImage = new Konva.Image({
|
const decorationImage = new Konva.Image({
|
||||||
id: decoration.id.toString(),
|
id: decoration.id.toString(),
|
||||||
x: decoration.decoration_pos_x * Floorplan.visualScale,
|
x: decoration.pos_x * Floorplan.visualScale,
|
||||||
y: decoration.decoration_pos_y * Floorplan.visualScale,
|
y: decoration.pos_y * Floorplan.visualScale,
|
||||||
image: decorationShape,
|
image: decorationShape,
|
||||||
offsetX: decoration.decoration_width * 0.5 * Floorplan.visualScale,
|
offsetX: decoration.width * 0.5 * Floorplan.visualScale,
|
||||||
offsetY: decoration.decoration_height * 0.5 * Floorplan.visualScale,
|
offsetY: decoration.height * 0.5 * Floorplan.visualScale,
|
||||||
rotation: decoration.decoration_rotation,
|
rotation: decoration.rotation,
|
||||||
width: decoration.decoration_width * Floorplan.visualScale,
|
width: decoration.width * Floorplan.visualScale,
|
||||||
height: decoration.decoration_height * Floorplan.visualScale,
|
height: decoration.height * Floorplan.visualScale,
|
||||||
draggable: draggable,
|
draggable: draggable,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -486,7 +500,7 @@ const createDecorationShape = (decoration:decoration, select?: boolean) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decorationShape.src = 'images/decorations/' + decoration.decoration_image
|
decorationShape.src = '/images/decorations/' + decoration.image
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupDecorationEvents = (decorationShape: Konva.Image) => {
|
const setupDecorationEvents = (decorationShape: Konva.Image) => {
|
||||||
@@ -500,12 +514,14 @@ const decorationClicked = (event: Konva.KonvaEventObject<any>) => {
|
|||||||
let decorationShape = event.target as Konva.Image
|
let decorationShape = event.target as Konva.Image
|
||||||
if(isInMode('edit')){
|
if(isInMode('edit')){
|
||||||
turnOffMode('tableSelected')
|
turnOffMode('tableSelected')
|
||||||
if ((Floorplan.transformer.nodes().length > 0 && Floorplan.transformer.nodes()[0] != decorationShape) || Floorplan.transformer.nodes().length == 0) {
|
if ((isInMode('decorationSelected') && Floorplan.selectedDecorationId != Number(decorationShape.id())) || !isInMode('decorationSelected')) {
|
||||||
selectDecorationShape(decorationShape)
|
selectDecorationShape(decorationShape)
|
||||||
} else {
|
} else {
|
||||||
deselectTables()
|
deselectTables()
|
||||||
decorationShape.moveToBottom()
|
decorationShape.moveToBottom()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
deselectTables()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,21 +541,22 @@ const getDecorationDataById = (id: number) => {
|
|||||||
const decorationTransformed = (event: Konva.KonvaEventObject<MouseEvent>|Konva.KonvaEventObject<TouchEvent|DragEvent|MouseEvent>) => {
|
const decorationTransformed = (event: Konva.KonvaEventObject<MouseEvent>|Konva.KonvaEventObject<TouchEvent|DragEvent|MouseEvent>) => {
|
||||||
let decorationShape = event.currentTarget as Konva.Image
|
let decorationShape = event.currentTarget as Konva.Image
|
||||||
const oldDecorationData = getDecorationDataById(Number(decorationShape.id()))
|
const oldDecorationData = getDecorationDataById(Number(decorationShape.id()))
|
||||||
const newDecoration: decoration = {
|
const newDecoration: floorplan_decoration = {
|
||||||
id: oldDecorationData.id,
|
id: oldDecorationData.id,
|
||||||
decoration_room: oldDecorationData.decoration_room,
|
room_id: oldDecorationData.room_id,
|
||||||
decoration_pos_x: Math.round(decorationShape.x() / Floorplan.visualScale),
|
pos_x: Math.round(decorationShape.x() / Floorplan.visualScale),
|
||||||
decoration_pos_y: Math.round(decorationShape.y() / Floorplan.visualScale),
|
pos_y: Math.round(decorationShape.y() / Floorplan.visualScale),
|
||||||
decoration_rotation: Math.round(decorationShape.rotation()),
|
rotation: Math.round(decorationShape.rotation()),
|
||||||
decoration_width: Math.round((decorationShape.scaleX() * decorationShape.width()) / Floorplan.visualScale),
|
width: Math.round((decorationShape.scaleX() * decorationShape.width()) / Floorplan.visualScale),
|
||||||
decoration_height: Math.round((decorationShape.scaleY() * decorationShape.height()) / Floorplan.visualScale),
|
height: Math.round((decorationShape.scaleY() * decorationShape.height()) / Floorplan.visualScale),
|
||||||
decoration_image: oldDecorationData.decoration_image,
|
image: oldDecorationData.image,
|
||||||
|
venue_id: oldDecorationData.venue_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
saveDecoration(newDecoration)
|
saveDecoration(newDecoration)
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveDecoration = (decorationToUpdate: decoration) => {
|
const saveDecoration = (decorationToUpdate: floorplan_decoration) => {
|
||||||
const decorations =
|
const decorations =
|
||||||
Floorplan
|
Floorplan
|
||||||
.decorations
|
.decorations
|
||||||
@@ -550,7 +567,7 @@ const saveDecoration = (decorationToUpdate: decoration) => {
|
|||||||
decorations.push(decorationToUpdate)
|
decorations.push(decorationToUpdate)
|
||||||
|
|
||||||
Floorplan.decorations = decorations
|
Floorplan.decorations = decorations
|
||||||
ajax("/ajax/updateDecoration", decorationToUpdate, 'post', null,null,null)
|
ajax("/floorplan/updateDecoration", decorationToUpdate, 'post', null,null,null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const showDecorator = () => $('#decorator').css('display', 'flex')
|
const showDecorator = () => $('#decorator').css('display', 'flex')
|
||||||
@@ -559,21 +576,22 @@ const hideDecorator = () => $('#decorator').css('display', 'flex').hide()
|
|||||||
const addDecoration = (e: Event) => {
|
const addDecoration = (e: Event) => {
|
||||||
const button = $(e.currentTarget)
|
const button = $(e.currentTarget)
|
||||||
|
|
||||||
const newDecoration: decoration = {
|
const newDecoration: floorplan_decoration = {
|
||||||
id: 0,
|
id: 0,
|
||||||
decoration_room: Floorplan.currentRoom.id,
|
room_id: Floorplan.currentRoom.id,
|
||||||
decoration_pos_x: Floorplan.visualScaleBasis / 2,
|
pos_x: Floorplan.visualScaleBasis / 2,
|
||||||
decoration_pos_y: Floorplan.visualScaleBasis / 2,
|
pos_y: Floorplan.visualScaleBasis / 2,
|
||||||
decoration_rotation: 0,
|
rotation: 0,
|
||||||
decoration_width: 200,
|
width: 200,
|
||||||
decoration_height: 200,
|
height: 200,
|
||||||
decoration_image: button.data('image')
|
image: button.data('image'),
|
||||||
|
venue_id: Floorplan.currentRoom.venue_id
|
||||||
}
|
}
|
||||||
|
|
||||||
ajax('/ajax/addDecoration', newDecoration, 'post', decorationAdded, null, null)
|
ajax('/floorplan/addDecoration', newDecoration, 'post', decorationAdded, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const decorationAdded = (decoration: decoration) => {
|
const decorationAdded = (decoration: floorplan_decoration) => {
|
||||||
Floorplan.decorations.push(decoration)
|
Floorplan.decorations.push(decoration)
|
||||||
createDecorationShape(decoration, true)
|
createDecorationShape(decoration, true)
|
||||||
|
|
||||||
@@ -582,11 +600,11 @@ const decorationAdded = (decoration: decoration) => {
|
|||||||
|
|
||||||
|
|
||||||
const deleteDecoration = () => ajax(
|
const deleteDecoration = () => ajax(
|
||||||
'/ajax/deleteDecoration',
|
'/floorplan/deleteDecoration',
|
||||||
getDecorationDataById(Floorplan.selectedDecorationId),
|
getDecorationDataById(Floorplan.selectedDecorationId),
|
||||||
'post', decorationDeleted, null, null)
|
'post', decorationDeleted, null, null)
|
||||||
|
|
||||||
const decorationDeleted = (deletedDecoration:decoration) => {
|
const decorationDeleted = (deletedDecoration:floorplan_decoration) => {
|
||||||
Floorplan.decorations = Floorplan.decorations.filter(decoration => decoration.id != deletedDecoration.id)
|
Floorplan.decorations = Floorplan.decorations.filter(decoration => decoration.id != deletedDecoration.id)
|
||||||
const decorationShape = Floorplan.stage.findOne(`#${deletedDecoration.id}`)
|
const decorationShape = Floorplan.stage.findOne(`#${deletedDecoration.id}`)
|
||||||
decorationShape.destroy()
|
decorationShape.destroy()
|
||||||
@@ -597,8 +615,12 @@ const setRoomBackground = (roomToLoad: room) => {
|
|||||||
const width = Floorplan.floorplanDiv.width()
|
const width = Floorplan.floorplanDiv.width()
|
||||||
const height = Floorplan.floorplanDiv.height()
|
const height = Floorplan.floorplanDiv.height()
|
||||||
|
|
||||||
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`)
|
Floorplan.floorplanDiv.css("background-size", `${width}px ${height}px`)
|
||||||
|
} else {
|
||||||
|
Floorplan.floorplanDiv.css("background-image", "none")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupKonva = () => {
|
const setupKonva = () => {
|
||||||
@@ -612,11 +634,14 @@ const setupKonva = () => {
|
|||||||
height: dimensions.height,
|
height: dimensions.height,
|
||||||
})
|
})
|
||||||
|
|
||||||
Floorplan.stage.on('click', e => {
|
const stageClick = (e: Konva.KonvaEventObject<any> ) => {
|
||||||
if(e.target == Floorplan.stage){
|
if(e.target == Floorplan.stage){
|
||||||
deselectTables()
|
deselectTables()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
Floorplan.stage.on('click', stageClick)
|
||||||
|
Floorplan.stage.on('tap', stageClick)
|
||||||
|
|
||||||
Floorplan.transformer = new Konva.Transformer({
|
Floorplan.transformer = new Konva.Transformer({
|
||||||
rotationSnaps: [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 225, 270, -15, -30, -45, -60, -75, -90, -105, -120, -135, -150, -165, -180, -225, -270, 360, -360],
|
rotationSnaps: [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 225, 270, -15, -30, -45, -60, -75, -90, -105, -120, -135, -150, -165, -180, -225, -270, 360, -360],
|
||||||
@@ -695,7 +720,7 @@ const redrawTable = (tableGroup: Konva.Group) => {
|
|||||||
const showAddTablePopup = () => showVirtualNumpad(lang('new_table_number'), 4, false, false, true, addTable);
|
const showAddTablePopup = () => showVirtualNumpad(lang('new_table_number'), 4, false, false, true, addTable);
|
||||||
|
|
||||||
const addTable = (tableNumber: number) => {
|
const addTable = (tableNumber: number) => {
|
||||||
const newTable : table = {
|
const newTable : floorplan_table = {
|
||||||
id: 0,
|
id: 0,
|
||||||
table_number: tableNumber,
|
table_number: tableNumber,
|
||||||
room_id: Floorplan.currentRoom.id,
|
room_id: Floorplan.currentRoom.id,
|
||||||
@@ -712,10 +737,10 @@ const addTable = (tableNumber: number) => {
|
|||||||
venue_id: 1
|
venue_id: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
ajax('/ajax/createTable', newTable, 'post', tableAdded, tableNotAdded, null)
|
ajax('/floorplan/createTable', newTable, 'post', tableAdded, tableNotAdded, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableAdded = (table: table) => {
|
const tableAdded = (table: floorplan_table) => {
|
||||||
deselectTables()
|
deselectTables()
|
||||||
const newTableGroup = createTableShape(table)
|
const newTableGroup = createTableShape(table)
|
||||||
Floorplan.tables.push(table)
|
Floorplan.tables.push(table)
|
||||||
@@ -740,10 +765,10 @@ const deleteTable = (tableNumber: number) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ajax(`/ajax/deleteTable`, tableToDelete, 'post', tableDeleted, null, null);
|
ajax(`/floorplan/deleteTable`, tableToDelete, 'post', tableDeleted, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableDeleted = (deletedTable: table) => {
|
const tableDeleted = (deletedTable: floorplan_table) => {
|
||||||
Floorplan.tables = Floorplan.tables.filter(table => table.table_number != deletedTable.table_number)
|
Floorplan.tables = Floorplan.tables.filter(table => table.table_number != deletedTable.table_number)
|
||||||
const tableGroup = getTableGroupFromTableNumber(deletedTable.table_number)
|
const tableGroup = getTableGroupFromTableNumber(deletedTable.table_number)
|
||||||
deselectTables()
|
deselectTables()
|
||||||
@@ -753,16 +778,16 @@ const tableDeleted = (deletedTable: table) => {
|
|||||||
const toggleMergeMode = () => toggleMode('merge')
|
const toggleMergeMode = () => toggleMode('merge')
|
||||||
|
|
||||||
|
|
||||||
const mergeTables = (table1: table, table2: table ) => {
|
const mergeTables = (table1: floorplan_table, table2: floorplan_table ) => {
|
||||||
toggleMergeMode()
|
toggleMergeMode()
|
||||||
if(table1.table_number == table2.table_number){
|
if(table1.table_number == table2.table_number){
|
||||||
posAlert(lang('error_self_merge'))
|
posAlert(lang('error_self_merge'))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ajax('/ajax/mergeTables', [table1, table2], 'post', tablesMerged, null, null)
|
ajax('/floorplan/mergeTables', [table1, table2], 'post', tablesMerged, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tablesMerged = (tables: Record<'child'|'parent'|'merged', table>) => {
|
const tablesMerged = (tables: Record<'child'|'parent'|'merged', floorplan_table>) => {
|
||||||
tableDeleted(tables['child'])
|
tableDeleted(tables['child'])
|
||||||
tableDeleted(tables['parent'])
|
tableDeleted(tables['parent'])
|
||||||
tableAdded(tables['merged'])
|
tableAdded(tables['merged'])
|
||||||
@@ -772,9 +797,9 @@ const tablesMerged = (tables: Record<'child'|'parent'|'merged', table>) => {
|
|||||||
tableGroup.draggable(true)
|
tableGroup.draggable(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const unmergeTable = () => ajax(`/ajax/unmergeTable/${Floorplan.selectedTableNumber}`, null, 'get', tablesUnmerged, null, null)
|
const unmergeTable = () => ajax(`/floorplan/unmergeTable/${Floorplan.selectedTableNumber}`, null, 'get', tablesUnmerged, null, null)
|
||||||
|
|
||||||
const tablesUnmerged = (tables: Record<'child'|'parent', table>) => {
|
const tablesUnmerged = (tables: Record<'child'|'parent', floorplan_table>) => {
|
||||||
const parentTable = tables['parent']
|
const parentTable = tables['parent']
|
||||||
const childTable = tables['child']
|
const childTable = tables['child']
|
||||||
|
|
||||||
@@ -786,16 +811,16 @@ const tablesUnmerged = (tables: Record<'child'|'parent', table>) => {
|
|||||||
|
|
||||||
const toggleTransferMode = () => toggleMode('transfer')
|
const toggleTransferMode = () => toggleMode('transfer')
|
||||||
|
|
||||||
const transferTables = (origin: table, destination: table) => {
|
const transferTables = (origin: floorplan_table, destination: floorplan_table) => {
|
||||||
if(origin.table_number == destination.table_number){
|
if(origin.table_number == destination.table_number){
|
||||||
posAlert(lang('transfer_self_error'))
|
posAlert(lang('transfer_self_error'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ajax(`/ajax/transferTable/${origin.table_number}/${destination.table_number}`, null, 'get', tableTransferred, null, null)
|
ajax(`/floorplan/transferTable/${origin.table_number}/${destination.table_number}`, null, 'get', tableTransferred, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableTransferred = (tables: Record<"origin"|"destination", table>) => {
|
const tableTransferred = (tables: Record<"origin"|"destination", floorplan_table>) => {
|
||||||
const origin = tables['origin']
|
const origin = tables['origin']
|
||||||
const destination = tables['destination']
|
const destination = tables['destination']
|
||||||
|
|
||||||
624
typescript/dredgepos.orderScreen.ts
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
type OrderScreenData = {
|
||||||
|
order_screen_pages: order_screen_page[]
|
||||||
|
sales_categories: sales_category[]
|
||||||
|
print_groups: print_group[]
|
||||||
|
custom_item: item
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderScreen = {
|
||||||
|
order_screen_pages: order_screen_page[]
|
||||||
|
order_items: orderItem[]
|
||||||
|
sales_categories: sales_category[]
|
||||||
|
print_groups: print_group[]
|
||||||
|
order_item_id_generator: Generator
|
||||||
|
selected_item_ids: number[]
|
||||||
|
order_item_ids_to_pulse: number[]
|
||||||
|
qty_override: number
|
||||||
|
print_group_override: print_group
|
||||||
|
custom_item: item,
|
||||||
|
selected_cover: number
|
||||||
|
table: floorplan_table,
|
||||||
|
last_added_item_ids: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
let OrderScreen : OrderScreen = {
|
||||||
|
order_screen_pages: null,
|
||||||
|
order_items: [],
|
||||||
|
print_groups: [],
|
||||||
|
sales_categories: [],
|
||||||
|
order_item_id_generator: newestId(),
|
||||||
|
selected_item_ids: [],
|
||||||
|
order_item_ids_to_pulse: [],
|
||||||
|
qty_override: 1,
|
||||||
|
print_group_override: null,
|
||||||
|
custom_item: null,
|
||||||
|
selected_cover: 0,
|
||||||
|
table: null,
|
||||||
|
last_added_item_ids: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadPageGroup = (e: Event) => {
|
||||||
|
const button = $(e.target)
|
||||||
|
const container = $('#pageGroupContainer')
|
||||||
|
|
||||||
|
$('.loadPageGroup').removeClass('active')
|
||||||
|
button.addClass('active')
|
||||||
|
|
||||||
|
let pageGroupId = button.data('page-group-id')
|
||||||
|
container.find('.pageGroup').hide()
|
||||||
|
|
||||||
|
let activeGrid = $(`.pageGroup[data-page-group-id=${pageGroupId}]`)
|
||||||
|
let navButtons = container.next('.pageNavigation')
|
||||||
|
navButtons.css('display', 'flex')
|
||||||
|
|
||||||
|
activeGrid.find('.gridPage').length > 1
|
||||||
|
? navButtons.show()
|
||||||
|
: navButtons.hide()
|
||||||
|
|
||||||
|
activeGrid.css('display', 'inline-flex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupOrderScreen = (data: OrderScreenData) => {
|
||||||
|
OrderScreen.order_screen_pages = data.order_screen_pages
|
||||||
|
OrderScreen.sales_categories = data.sales_categories
|
||||||
|
OrderScreen.print_groups = data.print_groups
|
||||||
|
OrderScreen.custom_item = data.custom_item
|
||||||
|
|
||||||
|
updateOrderBoxTotals()
|
||||||
|
$(document)
|
||||||
|
.on('click', '.nextButton', goToNextPage)
|
||||||
|
.on('click', '.prevButton', goToPrevPage)
|
||||||
|
.on('click', '.loadPageGroup', loadPageGroup)
|
||||||
|
.on('click', getElementsByAction('item'), itemButtonClicked)
|
||||||
|
.on('click', getElementsByAction('grid'), gridButtonClicked)
|
||||||
|
.on('click', '.closeGrid', hideGrids)
|
||||||
|
.on('click', '.freetextButton', freetext)
|
||||||
|
.on('click', '.openItemButton', customItem)
|
||||||
|
.on('click', '.orderBoxTable tbody tr', itemRowClicked)
|
||||||
|
.on('click', '.voidButton', voidButtonClicked)
|
||||||
|
.on('dblclick', '.voidButton', voidLastItem)
|
||||||
|
.on('click', '.numpadButton', overrideQty)
|
||||||
|
.on('click', '.accumulateButton', () => toggleMode('accumulate'))
|
||||||
|
.on('click', '.changeCoverNumberButton', changeCoverNumberPrompt)
|
||||||
|
.on('click', '.showCoverSelectorButton', showCoverSelector)
|
||||||
|
.on('click', '.coverSelectorButton', coverSelected)
|
||||||
|
.on('change', '[name=print_override]', printGroupOverride)
|
||||||
|
|
||||||
|
turnOnMode('accumulate')
|
||||||
|
|
||||||
|
$('.loadPageGroup').first().trigger('click')
|
||||||
|
|
||||||
|
let observer = new window.MutationObserver((mutations, observer) => updateOrderBoxTotals())
|
||||||
|
|
||||||
|
observer.observe($('.orderBoxTable tbody').get()[0], {
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
childList: true
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getElementsByAction = (action: string) => `[data-primary-action=${action}], [data-secondary-action=${action}]`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param direction 1 for forward, -1 for backwards.
|
||||||
|
* @param button
|
||||||
|
*/
|
||||||
|
const navigatePage = (direction: number, button: JQuery) => {
|
||||||
|
const grid =
|
||||||
|
button
|
||||||
|
.parent()
|
||||||
|
.parent()
|
||||||
|
.find('.pageGroup:visible')
|
||||||
|
grid.get()[0].scrollLeft += grid.width() * direction
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOrderBox = () => $('.orderBoxTable tbody')
|
||||||
|
const goToNextPage = (e: JQuery.TriggeredEvent) => navigatePage(1, $(e.target))
|
||||||
|
const goToPrevPage = (e: JQuery.TriggeredEvent) => navigatePage(-1, $(e.target))
|
||||||
|
|
||||||
|
const setItemQty = (orderItem: orderItem, qty: number) => {
|
||||||
|
if(!orderItem || !OrderScreen.order_items.find(existingItem => orderItem.id == existingItem.id)) return;
|
||||||
|
|
||||||
|
const instructionIds = getInstructionItems(orderItem.id).map(orderItem => orderItem.id)
|
||||||
|
|
||||||
|
if(qty < 1){
|
||||||
|
const newItems = OrderScreen.order_items.filter(existingOrderItem =>
|
||||||
|
(existingOrderItem.id != orderItem.id) && !instructionIds.includes(existingOrderItem.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 =
|
||||||
|
OrderScreen.order_items
|
||||||
|
.filter((orderItem, index) => index > itemIndex && orderItem.item.item_type === 'item')
|
||||||
|
?.first()
|
||||||
|
if(!nextItem) {
|
||||||
|
return OrderScreen.order_items.filter((orderItem, index) => index > itemIndex)
|
||||||
|
}
|
||||||
|
const nextItemIndex = OrderScreen.order_items.findIndex(orderItem => orderItem.id === nextItem.id);
|
||||||
|
return OrderScreen.order_items.slice(itemIndex, nextItemIndex)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLastInstructionItem = (orderItemId: number) => getInstructionItems(orderItemId).last()
|
||||||
|
|
||||||
|
const addInstructionToOrderBox = (instruction: orderItem) => {
|
||||||
|
//If no items are added, then you can't add an instruction row.
|
||||||
|
if(!OrderScreen.order_items.length) return
|
||||||
|
|
||||||
|
const addAfter = OrderScreen.selected_item_ids.length
|
||||||
|
? OrderScreen.selected_item_ids
|
||||||
|
.filter(selectedItemId => {
|
||||||
|
const orderItem = OrderScreen.order_items.find(orderItem => orderItem.id == selectedItemId)
|
||||||
|
const parentItem = getParentItem(selectedItemId, OrderScreen.order_items)
|
||||||
|
return !(orderItem.item.item_type == "instruction" && OrderScreen.selected_item_ids.includes(parentItem.id))
|
||||||
|
})
|
||||||
|
.map(selectedItemId => getLastInstructionItem(selectedItemId).id)
|
||||||
|
.unique()
|
||||||
|
: 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 => {
|
||||||
|
return {
|
||||||
|
id: OrderScreen.order_item_id_generator.next().value,
|
||||||
|
item: clone(item),
|
||||||
|
qty: qty,
|
||||||
|
print_group: printGroup,
|
||||||
|
cover: OrderScreen.selected_cover,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const addNewItem = (item: item, qty = 1) => {
|
||||||
|
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 = createNewOrderItem(item, qty, printGroup)
|
||||||
|
|
||||||
|
switch(item.item_type){
|
||||||
|
case 'instruction':
|
||||||
|
addInstructionToOrderBox(orderItem)
|
||||||
|
break
|
||||||
|
case 'item':
|
||||||
|
default:
|
||||||
|
addItemToOrderBox(orderItem)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderOrderBox = () => {
|
||||||
|
const orderBox = getOrderBox()
|
||||||
|
const newTbody = $('<tbody />')
|
||||||
|
OrderScreen.order_items.forEach(orderItem => {
|
||||||
|
const newRow = createOrderRow(orderItem)
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
orderBox.replaceWith(newTbody)
|
||||||
|
const element = newTbody.find('tr').last().get()[0]
|
||||||
|
if(element) {
|
||||||
|
element.scrollIntoView()
|
||||||
|
}
|
||||||
|
updateOrderBoxTotals()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const setOrderItems = (orderItems: orderItem[]) => {
|
||||||
|
OrderScreen.order_items = orderItems
|
||||||
|
renderOrderBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOrderRow = (orderItem: orderItem) => {
|
||||||
|
const row = $('.orderBoxTable').EmptyRow()
|
||||||
|
const price = money(orderItem.item.price1)
|
||||||
|
const itemCellText = $('<span/>').text(orderItem.item.name)
|
||||||
|
row
|
||||||
|
.addClass(`${orderItem.item.item_type}Row`)
|
||||||
|
.setColumnValue(lang('qty_header'), orderItem.qty)
|
||||||
|
.setColumnValue(lang('price_header'), price)
|
||||||
|
.setColumnValue(lang('id_header'), orderItem.item.id)
|
||||||
|
.setColumnValue(lang('total_price_header'), price.multiply(orderItem.qty))
|
||||||
|
.setColumnValue(lang('printgroup_header'), orderItem.print_group?.name)
|
||||||
|
.data('order-item-id', orderItem.id)
|
||||||
|
.data('order-item', orderItem)
|
||||||
|
.data('print_group', orderItem.print_group)
|
||||||
|
.data('cover', orderItem.cover)
|
||||||
|
.data('item', orderItem.item)
|
||||||
|
.find('td.itemCell')
|
||||||
|
.append(itemCellText)
|
||||||
|
|
||||||
|
changeCoverOnRow(row, orderItem.cover)
|
||||||
|
|
||||||
|
if(orderItem.item.item_type == 'instruction' && price.value <= 0){
|
||||||
|
row
|
||||||
|
.find('.totalPriceCell,.unitPriceCell')
|
||||||
|
.css('font-size', 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemButtonClicked = (e: JQuery.TriggeredEvent) => {
|
||||||
|
hideGrids()
|
||||||
|
const existingItemRows = $('.itemRow')
|
||||||
|
const button = $(e.target).closest('.posButton')
|
||||||
|
const item : item = button.data('item')
|
||||||
|
|
||||||
|
if(item.item_type == 'instruction' && existingItemRows.length < 1) return
|
||||||
|
|
||||||
|
const qty = OrderScreen.qty_override || 1
|
||||||
|
OrderScreen.qty_override = 1
|
||||||
|
|
||||||
|
addNewItem(item, qty)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridButtonClicked = (e: JQuery.TriggeredEvent) => {
|
||||||
|
const button = $(e.target).closest('.posButton')
|
||||||
|
const grid : number = button.data('grid')
|
||||||
|
ajax(`/order/getGridHtml/${grid}`, null, null,gridHtmlGenerated, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideGrids = () => $('.gridContainer').hide()
|
||||||
|
|
||||||
|
|
||||||
|
const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
|
||||||
|
const gridContainer = $('.gridContainer')
|
||||||
|
const cellDimensions = getGridCellDimensions()
|
||||||
|
const grid = gridData.grid
|
||||||
|
const gridHtml = gridData.gridHtml
|
||||||
|
|
||||||
|
gridContainer
|
||||||
|
.show()
|
||||||
|
.width(cellDimensions.width * grid.cols)
|
||||||
|
.children('.gridContainerHeader')
|
||||||
|
.children('span')
|
||||||
|
.text(grid.name)
|
||||||
|
.parent()
|
||||||
|
.parent()
|
||||||
|
.find('.pageGroup')
|
||||||
|
.html(gridHtml)
|
||||||
|
.show()
|
||||||
|
.parent()
|
||||||
|
.height(cellDimensions.height * grid.rows)
|
||||||
|
.closest('.gridContainer')
|
||||||
|
.find('.pageNavigation')
|
||||||
|
.toggle(gridContainer.find('.gridPage').length > 1)
|
||||||
|
.height(cellDimensions.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemRowClicked = (e: JQuery.TriggeredEvent) => {
|
||||||
|
const row = $(e.target).closest('tr')
|
||||||
|
const orderItem: orderItem = row.data('order-item')
|
||||||
|
|
||||||
|
if(isInMode('void')){
|
||||||
|
voidOrderItems([orderItem.id])
|
||||||
|
turnOffMode('void')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!row.hasClass('selected')) {
|
||||||
|
OrderScreen.selected_item_ids = array_push(OrderScreen.selected_item_ids, orderItem.id)
|
||||||
|
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 originalOrderItemList = clone(OrderScreen.order_items)
|
||||||
|
const originalSelectedIds = clone(OrderScreen.selected_item_ids)
|
||||||
|
orderItemIds.forEach(orderItemId => {
|
||||||
|
const orderItemToVoid = originalOrderItemList.find(item => item.id == orderItemId)
|
||||||
|
if(orderItemToVoid.item.item_type == "instruction") {
|
||||||
|
const parentItem = getParentItem(orderItemToVoid.id, originalOrderItemList)
|
||||||
|
if(originalSelectedIds.includes(parentItem.id) && parentItem.qty-1 < 1 || !originalSelectedIds.includes(parentItem.id)){
|
||||||
|
decrementItemQty(orderItemToVoid)
|
||||||
|
}
|
||||||
|
} else decrementItemQty(orderItemToVoid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidButtonClicked = () => {
|
||||||
|
if(isInMode('void')){
|
||||||
|
turnOffMode('void')
|
||||||
|
} else if(OrderScreen.selected_item_ids.length){
|
||||||
|
voidOrderItems(OrderScreen.selected_item_ids)
|
||||||
|
} else {
|
||||||
|
turnOnMode('void')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const voidLastItem = () => {
|
||||||
|
if (OrderScreen.order_items.length)
|
||||||
|
decrementItemQty(OrderScreen.order_items.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 completeTotal = lang('totalPrice', getTotals().format())
|
||||||
|
const selectedTotal = lang('selectedPrice', getSelectedTotals().format())
|
||||||
|
|
||||||
|
$('.orderBoxTotal').text(completeTotal)
|
||||||
|
$('.orderBoxSelectedTotal').text(selectedTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToElement = (JQueryElement: JQuery) => {
|
||||||
|
const element = JQueryElement.get()[0]
|
||||||
|
const container = JQueryElement.closest('.orderBox').get()[0]
|
||||||
|
const containerTop = $(container).scrollTop()
|
||||||
|
const containerBottom = containerTop + $(container).height()
|
||||||
|
const elemTop = element.offsetTop
|
||||||
|
const elemBottom = elemTop + $(element).height()
|
||||||
|
if (elemTop < containerTop) {
|
||||||
|
$(container).scrollTop(elemTop)
|
||||||
|
} else if (elemBottom > containerBottom) {
|
||||||
|
$(container).scrollTop(elemBottom - $(container).height())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const overrideQty = () => showVirtualNumpad(lang('multiplier'), 4, false, true, true, qtyOverridden)
|
||||||
|
|
||||||
|
const qtyOverridden = (qtyString: string) => OrderScreen.qty_override = Number(qtyString)
|
||||||
|
|
||||||
|
const printGroupOverride = (e: JQuery.TriggeredEvent) => {
|
||||||
|
const input = $(e.target)
|
||||||
|
const printGroupId = Number(input.val())
|
||||||
|
const orderBox = $('.orderBoxTable tbody')
|
||||||
|
const selectedRows = orderBox.find('tr.selected')
|
||||||
|
const newPrintGroup = OrderScreen.print_groups.where('id', printGroupId)
|
||||||
|
|
||||||
|
if(selectedRows.length && newPrintGroup){
|
||||||
|
selectedRows.each((index, row) => {
|
||||||
|
$(row).setColumnValue(lang('printgroup_header'), newPrintGroup.name)
|
||||||
|
$(row).data('print_group', newPrintGroup)
|
||||||
|
})
|
||||||
|
|
||||||
|
OrderScreen.print_group_override = null
|
||||||
|
resetToggle(input)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
OrderScreen.print_group_override = newPrintGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const freetext = () => showVirtualKeyboard('', 32,false, freetextSubmitted)
|
||||||
|
|
||||||
|
const freetextSubmitted = (text: string) => {
|
||||||
|
if(text.trim().length < 1) return
|
||||||
|
|
||||||
|
if($('.orderBoxTable tbody tr').length < 1){
|
||||||
|
posAlert(lang('freetext_no_order'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = Object.assign({}, OrderScreen.custom_item)
|
||||||
|
item.item_type = 'instruction'
|
||||||
|
item.name = text
|
||||||
|
|
||||||
|
addNewItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
const customItem = () => showVirtualKeyboard(lang('enter_item_name'), 32,false, customItemTextSubmitted)
|
||||||
|
|
||||||
|
const customItemTextSubmitted = (text: string) => {
|
||||||
|
const submitFunction = (priceString: string) => {
|
||||||
|
const price = currency(priceString, {fromCents: false})
|
||||||
|
|
||||||
|
const item = Object.assign({}, OrderScreen.custom_item)
|
||||||
|
item.item_type = 'item'
|
||||||
|
item.name = text
|
||||||
|
item.price1 = price.intValue
|
||||||
|
|
||||||
|
addNewItem(item)
|
||||||
|
}
|
||||||
|
showVirtualNumpad(lang('enter_item_price'), 4, false, true, true, submitFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = getGridCellDimensions().height
|
||||||
|
const coverSelector = $('.coverSelector')
|
||||||
|
|
||||||
|
const buttonPositionLeftPercent = getPercentageOfPageContainerWidth(button.offset().left)
|
||||||
|
const buttonWidthPercent = getPercentageOfPageContainerWidth(button.width())
|
||||||
|
|
||||||
|
coverSelector
|
||||||
|
.toggle(!coverSelector.is(':visible'))
|
||||||
|
.css({
|
||||||
|
width: buttonWidthPercent,
|
||||||
|
left: buttonPositionLeftPercent,
|
||||||
|
top: (button.offset().top + button.height()) + 'px'
|
||||||
|
})
|
||||||
|
.find('.coverSelectorButton')
|
||||||
|
.height(gridHeight)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const coverSelected = (event: JQuery.TriggeredEvent) => {
|
||||||
|
$('.coverSelector').hide()
|
||||||
|
const button = $(event.target)
|
||||||
|
const cover = Number(button.data('cover'))
|
||||||
|
const selectedRows = $('.orderBoxTable tbody').find('tr.itemRow.selected')
|
||||||
|
|
||||||
|
selectedRows.each( (_, selectedRow) => changeCoverOnRow($(selectedRow), cover))
|
||||||
|
OrderScreen.selected_cover = cover
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCoverOnRow = (row: JQuery, cover: number) => {
|
||||||
|
row.data('cover', cover)
|
||||||
|
const itemCell = row.find('.itemCell')
|
||||||
|
const existingCoverSpan = itemCell.find('small')
|
||||||
|
const coverSpan = existingCoverSpan.length > 0
|
||||||
|
? existingCoverSpan
|
||||||
|
: $('<small/>').appendTo(itemCell)
|
||||||
|
|
||||||
|
coverSpan.text(lang('selected_cover', cover.toString()))
|
||||||
|
if(cover < 1 || !row.hasClass('itemRow')) {
|
||||||
|
coverSpan.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCoverNumberPrompt = () =>
|
||||||
|
showVirtualNumpad(lang('how_many_covers'), 3, false, false, true, changeCoverNumberPromptSubmitted)
|
||||||
|
|
||||||
|
|
||||||
|
const changeCoverNumberPromptSubmitted = (value: string) => updateCoverNumbers(Number(value))
|
||||||
|
|
||||||
|
const updateCoverNumbers = (covers: number) => {
|
||||||
|
let newTable = clone(OrderScreen.table)
|
||||||
|
newTable.default_covers = covers
|
||||||
|
ajax('/order/updateCovers', newTable, 'post', coverNumbersUpdated, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const coverNumbersUpdated = (newTable: floorplan_table) => {
|
||||||
|
const covers = newTable.default_covers
|
||||||
|
OrderScreen.table = newTable
|
||||||
|
$('.changeCoverNumberButton').text(lang('covers', covers.toString()))
|
||||||
|
generateCoverSelector()
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateCoverSelector = () => {
|
||||||
|
const covers = OrderScreen.table.default_covers
|
||||||
|
const coverSelector = $('.coverSelector')
|
||||||
|
coverSelector.hide().children().remove()
|
||||||
|
|
||||||
|
for(let cover=0; cover<=covers; cover++) {
|
||||||
|
const buttonText = cover==0 ? lang('cover_zero') : lang('selected_cover', cover.toString())
|
||||||
|
loadTemplate('#posButtonTemplate')
|
||||||
|
.find('a')
|
||||||
|
.first()
|
||||||
|
.addClass('coverSelectorButton')
|
||||||
|
.text(buttonText)
|
||||||
|
.data('cover', cover)
|
||||||
|
.appendTo(coverSelector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
OrderScreen.table = $('#pageContainer').data('table') || null
|
||||||
|
$('.coverSelector, .gridContainer').hide()
|
||||||
|
if(OrderScreen.table)
|
||||||
|
ajax(`/order/getOrderScreenData/${OrderScreen.table.table_number}`, null, 'get', setupOrderScreen, null, null)
|
||||||
|
})
|
||||||
53
typescript/dredgepos.tables.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
interface JQuery {
|
||||||
|
getColumnValue(columnHeading: string) : string
|
||||||
|
setColumnValue(columnHeading: string, value: any) : JQuery
|
||||||
|
getColumnIndex(columnHeading: string) : number
|
||||||
|
EmptyRow() : JQuery<HTMLTableRowElement>
|
||||||
|
filterByData(prop: string, value: any) : JQuery
|
||||||
|
pulse() : JQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.pulse = function(this: JQuery) {
|
||||||
|
pulseElement(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.EmptyRow = function(this: JQuery) {
|
||||||
|
const headingRow = this.find('th').first().closest('tr')
|
||||||
|
const headingCells = headingRow.find('th')
|
||||||
|
const newRow = $('<tr/>')
|
||||||
|
headingCells.each( (index, cell) => {
|
||||||
|
const newCell = $('<td/>')
|
||||||
|
const attributes = Array.from(cell.attributes)
|
||||||
|
newCell.data('column', cell.innerText.trim())
|
||||||
|
newCell.data('column-index', index)
|
||||||
|
attributes.forEach(attribute => newCell.attr(attribute.name, attribute.value))
|
||||||
|
newRow.append(newCell)
|
||||||
|
})
|
||||||
|
|
||||||
|
return newRow as JQuery<HTMLTableRowElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.getColumnIndex = function(this: JQuery, columnHeading: string){
|
||||||
|
return this
|
||||||
|
.find('td')
|
||||||
|
.filterByData('column', columnHeading)
|
||||||
|
.data('column-index')
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.getColumnValue = function(this: JQuery, columnHeading: string){
|
||||||
|
return this.find('td').filterByData('column', columnHeading).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.setColumnValue = function(this: JQuery, columnHeading: string, value: any){
|
||||||
|
this.find('td').filterByData('column', columnHeading).text(value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
$.fn.filterByData = function(prop: string, val: any) {
|
||||||
|
return this.filter(
|
||||||
|
function() {
|
||||||
|
return $(this).data(prop)==val
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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('submitfunction', submitFunction)
|
numpad.data('submit-function', submitFunction)
|
||||||
numpad.data('password', isPassword);
|
numpad.data('password', isPassword);
|
||||||
numpad.data('allowdecimals', allowDecimals);
|
numpad.data('allow-decimals', 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 = numpad.data('maxlength')
|
let maxlength : number = numpad.data('maxlength')
|
||||||
let allowDecimals = numpad.data('allowdecimals')
|
let allowDecimals: boolean = numpad.data('allow-decimals')
|
||||||
let submitFunction = numpad.data('submitfunction')
|
let submitFunction : Function = numpad.data('submit-function')
|
||||||
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('submitfunction', submitFunction)
|
keyboard.data('submit-function', 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('submitfunction')
|
let submitFunction = keyboard.data('submit-function')
|
||||||
submitFunction(inputBox.val());
|
submitFunction(inputBox.val());
|
||||||
break;
|
break;
|
||||||
case 'shift':
|
case 'shift':
|
||||||
@@ -216,7 +216,6 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
|
|||||||
}
|
}
|
||||||
|
|
||||||
let setKeyboardLayout = (layout: string, modifier = '') => {
|
let setKeyboardLayout = (layout: string, modifier = '') => {
|
||||||
|
|
||||||
if (modifier != '') modifier = `_${modifier}`
|
if (modifier != '') modifier = `_${modifier}`
|
||||||
Application.keyboard.currentLayout = layout
|
Application.keyboard.currentLayout = layout
|
||||||
let layoutToLoad = Application.keyboard.layouts[layout]
|
let layoutToLoad = Application.keyboard.layouts[layout]
|
||||||
@@ -226,11 +225,9 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
|
|||||||
We start at 1 instead of 0. Makes it easier for non-programmers
|
We start at 1 instead of 0. Makes it easier for non-programmers
|
||||||
and translators making their own language packs
|
and translators making their own language packs
|
||||||
*/
|
*/
|
||||||
index = index + 1;
|
index = index + 1
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
let currentRow : string[] = layoutToLoad[`row${index}${modifier}`]
|
let currentRow : string[] = layoutToLoad[`row${index}${modifier}`]
|
||||||
|
|
||||||
$(row).children('a').each((keyIndex, button) => {
|
$(row).children('a').each((keyIndex, button) => {
|
||||||
let key = $(button);
|
let key = $(button);
|
||||||
let keyValue: string = currentRow[keyIndex];
|
let keyValue: string = currentRow[keyIndex];
|
||||||
115
typescript/types.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
type PosMode = "edit" | "void" | "transfer" | "default" | "tableSelected" | "decorationSelected" | "activeTableSelected" | "merge" | "reservedTableSelected" | "accumulate"
|
||||||
|
type PosModes = PosMode[]
|
||||||
|
|
||||||
|
interface order {
|
||||||
|
clerk: string
|
||||||
|
split: boolean
|
||||||
|
items: orderItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface orderItem {
|
||||||
|
id: number
|
||||||
|
qty: number
|
||||||
|
print_group: print_group
|
||||||
|
item: item
|
||||||
|
cover: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface print_group {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
printer_id: number,
|
||||||
|
venue_id: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ajaxResult {
|
||||||
|
status: string
|
||||||
|
data : any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApplicationState {
|
||||||
|
keyboard: keyboard
|
||||||
|
mode: PosModes
|
||||||
|
languageVars: Record<any, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface floorplan_table {
|
||||||
|
table_number: number,
|
||||||
|
room_id: number
|
||||||
|
venue_id: number
|
||||||
|
pos_x: number
|
||||||
|
pos_y: number
|
||||||
|
shape: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
default_covers: number
|
||||||
|
rotation: number
|
||||||
|
merged_children: string
|
||||||
|
previous_state: string
|
||||||
|
status: number
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface floorplan_decoration {
|
||||||
|
id: number
|
||||||
|
room_id: number
|
||||||
|
pos_x: number
|
||||||
|
pos_y: number
|
||||||
|
rotation: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
image: string
|
||||||
|
venue_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface room {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
background_image: string
|
||||||
|
venue_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface reservation {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
time: number,
|
||||||
|
covers: number,
|
||||||
|
created_at: number,
|
||||||
|
floorplan_table_id: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface keyboard {
|
||||||
|
capsLock: boolean
|
||||||
|
shift: boolean
|
||||||
|
layouts: VirtualKeyboard
|
||||||
|
currentLayout: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number}
|
||||||
|
interface grid {id: number; name: string; rows: number; cols: number; data: string}
|
||||||
|
|
||||||
|
interface item {
|
||||||
|
id: number
|
||||||
|
code: string
|
||||||
|
sales_category_id: number
|
||||||
|
name: string
|
||||||
|
item_type: string
|
||||||
|
price1: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type sales_category = {
|
||||||
|
id: number
|
||||||
|
parent: number
|
||||||
|
name: string
|
||||||
|
print_group_id: string
|
||||||
|
venue_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Array<T> {
|
||||||
|
where(property: string, value: any): T
|
||||||
|
first(): T
|
||||||
|
last(): T
|
||||||
|
unique(): this
|
||||||
|
collect<y>(func: (item: T) => y[]): y[]
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
declare namespace currency {
|
declare namespace currency {
|
||||||
type Any = number | string | currency;
|
type Any = number | string | currency;
|
||||||
type Format = (currency?: currency, opts?: Options) => string;
|
type Format = (currency?: currency, opts?: Options) => string;
|
||||||
interface Constructor {
|
interface Constructor {
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
format?: currency.Format,
|
format?: currency.Format,
|
||||||
fromCents?: boolean
|
fromCents?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface currency {
|
interface currency {
|
||||||
add(number: currency.Any): currency;
|
add(number: currency.Any): currency;
|
||||||
subtract(number: currency.Any): currency;
|
subtract(number: currency.Any): currency;
|
||||||
multiply(number: currency.Any): currency;
|
multiply(number: currency.Any): currency;
|
||||||
@@ -33,6 +33,6 @@
|
|||||||
toJSON(): number;
|
toJSON(): number;
|
||||||
readonly intValue: number;
|
readonly intValue: number;
|
||||||
readonly value: number;
|
readonly value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const currency: currency.Constructor;
|
declare const currency: currency.Constructor;
|
||||||
BIN
wwwroot/fonts/OpenSans-Bold.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-BoldItalic.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-ExtraBold.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-ExtraBoldItalic.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-Italic.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-Light.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-LightItalic.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-Medium.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-MediumItalic.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-Regular.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-SemiBold.ttf
Normal file
BIN
wwwroot/fonts/OpenSans-SemiBoldItalic.ttf
Normal file
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |