38 Commits

Author SHA1 Message Date
dredgy
3f4d6b650e Merge pull request #15 from dredgy/make_order_screen_reactive
Fixed slight bug in void system
2022-09-14 11:51:11 +10:00
c146a7209d Fixed slight bug in void system 2022-09-14 11:48:25 +10:00
dredgy
2e213d95ad Merge pull request #14 from dredgy/modals
Added a simple close modal feature.
2022-08-22 14:23:21 +10:00
dredgy
89e9e0b7a7 Added a simple close modal feature. 2022-08-22 14:22:54 +10:00
dredgy
dd74054930 Merge pull request #13 from dredgy/make_order_screen_reactive
Make order screen reactive
2022-08-01 10:52:56 +10:00
dredgy
5094bbaf2b Order Screen is now Reactive 2022-08-01 10:52:15 +10:00
dredgy
f824dd3beb Orderscreen fully reactive 2022-08-01 09:59:53 +10:00
dredgy
42e96b1769 Can now void individual instruction rows 2022-07-31 22:16:08 +10:00
334391eb38 Corrected double stacking of instruction items 2022-07-31 18:50:11 +10:00
dredgy
76b7fdea6c Void functionality complete 2022-07-26 15:54:09 +10:00
dredgy
9fa1f7c888 Void stuff not working still 2022-07-25 15:46:51 +10:00
dredgy
983b7ae909 Void functions mostly working, pending testing. 2022-07-25 12:59:23 +10:00
dredgy
0507fcff65 Adding instructions and items now complete 2022-07-25 11:58:57 +10:00
caae3a4380 Most reactive features done 2022-07-25 06:52:08 +10:00
dredgy
89b881e081 Merge pull request #12 from dredgy/test_data_update
Update test data
2022-07-09 19:45:23 +10:00
9698671de7 Preparing for reactivity 2022-07-07 18:04:39 +10:00
dredgy
f133284309 Merge pull request #11 from dredgy/make_order_screen_reactive
Light fixes
2022-07-07 15:56:49 +10:00
dredgy
9558074c4f Slight refactoring to button action attributes 2022-07-07 15:42:40 +10:00
dredgy
ac30d8147e Few updates to cover selector width calculation 2022-07-07 14:48:32 +10:00
dredgy
b9a0b578e3 Updated typescript 2022-07-06 14:28:55 +10:00
dredgy
0a2ffa123a Merge pull request #10 from dredgy/test_data_update
instruction row now hides price if $0
2022-07-03 19:27:44 +10:00
510bd8cfb9 instruction row now hides price if $0 2022-07-03 19:09:31 +10:00
dredgy
b33db1987b Merge pull request #9 from dredgy/test_data_update
Migration now orders pages correctly, adds demo floorplan. Some small…
2022-07-02 23:29:21 +10:00
aa37b24293 Migration now orders pages correctly, adds demo floorplan. Some small fixes on order screen. 2022-07-02 23:28:42 +10:00
dredgy
627843f3a3 Merge pull request #8 from dredgy/orderscreen_view
Orderscreen view
2022-07-02 14:23:28 +10:00
6c7bb9eff4 Order Screen fully ported to Giraffe View Engine 2022-07-02 14:22:52 +10:00
905adcd7bd Migration test data complete 2022-07-01 15:25:10 +10:00
5e78701b0b Buttons now render entirely in Giraffe 2022-06-30 23:43:16 +10:00
420c6530e0 Lost source code, switching back to main branch 2022-06-29 22:03:45 +10:00
100a772297 Updates to Order Screen 2022-05-12 20:03:39 +10:00
a587423d3e view progression 2022-03-07 21:36:38 +10:00
cc7d06e78b Change to background image loader 2022-02-27 11:57:52 +10:00
05a1a71e6b CSS background in quotes 2022-02-26 22:31:59 +10:00
dredgy
b6aadc072f Merge pull request #7 from dredgy/installer
Migration system added.
2022-02-26 22:24:07 +10:00
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
178 changed files with 2832 additions and 2120 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,16 +1,15 @@
module DredgePos.Authenticate.Controller
open Giraffe
open DredgeFramework
open Microsoft.AspNetCore.Http
open DredgePos.Global.Controller
let loadAuthenticatePage (): HttpHandler =
let variables = map ["title", "Log In"]
let scripts = ["dredgepos.authenticate.js"]
let styles = ["dredgepos.authenticate.css"]
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
Theme.loadTemplateWithVarsScriptsAndStyles "authenticate" variables scripts styles
|> htmlString
View.index scripts styles metaTags
let loginWithLoginCode (context: HttpContext) (login_code: int) =
if Model.clerkLogin login_code context then ajaxSuccess "success"

View File

@@ -11,10 +11,10 @@ let getClerkByLoginCode (loginCode: int) =
let clerk =
select {
table "clerks"
where (eq "clerk_login_code" loginCode)
where (eq "login_code" loginCode)
take 1
}
|> db.Select<clerk>
|> Database.Select<clerk>
|> EnumerableToArray
if (clerk |> length) > 0 then
@@ -26,19 +26,19 @@ let deleteSession sessionId context =
delete {
table "sessions"
where (eq "session_id" sessionId)
} |> db.Delete |> ignore
} |> Database.Delete |> ignore
Browser.deleteCookie "dredgepos_clerk_logged_in" context
let deleteSessionByClerkId clerk_id context =
delete {
table "sessions"
where (eq "clerk_id" clerk_id)
} |> db.Delete |> ignore
} |> Database.Delete |> ignore
Browser.deleteCookie "dredgepos_clerk_logged_in" 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
let newSessionId = (Guid.NewGuid().ToString "N") + (Guid.NewGuid().ToString "N")
@@ -53,18 +53,14 @@ let createNewSession (clerk: clerk) context =
table "sessions"
value newSession
}
|> db.Insert
|> Database.Insert
|> ignore
Browser.setCookie "dredgepos_clerk_logged_in" newSessionId (DateTimeOffset.UtcNow.AddHours(24.0)) context
let sessionExists (sessionId: string) context =
let sessions =
select {
table "sessions"
where (eq "session_id" sessionId)
} |> db.Select<session>
let sessions = Entity.GetAllByColumn<session> "session_id" sessionId
match sessions |> length with
| 0 -> false
@@ -78,11 +74,11 @@ let sessionExists (sessionId: string) context =
false
let checkAuthentication clerk =
let existingClerk = getClerkByLoginCode clerk.clerk_login_code
let existingClerk = getClerkByLoginCode clerk.login_code
existingClerk.IsSome
&& existingClerk.Value.id = clerk.id
&& existingClerk.Value.clerk_name = clerk.clerk_name
&& existingClerk.Value.clerk_login_code = clerk.clerk_login_code
&& existingClerk.Value.name = clerk.name
&& existingClerk.Value.login_code = clerk.login_code
let getLoginCookie context = Browser.getCookie "dredgepos_clerk_logged_in" context
@@ -91,7 +87,7 @@ let getSession (sessionId: string) =
select {
table "sessions"
where (eq "session_id" sessionId)
} |> db.Select<session>
} |> Database.Select<session>
match sessions |> length with
| 0 -> {session_id = ""; clerk_json = ""; clerk_id= 0; expires= 0; id=0}
@@ -99,7 +95,7 @@ let getSession (sessionId: string) =
let getCurrentClerk 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
| "" ->
Browser.redirect "/login" context

View File

@@ -3,7 +3,7 @@
open Saturn
open Giraffe
let homepage = (warbler (fun _ -> Controller.loadAuthenticatePage() ))
let homepage = (warbler (fun _ -> htmlView Controller.loadAuthenticatePage ))
let handlePostRoute<'a> handlerFunction post next ctx = json (handlerFunction ctx post) next ctx
let pipeline = pipeline{

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

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

View File

@@ -1,46 +1,98 @@
module db
module Database
open Dapper
open Dapper.FSharp
open Dapper.FSharp.PostgreSQL
open DredgeFramework
open DredgePos.Types
open Npgsql
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 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.Globalization
open FSharp.Data.Sql
open System
open System.Drawing
open System.IO
open System.Linq
open System.Xml;
open System.Xml.XPath;
open System.Xml.Xsl
open DredgePos.Types
open FSharp.Reflection
open Thoth.Json.Net
@@ -35,8 +34,9 @@ let EnumerableToArray (enumerable: IEnumerable<'T>) = enumerable.ToArray()
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 FileExists = File.Exists
let length (variable: 'T[]) = variable.Length
let first (array: 'a[]) = array[0]
@@ -101,3 +101,13 @@ let GetImageSize image =
loadedImage.Width, loadedImage.Height
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
open Dapper
open Dapper.FSharp
open DredgeFramework
open Pluralize.NET.Core
@@ -15,7 +16,7 @@ let Create (record: 'x)=
value record
excludeColumn "id"
}
|> db.InsertOutput
|> Database.InsertOutput
|> first
@@ -28,7 +29,7 @@ let inline Update (record: ^x) =
where (eq "id" id)
excludeColumn "id"
}
|> db.Update
|> Database.Update
let GetAll<'x> =
let tableName = GetDatabaseTable<'x>
@@ -36,7 +37,7 @@ let GetAll<'x> =
select {
table tableName
}
|> db.Select<'x>
|> Database.Select<'x>
let GetAllByColumn<'x> (column: string) (value: obj) =
let tableName = GetDatabaseTable<'x>
@@ -44,7 +45,9 @@ let GetAllByColumn<'x> (column: string) (value: obj) =
select {
table tableName
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 GetById<'x> (id: int) = GetAllByColumn<'x> "id" id |> first
@@ -67,7 +70,7 @@ let DeleteById<'x> id =
delete {
table tableName
where (eq "id" id)
} |> db.Delete |> ignore
} |> Database.Delete |> ignore
entity

View File

@@ -13,7 +13,7 @@ type reservation = {
[<CLIMutable>]
type venue = {
id: int
venue_name: string
name: string
}
[<CLIMutable>]
@@ -38,7 +38,7 @@ type floorplan_table = {
type print_group = {
id: int
name: string
printer: int
printer_id: int
venue_id: int
}
@@ -47,14 +47,14 @@ type sales_category = {
id: int
parent: int
name: string
print_group: int
print_group_id: int
venue_id: int
}
[<CLIMutable>]
type floorplan_room = {
type room = {
id: int
room_name: string
name: string
background_image: string
venue_id: int
}
@@ -62,18 +62,23 @@ type floorplan_room = {
[<CLIMutable>]
type floorplan_decoration = {
id: int
decoration_room: int
decoration_pos_x: int
decoration_pos_y: int
decoration_rotation: int
decoration_width: int
decoration_height: int
decoration_image: string
room_id: int
pos_x: int
pos_y: int
rotation: int
width: int
height: int
image: string
venue_id: int
}
[<CLIMutable>]
type clerk = {id: int; clerk_name: string; clerk_login_code: int; clerk_usergroup: int}
type clerk = {
id: int
name: string
login_code: int
user_group_id: int
}
[<CLIMutable>]
type session = {id: int; session_id: string; clerk_json: string; clerk_id: int; expires: int}
@@ -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}
[<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>]
type button = {
@@ -100,13 +105,30 @@ type button = {
[<CLIMutable>]
type item = {
id: int
item_code: string
item_category: int
item_name: string
code: string
sales_category_id: int
name: string
item_type: string
price1: int
price2: int
price3: int
price4: int
price5: int
}
[<CLIMutable>]
type db_config = {
db_name: string
username: string
password: string
host: string
port: int
}
[<CLIMutable>]
type config = {
database: db_config
}
[<CLIMutable>]
type migration = {
id: int
name: string
timestamp: int
}

View File

@@ -13,18 +13,31 @@
<Compile Include="Core\Language.module.fs" />
<Compile Include="Core\Theme.module.fs" />
<Compile Include="Printer.module.fs" />
<Compile Include="Global\View.fs" />
<Compile Include="Global\Controller.fs" />
<Compile Include="Global\Router.fs" />
<Compile Include="Entities\Floorplan_Decorations\Model.fs" />
<Compile Include="Entities\Floorplan_Decorations\View.fs" />
<Compile Include="Entities\Floorplan_Decorations\Controller.fs" />
<Compile Include="Entities\Floorplan_Decorations\Router.fs" />
<Compile Include="Entities\Buttons\Model.fs" />
<Compile Include="Authenticate\Model.fs" />
<Compile Include="Authenticate\View.fs" />
<Compile Include="Authenticate\Controller.fs" />
<Compile Include="Authenticate\Router.fs" />
<Compile Include="Ajax\Controller.fs" />
<Compile Include="Ajax\Router.fs" />
<Compile Include="Migrations\CreateDatabaseSchema.fs" />
<Compile Include="Migrations\PopulateTestData.fs" />
<Compile Include="Installer\Model.fs" />
<Compile Include="Installer\Controller.fs" />
<Compile Include="Installer\Router.fs" />
<Compile Include="Floorplan\Model.fs" />
<Compile Include="Floorplan\View.fs" />
<Compile Include="Floorplan\Controller.fs" />
<Compile Include="Floorplan\Router.fs" />
<Compile Include="OrderScreen\Model.fs" />
<Compile Include="OrderScreen\View.fs" />
<Compile Include="OrderScreen\Controller.fs" />
<Compile Include="OrderScreen\Router.fs" />
<Compile Include="Reservations\Model.fs" />
@@ -106,168 +119,15 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\alert.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\authenticate.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\backupPayentsplitter.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\button.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\buttonWithImage.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\BeerPage1.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\BeerPage2.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\DessertsPage1.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\MainsPage1.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\StartersPage1.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categories\WinePage1.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categoryButton.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\categoryMenu.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\components\posButton.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\decoratorItem.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\decoratorRow.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\floorplan.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\keyboards.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderBox.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderBoxTable.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen.tpl - Copy.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\button_image.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\change_cover_number_button.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\cover_selector.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\grid_button.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\grid_container.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\page.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\page_group.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\orderScreen\page_group_button.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\paymentSplitter.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\popupBoxItems.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\roomButton.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\virtualKeyboard.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="views\restaurant\virtualNumpad.tpl.htm">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Folder Include="wwwroot\styles" />
</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="FSharp.Data" Version="4.0.1" />
<PackageReference Include="Giraffe" Version="6.0.0-alpha-2" />
<PackageReference Include="Npgsql" Version="6.0.0" />
<PackageReference Include="Pluralize.NET.Core" Version="1.0.0" />
<PackageReference Include="Saturn" Version="0.15.0-preview03" />
<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="Saturn" Version="0.15.0" />
<PackageReference Include="System.Drawing.Common" Version="6.0.0-preview.5.21301.5" />
<PackageReference Include="Thoth.Json.Net" Version="5.0.0" />
<PackageReference Update="FSharp.Core" Version="6.0.2-beta.21631.1" />

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

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

View File

@@ -1,27 +1,13 @@
module DredgePos.Entities.Floorplan_Decorations.Controller
open DredgeFramework
open System
open System.IO
let getImageHTML (imageName: string, imageUrl: string) =
let vars = map [
"image_name", imageName
"image_url", imageUrl
]
Theme.loadTemplateWithVars "decoratorItem" vars
let getImageRowHtml (imagesInRow: string[]) =
let vars = map ["decorations", String.Join("", imagesInRow)]
Theme.loadTemplateWithVars "decoratorRow" vars
let generateDecorator () =
"wwwroot/images/decorations"
|> Directory.GetFiles
|> Array.filter Model.isImageFile
|> Array.map Model.GetFileNameWithoutExtension
|> Array.map Model.getImageName
|> Array.map getImageHTML
|> Array.map View.decoratorItem
|> Array.chunkBySize 4
|> Array.map getImageRowHtml
|> JoinArray ""
|> Array.map View.decoratorRow

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

View File

@@ -2,15 +2,18 @@
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: floorplan_room) =
let makeRoomButton (room: room) =
let vars = map [
"roomId", room.id |> string
"roomName", room.room_name
"roomName", room.name
]
Theme.loadTemplateWithVars "roomButton" vars
@@ -47,7 +50,7 @@ let getFloorplanData (id: int) =
tables = tableList
decorations = Entity.GetAllInVenue<floorplan_decoration>
activeTableNumbers = Model.getActiveTables (getCurrentVenue())
rooms = Entity.GetAllInVenue<floorplan_room>
rooms = Entity.GetAllInVenue<room>
reservations = reservationList
|}
|> ajaxSuccess
@@ -70,24 +73,24 @@ let deleteTable (table: floorplan_table) =
table |> ajaxSuccess |> json
let transferTable (origin, destination) =
Model.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.decoration_image
let image = "wwwroot/images/decorations/" + data.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
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
}
@@ -105,23 +108,12 @@ let DeleteDecoration (decorationToDelete: floorplan_decoration) =
|> ajaxSuccess
|> json
let loadFloorplan (ctx: HttpContext) : HttpHandler =
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
let roomMenu =
Entity.GetAllInVenue<floorplan_room>
|> Array.map makeRoomButton
|> joinWithNewLine
let variables = map [
"title", "Floorplan"
"roomMenu", roomMenu
"decorator", Entities.Floorplan_Decorations.Controller.generateDecorator()
]
let styles = ["dredgepos.floorplan.css"]
let scripts = ["./external/konva.min.js" ; "dredgepos.floorplan.js"]
let currentClerk = recordToMap <| Authenticate.Model.getCurrentClerk ctx
let arrays = map ["clerk", currentClerk]
htmlString <| Theme.loadTemplateWithVarsArraysScriptsAndStyles "floorplan" variables arrays scripts styles
View.index styles scripts metaTags currentClerk (Floorplan_Decorations.Controller.generateDecorator ()) roomMenu

View File

@@ -39,7 +39,7 @@ let tablesInRoom (roomId: int) = //Get a list of all tables in a particular room
table "floorplan_tables"
where (eq "room_id" roomId)
}
|> db.Select<floorplan_table>
|> Database.Select<floorplan_table>
let getActiveTables (venueId: int) =
@@ -47,7 +47,7 @@ let getActiveTables (venueId: int) =
table "floorplan_tables"
where (eq "venue_id" venueId)
}
|> db.Select
|> Database.Select
|> Array.filter tableIsOpen
|> Array.map (fun table -> table.table_number)
@@ -87,28 +87,34 @@ let saveOrderToTable orderXML tableNumber =
File.WriteAllText(tableFile, tableXML)
let getTable (tableNumber : int) =
let getTableSafely (tableNumber: int) =
let query = select {
table "floorplan_tables"
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
}
let result = query |> db.Select<floorplan_table>
result |> first
query
|> Database.Select<floorplan_table>
|> Array.tryItem 0
let getTable (tableNumber : int) =
match getTableSafely tableNumber with
| None -> failwith $"Table {tableNumber} not found in current venue"
| Some table -> table
let getTableById (id : int) =
select {
table "floorplan_tables"
where (eq "id" id)
}
|> db.Select<floorplan_table>
|> Database.Select<floorplan_table>
|> first
let getRoom (roomId: int) =
select {
table "floorplan_rooms"
table "rooms"
where (eq "id" roomId)
} |> db.Select<floorplan_room> |> first
} |> Database.Select<room> |> first
let updateTablePosition (floorplanTable: floorplan_table) = Entity.Update floorplanTable
@@ -117,7 +123,7 @@ let createEmptyReservation (reservation: reservation) =
table "floorplan_tables"
set {| status = 2 |}
where(eq "id" reservation.floorplan_table_id)
} |> db.Update |> ignore
} |> Database.Update |> ignore
Entity.Create reservation
@@ -154,7 +160,7 @@ let tableExists (tableNumber: int) =
select{
table "floorplan_tables"
where (eq "table_number" tableNumber + eq "venue_id" (getCurrentVenue()))
} |> db.Select<floorplan_table> |> length
} |> Database.Select<floorplan_table> |> length
match numberOfResults with
| 0 ->
@@ -169,14 +175,14 @@ let tableExists (tableNumber: int) =
| _ ->
let parentTableData = getTable allTables[0]
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()]
| _ ->
let tableData = getTable tableNumber
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) =
@@ -184,7 +190,7 @@ let addNewTableWithoutOutput (newTable: floorplan_table) =
table "floorplan_tables"
value newTable
}
|> db.Insert
|> Database.Insert
let addNewTable (newTable: floorplan_table) = Entity.Create newTable
@@ -238,7 +244,7 @@ let mergeTables parent child = //Merge two tables together
default_covers = parentTable.default_covers + childTable.default_covers
|}
where (eq "table_number" parent + eq "venue_id" (getCurrentVenue()))
} |> db.Update |> ignore
} |> Database.Update |> ignore
Entity.DeleteById<floorplan_table> newChildTable.id
|> ignore
@@ -251,7 +257,7 @@ let updateUnmergedTables parentTable childTable =
table "floorplan_tables"
set parentTable
where(eq "table_number" parentTable.table_number + eq "venue_id" (getCurrentVenue()))
} |> db.Update |> ignore
} |> Database.Update |> ignore
addNewTableWithoutOutput childTable |> ignore
true

View File

@@ -1,16 +1,17 @@
module DredgePos.Floorplan.Router
open DredgePos
open DredgePos.Global.Router
open DredgePos.Types
open Saturn
open Giraffe
let floorplan = (warbler (fun ctx -> DredgePos.Floorplan.Controller.loadFloorplan (snd ctx)))
let floorplan = (htmlViewWithContext Controller.loadFloorplanView)
let router = router {
pipe_through Ajax.Router.pipeline
get "/" floorplan
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)

86
Floorplan/View.fs Normal file
View File

@@ -0,0 +1,86 @@
module DredgePos.Floorplan.View
open DredgePos.Global
open DredgePos.Global.View
open DredgePos.Entities
open DredgePos.Types
open Giraffe.ViewEngine
open DredgeFramework
let pageContainer (clerk: clerk) roomMenu =
let loggedInText = str (language.getAndReplace "logged_in_as" [clerk.name])
div [_id "pageContainer"] [
div [_id "floorplanLeftColumn"] [
div [_class "topCell"] [
a [_class "posHeader"] [loggedInText]
]
div [_class "middleCell"] []
div [_class "bottomCell"] []
]
div [_id "floorplanCenterColumn"] [
div [_class "topCell"] [
yield! roomMenu
]
div [_class "middleCell"] [
div [_id "floorplanCanvas"] []
div [_id "floorplanCanvas"] []
]
div [_class "bottomCell"] [
div [_class "editControls" ; VisibleInMode ["tableSelected"]] [
div [_class "posHeader currentTable"] [
b [_class "selectedTableNumber"] []
a [_class "reservationStatus"; VisibleInMode ["reservedTableSelected"]] []
small [_class "selectedTableCovers"] []
]
a [_class "posButton placeOrderButton"] [lang "order_table"]
a [_class "posButton reserveTableButton"; InvisibleInMode ["reservedTableSelected"; "activeTableSelected"]] [lang "reserve_table"]
a [_class "posButton unreserveTableButton"; VisibleInMode ["reservedTableSelected"]] [lang "unreserve_table"]
a [_class "posButton payTableButton"; VisibleInMode ["activeTableSelected"]] [lang "pay_table"]
a [_class "posButton viewTableButton"; VisibleInMode ["activeTableSelected"]] [lang "view_table"]
]
]
]
div [_id "floorplanRightColumn"] [
div [_class "topCell"] [
a [_class "posButton logOut"] [str "×"]
]
div [_class "middleCell"] [
a [_class "posButton editModeButton"] [lang "edit_floorplan"]
div [_class "floorplanControls useVisibility"; VisibleInMode ["edit"]] [
a [_class "posButton addTableButton"] [lang "add_table"]
a [_class "posButton addDecoration"] [lang "add_decoration"]
a [_class "posButton deleteDecoration useVisibility"; VisibleInMode ["decorationSelected"; "edit"] ] [lang "delete_decoration"]
a [_class "posButton deleteTableButton useVisibility"; VisibleInMode ["tableSelected"; "edit"]] [lang "delete_table"]
a [_class "posButton changeShapeButton useVisibility"; VisibleInMode ["tableSelected"; "edit"]] [lang "change_shape"]
]
div [_class "mergeControls useVisibility"; VisibleInMode ["tableSelected"]] [
a [_class "posButton mergeButton"; ActiveInMode "merge"] [lang "merge_table"]
a [_class "posButton unmergeButton"; VisibleInMode ["tableSelected"]] [lang "unmerge_table"]
a [_class "posButton transferTableButton" ; ActiveInMode "transfer" ; VisibleInMode ["activeTableSelected"]] [lang "transfer_table"]
]
]
div [_class "bottomCell"] []
]
]
let roomButton (room: room) = a [_class "posButton roomButton"; Value (string room.id)] [str room.name ]
let index styles scripts tags clerk decoratorRows roomMenu =
[|
pageContainer clerk roomMenu
decoratorRows |> Floorplan_Decorations.View.decorator
|]
|> HtmlPage "Floorplan" (GetScripts scripts) (GetStyles styles) (GetMetaTags tags)

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

18
Global/Router.fs Normal file
View File

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

134
Global/View.fs Normal file
View File

@@ -0,0 +1,134 @@
module DredgePos.Global.View
open DredgeFramework
open DredgePos.Types
open Giraffe.ViewEngine
let Value = attr "data-value"
let _table (value: floorplan_table) = value |> jsonEncode |> (attr "data-table")
let VisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-visible-in-mode")
let InvisibleInMode (value: string list) = value |> jsonEncode |> (attr "data-invisible-in-mode")
let ActiveInMode (value: string) = value |> (attr "data-active-in-mode")
let innerText = str
let lang key = language.get key |> str
let template = tag "template"
let scriptToHTML (scriptFile: string) =
let scriptPath = $"/scripts/{scriptFile}"
match FileExists ("wwwroot" + scriptPath) with
| true -> script [_src scriptPath] []
| false -> comment $"[Missing script: {scriptFile}]"
let GetScripts (scripts: string[]) = scripts |> Array.map scriptToHTML
let styleToHTML (stylesheet:string) =
let stylePath = $"/styles/{stylesheet}"
match FileExists ("wwwroot" + stylePath) with
| true -> link [_rel "stylesheet" ; _href stylePath]
| false -> comment $"[Missing style: {stylesheet}]"
let GetStyles (scripts: string[]) = scripts |> Array.map styleToHTML
let tagToHtml (tag, content) = meta [_name tag; _content content]
let GetMetaTags (tags: (string * string)[]) = tags |> Array.map tagToHtml
let VirtualKeyboardRow numberOfButtons =
let buttons = Array.init numberOfButtons (fun _ -> a [] [])
div [_class "virtualKeyboardRow"] [
yield! buttons
]
let VirtualKeyboard =
div [_id "virtualKeyboard"] [
div [_class "headingRow"] [
h3 [_id "virtualKeyboardHeading"] []
a [_class "posButton closeKeyboards"] [str "X"]
]
input [_type "text"; _name "virtualKeyboardInput"; _id "virtualKeyboardInput"]
div [_id "virtualKeyboardButtons"] [
VirtualKeyboardRow 13
VirtualKeyboardRow 14
VirtualKeyboardRow 13
VirtualKeyboardRow 11
VirtualKeyboardRow 1
]
span [_class "forceFocus"] []
]
let VirtualNumpadButton (text: string) =
a [_href "#"; Value (text.ToLower()); _class "posButton virtualNumpadButton"] [str text]
let VirtualNumpadRow (buttons:string[]) =
div [_class "virtualNumpadRow"] [
yield! Array.map VirtualNumpadButton buttons
]
let VirtualNumpad =
div [_id "virtualNumpad"] [
div [_class "headingRow"] [
h3 [_id "virtualNumpadHeading"] []
a [_class "posButton closeKeyboards"] [str "X"]
]
div [_id "virtualNumpadInput"] []
div [_id "virtualNumpadButtons"] [
VirtualNumpadRow [|"1"; "2"; "3"|]
VirtualNumpadRow [|"4"; "5"; "6"|]
VirtualNumpadRow [|"7"; "8"; "9"|]
VirtualNumpadRow [|"0"; "."; "Clear"|]
VirtualNumpadRow [|"Submit"|]
]
]
let alert =
div [_id "alert"] [
div [_id "alertHeading"] []
div [_id "alertMessage"] []
div [_id "alertButtons"] [
a [_class "posButton"; _id "alertOk"] [lang "alert_ok"]
a [_class "posButton"; _id "alertYes"] [lang "alert_yes"]
a [_class "posButton"; _id "alertNo"] [lang "alert_no"]
]
]
let keyboards = [|
VirtualKeyboard
VirtualNumpad
alert
|]
let posButton (extraClasses: string) attrs content =
let allAttrs = [_class $"posButton {extraClasses}"] |> List.append attrs
a allAttrs content
let PosButton classes (attrs: Map<string, 'x>) text =
let attrArray =
attrs
|> Map.map (fun key value ->
(attr key) (string value)
)
|> Map.values
|> Array.ofSeq
posButton classes [
yield! attrArray
] [str text]
let HtmlPage pageTitle scripts styles tags content =
html [] [
head [] [
title [] [innerText pageTitle]
link [_rel "manifest" ; _href "/manifest.webmanifest"]
yield! styles
yield! scripts
yield! tags
]
body [] [
yield! content
yield! keyboards
]
]
let HTMLPageWithScripts pageTitle scripts content = HtmlPage pageTitle scripts [||] content
let HTMLPageWithStyles pageTitle styles content = HtmlPage pageTitle [||] styles content
let HTMLPageWithNoScriptsOrStyles pageTitle content = HtmlPage pageTitle [||] [||] content

32
Installer/Controller.fs Normal file
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

13
Installer/Router.fs Normal file
View File

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

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

View File

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

View File

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

View File

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

167
OrderScreen/View.fs Normal file
View File

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

View File

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

View File

@@ -10,11 +10,11 @@ let updateReservation (reservation: reservation) =
table "reservations"
set reservation
where(eq "id" reservation.id)
} |> db.Update |> ignore
} |> Database.Update |> ignore
reservation
let DeleteReservation (tableId: int) =
delete {
table "reservations"
where (eq "floorplan_table_id" tableId)
} |> db.Delete |> ignore
} |> Database.Delete |> ignore

9
config.json Normal file
View File

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

1212
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

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

View File

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

View File

@@ -11,7 +11,7 @@ interface floorplan{
tableLayer: Konva.Layer
rooms: room[]
tables: floorplan_table[]
decorations: decoration[]
decorations: floorplan_decoration[]
activeTableNumbers: number[]
selectedTableNumber: number
selectedDecorationId: number
@@ -25,7 +25,7 @@ interface floorplan{
interface floorplan_data{
tables: floorplan_table[]
decorations: decoration[]
decorations: floorplan_decoration[]
activeTableNumbers: number[]
rooms: room[]
reservations:reservation[]
@@ -122,7 +122,7 @@ const loadRoom = (roomToLoad: room) => {
button.addClass('active')
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))
tablesInRoom.forEach(createTableShape)
if(!isInMode('transfer')) {
@@ -173,7 +173,7 @@ const createTableShape = (table: floorplan_table) => {
stroke: "black",
strokeWidth: 4,
draggable: false,
listening: true
listening: true,
});
break;
default:
@@ -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 decorationShape = new Image()
decorationShape.onload = () => {
const decorationImage = new Konva.Image({
id: decoration.id.toString(),
x: decoration.decoration_pos_x * Floorplan.visualScale,
y: decoration.decoration_pos_y * Floorplan.visualScale,
x: decoration.pos_x * Floorplan.visualScale,
y: decoration.pos_y * Floorplan.visualScale,
image: decorationShape,
offsetX: decoration.decoration_width * 0.5 * Floorplan.visualScale,
offsetY: decoration.decoration_height * 0.5 * Floorplan.visualScale,
rotation: decoration.decoration_rotation,
width: decoration.decoration_width * Floorplan.visualScale,
height: decoration.decoration_height * Floorplan.visualScale,
offsetX: decoration.width * 0.5 * Floorplan.visualScale,
offsetY: decoration.height * 0.5 * Floorplan.visualScale,
rotation: decoration.rotation,
width: decoration.width * Floorplan.visualScale,
height: decoration.height * Floorplan.visualScale,
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) => {
@@ -541,22 +541,22 @@ const getDecorationDataById = (id: number) => {
const decorationTransformed = (event: Konva.KonvaEventObject<MouseEvent>|Konva.KonvaEventObject<TouchEvent|DragEvent|MouseEvent>) => {
let decorationShape = event.currentTarget as Konva.Image
const oldDecorationData = getDecorationDataById(Number(decorationShape.id()))
const newDecoration: decoration = {
const newDecoration: floorplan_decoration = {
id: oldDecorationData.id,
decoration_room: oldDecorationData.decoration_room,
decoration_pos_x: Math.round(decorationShape.x() / Floorplan.visualScale),
decoration_pos_y: Math.round(decorationShape.y() / Floorplan.visualScale),
decoration_rotation: Math.round(decorationShape.rotation()),
decoration_width: Math.round((decorationShape.scaleX() * decorationShape.width()) / Floorplan.visualScale),
decoration_height: Math.round((decorationShape.scaleY() * decorationShape.height()) / Floorplan.visualScale),
decoration_image: oldDecorationData.decoration_image,
room_id: oldDecorationData.room_id,
pos_x: Math.round(decorationShape.x() / Floorplan.visualScale),
pos_y: Math.round(decorationShape.y() / Floorplan.visualScale),
rotation: Math.round(decorationShape.rotation()),
width: Math.round((decorationShape.scaleX() * decorationShape.width()) / Floorplan.visualScale),
height: Math.round((decorationShape.scaleY() * decorationShape.height()) / Floorplan.visualScale),
image: oldDecorationData.image,
venue_id: oldDecorationData.venue_id,
}
saveDecoration(newDecoration)
}
const saveDecoration = (decorationToUpdate: decoration) => {
const saveDecoration = (decorationToUpdate: floorplan_decoration) => {
const decorations =
Floorplan
.decorations
@@ -576,22 +576,22 @@ const hideDecorator = () => $('#decorator').css('display', 'flex').hide()
const addDecoration = (e: Event) => {
const button = $(e.currentTarget)
const newDecoration: decoration = {
const newDecoration: floorplan_decoration = {
id: 0,
decoration_room: Floorplan.currentRoom.id,
decoration_pos_x: Floorplan.visualScaleBasis / 2,
decoration_pos_y: Floorplan.visualScaleBasis / 2,
decoration_rotation: 0,
decoration_width: 200,
decoration_height: 200,
decoration_image: button.data('image'),
room_id: Floorplan.currentRoom.id,
pos_x: Floorplan.visualScaleBasis / 2,
pos_y: Floorplan.visualScaleBasis / 2,
rotation: 0,
width: 200,
height: 200,
image: button.data('image'),
venue_id: Floorplan.currentRoom.venue_id
}
ajax('/floorplan/addDecoration', newDecoration, 'post', decorationAdded, null, null)
}
const decorationAdded = (decoration: decoration) => {
const decorationAdded = (decoration: floorplan_decoration) => {
Floorplan.decorations.push(decoration)
createDecorationShape(decoration, true)
@@ -604,7 +604,7 @@ const deleteDecoration = () => ajax(
getDecorationDataById(Floorplan.selectedDecorationId),
'post', decorationDeleted, null, null)
const decorationDeleted = (deletedDecoration:decoration) => {
const decorationDeleted = (deletedDecoration:floorplan_decoration) => {
Floorplan.decorations = Floorplan.decorations.filter(decoration => decoration.id != deletedDecoration.id)
const decorationShape = Floorplan.stage.findOne(`#${deletedDecoration.id}`)
decorationShape.destroy()
@@ -615,9 +615,11 @@ const setRoomBackground = (roomToLoad: room) => {
const width = Floorplan.floorplanDiv.width()
const height = Floorplan.floorplanDiv.height()
if(roomToLoad.background_image) {
Floorplan.floorplanDiv.css("background-image", `url(/images/rooms/${roomToLoad?.background_image})`)
if(roomToLoad.background_image != "") {
Floorplan.floorplanDiv.css("background-image", `url('/images/rooms/${roomToLoad.background_image}')`)
Floorplan.floorplanDiv.css("background-size", `${width}px ${height}px`)
} else {
Floorplan.floorplanDiv.css("background-image", "none")
}
}

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ interface orderItem {
interface print_group {
id: number,
name: string,
printer: number,
printer_id: number,
venue_id: number,
}
@@ -50,21 +50,21 @@ interface floorplan_table {
id: number
}
interface decoration {
interface floorplan_decoration {
id: number
decoration_room: number
decoration_pos_x: number
decoration_pos_y: number
decoration_rotation: number
decoration_width: number
decoration_height: number
decoration_image: string
room_id: number
pos_x: number
pos_y: number
rotation: number
width: number
height: number
image: string
venue_id: number
}
interface room {
id: number
room_name: string
name: string
background_image: string
venue_id: number
}
@@ -87,29 +87,29 @@ interface keyboard {
}
interface order_screen_page{id: number; order_screen_page_group_id: number; grid_id: number}
interface grid {id: number; grid_name: string; grid_rows: number; grid_cols: number; grid_data: string}
interface grid {id: number; name: string; rows: number; cols: number; data: string}
interface item {
id: number
item_code: string
item_category: number
item_name: string
code: string
sales_category_id: number
name: string
item_type: string
price1: number
price2: number
price3: number
price4: number
price5: number
}
type sales_category = {
id: number
parent: number
name: string
print_group: string
print_group_id: string
venue_id: number
}
interface Array<T> {
where(property: string, value: any): T
first(): T
last(): T
unique(): this
collect<y>(func: (item: T) => y[]): y[]
}

View File

@@ -1,9 +0,0 @@
<div id="alert" >
<div id="alertHeading"></div>
<div id="alertMessage"></div>
<div id="alertButtons">
<a class="posButton" id="alertOk"><!--[lang:alert_ok]--></a>
<a class="posButton" id="alertYes"><!--[lang:alert_yes]--></a>
<a class="posButton" id="alertNo"><!--[lang:alert_no]--></a>
</div>
</div>

View File

@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title><!--[var:title]--></title>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name = "viewport" content = "user-scalable = no, initial-scale=0.8,maximum-scale=0.8 ,shrink-to-fit=yes" />
<link rel="manifest" href="/manifest.webmanifest">
</head>
<body>
<div id="authenticator">
</div>
<!--[template:keyboards]-->
</body>
</html>

View File

@@ -1,113 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>DredgePOS</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/theme.css?id=ax" media="screen" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/paymentSplitter.css?id=ax" media="screen" />
<meta name = "viewport" content = "width=1280, initial-scale = 0.8, user-scalable = no, shrink-to-fit=no" />
<script type="text/javascript" src="currency.min.js"></script>
<script type="text/javascript" src="posFunctions.js"></script>
<script type="text/javascript" src="paymentFunctions.js"></script>
</head>
<body class="darkMode">
<div id="pageContainer">
<div id="flexWrapper">
<div id="header">
<h1>Paying Table</h2>
</div>
<div id="stucture">
<div id="leftColumn">
<h2>Whole Table</h2>
<table id="first" cellspacing="0">
<thead>
<th class="hide">Item Code</th>
<th>Qty</th>
<th>Item</th>
<th class="hide">Unit Price</th>
<th class="hide">Print Group</th>
<th class="totalpriceCell">Total Price</th>
<th class="hide">Cover</th>
<th class="clerkCell">Clerk</th>
<th class="hide">Selected Qty</th>
<th class="hide">Original Qty</th>
<th class="hide">Category</th>
<th class="hide">Department</th>
<th class="hide"><!--[lang:has_split_header]--></th>
<th class="hide"><!--[lang:orig_tprice_header]--></th>
</thead>
<tbody>
<!--[var:tableHTML]-->
</tbody>
</table>
</div>
<div id="controlColumn">
<a class="posButton" onclick="moveItems('#first', '#second')">&gt;</a>
<a class="posButton" onclick="moveItems('#second', '#first')">&lt;</a>
</div>
<div id="centerColumn">
<h2>Partial Table</h2>
<table id="second" cellspacing="0">
<thead>
<th class="hide">Item Code</th>
<th>Qty</th>
<th>Item</th>
<th class="hide">Unit Price</th>
<th class="hide">Print Group</th>
<th class="totalpriceCell">Total Price</th>
<th class="hide">Cover</th>
<th class="clerkCell hide">Clerk</th>
<th class="hide">Selected Qty</th>
<th class="hide">Category</th>
<th class="hide">Department</th>
<th class="hide"><!--[lang:has_split_header]--></th>
<th class="hide"><!--[lang:orig_tprice_header]--></th>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="rightColumn">
<h2>Controls</h2>
<div id="rightColumnContainer">
<a class="posButton heading" onclick="">Selection Tools</a>
<a class="posButton qtySelector" onclick="overrideQuantitySelect()">Select Quantity</a>
<a class="posButton" onclick="overrideQuantitySelect()">Select Amount</a>
<a class="selectDepartment posButton">Select By Department</a>
<a class="selectCategory posButton">Select By Category</a>
<a class="selectCovers posButton">Select by Cover #</a>
<a class="selectPrintGroup posButton">Select by Printed With</a>
<a class="selectAll posButton">Select All</a>
<a class="posButton heading">Select By Fraction</a>
<a class="posButton selectFraction" data-value="2">Select 12</a>
<a class="posButton selectFraction" data-value="3">Select 13</a>
<a class="posButton selectFraction" data-value="4">Select 14</a>
<a class="posButton selectFraction" data-value="5">Select 15</a>
<a class="posButton selectFraction" data-value="6">Select 16</a>
<a class="posButton selectFraction" data-value="7">Select 17</a>
<a class="posButton selectFraction" data-value="8">Select 18</a>
<a class="posButton selectFraction" data-value="<!--[var:uniqueCovers]-->" posButton">Select 1covers</a>
<a class="posButton heading" onclick="loadScreen('tableMap')">Exit</a>
</div>
</div>
</div>
</div>
</div>
<div id="coverControls" class="popupBox"><!--[var:coverButtons]--></div>
<div id="departmentControl" class="popupBox"><!--[var:departmentButtons]--></div>
<div id="categoryControl" class="popupBox"><!--[var:categoryButtons]--></div>
<div id="printGroupControl" class="popupBox"><!--[var:printGroupButtons]--></div>
<!--[template:keyboards]-->
<script type="text/javascript">
$(document).ready( function () {
covers = '<!--[var:covers]-->'
dredgePosSetup('#pageContainer');
setupPaymentSplitter();
} );
</script>
</body>
</html>

View File

@@ -1,14 +0,0 @@
<div class="itemCellWrapper">
<div
class="definedButton item <!--[arr:item|button_classes]--> posButton"
data-type="<!--[arr:item|type]-->"
data-id="<!--[arr:item|itemid]-->"
data-grid="<!--[arr:item|button_grid_id]-->"
data-name="<!--[arr:item|itemname]-->"
data-price="<!--[arr:item|price]-->"
data-category="<!--[arr:item|c_name]-->"
data-department="<!--[arr:item|dep_name]-->"
data-printgroup="<!--[arr:item|pg_name]-->">
<a><!--[arr:item|itemname]--></a>
</div>
</div>

View File

@@ -1,14 +0,0 @@
<div class="itemCellWrapper"> <div
class="definedButton item hasImage <!--[arr:item|button_classes]--> posButton"
data-type="<!--[arr:item|type]-->"
data-id="<!--[arr:item|itemid]-->"
data-name="<!--[arr:item|itemname]-->"
data-price="<!--[arr:item|price]-->"
data-grid="<!--[arr:item|button_grid_id]-->"
data-category="<!--[arr:item|c_name]-->"
data-department="<!--[arr:item|dep_name]-->"
data-printgroup="<!--[arr:item|pg_name]-->">
<a style="background-image:url('images/<!--[arr:item|image]-->')"></a>
<a><!--[arr:item|itemname]--></a>
</div></div>

View File

@@ -1,67 +0,0 @@
<div class="itemPage">
<table cellspacing="0" cellpadding="0">
<tr>
<td><!--[button:CRAFTBEER001]--></td>
<td><!--[button:CRAFTBEER002]--></td>
<td><!--[button:CRAFTBEER003]--></td>
<td><!--[button:CRAFTBEER004]--></td>
<td><!--[button:CRAFTBEER005]--></td>
<td><!--[button:CRAFTBEER006]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER007]--></td>
<td><!--[button:CRAFTBEER008]--></td>
<td><!--[button:CRAFTBEER009]--></td>
<td><!--[button:CRAFTBEER010]--></td>
<td><!--[button:CRAFTBEER012]--></td>
<td><!--[button:INSTRUCTION005]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><!--[button:INSTRUCTION006]--></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER013]--></td>
<td><!--[button:CRAFTBEER014]--></td>
<td><!--[button:CRAFTBEER015]--></td>
<td><!--[button:CRAFTBEER016]--></td>
<td><!--[button:CRAFTBEER017]--></td>
<td><!--[button:CRAFTBEER018]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER019]--></td>
<td><!--[button:CRAFTBEER020]--></td>
<td><!--[button:CRAFTBEER021]--></td>
<td><!--[button:CRAFTBEER022]--></td>
<td><!--[button:CRAFTBEER023]--></td>
<td><!--[button:CRAFTBEER024]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,67 +0,0 @@
<div class="itemPage">
<table cellspacing="0" cellpadding="0">
<tr>
<td><!--[button:CRAFTBEER025]--></td>
<td><!--[button:CRAFTBEER026]--></td>
<td><!--[button:CRAFTBEER027]--></td>
<td><!--[button:CRAFTBEER028]--></td>
<td><!--[button:CRAFTBEER029]--></td>
<td><!--[button:CRAFTBEER030]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER031]--></td>
<td><!--[button:CRAFTBEER032]--></td>
<td><!--[button:CRAFTBEER033]--></td>
<td><!--[button:CRAFTBEER034]--></td>
<td><!--[button:CRAFTBEER035]--></td>
<td><!--[button:CRAFTBEER036]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER037]--></td>
<td><!--[button:CRAFTBEER038]--></td>
<td><!--[button:CRAFTBEER039]--></td>
<td><!--[button:CRAFTBEER040]--></td>
<td><!--[button:CRAFTBEER041]--></td>
<td><!--[button:CRAFTBEER042]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:CRAFTBEER043]--></td>
<td><!--[button:CRAFTBEER044]--></td>
<td><!--[button:CRAFTBEER045]--></td>
<td><!--[button:CRAFTBEER046]--></td>
<td><!--[button:CRAFTBEER047]--></td>
<td><!--[button:CRAFTBEER048]--></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,66 +0,0 @@
<div class="itemPage"><table cellspacing="0" cellpadding="0">
<tr>
<td><!--[button:DESSERT001]--></td>
<td></td>
<td><!--[button:DESSERT002]--></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td><!--[button:DESSERT004]--></td>
<td></td>
<td><!--[button:DESSERT003]--></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,67 +0,0 @@
<div class="itemPage"><table cellspacing="0" cellpadding="0">
<tr>
<td><!--[button:MAIN001]--></td>
<td></td>
<td><!--[button:MAIN003]--></td>
<td></td>
<td><!--[button:MAIN004]--></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:MAIN005]--></td>
<td></td>
<td><!--[button:MAIN006]--></td>
<td></td>
<td><!--[button:MAIN007]--></td>
<td></td>
</td>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,68 +0,0 @@
<div class="itemPage">
<table cellspacing="0" cellpadding="0">
<tr>
<td><!--[button:STARTER001]--></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:STARTER002]--></td>
<td></td>
<td><!--[button:DIP002]--></td>
<td><!--[button:DIP003]--></td>
<td><!--[button:DIP004]--></td>
<td><!--[button:DIP005]--></td>
</tr>
<tr>
<td><!--[button:STARTER003]--></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:STARTER004]--></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><!--[button:STARTER005]--></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,67 +0,0 @@
<div class="itemPage"><table cellspacing="0" cellpadding="0">
<tr>
<td></td>
<td></td>
<td><!--[button:WINE002]--></td>
<td><!--[button:WINE001]--></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td><!--[button:WINE004]--></td>
<td><!--[button:WINE003]--></td>
<td></td>
<td></td>
</td>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table></div>

View File

@@ -1,3 +0,0 @@
<a onclick="loadCategory('<!--[arr:cat|c_name]-->')" class="posButton categoryButton">
<!--[arr:cat|c_name]-->
</a>

View File

@@ -1,5 +0,0 @@
<!--[categoryButton:1]-->
<!--[categoryButton:2]-->
<!--[categoryButton:4]-->
<!--[categoryButton:3]-->
<!--[categoryButton:5]-->

View File

@@ -1,3 +0,0 @@
<a class="posButton <!--[var:classes]-->" <!--[var: attributes]-->>
<!--[var:text]-->
</a>

View File

@@ -1,4 +0,0 @@
<div class="decoratorItem" data-image="<!--[var:image_url]-->">
<a style="background-image:url('/images/decorations/<!--[var:image_url]-->')"></a>
<a><!--[var:image_name]--></a>
</div>

View File

@@ -1,3 +0,0 @@
<div class="decoratorRow">
<!--[var:decorations]-->
</div>

View File

@@ -1,79 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>DredgePOS</title>
<script src="https://unpkg.com/current-device/umd/current-device.min.js"></script>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name = "viewport" content = "user-scalable = no, initial-scale=0.8,maximum-scale=0.8 ,shrink-to-fit=yes" />
<link rel="manifest" href="/manifest.webmanifest">
</head>
<body class="darkMode">
<div id="pageContainer">
<div id="floorplanLeftColumn">
<div class="topCell">
<a class="posHeader">Logged in as <!--[arr:clerk|clerk_name]--></a>
</div>
<div class="middleCell">
</div>
<div class="bottomCell">
</div>
</div>
<div id="floorplanCenterColumn">
<div class="topCell">
<!--[var:roomMenu]-->
</div>
<div class="middleCell">
<div id="floorplanCanvas"></div>
</div>
<div class="bottomCell">
<div class="editControls" data-visible-in-mode='["tableSelected"]'>
<div class="posHeader currentTable">
<b class="selectedTableNumber"></b>
<a class="reservationStatus" data-visible-in-mode='["reservedTableSelected"]'></a>
<small class="selectedTableCovers"></small>
</div>
<a class="posButton placeOrderButton"><!--[lang:order_table]--></a>
<a class="posButton reserveTableButton" data-invisible-in-mode='["reservedTableSelected", "activeTableSelected"]'><!--[lang:reserve_table]--></a>
<a class="posButton unreserveTableButton" data-visible-in-mode='["reservedTableSelected"]'><!--[lang:unreserve_table]--></a>
<a class="posButton payTableButton" data-visible-in-mode='["activeTableSelected"]'><!--[lang:pay_table]--></a>
<a class="posButton viewTableButton" data-visible-in-mode='["activeTableSelected"]'><!--[lang:view_table]--></a>
</div>
</div>
</div>
<div id="floorplanRightColumn">
<div class="topCell">
<a class="posButton logOut" onclick="logout()">×</a>
</div>
<div class="middleCell">
<a class="posButton editModeButton"><!--[lang:edit_floorplan]--></a>
<div class="floorplanControls useVisibility" data-visible-in-mode='["edit"]'>
<a class="posButton addTableButton" ><!--[lang:add_table]--></a>
<a class="posButton addDecoration"><!--[lang:add_decoration]--></a>
<a class="posButton deleteDecoration useVisibility" data-visible-in-mode='["decorationSelected", "edit"]'><!--[lang:delete_decoration]--></a>
<a class="posButton deleteTableButton useVisibility" data-visible-in-mode='["tableSelected", "edit"]'><!--[lang:delete_table]--></a>
<a class="posButton changeShapeButton useVisibility" data-visible-in-mode='["tableSelected", "edit"]'><!--[lang:change_shape]--></a>
</div>
<div class="mergeControls useVisibility" data-visible-in-mode='["tableSelected"]'>
<a class="posButton mergeButton" data-active-in-mode="merge"><!--[lang:merge_table]--></a>
<a class="posButton unmergeButton" data-visible-in-mode='["tableSelected"]'><!--[lang:unmerge_table]--></a>
<a class="transferTableButton posButton" data-active-in-mode="transfer" data-visible-in-mode='["activeTableSelected"]'><!--[lang:transfer_table]--></a>
</div>
</div>
<div class="bottomCell">
</div>
</div>
</div>
<!--[template:keyboards]-->
<div id="decorator">
<div id="decoratorHeader">
<h2><!--[lang:choose_decoration]--></h2>
<a class="posButton" onclick="$('#decorator').css('display','none')">×</a>
</div>
<div id="decoratorContent"><!--[var:decorator]--></div>
</div>
</body>
</html>

View File

@@ -1,3 +0,0 @@
<!--[template:alert]-->
<!--[template:virtualNumpad]-->
<!--[template:virtualKeyboard]-->

View File

@@ -1 +0,0 @@
<b>OrderBox Loaded</b>

View File

@@ -1,21 +0,0 @@
<table cellspacing="0" id="orderBox" data-tablenumber="<!--[var:tableNumber]-->" data-covers="<!--[var:covers]-->" data-clerk="<!--[arr:clerk|clerk_name]-->" class="">
<thead>
<tr>
<th class="idCell hide"><!--[lang:id_header]--></th>
<th class="qtyCell"><!--[lang:qty_header]--></th>
<th class="itemCell"><!--[lang:item_header]--></th>
<th class="totalPriceCell"><!--[lang:total_price_header]--></th>
<th class="printGroupCell"><!--[lang:printgroup_header]--></th>
<th class="unitPriceCell hide"><!--[lang:individual_price_header]--></th>
<th class="sortkeyCell hide"><!--[lang:sortkey_header]--></th>
<th class="typeCell hide"><!--[lang:type_header]--></th>
<th class="coverCell hide"><!--[lang:cover_header]--></th>
<th class="departmentCell hide"><!--[lang:department_header]--></th>
<th class="categoryCell hide"><!--[lang:category_header]--></th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
</tfoot>
</table>

View File

@@ -1,107 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>DredgePOS</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/theme.css?id=ax" media="screen" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/screen.css?id=ax" media="screen" />
<meta name = "viewport" content = "width=1280, initial-scale = 0.8, user-scalable = no, shrink-to-fit=no" />
<script type="text/javascript" src="posFunctions.js"></script>
</head>
<body class="darkMode">
<div id="pageContainer">
<div id="leftColumn">
<div id="tableDetails">
<h2><span id="activeTable"><!--[var:activeTable]--></span></h2>
<p class="posButton coverNumbers"><span id="covers"><!--[var:covers]--></span> covers </p>
<p class="clerk"><!--[var:loggedInAs]--></p>
</div>
<div id="orderBoxContainer">
<!--[template:orderBoxTable]-->
</div>
<div id="leftColumnFooter">
<p class="messageBox"></p>
<h2>Total Price: &nbsp; &nbsp;<span class="orderBoxTotals">$0.00</span></h2>
<p class="selectedTotal">($0.00 Selected)</p>
</div>
</div>
<div id="rightColumn">
<div id="topHalf">
<div id="courseSelect" class="posButtonGroup">
<h2><!--[lang:print_with]--></h2>
<a onclick="setPrintGroupOverride(false, this)" class="toggle default posButton active"><!--[lang:print_with_category|default]--></a>
<a onclick="setPrintGroupOverride('Starters',this)" class="posButton toggle"><!--[lang:print_with_category|Starters]--></a>
<a onclick="setPrintGroupOverride('Mains',this)" class="posButton toggle"><!--[lang:print_with_category|Mains]--></a>
<a onclick="setPrintGroupOverride('Desserts',this)" class="posButton toggle"><!--[lang:print_with_category|Desserts]--></a>
<a onclick="setPrintGroupOverride('Drinks',this)" class="posButton toggle"><!--[lang:print_with_category|Drinks]--></a>
</div>
<div id="functions">
<h2><!--[lang:functions_header]--></h2>
<a onclick="loadScreen('tableMap')" class="posButton"><!--[lang:close_order_function]--></a>
<a class="posButton accumulateButton"><!--[lang:accumulate_function]--></a>
<a class="posButton void"><!--[lang:void]--></a>
<a class="posButton"><!--[lang:pay_function]--></a>
<a class="posButton saveOrder"><!--[lang:print_function]--></a>
</div>
<div id="positionControl">
<a class="posButton freeText" data-type="instruction" data-id="freetext"
data-name=""
data-price="0"
data-category="0"
data-department="0"
data-printgroup="0"><!--[lang:freetext_button]--></a>
<div type="text" class="positionInput"><!--[lang:select_covers]--></div>
<div id="positions">
</div>
</form>
</div>
<div id="topNumPad" class="numPad">
<a class="posButton numberButton">1</a>
<a class="posButton numberButton">2</a>
<a class="posButton numberButton">3</a>
<a class="posButton numberButton">4</a>
<a class="posButton numberButton">5</a>
<a class="posButton numberButton">6</a>
<a class="posButton numberButton">7</a>
<a class="posButton numberButton">8</a>
<a class="posButton numberButton">9</a>
<a class="posButton numberButton">0</a>
<a class="posButton numberButton">.</a>
<a class="posButton multiplier">×</a>
</div>
</div>
<div id="bottomHalf">
<!--[template:categoryMenu]-->
<div id="itemGrid">
<div id="itemWrapper">
<!--[template:categories/StartersPage1]-->
</div>
</div>
<div id="innerNavigation">
<a class="posButton" onclick="scrollPage('left')">&lt;</a>
<a class="posButton" onclick="scrollPage('right')">&gt;</a>
</div>
</div>
</div>
</div>
<!--[template:keyboards]-->
<script type="text/javascript">
$(document).ready( function () {
//Base grid width must be defined for multipage functionality to work.
baseGridWidth = 850;
dredgePosSetup();
$("#orderBox tbody tr").on( 'click', function ( e ) {
selectRow($(this));
} )
} );
</script>
</body>
</html>

View File

@@ -1,84 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>DredgePOS</title>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name = "viewport" content = "user-scalable = no ,shrink-to-fit=yes" />
<link rel="manifest" href="/manifest.webmanifest">
</head>
<body>
<div id="pageContainer" <!--[var: containerAttributes]-->>
<div id="leftColumn">
<h1 class="tableHeading"><!--[var: orderNumber]--></h1>
<div class="tableInfo">
<!--[var: changeCoverNumberButton]-->
<a class="posButton">Logged in as <!--[arr:clerk|clerk_name]--></a>
</div>
<div class="orderBox">
<table class="orderBoxTable">
<thead>
<tr>
<th class="orderBoxCell qtyCell"><!--[lang:qty_header]--></th>
<th class="orderBoxCell itemIdCell hidden"><!--[lang:id_header]--></th>
<th class="orderBoxCell itemCell"><!--[lang:item_header]--></th>
<th class="orderBoxCell unitPriceCell hidden"><!--[lang:price_header]--></th>
<th class="orderBoxCell totalPriceCell"><!--[lang:total_price_header]--></th>
<th class="orderBoxCell printGroupCell"><!--[lang:printgroup_header]--></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="orderBoxInfo">
<span class="voidModeWarning" data-visible-in-mode='["void"]'><!--[lang:void_mode]--></span>
</div>
<div class="orderBoxFooter">
<span class="orderBoxTotal"><!--[lang:totalPrice|0.00]--></span>
<small class="orderBoxSelectedTotal"><!--[lang:selectedPrice|0.00]--></small>
</div>
</div>
<div id="rightColumn">
<div id="topHalf">
<div class="functionButtons">
<div class="printGroupButtons toggleGroup">
<input type="hidden" name="print_override" class="value" />
<!--[var:salesCategoryOverrideButtons]-->
</div>
<div class="functionColumn">
<a class="posButton accumulateButton" data-active-in-mode="accumulate"><!--[lang:accumulate_function]--></a>
<a class="showCoverSelectorButton posButton"><!--[lang:select_covers]--></a>
</div>
<div class="functionColumn">
<a class="posButton voidButton" data-active-in-mode="void"><!--[lang:void]--></a>
<a class="posButton openItemButton"><!--[lang:custom_item_button]--></a>
<a class="freetextButton posButton"><!--[lang:freetext_button]--></a>
<a class="numpadButton posButton"><!--[lang:numpad_button]--></a>
</div>
<div class="functionColumn">
<a class="posButton"><!--[lang:pay_function]--></a>
<a class="posButton"><!--[lang:print_function]--></a>
</div>
</div>
</div>
<div id="pageList">
<!--[var:categoryList]-->
</div>
<div id="pageGroupContainer">
<!--[var:pageGroups]-->
</div>
<div class="pageNavigation">
<a class="posButton prevButton"><!--[lang:prev_page]--></a>
<a class="posButton nextButton"><!--[lang:next_page]--></a>
</div>
</div>
</div>
<!--[template:keyboards]-->
<!--[template:orderScreen/grid_container]-->
<!--[template:orderScreen/cover_selector]-->
<template id="posButtonTemplate">
<!--[template:components/posButton]-->
</template>
</body>
</html>

View File

@@ -1 +0,0 @@
<span class="buttonImg" style="background-image:url(/images/items/<!--[var:image]-->);"></span>

View File

@@ -1 +0,0 @@
<a href="#" class="posButton changeCoverNumberButton"><!--[var:covers]--></a>

View File

@@ -1,3 +0,0 @@
<div class="coverSelector">
<!--[var:coverSelectorButtons]-->
</div>

View File

@@ -1,6 +0,0 @@
<a href="#" class="posButton <!--[var:extra_classes]-->"
data-primary-action="<!--[var:primary_action]-->"
data-secondary-action="<!--[var:secondary_action]-->" <!--[var: extra_data]--> <!--[var: extra_styles]-->>
<!--[var:image]-->
<span class="text"><!--[var:text]--></span>
</a>

View File

@@ -1,13 +0,0 @@
<div class="gridContainer">
<div class="gridContainerHeader">
<span></span>
<div class="posButton closeGrid">×</div>
</div>
<div class="gridContainerGrid">
<div class="pageGroup"></div>
</div>
<div class="pageNavigation">
<a class="posButton prevButton"><!--[lang:prev_page]--></a>
<a class="posButton nextButton"><!--[lang:next_page]--></a>
</div>
</div>

View File

@@ -1,6 +0,0 @@
<div class="gridPage" style="
grid-template-columns: repeat(<!--[var:cols]-->, 1fr);
grid-template-rows: repeat(<!--[var:rows]-->, 1fr);
">
<!--[var:pageButtons]-->
</div>

View File

@@ -1,3 +0,0 @@
<div class="pageGroup" data-page-group-id="<!--[var:page_group_id]-->">
<!--[var:pages]-->
</div>

View File

@@ -1,3 +0,0 @@
<a data-page-group-id="<!--[arr:page|id]-->" class="posButton loadPageGroup">
<!--[arr:page|label]-->
</a>

View File

@@ -1,157 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>DredgePOS</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://unpkg.com/current-device/umd/current-device.min.js"></script>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/theme.css?id=ax" media="screen" />
<link rel="stylesheet" type="text/css" href="themes/restaurant/paymentSplitter.css?id=ax" media="screen" />
<meta name = "viewport" content = "user-scalable = no, initial-scale=0.8 ,shrink-to-fit=yes" />
<script type="text/javascript" src="currency.min.js"></script>
<script type="text/javascript" src="posFunctions.js"></script>
<script type="text/javascript" src="paymentFunctions.js"></script>
</head>
<body class="darkMode" data-tablenumber="<!--[var:activeTable]-->">
<div id="pageContainer">
<div id="flexWrapper">
<div id="header">
<a class="posButton"><!--[var:loggedInAs]--></a>
<h1><!--[lang:paying_table|<!--[var:activeTable]-->]--></h1>
<a class="posButton exit" onclick="loadScreen('tableMap')">×</a>
</div>
<div id="pageStructure">
<div id="leftColumn">
<h2>Whole Table</h2>
<div class="tableWrapper">
<table id="first" cellspacing="0">
<thead>
<th class="hide"><!--[lang:id_header]--></th>
<th><!--[lang:qty_header]--></th>
<th><!--[lang:item_header]--></th>
<th class="hide"><!--[lang:individual_price_header]--></th>
<th class="hide"><!--[lang:printgroup_header]--></th>
<th class="totalpriceCell"><!--[lang:total_price_header]--></th>
<th class="hide"><!--[lang:cover_header]--></th>
<th class="clerkCell"><!--[lang:clerk_header]--></th>
<th class="hide"><!--[lang:orig_qty_header]--></th>
<th class="hide"><!--[lang:category_header]--></th>
<th class="hide"><!--[lang:department_header]--></th>
<th class="hide"><!--[lang:orig_tprice_header]--></th>
</thead>
<tbody>
<!--[var:tableHTML]-->
</tbody>
</table>
</div>
<div class="tableFooter">
<div class="totalsRow">
<h3 class="firstTotal"></h3>
<p class="firstSelectedTotal"></p>
</div>
<div class="controlRow">
<a class="posButton payAll"><!--[lang:pay_all]--></a>
</div>
</div>
</div>
<div id="controlColumn">
<a class="posButton" onclick="moveItems('#first', '#second')">&gt;</a>
<a class="posButton" onclick="moveItems('#second', '#first')">&lt;</a>
</div>
<div id="centerColumn">
<h2>Partial Table</h2>
<div class="tableWrapper">
<table id="second" cellspacing="0">
<thead>
<th class="hide"><!--[lang:id_header]--></th>
<th><!--[lang:qty_header]--></th>
<th><!--[lang:item_header]--></th>
<th class="hide"><!--[lang:individual_price_header]--></th>
<th class="hide"><!--[lang:printgroup_header]--></th>
<th class="totalpriceCell"><!--[lang:total_price_header]--></th>
<th class="hide"><!--[lang:cover_header]--></th>
<th class="clerkCell"><!--[lang:clerk_header]--></th>
<th class="hide"><!--[lang:orig_qty_header]--></th>
<th class="hide"><!--[lang:category_header]--></th>
<th class="hide"><!--[lang:department_header]--></th>
<th class="hide"><!--[lang:orig_tprice_header]--></th>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="tableFooter">
<div class="totalsRow">
<h3 class="secondTotal"></h3>
<p class="secondSelectedTotal"></p>
</div>
<div class="controlRow">
<a class="posButton payPartial"><!--[lang:pay_table]--></a>
<a class="posButton transferPartial">Transfer to Another Table</a>
<a class="posButton selectAllSecond">Select All</a>
</div>
</div>
</div>
<div id="rightColumn">
<h2>Controls</h2>
<div id="rightColumnContainer">
<div class="row">
<a class="heading" onclick="">Selection Tools</a>
</div>
<div class="row">
<a class="posButton qtySelector">Select Quantity</a>
<a class="posButton selectAmount">Select Amount</a>
</div>
<div class="row">
<a class="selectDepartment posButton">Select By Department</a>
<a class="selectCategory posButton">Select By Category</a>
</div>
<div class="row">
<a class="selectCovers posButton">Select by Cover #</a>
<a class="selectPrintGroup posButton">Select by Printed With</a>
</div>
<div class="row">
<a class="selectAll posButton">Select All</a>
</div>
<div class="row">
<a class="heading">Select By Fraction</a>
</div>
<div class="row">
<a class="posButton selectFraction" data-value="2">Select 12</a>
<a class="posButton selectFraction" data-value="3">Select 13</a>
</div>
<div class="row">
<a class="posButton selectFraction" data-value="4">Select 14</a>
<a class="posButton selectFraction" data-value="5">Select 15</a>
</div>
<div class="row">
<a class="posButton selectFraction" data-value="6">Select 16</a>
<a class="posButton selectFraction" data-value="7">Select 17</a>
</div>
<div class="row">
<a class="posButton selectFraction" data-value="8">Select 18</a>
<a class="posButton selectFraction" data-value="<!--[var:covers]-->">Select 1covers</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="coverControls" class="popupBox"><!--[var:coverButtons]--></div>
<div id="departmentControl" class="popupBox"><!--[var:departmentButtons]--></div>
<div id="categoryControl" class="popupBox"><!--[var:categoryButtons]--></div>
<div id="printGroupControl" class="popupBox"><!--[var:printGroupButtons]--></div>
<!--[template:keyboards]-->
<script type="text/javascript">
$(document).ready( function () {
covers = '<!--[var:covers]-->'
dredgePosSetup('#pageContainer');
setupPaymentSplitter();
} );
</script>
</body>
</html>

View File

@@ -1 +0,0 @@
<a data-value="<!--[var:value]-->" class="posButton <!--[var:class]-->"><!--[var:text]--></a>

View File

@@ -1 +0,0 @@
<a class="posButton roomButton" data-value="<!--[var:roomId]-->"><!--[var:roomName]--></a>

View File

@@ -1,71 +0,0 @@
<div id="virtualKeyboard">
<div class="headingRow">
<h3 id="virtualKeyboardHeading"></h3>
<a class="posButton closeKeyboards">X</a>
</div>
<input type="text" name="virtualKeyboardInput" id="virtualKeyboardInput" />
<div id="virtualKeyboardButtons">
<div class="virtualKeyboardRow">
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
</div>
<div class="virtualKeyboardRow">
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
</div>
<div class="virtualKeyboardRow">
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
</div>
<div class="virtualKeyboardRow">
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
<a></a>
</div>
<div class="virtualKeyboardRow">
<a></a>
</div>
<span class="forceFocus"></span>
</div>
</div>

View File

@@ -1,28 +0,0 @@
<div id="virtualNumpad">
<div class="headingRow">
<h3 id="virtualNumpadHeading"></h3>
<a class="posButton closeKeyboards">X</a>
</div>
<div id="virtualNumpadInput"></div>
<div id="virtualNumpadButtons">
<div class="virtualNumpadRow">
<a href="#" data-value="1" class="posButton virtualNumpadButton">1</a>
<a href="#" data-value="2" class="posButton virtualNumpadButton">2</a>
<a href="#" data-value="3" class="posButton virtualNumpadButton">3</a>
</div><div class="virtualNumpadRow">
<a href="#" data-value="4" class="posButton virtualNumpadButton">4</a>
<a href="#" data-value="5" class="posButton virtualNumpadButton">5</a>
<a href="#" data-value="6" class="posButton virtualNumpadButton">6</a>
</div><div class="virtualNumpadRow">
<a href="#" data-value="7" class="posButton virtualNumpadButton">7</a>
<a href="#" data-value="8" class="posButton virtualNumpadButton">8</a>
<a href="#" data-value="9" class="posButton virtualNumpadButton">9</a>
</div><div class="virtualNumpadRow">
<a href="#" data-value="0" class="posButton virtualNumpadButton">0</a>
<a href="#" data-value="." class="posButton virtualNumpadButton">.</a>
<a href="#" data-value="clear" class="posButton virtualNumpadButton virtualNumpadClear">Clear</a>
</div><div class="virtualNumpadRow">
<a href="#" data-value="submit" class="posButton virtualNumpadButton virtualNumpadSubmit">Enter</a>
</div>
</div>
</div>

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

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