13 Commits

Author SHA1 Message Date
207edf0de3 Migration system added.
Install scripts for database schema and dummy data too.
2022-02-26 22:23:30 +10:00
dredgy
6439b4326c Merge pull request #6 from dredgy/add_view_engine
Moved to Giraffe View Engine
2022-02-22 15:23:22 +10:00
cb58262507 Moved to Giraffe View Engine 2022-02-22 15:22:32 +10:00
dredgy
b5a2514495 Merge pull request #5 from dredgy/npm_scripts
Npm scripts
2022-02-14 19:04:54 +10:00
a1d3ad19c3 Updated gitignore 2022-02-14 16:33:03 +10:00
300fb23905 Updated gitignore 2022-02-14 16:32:11 +10:00
6892b3d34c Restructured files, made build script 2022-02-14 16:31:08 +10:00
c73184808c Added solution 2022-02-14 16:06:08 +10:00
dredgy
c7f3d7d2fb Merge pull request #4 from dredgy/project-structure
Project structure
2022-02-13 19:35:26 +10:00
e3794fc222 Unversioned file was tracking 2022-02-13 19:35:02 +10:00
39dbba45f7 Separated models, controllers and routers 2022-02-13 19:33:50 +10:00
dredgy
6cbd670260 Merge pull request #3 from dredgy/order_screen
Join With Line function
2022-02-13 13:10:24 +10:00
dredgy
abfb2331ba Merge pull request #2 from dredgy/order_screen
Order screen
2022-01-23 18:18:06 +10:00
200 changed files with 1564 additions and 1801 deletions

6
.gitignore vendored
View File

@@ -6,5 +6,7 @@
/tables/* /tables/*
/Properties/ /Properties/
/.idea/ /.idea/
/wwwroot/scripts/js/ /wwwroot/scripts/*.js
/wwwroot/styles/css/* /wwwroot/scripts/*.js.map
/wwwroot/styles/*
/Folder.DotSettings.user

15
Ajax/Controller.fs Normal file
View 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
View 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)
}

View File

@@ -1,156 +0,0 @@
module AjaxController
open DredgeFramework
open DredgePos
open DredgePos.Types
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 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 (id: int) =
let tableList = Entity.GetAllInVenue<floorplan_table>
let reservationList = getReservationList tableList
{|
tables = tableList
decorations = Entity.GetAllInVenue<floorplan_decoration>
activeTableNumbers = Floorplan.getActiveTables (getCurrentVenue())
rooms = Entity.GetAllInVenue<floorplan_room>
reservations = reservationList
|}
|> ajaxSuccess
|> json
let getOrderScreenData (tableNumber: int) =
{|
order_screen_pages = Entity.GetAllInVenue<order_screen_page_group>
sales_categories = Entity.GetAllInVenue<sales_category>
print_groups = Entity.GetAllInVenue<print_group>
custom_item = Entity.GetAllByColumn<item> "item_code" "OPEN000" |> first
table = getTable tableNumber
|}
|> ajaxSuccess
|> json
let getKeyboardLayout (language: string) =
let layout = $"""wwwroot/languages/{language}/keyboardLayout.json"""
|> GetFileContents
map [
"status", "success"
"data", layout
] |> 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) =
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
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 newEmptyReservation (reservation: reservation) =
let newReservation = {reservation with
created_at = CurrentTime()
time = CurrentTime()
}
if reservation.floorplan_table_id > 0 then
let table = {(getTableById reservation.floorplan_table_id) with
status = 2
default_covers = 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
let loadGrid (gridId: int) =
let grid = Entity.GetById<grid> gridId
let gridHtml = OrderScreen.loadGrid gridId
if gridHtml = "Error" then ajaxFail gridHtml
else ajaxSuccess {|grid=grid;gridHtml=gridHtml|}
|> json

View 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"

View File

@@ -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
View 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
View 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|]

View File

@@ -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

98
Core/Database.module.fs Normal file
View 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

View File

@@ -2,14 +2,13 @@
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
@@ -35,8 +34,9 @@ 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]
@@ -101,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)
)

View File

@@ -1,4 +1,5 @@
module Entity module Entity
open Dapper
open Dapper.FSharp open Dapper.FSharp
open DredgeFramework open DredgeFramework
open Pluralize.NET.Core open Pluralize.NET.Core
@@ -15,7 +16,7 @@ let Create (record: 'x)=
value record value record
excludeColumn "id" excludeColumn "id"
} }
|> db.InsertOutput |> Database.InsertOutput
|> first |> first
@@ -28,7 +29,7 @@ let inline Update (record: ^x) =
where (eq "id" id) where (eq "id" id)
excludeColumn "id" excludeColumn "id"
} }
|> db.Update |> Database.Update
let GetAll<'x> = let GetAll<'x> =
let tableName = GetDatabaseTable<'x> let tableName = GetDatabaseTable<'x>
@@ -36,7 +37,7 @@ let GetAll<'x> =
select { select {
table tableName table tableName
} }
|> db.Select<'x> |> Database.Select<'x>
let GetAllByColumn<'x> (column: string) (value: obj) = let GetAllByColumn<'x> (column: string) (value: obj) =
let tableName = GetDatabaseTable<'x> let tableName = GetDatabaseTable<'x>
@@ -44,7 +45,9 @@ let GetAllByColumn<'x> (column: string) (value: obj) =
select { select {
table tableName table tableName
where (eq column value) where (eq column value)
} |> db.Select<'x> } |> Database.Select<'x>
let GetFirstByColumn<'x> (column: string) (value: obj) = (GetAllByColumn<'x> column value) |> first
let GetAllInVenue<'x> = GetAllByColumn<'x> "venue_id" (getCurrentVenue ()) let GetAllInVenue<'x> = GetAllByColumn<'x> "venue_id" (getCurrentVenue ())
let GetById<'x> (id: int) = GetAllByColumn<'x> "id" id |> first let GetById<'x> (id: int) = GetAllByColumn<'x> "id" id |> first
@@ -67,7 +70,7 @@ let DeleteById<'x> id =
delete { delete {
table tableName table tableName
where (eq "id" id) where (eq "id" id)
} |> db.Delete |> ignore } |> Database.Delete |> ignore
entity entity

View File

@@ -10,8 +10,8 @@ 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
@@ -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 =
@@ -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

View File

@@ -13,7 +13,7 @@ type reservation = {
[<CLIMutable>] [<CLIMutable>]
type venue = { type venue = {
id: int id: int
venue_name: string name: string
} }
[<CLIMutable>] [<CLIMutable>]
@@ -38,7 +38,7 @@ type floorplan_table = {
type print_group = { type print_group = {
id: int id: int
name: string name: string
printer: int printer_id: int
venue_id: int venue_id: int
} }
@@ -47,14 +47,14 @@ type sales_category = {
id: int id: int
parent: int parent: int
name: string name: string
print_group: int print_group_id: int
venue_id: int venue_id: int
} }
[<CLIMutable>] [<CLIMutable>]
type floorplan_room = { type room = {
id: int id: int
room_name: string name: string
background_image: string background_image: string
venue_id: int venue_id: int
} }
@@ -62,18 +62,23 @@ type floorplan_room = {
[<CLIMutable>] [<CLIMutable>]
type floorplan_decoration = { type floorplan_decoration = {
id: int id: int
decoration_room: int room_id: int
decoration_pos_x: int pos_x: int
decoration_pos_y: int pos_y: int
decoration_rotation: int rotation: int
decoration_width: int width: int
decoration_height: int height: int
decoration_image: string image: string
venue_id: int venue_id: int
} }
[<CLIMutable>] [<CLIMutable>]
type clerk = {id: int; clerk_name: string; clerk_login_code: int; clerk_usergroup: int} type clerk = {
id: int
name: string
login_code: int
user_group_id: int
}
[<CLIMutable>] [<CLIMutable>]
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int} type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
@@ -82,7 +87,7 @@ type session = {id: int; session_id: string; clerk_json: string; clerk_id: int;
type order_screen_page_group = {id: int; order: int; venue_id: int; label: string; grid_id: int} type order_screen_page_group = {id: int; order: int; venue_id: int; label: string; grid_id: int}
[<CLIMutable>] [<CLIMutable>]
type grid = {id: int; grid_name: string; grid_rows: int; grid_cols: int; grid_data: string} type grid = {id: int; name: string; rows: int; cols: int; data: string}
[<CLIMutable>] [<CLIMutable>]
type button = { type button = {
@@ -100,13 +105,30 @@ type button = {
[<CLIMutable>] [<CLIMutable>]
type item = { type item = {
id: int id: int
item_code: string code: string
item_category: int sales_category_id: int
item_name: string name: string
item_type: string item_type: string
price1: int price1: int
price2: int }
price3: int
price4: int [<CLIMutable>]
price5: int 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
} }

View File

@@ -1,46 +0,0 @@
module db
open Dapper
open Dapper.FSharp
open Dapper.FSharp.PostgreSQL
open DredgeFramework
let connString = "Server=localhost;Port=5432;User Id=postgres;Password=root;Database=dredgepos;Include Error Detail=true"
//let connString = "server=localhost;uid=root;pwd=;database=dredgepos;table cache = false"
let connection = new Npgsql.NpgsqlConnection(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.InsertOutputAsync<'a, 'a>
|> RunSynchronously
|> EnumerableToArray
let Update<'a> asyncQuery =
asyncQuery
|> connection.UpdateOutputAsync<'a, 'a>
|> RunSynchronously
|> EnumerableToArray
let Delete<'a> asyncQuery =
asyncQuery
|> connection.DeleteAsync
|> RunSynchronously

View File

@@ -1,48 +0,0 @@
module Decorations
open System
open System.IO
open System.Text.RegularExpressions
open DredgeFramework
open Dapper.FSharp
open DredgePos
open Types
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 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 ""

View File

@@ -5,23 +5,42 @@
</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="Floorplan.module.fs" />
<Compile Include="Printer.module.fs" /> <Compile Include="Printer.module.fs" />
<Compile Include="Orders.module.fs" /> <Compile Include="Global\View.fs" />
<Compile Include="OrderScreen.module.fs" /> <Compile Include="Global\Controller.fs" />
<Compile Include="Decorations.module.fs" /> <Compile Include="Global\Router.fs" />
<Compile Include="Clerk.module.fs" /> <Compile Include="Entities\Floorplan_Decorations\Model.fs" />
<Compile Include="Session.module.fs" /> <Compile Include="Entities\Floorplan_Decorations\View.fs" />
<Compile Include="PageController.fs" /> <Compile Include="Entities\Floorplan_Decorations\Controller.fs" />
<Compile Include="AjaxController.fs" /> <Compile Include="Entities\Floorplan_Decorations\Router.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\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>
@@ -38,27 +57,75 @@
<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 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.Contrib" Version="2.0.78" />
<PackageReference Include="Dapper.FSharp" Version="2.4.1" /> <PackageReference Include="Dapper.FSharp" Version="2.4.1" />
<PackageReference Include="FSharp.Data" Version="4.0.1" />
<PackageReference Include="Giraffe" Version="6.0.0-alpha-2" /> <PackageReference Include="Giraffe" Version="6.0.0-alpha-2" />
<PackageReference Include="Npgsql" Version="6.0.0" /> <PackageReference Include="Npgsql" Version="6.0.0" />
<PackageReference Include="Pluralize.NET.Core" Version="1.0.0" /> <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" /> <PackageReference Update="FSharp.Core" Version="6.0.2-beta.21631.1" />
@@ -70,12 +137,10 @@
<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\fonts" />
<Folder Include="wwwroot\styles\css" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<_ContentIncludedByDefault Remove="wwwroot\themes\default\index.tpl.html" /> <_ContentIncludedByDefault Remove="wwwroot\themes\default\index.tpl.html" />
<_ContentIncludedByDefault Remove="wwwroot\themes\default\scripts\global.js" /> <_ContentIncludedByDefault Remove="wwwroot\themes\default\scripts\global.js" />
<_ContentIncludedByDefault Remove="wwwroot\themes\default\scripts\index.js" /> <_ContentIncludedByDefault Remove="wwwroot\themes\default\scripts\index.js" />
<_ContentIncludedByDefault Remove="wwwroot\themes\default\styles\global.css" /> <_ContentIncludedByDefault Remove="wwwroot\themes\default\styles\global.css" />

16
DredgePos.sln Normal file
View 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

View 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

View 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

View File

@@ -0,0 +1,2 @@
module DredgePos.Entities.Floorplan_Decorations.Router

View 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"] [
div [_id "decoratorHeader"] [
h2 [] [lang "choose_decoration"]
a [_class "posButton hideDecorator"] [str "×"]
]
div [_id "decoratorContent"] [
yield! decorationRows
]
]

119
Floorplan/Controller.fs Normal file
View 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

View File

@@ -1,19 +1,12 @@
module Floorplan module DredgePos.Floorplan.Model
open DredgePos
open DredgePos.Types open DredgePos.Types
open Org.BouncyCastle.Asn1.X509
open Reservations
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 activeTablePath = "tables/active/" let activeTablePath = "tables/active/"
let getTableFile (tableNumber: int) = let getTableFile (tableNumber: int) =
@@ -46,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) =
@@ -54,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)
@@ -100,7 +93,7 @@ let getTable (tableNumber : int) =
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue())) where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
} }
let result = query |> db.Select<floorplan_table> let result = query |> Database.Select<floorplan_table>
result |> first result |> first
let getTableById (id : int) = let getTableById (id : int) =
@@ -108,14 +101,14 @@ let getTableById (id : int) =
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 "floorplan_rooms"
where (eq "id" roomId) where (eq "id" roomId)
} |> db.Select<floorplan_room> |> first } |> Database.Select<room> |> first
let updateTablePosition (floorplanTable: floorplan_table) = Entity.Update floorplanTable let updateTablePosition (floorplanTable: floorplan_table) = Entity.Update floorplanTable
@@ -124,7 +117,7 @@ let createEmptyReservation (reservation: reservation) =
table "floorplan_tables" table "floorplan_tables"
set {| status = 2 |} set {| status = 2 |}
where(eq "id" reservation.floorplan_table_id) where(eq "id" reservation.floorplan_table_id)
} |> db.Update |> ignore } |> Database.Update |> ignore
Entity.Create reservation Entity.Create reservation
@@ -161,7 +154,7 @@ let tableExists (tableNumber: int) =
select{ select{
table "floorplan_tables" table "floorplan_tables"
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue())) 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 ->
@@ -176,14 +169,14 @@ let tableExists (tableNumber: int) =
| _ -> | _ ->
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) =
@@ -191,7 +184,7 @@ let addNewTableWithoutOutput (newTable: floorplan_table) =
table "floorplan_tables" table "floorplan_tables"
value newTable value newTable
} }
|> db.Insert |> Database.Insert
let addNewTable (newTable: floorplan_table) = Entity.Create newTable let addNewTable (newTable: floorplan_table) = Entity.Create newTable
@@ -245,7 +238,7 @@ let mergeTables parent child = //Merge two tables together
default_covers = parentTable.default_covers + childTable.default_covers default_covers = parentTable.default_covers + childTable.default_covers
|} |}
where (eq "table_number" parent + eq "venue_id" (getCurrentVenue())) where (eq "table_number" parent + eq "venue_id" (getCurrentVenue()))
} |> db.Update |> ignore } |> Database.Update |> ignore
Entity.DeleteById<floorplan_table> newChildTable.id Entity.DeleteById<floorplan_table> newChildTable.id
|> ignore |> ignore
@@ -258,7 +251,7 @@ let updateUnmergedTables parentTable childTable =
table "floorplan_tables" table "floorplan_tables"
set parentTable set parentTable
where(eq "table_number" parentTable.table_number + eq "venue_id" (getCurrentVenue())) 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
@@ -281,13 +274,7 @@ 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 makeRoomButton (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[]) =
tableList |> Array.collect Entity.GetAllRelated<reservation, floorplan_table> tableList |> Array.collect Entity.GetAllRelated<reservation, floorplan_table>

25
Floorplan/Router.fs Normal file
View 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
}

88
Floorplan/View.fs Normal file
View File

@@ -0,0 +1,88 @@
module DredgePos.Floorplan.View
open DredgePos.Global
open DredgePos.Global.View
open DredgePos.Entities
open DredgePos.Types
open Giraffe.ViewEngine
open DredgeFramework
let VisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-visible-in-mode")
let InvisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-invisible-in-mode")
let ActiveInMode (value: string) = value |> (attr "data-active-in-mode")
let pageContainer (clerk: clerk) roomMenu =
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)

7
Global/Controller.fs Normal file
View 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"|]

11
Global/Router.fs Normal file
View File

@@ -0,0 +1,11 @@
module DredgePos.Global.Router
open Saturn
open Giraffe
let htmlViewWithContext func =
(fun ctx ->
func (snd ctx)
|> htmlView
)
|> warbler

109
Global/View.fs Normal file
View File

@@ -0,0 +1,109 @@
module DredgePos.Global.View
open Giraffe.ViewEngine
open DredgeFramework
let Value = attr "data-value"
let innerText = str
let lang key = language.get key |> str
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 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
View 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
View File

@@ -0,0 +1,2 @@
module DredgePos.Installer.Model

10
Installer/Router.fs Normal file
View File

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

View 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

View File

@@ -0,0 +1,345 @@
module DredgePos.Migrations.PopulateTestData
open DredgeFramework
open DredgePos.Types
open System.IO
let CreatePageFromDirectory index (dir: string) =
let dirName = DirectoryInfo(dir).Name
let printGroup =
match dirName.ToLower() with
| "beer" | "wine" -> (Entity.GetFirstByColumn<print_group> "name" "Beverage").id
| _ -> (Entity.GetFirstByColumn<print_group> "name" "Food").id
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=index
venue_id=1
label=dirName
grid_id=NewGrid.id
} |> 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="Food"
printer_id=1
venue_id=1
} |> ignore
Entity.Create {
id=0
name="Beverage"
printer_id=1
venue_id=1
} |> ignore
path
let CreateDefaultVenue (path: string) =
let venue: venue = {
id=0
name="Megalomania"
}
Entity.Create venue
|>ignore
path
let CreateDefaultClerk (path: string) =
let venue: clerk = {
id=0
name="Josh"
login_code=1408
user_group_id=1
}
Entity.Create venue
|>ignore
path
let CreateDefaultSalesCategories (path: string) =
Entity.Create {
id=0
parent=0
name="Food"
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Food").id
venue_id=1
} |> ignore
Entity.Create {
id=0
parent=0
name="Beverage"
print_group_id=(Entity.GetFirstByColumn<print_group> "name" "Beverage").id
venue_id=1
} |> 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 GridData =
[|
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
|]
|> Array.mapi (fun index current ->
let isFirstColumn = (index % 6) = 0
if not isFirstColumn then current else
let entree = Entrees |> Array.tryItem (index/6)
match entree with
| None -> 0
| 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 -> 0
| 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 getId index =
match Mains |> Array.tryItem index with
| None -> 0
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; getId 2; 0;
0; 0; 0; 0; 0; 0;
getId 3; 0; getId 4; 0; getId 5; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
|]
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 SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Dessert"
let Desserts = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let getId index =
match Desserts |> Array.tryItem index with
| None -> 0
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; 0 ; 0;
0; 0; 0; 0; 0; 0;
0; getId 2; 0; getId 4; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
|]
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 SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Beer"
let Beers = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let grid =
Entity.GetFirstByColumn<order_screen_page_group> "label" "Beer"
|> Entity.GetRelated<grid, order_screen_page_group>
let GridData =
Beers
|> Array.chunkBySize 24
|> Array.map (fun beerPage ->
let getId index =
match beerPage |> Array.tryItem index with
| None -> 0
| Some x -> x.id
[|
getId 0; getId 1; getId 2; getId 3; getId 4 ; getId 5;
0; 0; 0; 0; 0 ;0;
getId 6; getId 7; getId 8; getId 9; getId 10 ; getId 11;
0; 0; 0; 0; 0 ;0;
getId 12; getId 13; getId 14; getId 15; getId 16 ; getId 17;
0; 0; 0; 0; 0 ;0;
getId 18; getId 19; getId 20; getId 21; getId 22 ; getId 23;
0; 0; 0; 0; 0 ;0;
|]
)
|> Array.mapi (fun index beerpage -> (map [$"page{index+1}", beerpage]))
|> jsonEncode
let newGrid = {grid with data=GridData}
Entity.Update newGrid |> ignore
let populateSteakTemperaturesGrid () =
let SalesCategory = Entity.GetFirstByColumn<sales_category> "name" "Steak Temperatures"
let Temps = Entity.GetAllByColumn<item> "sales_category_id" SalesCategory.id
let grid =
Entity.GetFirstByColumn<order_screen_page_group> "label" "Steak Temperatures"
|> Entity.GetRelated<grid, order_screen_page_group>
let getId index =
match Temps |> Array.tryItem index with
| None -> 0
| Some x -> x.id
let GridData =
[|
getId 0; 0; getId 1; 0; getId 2; 0;
0; 0; 0; 0; 0; 0;
getId 3; 0; getId 4; 0; getId 5; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
0; 0; 0; 0; 0; 0;
|]
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" -> "instruction"
| "steak temperatures" -> "instruction"
| _ -> "item"
let categories = (Entity.GetAllByColumn<sales_category> "name" dirName)
let categoryID =
if categories.Length > 0 then categories[0].id
else 1
let newItem = Entity.Create {
id = 0
code = $"{dirName}0{index+1}" |> StringReplace " " ""
sales_category_id=categoryID
name=fileName
item_type=itemType
price1=10
}
let classes =
match dirName.ToLower() with
| "beer" | "dessert" -> "doubleHeight"
| "mains" | "wine" | "steak temperatures" -> "doubleHeight doubleWidth"
| "entrees" -> "doubleWidth"
| _ -> ""
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 run () =
"wwwroot/images/items"
|> CreateDefaultVenue
|> CreateDefaultClerk
|> CreateDefaultPrintGroups
|> CreateDefaultSalesCategories
|> Directory.GetDirectories
|> Array.mapi CreatePageFromDirectory
|> Array.iter CreateItemsAndButtons
|> CreateRooms
|> PopulateGrids

View File

@@ -1,46 +1,35 @@
module PageController module DredgePos.OrderScreen.Controller
open System open DredgePos
open DredgePos.Types
open FSharp.Data
open Microsoft.AspNetCore.Http
open Floorplan
open Giraffe
open DredgeFramework open DredgeFramework
open DredgePos.Types
open Giraffe
open Microsoft.AspNetCore.Http
let loadHomePage(): HttpHandler = let getOrderScreenData (tableNumber: int) =
let variables = map ["title", "Log In"] {|
let scripts = ["dredgepos.authenticate.js"] order_screen_pages = Entity.GetAllInVenue<order_screen_page_group>
let styles = ["dredgepos.authenticate.css"] sales_categories = Entity.GetAllInVenue<sales_category>
print_groups = Entity.GetAllInVenue<print_group>
custom_item = Entity.GetAllByColumn<item> "item_code" "OPEN000" |> first
table = Floorplan.Model.getTable tableNumber
|}
|> ajaxSuccess
|> json
Theme.loadTemplateWithVarsScriptsAndStyles "authenticate" variables scripts styles let loadGrid (gridId: int) =
|> htmlString let grid = Entity.GetById<grid> gridId
let gridHtml = Model.loadGrid gridId
let loadFloorplan (ctx: HttpContext) : HttpHandler = if gridHtml = "Error" then ajaxFail gridHtml
Session.RequireClerkAuthentication ctx else ajaxSuccess {|grid=grid;gridHtml=gridHtml|}
|> json
let roomMenu =
Entity.GetAllInVenue<floorplan_room>
|> Array.map makeRoomButton
|> joinWithNewLine
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 loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler = let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
Session.RequireClerkAuthentication ctx Authenticate.Model.RequireClerkAuthentication ctx
let covers = if tableNumber > 0 then (getTable tableNumber).default_covers else 0 let table = Floorplan.Model.getTable tableNumber
let covers = if tableNumber > 0 then table.default_covers else 0
let coverString = language.getAndReplace "covers" [covers] let coverString = language.getAndReplace "covers" [covers]
let changeCoverNumberButton = if tableNumber > 0 then Theme.loadTemplateWithVars "orderScreen/change_cover_number_button" (map ["covers", coverString]) else "" let changeCoverNumberButton = if tableNumber > 0 then Theme.loadTemplateWithVars "orderScreen/change_cover_number_button" (map ["covers", coverString]) else ""
@@ -51,7 +40,7 @@ let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
let containerAttributes = let containerAttributes =
if tableNumber > 0 then if tableNumber > 0 then
map ["data-table", jsonEncode (getTable tableNumber)] map ["data-table", jsonEncode table]
|> Theme.htmlAttributes |> Theme.htmlAttributes
else "" else ""
@@ -67,8 +56,8 @@ let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
|> joinWithNewLine |> joinWithNewLine
let grids = let grids =
OrderScreen.getAllPageGrids () Model.getAllPageGrids ()
|> Array.map OrderScreen.getPagesHTML |> Array.map Model.getPagesHTML
|> joinWithNewLine |> joinWithNewLine
let coverSelectorButtons = let coverSelectorButtons =
@@ -87,26 +76,14 @@ let loadOrderScreen (ctx: HttpContext) (tableNumber: int) : HttpHandler =
"orderNumber", orderNumber "orderNumber", orderNumber
"changeCoverNumberButton", changeCoverNumberButton "changeCoverNumberButton", changeCoverNumberButton
"covers", coverString "covers", coverString
"salesCategoryOverrideButtons", OrderScreen.generateSalesCategoryOverrideButtons () "salesCategoryOverrideButtons", Model.generateSalesCategoryOverrideButtons ()
"coverSelectorButtons", coverSelectorButtons "coverSelectorButtons", coverSelectorButtons
] ]
let styles = ["dredgepos.orderScreen.css"] let styles = ["dredgepos.orderScreen.css"]
let scripts = ["dredgepos.tables.js";"../external/currency.min.js";"dredgepos.orderScreen.js"; ] let scripts = ["dredgepos.tables.js";"./external/currency.min.js";"dredgepos.orderScreen.js"; ]
let currentClerk = recordToMap <| Session.getCurrentClerk ctx let currentClerk = recordToMap <| Authenticate.Model.getCurrentClerk ctx
let arrays = map ["clerk", currentClerk] let arrays = map ["clerk", currentClerk]
Theme.loadTemplateWithVarsArraysScriptsAndStyles "orderScreen" variables arrays scripts styles Theme.loadTemplateWithVarsArraysScriptsAndStyles "orderScreen" variables arrays scripts styles
|> htmlString |> htmlString
let getOpenTables() =
let rows = openTables()
rows |> jsonEncode
let mergeTables parent child =
mergeTables parent child |> ignore
"done"
let unmergeTables table =
unmergeTable table |> ignore
"done"

View File

@@ -1,4 +1,5 @@
module OrderScreen module DredgePos.OrderScreen.Model
open DredgeFramework open DredgeFramework
open DredgePos open DredgePos
open DredgePos.Types open DredgePos.Types
@@ -26,7 +27,7 @@ let getImageButtonData (button: button) =
{| {|
extra_data = extraData extra_data = extraData
text = item.item_name text = item.name
|} |}
let getGridButtonData (button: button) = let getGridButtonData (button: button) =
@@ -38,7 +39,7 @@ let getGridButtonData (button: button) =
let grid = Entity.GetById<grid> gridId let grid = Entity.GetById<grid> gridId
{| {|
extra_data = map ["data-grid", jsonEncode gridId] |> htmlAttributes extra_data = map ["data-grid", jsonEncode gridId] |> htmlAttributes
text = grid.grid_name text = grid.name
|} |}
let getActionData (button: button) (action: string) = let getActionData (button: button) (action: string) =
@@ -92,7 +93,7 @@ let renderButton (buttonId: int) =
loadTemplateWithVars "orderScreen/grid_button" vars loadTemplateWithVars "orderScreen/grid_button" vars
let renderPage (grid: grid) (buttonHTML: string) = let renderPage (grid: grid) (buttonHTML: string) =
let vars = map ["pageButtons", buttonHTML; "rows", string grid.grid_rows; "cols", string grid.grid_cols] let vars = map ["pageButtons", buttonHTML; "rows", string grid.rows; "cols", string grid.cols]
loadTemplateWithVars "orderScreen/page" vars loadTemplateWithVars "orderScreen/page" vars
let renderPageGroup (pageGroup: order_screen_page_group) (pageHTML: string) = let renderPageGroup (pageGroup: order_screen_page_group) (pageHTML: string) =
@@ -113,7 +114,7 @@ let generateSalesCategoryOverrideButtons () =
let renderGrid (grid: grid) = let renderGrid (grid: grid) =
let gridData = grid.grid_data |> Decode.Auto.fromString<Map<string, int[]>> let gridData = grid.data |> Decode.Auto.fromString<Map<string, int[]>>
match gridData with match gridData with
| Error _ -> "Error" | Error _ -> "Error"
@@ -133,5 +134,3 @@ let getPagesHTML (gridInfo: grid * order_screen_page_group) =
renderGrid grid renderGrid grid
|> renderPageGroup pageGroup |> renderPageGroup pageGroup

15
OrderScreen/Router.fs Normal file
View File

@@ -0,0 +1,15 @@
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))
get "/" (warbler (fun ctx -> Controller.loadOrderScreen (snd ctx) 0))
getf "/%i" (fun number -> (warbler (fun ctx -> Controller.loadOrderScreen (snd ctx) number)))
}

View File

@@ -1,3 +0,0 @@
module Orders
let getHighestOrderNumber () = 6

View File

@@ -1,71 +1,27 @@
namespace WebApplication namespace DredgePos
open DredgePos open DredgePos
open DredgePos.Types
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 floorplanRouter = router {
pipe_through browser
post "/authenticateClerk" (bindJson<int> (handlePostRoute AjaxController.loginWithLoginCode) )
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 "/getKeyboardLayout/%s" AjaxController.getKeyboardLayout
get "/languageVars" (json <| AjaxController.getLanguageVars)
get "/getOpenTables" (json <| Floorplan.getActiveTables (DredgeFramework.getCurrentVenue()))
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 orderScreenRouter = router {
pipe_through browser
getf "/getOrderScreenData/%i" AjaxController.getOrderScreenData
getf "/getGridHtml/%i" AjaxController.loadGrid
post "/updateCovers" (bindJson<floorplan_table> (fun table -> Entity.Update table |> Array.head |> DredgeFramework.ajaxSuccess |> json))
}
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
get "/order" (warbler (fun ctx -> PageController.loadOrderScreen (snd ctx) 0)) forward "/order" DredgePos.OrderScreen.Router.router
getf "/order/%i" (fun number -> (warbler (fun ctx -> PageController.loadOrderScreen (snd ctx) number))) forward "/login" DredgePos.Authenticate.Router.router
forward "/ajax" floorplanRouter forward "/reservations" DredgePos.Reservations.Router.router
forward "/orderScreen" orderScreenRouter forward "/install" DredgePos.Installer.Router.router
} }
let app = application { let app = application {
use_mime_types [(".woff", "application/font-woff")] 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"
} }
run app run app

View 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

View File

@@ -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
View 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 )
}

9
config.json Normal file
View File

@@ -0,0 +1,9 @@
{
"database": {
"db_name": "dredgepos",
"username": "postgres",
"password": "root",
"host": "localhost",
"port": 5432
}
}

5
package-lock.json generated
View File

@@ -578,6 +578,11 @@
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
}, },
"tsc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q=="
},
"typescript": { "typescript": {
"version": "4.4.4", "version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",

View File

@@ -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",
"tsc": "^2.0.4",
"typescript": "^4.4.4" "typescript": "^4.4.4"
}, },
"description": "", "description": "",
"license": "", "license": "",
"repository": "" "repository": "",
"scripts": {
"sass": "sass sass:wwwroot/styles",
"typescript": "tsc",
"build": "npm run sass && npm run typescript"
}
} }

10
sql.log Normal file
View 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
);

View File

@@ -7,12 +7,12 @@
"noImplicitAny":true, "noImplicitAny":true,
"removeComments":false, "removeComments":false,
"preserveConstEnums":true, "preserveConstEnums":true,
"outDir":"../js", "outDir":"wwwroot/scripts",
"target":"ES2016", "target":"ES2016",
"sourceMap":true, "sourceMap":true,
"moduleResolution": "node" "moduleResolution": "node"
}, },
"include":[ "include":[
"*" "typescript"
] ]
} }

View File

@@ -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()

View File

@@ -11,7 +11,7 @@ interface floorplan{
tableLayer: Konva.Layer tableLayer: Konva.Layer
rooms: room[] rooms: room[]
tables: floorplan_table[] tables: floorplan_table[]
decorations: decoration[] decorations: floorplan_decoration[]
activeTableNumbers: number[] activeTableNumbers: number[]
selectedTableNumber: number selectedTableNumber: number
selectedDecorationId: number selectedDecorationId: number
@@ -25,7 +25,7 @@ interface floorplan{
interface floorplan_data{ interface floorplan_data{
tables: floorplan_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 = () => {
@@ -122,7 +122,7 @@ const loadRoom = (roomToLoad: room) => {
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')) {
@@ -173,7 +173,7 @@ const createTableShape = (table: floorplan_table) => {
stroke: "black", stroke: "black",
strokeWidth: 4, strokeWidth: 4,
draggable: false, draggable: false,
listening: true listening: true,
}); });
break; break;
default: default:
@@ -276,7 +276,7 @@ const saveTable = (tableToUpdate: floorplan_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[]) => {
@@ -347,7 +347,7 @@ const createEmptyReservation = (covers: number) => {
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) => {
@@ -366,7 +366,7 @@ const addReservationName = (name: string) => {
hideVirtualKeyboard() hideVirtualKeyboard()
const reservation = Floorplan.reservations.filter(reservation => reservation.floorplan_table_id == getSelectedTableData().id)[0] const reservation = Floorplan.reservations.filter(reservation => reservation.floorplan_table_id == getSelectedTableData().id)[0]
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) => {
@@ -386,7 +386,7 @@ const updateTableData = (tableToRemove: floorplan_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: floorplan_table) => { const tableUnreserved = (table: floorplan_table) => {
@@ -469,21 +469,21 @@ const tableDblClicked = (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,
}); });
@@ -500,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) => {
@@ -541,22 +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, 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
@@ -567,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')
@@ -576,22 +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 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)
@@ -600,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()
@@ -616,8 +616,10 @@ const setRoomBackground = (roomToLoad: room) => {
const height = Floorplan.floorplanDiv.height() const height = Floorplan.floorplanDiv.height()
if(roomToLoad.background_image) { if(roomToLoad.background_image) {
Floorplan.floorplanDiv.css("background-image", `url(images/rooms/${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")
} }
} }
@@ -735,7 +737,7 @@ 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: floorplan_table) => { const tableAdded = (table: floorplan_table) => {
@@ -763,7 +765,7 @@ 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: floorplan_table) => { const tableDeleted = (deletedTable: floorplan_table) => {
@@ -782,7 +784,7 @@ const mergeTables = (table1: floorplan_table, table2: floorplan_table ) => {
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', floorplan_table>) => { const tablesMerged = (tables: Record<'child'|'parent'|'merged', floorplan_table>) => {
@@ -795,7 +797,7 @@ const tablesMerged = (tables: Record<'child'|'parent'|'merged', floorplan_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', floorplan_table>) => { const tablesUnmerged = (tables: Record<'child'|'parent', floorplan_table>) => {
const parentTable = tables['parent'] const parentTable = tables['parent']
@@ -815,7 +817,7 @@ const transferTables = (origin: floorplan_table, destination: floorplan_table) =
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", floorplan_table>) => { const tableTransferred = (tables: Record<"origin"|"destination", floorplan_table>) => {

View File

@@ -123,7 +123,7 @@ const addItemToOrderBox = (orderItem:orderItem) => {
const existingRow = orderBox const existingRow = orderBox
.find('tr') .find('tr')
.filterByData('item', orderItem.item) .filterByData('item', orderItem.item)
.filterByData('print_group', orderItem.print_group) .filterByData('print_group', orderItem.print_group_id)
.filterByData('cover', orderItem.cover) .filterByData('cover', orderItem.cover)
.last() .last()
@@ -186,13 +186,13 @@ const addInstructionToOrderBox = (instruction: orderItem) => {
const addNewItem = (item: item, qty = 1) => { const addNewItem = (item: item, qty = 1) => {
const salesCategory = OrderScreen.sales_categories.where('id', item.item_category) const salesCategory = OrderScreen.sales_categories.where('id', item.category)
const printGroup = OrderScreen.print_group_override ?? OrderScreen.print_groups.where('id', salesCategory.print_group) const printGroup = OrderScreen.print_group_override ?? OrderScreen.print_groups.where('id', salesCategory.print_group_id)
const orderItem : orderItem = { const orderItem : orderItem = {
id: OrderScreen.order_item_id_generator.next().value, id: OrderScreen.order_item_id_generator.next().value,
item: item, item: item,
qty: qty, qty: qty,
print_group: printGroup, print_group_id: printGroup,
cover: OrderScreen.selected_cover, cover: OrderScreen.selected_cover,
} }
@@ -258,17 +258,17 @@ const renderOrderBox = () => {
const createOrderRow = (orderItem: orderItem) => { const createOrderRow = (orderItem: orderItem) => {
const row = $('.orderBoxTable').EmptyRow() const row = $('.orderBoxTable').EmptyRow()
const price = money(orderItem.item.price1) const price = money(orderItem.item.price1)
const itemCellText = $('<span/>').text(orderItem.item.item_name) const itemCellText = $('<span/>').text(orderItem.item.name)
row row
.addClass(`${orderItem.item.item_type}Row`) .addClass(`${orderItem.item.item_type}Row`)
.setColumnValue(lang('qty_header'), orderItem.qty) .setColumnValue(lang('qty_header'), orderItem.qty)
.setColumnValue(lang('price_header'), price) .setColumnValue(lang('price_header'), price)
.setColumnValue(lang('id_header'), orderItem.item.id) .setColumnValue(lang('id_header'), orderItem.item.id)
.setColumnValue(lang('total_price_header'), price.multiply(orderItem.qty)) .setColumnValue(lang('total_price_header'), price.multiply(orderItem.qty))
.setColumnValue(lang('printgroup_header'), orderItem.print_group?.name) .setColumnValue(lang('printgroup_header'), orderItem.print_group_id?.name)
.data('order-item-id', orderItem.id) .data('order-item-id', orderItem.id)
.data('order-item-id', orderItem.id) .data('order-item-id', orderItem.id)
.data('print_group', orderItem.print_group) .data('print_group', orderItem.print_group_id)
.data('cover', orderItem.cover) .data('cover', orderItem.cover)
.data('item', orderItem.item) .data('item', orderItem.item)
.find('td.itemCell') .find('td.itemCell')
@@ -303,7 +303,7 @@ const itemButtonClicked = (e: JQuery.TriggeredEvent) => {
const gridButtonClicked = (e: JQuery.TriggeredEvent) => { const gridButtonClicked = (e: JQuery.TriggeredEvent) => {
const button = $(e.target).closest('.posButton') const button = $(e.target).closest('.posButton')
const grid : number = button.data('grid') const grid : number = button.data('grid')
ajax(`/orderScreen/getGridHtml/${grid}`, null, null,gridHtmlGenerated, null, null) ajax(`/order/getGridHtml/${grid}`, null, null,gridHtmlGenerated, null, null)
} }
@@ -319,17 +319,17 @@ const gridHtmlGenerated = (gridData: {gridHtml:string, grid: grid}) => {
gridContainer gridContainer
.show() .show()
.width(gridCellWidth * grid.grid_cols) .width(gridCellWidth * grid.cols)
.children('.gridContainerHeader') .children('.gridContainerHeader')
.children('span') .children('span')
.text(grid.grid_name) .text(grid.name)
.parent() .parent()
.parent() .parent()
.find('.pageGroup') .find('.pageGroup')
.html(gridHtml) .html(gridHtml)
.show() .show()
.parent() .parent()
.height(gridCellHeight * grid.grid_rows) .height(gridCellHeight * grid.rows)
.closest('.gridContainer') .closest('.gridContainer')
.find('.pageNavigation') .find('.pageNavigation')
.toggle(gridContainer.find('.gridPage').length > 1) .toggle(gridContainer.find('.gridPage').length > 1)
@@ -495,7 +495,7 @@ const freetextSubmitted = (text: string) => {
const item = Object.assign({}, OrderScreen.custom_item) const item = Object.assign({}, OrderScreen.custom_item)
item.item_type = 'instruction' item.item_type = 'instruction'
item.item_name = text item.name = text
addNewItem(item) addNewItem(item)
@@ -509,7 +509,7 @@ const customItemTextSubmitted = (text: string) => {
const item = Object.assign({}, OrderScreen.custom_item) const item = Object.assign({}, OrderScreen.custom_item)
item.item_type = 'item' item.item_type = 'item'
item.item_name = text item.name = text
item.price1 = price.intValue item.price1 = price.intValue
addNewItem(item) addNewItem(item)
@@ -571,7 +571,7 @@ const changeCoverNumberPromptSubmitted = (value: string) => updateCoverNumbers(N
const updateCoverNumbers = (covers: number) => { const updateCoverNumbers = (covers: number) => {
let newTable = Object.assign({}, OrderScreen.table) let newTable = Object.assign({}, OrderScreen.table)
newTable.default_covers = covers newTable.default_covers = covers
ajax('/orderScreen/updateCovers', newTable, 'post', coverNumbersUpdated, null, null) ajax('/order/updateCovers', newTable, 'post', coverNumbersUpdated, null, null)
} }
const coverNumbersUpdated = (newTable: floorplan_table) => { const coverNumbersUpdated = (newTable: floorplan_table) => {
@@ -600,5 +600,5 @@ const generateCoverSelector = () => {
$(() => { $(() => {
OrderScreen.table = $('#pageContainer').data('table') || null OrderScreen.table = $('#pageContainer').data('table') || null
ajax('/orderScreen/getOrderScreenData/1', null, 'get', setupOrderScreen, null, null) ajax('/order/getOrderScreenData/1', null, 'get', setupOrderScreen, null, null)
}) })

View File

@@ -228,7 +228,6 @@ let showVirtualNumpad = (heading: string, maxlength = 4, isPassword: boolean, al
index = index + 1 index = index + 1
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];

View File

@@ -10,7 +10,7 @@ interface order {
interface orderItem { interface orderItem {
id: number id: number
qty: number qty: number
print_group: print_group print_group_id: print_group
item: item item: item
cover: number cover: number
} }
@@ -18,7 +18,7 @@ interface orderItem {
interface print_group { interface print_group {
id: number, id: number,
name: string, name: string,
printer: number, printer_id: number,
venue_id: number, venue_id: number,
} }
@@ -50,21 +50,21 @@ interface floorplan_table {
id: number id: number
} }
interface decoration { interface floorplan_decoration {
id: number id: number
decoration_room: number room_id: number
decoration_pos_x: number pos_x: number
decoration_pos_y: number pos_y: number
decoration_rotation: number rotation: number
decoration_width: number width: number
decoration_height: number height: number
decoration_image: string image: string
venue_id: number venue_id: number
} }
interface room { interface room {
id: number id: number
room_name: string name: string
background_image: string background_image: string
venue_id: number venue_id: number
} }
@@ -87,26 +87,22 @@ interface keyboard {
} }
interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number} interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number}
interface grid {id: number; grid_name: string; grid_rows: number; grid_cols: number; grid_data: string} interface grid {id: number; name: string; rows: number; cols: number; data: string}
interface item { interface item {
id: number id: number
item_code: string code: string
item_category: number category: number
item_name: string name: string
item_type: string item_type: string
price1: number price1: number
price2: number
price3: number
price4: number
price5: number
} }
type sales_category = { type sales_category = {
id: number id: number
parent: number parent: number
name: string name: string
print_group: string print_group_id: string
venue_id: number venue_id: number
} }

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Some files were not shown because too many files have changed in this diff Show More