diff --git a/app/controllers/BoardsController.asp b/app/controllers/BoardsController.asp
index 83a4454..99076bb 100644
--- a/app/controllers/BoardsController.asp
+++ b/app/controllers/BoardsController.asp
@@ -50,12 +50,14 @@ Class BoardsController_Class
slug = boards_Repository().UniqueSlug(GenerateSlug(boardName), 0)
Dim board : Set board = New POBO_boards
- board.name = boardName
- board.slug = slug
- board.created_at = Now()
- board.created_by = currentUsername
- board.updated_at = Now()
- board.updated_by = currentUsername
+ board.name = boardName
+ board.slug = slug
+ board.import_from_printstream = (Request.Form("import_from_printstream") = "on")
+ board.printstream_job_name = Trim(CStr(Request.Form("printstream_job_name")))
+ board.created_at = Now()
+ board.created_by = currentUsername
+ board.updated_at = Now()
+ board.updated_by = currentUsername
boards_Repository().AddNew board
@@ -117,6 +119,11 @@ Class BoardsController_Class
"""swim_lane_id"":" & cardItem.swim_lane_id & "," & _
"""job_number"":" & JsonStr(cardItem.job_number) & "," & _
"""job_name"":" & JsonStr(cardItem.job_name) & "," & _
+ """customer_name"":" & JsonStr(cardItem.customer_name) & "," & _
+ """delivery_date"":" & JsonDateStr(cardItem.delivery_date) & "," & _
+ """quantity"":" & JsonStr(cardItem.quantity) & "," & _
+ """notes"":" & JsonStr(cardItem.notes) & "," & _
+ """full_note"":" & JsonStr(cardItem.full_note) & "," & _
"""position"":" & cardItem.position & "}"
firstCard = False
Loop
@@ -168,10 +175,12 @@ Class BoardsController_Class
Dim newSlug : newSlug = boards_Repository().UniqueSlug(GenerateSlug(newName), CLng(board.id))
- board.name = newName
- board.slug = newSlug
- board.updated_at = Now()
- board.updated_by = GetCurrentUsername()
+ board.name = newName
+ board.slug = newSlug
+ board.import_from_printstream = (Request.Form("import_from_printstream") = "on")
+ board.printstream_job_name = Trim(CStr(Request.Form("printstream_job_name")))
+ board.updated_at = Now()
+ board.updated_by = GetCurrentUsername()
boards_Repository().Update board
@@ -226,6 +235,24 @@ Class BoardsController_Class
JsonStr = """" & v & """"
End Function
+ Private Function JsonDateStr(d)
+ If IsNull(d) Or IsEmpty(d) Then
+ JsonDateStr = "null"
+ Exit Function
+ End If
+ On Error Resume Next
+ Dim dt, s
+ dt = CDate(d)
+ s = Year(dt) & "-" & Right("0" & Month(dt), 2) & "-" & Right("0" & Day(dt), 2)
+ If Err.Number <> 0 Then
+ Err.Clear
+ JsonDateStr = "null"
+ Else
+ JsonDateStr = """" & s & """"
+ End If
+ On Error GoTo 0
+ End Function
+
End Class
Dim BoardsController_Class__Singleton
diff --git a/app/controllers/CardsController.asp b/app/controllers/CardsController.asp
index 0d03abc..ead4d08 100644
--- a/app/controllers/CardsController.asp
+++ b/app/controllers/CardsController.asp
@@ -37,22 +37,39 @@ Class CardsController_Class
Dim username : username = GetCurrentUsername()
Dim card : Set card = New POBO_cards
- card.board_id = boardId
- card.column_id = columnId
- card.swim_lane_id = swimLaneId
- card.job_number = jobNum
- card.job_name = jobName
- card.position = nextPos
- card.created_at = Now()
- card.created_by = username
- card.updated_at = Now()
- card.updated_by = username
+ card.board_id = boardId
+ card.column_id = columnId
+ card.swim_lane_id = swimLaneId
+ card.job_number = jobNum
+ card.job_name = jobName
+ card.customer_name = Trim(CStr(Request.Form("customer_name") & ""))
+ card.delivery_date = Trim(CStr(Request.Form("delivery_date") & ""))
+ card.quantity = Trim(CStr(Request.Form("quantity") & ""))
+ card.notes = Trim(CStr(Request.Form("notes") & ""))
+ card.full_note = CStr(Request.Form("full_note") & "")
+ card.position = nextPos
+ card.created_at = Now()
+ card.created_by = username
+ card.updated_at = Now()
+ card.updated_by = username
+ On Error Resume Next
cards_Repository().AddNew card
+ If Err.Number <> 0 Then
+ Response.Write "{""ok"":false,""error"":" & JsonString(Err.Description) & "}"
+ Err.Clear
+ Exit Sub
+ End If
+ On Error GoTo 0
Response.Write "{""ok"":true,""id"":" & card.id & "," & _
"""job_number"":" & JsonString(card.job_number) & "," & _
"""job_name"":" & JsonString(card.job_name) & "," & _
+ """customer_name"":" & JsonString(card.customer_name) & "," & _
+ """delivery_date"":" & JsonDateStr(card.delivery_date) & "," & _
+ """quantity"":" & JsonString(card.quantity) & "," & _
+ """notes"":" & JsonString(card.notes) & "," & _
+ """full_note"":" & JsonString(card.full_note) & "," & _
"""column_id"":" & card.column_id & "," & _
"""swim_lane_id"":" & card.swim_lane_id & "," & _
"""position"":" & card.position & "}"
@@ -77,14 +94,31 @@ Class CardsController_Class
card.job_number = Trim(CStr(Request.Form("job_number")))
card.job_name = Trim(CStr(Request.Form("job_name")))
+ card.customer_name = Trim(CStr(Request.Form("customer_name") & ""))
+ card.delivery_date = Trim(CStr(Request.Form("delivery_date") & ""))
+ card.quantity = Trim(CStr(Request.Form("quantity") & ""))
+ card.notes = Trim(CStr(Request.Form("notes") & ""))
+ card.full_note = CStr(Request.Form("full_note") & "")
card.updated_at = Now()
card.updated_by = GetCurrentUsername()
+ On Error Resume Next
cards_Repository().Update card
+ If Err.Number <> 0 Then
+ Response.Write "{""ok"":false,""error"":" & JsonString(Err.Description) & "}"
+ Err.Clear
+ Exit Sub
+ End If
+ On Error GoTo 0
Response.Write "{""ok"":true," & _
"""job_number"":" & JsonString(card.job_number) & "," & _
- """job_name"":" & JsonString(card.job_name) & "}"
+ """job_name"":" & JsonString(card.job_name) & "," & _
+ """customer_name"":" & JsonString(card.customer_name) & "," & _
+ """delivery_date"":" & JsonDateStr(card.delivery_date) & "," & _
+ """quantity"":" & JsonString(card.quantity) & "," & _
+ """notes"":" & JsonString(card.notes) & "," & _
+ """full_note"":" & JsonString(card.full_note) & "}"
End Sub
' POST /cards/:id/move — form: column_id, swim_lane_id, position, [sibling_ids CSV for reorder]
@@ -147,7 +181,31 @@ Class CardsController_Class
End Function
Private Function JsonString(s)
- JsonString = """" & Replace(Replace(CStr(s), "\", "\\"), """", "\""") & """"
+ Dim v : v = CStr(s & "")
+ v = Replace(v, "\", "\\")
+ v = Replace(v, """", "\""")
+ v = Replace(v, vbCrLf, "\n")
+ v = Replace(v, vbLf, "\n")
+ v = Replace(v, vbCr, "\n")
+ JsonString = """" & v & """"
+ End Function
+
+ Private Function JsonDateStr(d)
+ If IsNull(d) Or IsEmpty(d) Then
+ JsonDateStr = "null"
+ Exit Function
+ End If
+ On Error Resume Next
+ Dim dt, s
+ dt = CDate(d)
+ s = Year(dt) & "-" & Right("0" & Month(dt), 2) & "-" & Right("0" & Day(dt), 2)
+ If Err.Number <> 0 Then
+ Err.Clear
+ JsonDateStr = "null"
+ Else
+ JsonDateStr = """" & s & """"
+ End If
+ On Error GoTo 0
End Function
End Class
diff --git a/app/controllers/ColumnsController.asp b/app/controllers/ColumnsController.asp
index 697080f..01a2305 100644
--- a/app/controllers/ColumnsController.asp
+++ b/app/controllers/ColumnsController.asp
@@ -102,19 +102,42 @@ Class ColumnsController_Class
End If
Dim rawJson : rawJson = GetRawJsonFromRequest()
- Dim parsed : Set parsed = JSON.parse(rawJson)
+ Dim parser : Set parser = New aspJSON
+ Dim parsed
- If IsNull(parsed) Or IsEmpty(parsed) Then
- Response.Write "{""ok"":false,""error"":""Invalid JSON""}"
+ On Error Resume Next
+ parser.loadJSON rawJson
+ If Err.Number <> 0 Then
+ Err.Clear
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}"
+ Exit Sub
+ End If
+ Set parsed = parser.data
+ On Error GoTo 0
+
+ If parsed Is Nothing Or parsed.Count = 0 Then
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}"
Exit Sub
End If
Dim username : username = GetCurrentUsername()
Dim i, item
+ On Error Resume Next
For i = 0 To parsed.Count - 1
Set item = parsed.Item(i)
board_columns_Repository().UpdatePosition CLng(item.Item("id")), CLng(item.Item("position")), Now(), username
+ If Err.Number <> 0 Then
+ Err.Clear
+ On Error GoTo 0
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid reorder item at index " & i & """}"
+ Exit Sub
+ End If
Next
+ On Error GoTo 0
+ Set parser = Nothing
Response.Write "{""ok"":true}"
End Sub
diff --git a/app/controllers/SwimLanesController.asp b/app/controllers/SwimLanesController.asp
index 0b0a4ed..fd6dbe4 100644
--- a/app/controllers/SwimLanesController.asp
+++ b/app/controllers/SwimLanesController.asp
@@ -102,19 +102,42 @@ Class SwimLanesController_Class
End If
Dim rawJson : rawJson = GetRawJsonFromRequest()
- Dim parsed : Set parsed = JSON.parse(rawJson)
+ Dim parser : Set parser = New aspJSON
+ Dim parsed
- If IsNull(parsed) Or IsEmpty(parsed) Then
- Response.Write "{""ok"":false,""error"":""Invalid JSON""}"
+ On Error Resume Next
+ parser.loadJSON rawJson
+ If Err.Number <> 0 Then
+ Err.Clear
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}"
+ Exit Sub
+ End If
+ Set parsed = parser.data
+ On Error GoTo 0
+
+ If parsed Is Nothing Or parsed.Count = 0 Then
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}"
Exit Sub
End If
Dim username : username = GetCurrentUsername()
Dim i, item
+ On Error Resume Next
For i = 0 To parsed.Count - 1
Set item = parsed.Item(i)
swim_lanes_Repository().UpdatePosition CLng(item.Item("id")), CLng(item.Item("position")), Now(), username
+ If Err.Number <> 0 Then
+ Err.Clear
+ On Error GoTo 0
+ Set parser = Nothing
+ Response.Write "{""ok"":false,""error"":""Invalid reorder item at index " & i & """}"
+ Exit Sub
+ End If
Next
+ On Error GoTo 0
+ Set parser = Nothing
Response.Write "{""ok"":true}"
End Sub
diff --git a/app/models/POBO_boards.asp b/app/models/POBO_boards.asp
index 6e2ba45..b50446d 100644
--- a/app/models/POBO_boards.asp
+++ b/app/models/POBO_boards.asp
@@ -5,20 +5,24 @@ Class POBO_boards
Private p_id
Private p_name
Private p_slug
+ Private p_import_from_printstream
+ Private p_printstream_job_name
Private p_created_at
Private p_created_by
Private p_updated_at
Private p_updated_by
Private Sub Class_Initialize()
- p_id = 0
- p_name = ""
- p_slug = ""
- p_created_at = #1/1/1970#
- p_created_by = ""
- p_updated_at = #1/1/1970#
- p_updated_by = ""
- Properties = Array("id","name","slug","created_at","created_by","updated_at","updated_by")
+ p_id = 0
+ p_name = ""
+ p_slug = ""
+ p_import_from_printstream = False
+ p_printstream_job_name = ""
+ p_created_at = #1/1/1970#
+ p_created_by = ""
+ p_updated_at = #1/1/1970#
+ p_updated_by = ""
+ Properties = Array("id","name","slug","import_from_printstream","printstream_job_name","created_at","created_by","updated_at","updated_by")
End Sub
Public Property Get PrimaryKey() : PrimaryKey = "id" : End Property
@@ -33,6 +37,12 @@ Class POBO_boards
Public Property Get slug() : slug = p_slug : End Property
Public Property Let slug(v) : p_slug = CStr(v) : End Property
+ Public Property Get import_from_printstream() : import_from_printstream = p_import_from_printstream : End Property
+ Public Property Let import_from_printstream(v) : p_import_from_printstream = CBool(v) : End Property
+
+ Public Property Get printstream_job_name() : printstream_job_name = p_printstream_job_name : End Property
+ Public Property Let printstream_job_name(v) : p_printstream_job_name = CStr(v) : End Property
+
Public Property Get created_at() : created_at = p_created_at : End Property
Public Property Let created_at(v) : p_created_at = CDate(v) : End Property
diff --git a/app/models/POBO_cards.asp b/app/models/POBO_cards.asp
index 619388c..dd26dc9 100644
--- a/app/models/POBO_cards.asp
+++ b/app/models/POBO_cards.asp
@@ -8,6 +8,11 @@ Class POBO_cards
Private p_swim_lane_id
Private p_job_number
Private p_job_name
+ Private p_customer_name
+ Private p_delivery_date
+ Private p_quantity
+ Private p_notes
+ Private p_full_note
Private p_position
Private p_created_at
Private p_created_by
@@ -15,18 +20,23 @@ Class POBO_cards
Private p_updated_by
Private Sub Class_Initialize()
- p_id = 0
- p_board_id = 0
- p_column_id = 0
- p_swim_lane_id = 0
- p_job_number = ""
- p_job_name = ""
- p_position = 0
- p_created_at = #1/1/1970#
- p_created_by = ""
- p_updated_at = #1/1/1970#
- p_updated_by = ""
- Properties = Array("id","board_id","column_id","swim_lane_id","job_number","job_name","position","created_at","created_by","updated_at","updated_by")
+ p_id = 0
+ p_board_id = 0
+ p_column_id = 0
+ p_swim_lane_id = 0
+ p_job_number = ""
+ p_job_name = ""
+ p_customer_name = ""
+ p_delivery_date = Null
+ p_quantity = ""
+ p_notes = ""
+ p_full_note = ""
+ p_position = 0
+ p_created_at = #1/1/1970#
+ p_created_by = ""
+ p_updated_at = #1/1/1970#
+ p_updated_by = ""
+ Properties = Array("id","board_id","column_id","swim_lane_id","job_number","job_name","customer_name","delivery_date","quantity","notes","full_note","position","created_at","created_by","updated_at","updated_by")
End Sub
Public Property Get PrimaryKey() : PrimaryKey = "id" : End Property
@@ -50,6 +60,23 @@ Class POBO_cards
Public Property Get job_name() : job_name = p_job_name : End Property
Public Property Let job_name(v) : p_job_name = CStr(v) : End Property
+ Public Property Get customer_name() : customer_name = p_customer_name : End Property
+ Public Property Let customer_name(v) : p_customer_name = CStr(v & "") : End Property
+
+ Public Property Get delivery_date() : delivery_date = p_delivery_date : End Property
+ Public Property Let delivery_date(v)
+ If IsDate(v) Then p_delivery_date = CDate(v) Else p_delivery_date = Null
+ End Property
+
+ Public Property Get quantity() : quantity = p_quantity : End Property
+ Public Property Let quantity(v) : p_quantity = CStr(v & "") : End Property
+
+ Public Property Get notes() : notes = p_notes : End Property
+ Public Property Let notes(v) : p_notes = CStr(v & "") : End Property
+
+ Public Property Get full_note() : full_note = p_full_note : End Property
+ Public Property Let full_note(v) : p_full_note = CStr(v & "") : End Property
+
Public Property Get position() : position = p_position : End Property
Public Property Let position(v) : p_position = CDbl(v) : End Property
diff --git a/app/repositories/boards_Repository.asp b/app/repositories/boards_Repository.asp
index 3465774..14e7d59 100644
--- a/app/repositories/boards_Repository.asp
+++ b/app/repositories/boards_Repository.asp
@@ -2,7 +2,7 @@
Class boards_Repository_Class
Public Function FindByID(id)
- Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [id] = ?"
+ Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [id] = ?"
Dim rs : Set rs = DAL.Query(sql, Array(id))
If rs.EOF Then
Err.Raise 1, "boards_Repository_Class", "Board not found with id = " & id
@@ -13,7 +13,7 @@ Class boards_Repository_Class
End Function
Public Function FindBySlug(slug)
- Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [slug] = ?"
+ Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [slug] = ?"
Dim rs : Set rs = DAL.Query(sql, Array(slug))
If rs.EOF Then
Set FindBySlug = Nothing
@@ -24,7 +24,7 @@ Class boards_Repository_Class
End Function
Public Function GetAll()
- Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] ORDER BY [name] ASC"
+ Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] ORDER BY [name] ASC"
Dim rs : Set rs = DAL.Query(sql, Empty)
Dim list : Set list = New LinkedList_Class
Do Until rs.EOF
@@ -60,8 +60,8 @@ Class boards_Repository_Class
End Function
Public Sub AddNew(ByRef model)
- Dim sql : sql = "INSERT INTO [boards] ([name],[slug],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?)"
- DAL.Execute sql, Array(model.name, model.slug, model.created_at, model.created_by, model.updated_at, model.updated_by)
+ Dim sql : sql = "INSERT INTO [boards] ([name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?)"
+ DAL.Execute sql, Array(model.name, model.slug, model.import_from_printstream, model.printstream_job_name, model.created_at, model.created_by, model.updated_at, model.updated_by)
Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty)
If Not rsId.EOF Then
If Not IsNull(rsId(0)) Then model.id = rsId(0)
@@ -70,8 +70,8 @@ Class boards_Repository_Class
End Sub
Public Sub Update(model)
- Dim sql : sql = "UPDATE [boards] SET [name]=?,[slug]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
- DAL.Execute sql, Array(model.name, model.slug, model.updated_at, model.updated_by, model.id)
+ Dim sql : sql = "UPDATE [boards] SET [name]=?,[slug]=?,[import_from_printstream]=?,[printstream_job_name]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
+ DAL.Execute sql, Array(model.name, model.slug, model.import_from_printstream, model.printstream_job_name, model.updated_at, model.updated_by, model.id)
End Sub
Public Sub Delete(id)
diff --git a/app/repositories/cards_Repository.asp b/app/repositories/cards_Repository.asp
index 05026f2..90a322a 100644
--- a/app/repositories/cards_Repository.asp
+++ b/app/repositories/cards_Repository.asp
@@ -2,7 +2,7 @@
Class cards_Repository_Class
Private Function SelectBase()
- SelectBase = "SELECT [id],[board_id],[column_id],[swim_lane_id],[job_number],[job_name],[position],[created_at],[created_by],[updated_at],[updated_by] FROM [cards]"
+ SelectBase = "SELECT [id],[board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by] FROM [cards]"
End Function
Public Function FindByID(id)
@@ -52,8 +52,15 @@ Class cards_Repository_Class
End Function
Public Sub AddNew(ByRef model)
- Dim sql : sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?,?,?)"
- DAL.Execute sql, Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by)
+ Dim sql, params
+ If QuantityIsBlank(model.quantity) Then
+ sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,NULL,?,?,?,?,?,?,?)"
+ params = Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.customer_name, model.delivery_date, model.notes, model.full_note, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by)
+ Else
+ sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
+ params = Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.customer_name, model.delivery_date, model.quantity, model.notes, model.full_note, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by)
+ End If
+ DAL.Execute sql, params
Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty)
If Not rsId.EOF Then
If Not IsNull(rsId(0)) Then model.id = rsId(0)
@@ -62,10 +69,21 @@ Class cards_Repository_Class
End Sub
Public Sub Update(model)
- Dim sql : sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
- DAL.Execute sql, Array(model.job_number, model.job_name, model.updated_at, model.updated_by, model.id)
+ Dim sql, params
+ If QuantityIsBlank(model.quantity) Then
+ sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[customer_name]=?,[delivery_date]=?,[quantity]=NULL,[notes]=?,[full_note]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
+ params = Array(model.job_number, model.job_name, model.customer_name, model.delivery_date, model.notes, model.full_note, model.updated_at, model.updated_by, model.id)
+ Else
+ sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[customer_name]=?,[delivery_date]=?,[quantity]=?,[notes]=?,[full_note]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
+ params = Array(model.job_number, model.job_name, model.customer_name, model.delivery_date, model.quantity, model.notes, model.full_note, model.updated_at, model.updated_by, model.id)
+ End If
+ DAL.Execute sql, params
End Sub
+ Private Function QuantityIsBlank(v)
+ QuantityIsBlank = (Len(Trim(CStr(v & ""))) = 0)
+ End Function
+
Public Sub Move(id, columnId, swimLaneId, position, updatedAt, updatedBy)
Dim sql : sql = "UPDATE [cards] SET [column_id]=?,[swim_lane_id]=?,[position]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?"
DAL.Execute sql, Array(columnId, swimLaneId, position, updatedAt, updatedBy, id)
diff --git a/app/views/Boards/Create.asp b/app/views/Boards/Create.asp
index 735ec4a..9e941d1 100644
--- a/app/views/Boards/Create.asp
+++ b/app/views/Boards/Create.asp
@@ -19,6 +19,18 @@
+
+
+
+
+
+
+
+
+
+
Cancel
@@ -31,11 +43,19 @@
diff --git a/app/views/Boards/Show.asp b/app/views/Boards/Show.asp
index dfab647..4e0b162 100644
--- a/app/views/Boards/Show.asp
+++ b/app/views/Boards/Show.asp
@@ -10,8 +10,11 @@ Response.CodePage = 65001
-
-
+
+
+
+
+
@@ -23,6 +26,20 @@ Response.CodePage = 65001
<%= H(board.name) %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/db/migrations/20260423100000_add_printstream_to_boards.asp b/db/migrations/20260423100000_add_printstream_to_boards.asp
new file mode 100644
index 0000000..1f21cb3
--- /dev/null
+++ b/db/migrations/20260423100000_add_printstream_to_boards.asp
@@ -0,0 +1,15 @@
+<%
+'=======================================================================================================================
+' MIGRATION: add_printstream_to_boards
+'=======================================================================================================================
+
+Sub Migration_Up(migration)
+ migration.ExecuteSQL "ALTER TABLE [boards] ADD COLUMN [import_from_printstream] YESNO"
+ migration.ExecuteSQL "ALTER TABLE [boards] ADD COLUMN [printstream_job_name] MEMO"
+End Sub
+
+Sub Migration_Down(migration)
+ migration.ExecuteSQL "ALTER TABLE [boards] DROP COLUMN [printstream_job_name]"
+ migration.ExecuteSQL "ALTER TABLE [boards] DROP COLUMN [import_from_printstream]"
+End Sub
+%>
diff --git a/db/migrations/20260424100000_add_printstream_fields_to_cards.asp b/db/migrations/20260424100000_add_printstream_fields_to_cards.asp
new file mode 100644
index 0000000..907596d
--- /dev/null
+++ b/db/migrations/20260424100000_add_printstream_fields_to_cards.asp
@@ -0,0 +1,19 @@
+<%
+'=======================================================================================================================
+' MIGRATION: add_printstream_fields_to_cards
+'=======================================================================================================================
+
+Sub Migration_Up(migration)
+ migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [customer_name] VARCHAR(255)"
+ migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [delivery_date] DATETIME"
+ migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [quantity] VARCHAR(50)"
+ migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [notes] MEMO"
+End Sub
+
+Sub Migration_Down(migration)
+ migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [notes]"
+ migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [quantity]"
+ migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [delivery_date]"
+ migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [customer_name]"
+End Sub
+%>
diff --git a/db/migrations/20260424200000_add_full_note_to_cards.asp b/db/migrations/20260424200000_add_full_note_to_cards.asp
new file mode 100644
index 0000000..e360855
--- /dev/null
+++ b/db/migrations/20260424200000_add_full_note_to_cards.asp
@@ -0,0 +1,13 @@
+<%
+'=======================================================================================================================
+' MIGRATION: add_full_note_to_cards
+'=======================================================================================================================
+
+Sub Migration_Up(migration)
+ migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [full_note] MEMO"
+End Sub
+
+Sub Migration_Down(migration)
+ migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [full_note]"
+End Sub
+%>
diff --git a/db/webdata.accdb b/db/webdata.accdb
index e294807..d843e93 100644
Binary files a/db/webdata.accdb and b/db/webdata.accdb differ
diff --git a/public/css/kanban.css b/public/css/kanban.css
index 64c4f80..1603cc5 100644
--- a/public/css/kanban.css
+++ b/public/css/kanban.css
@@ -27,7 +27,6 @@ body.kanban-page .navbar {
body.kanban-page .navbar-brand {
color: #f4f8ff !important;
- font-family: "Fraunces", Georgia, serif;
letter-spacing: -0.01em;
}
@@ -39,6 +38,33 @@ body.kanban-page .navbar-brand {
flex-shrink: 0;
}
+.board-header-search {
+ width: min(440px, 36vw);
+ margin: 0 0.75rem;
+}
+
+.board-header-search .input-group-text {
+ border-color: rgba(214, 229, 250, 0.7);
+ background: rgba(255, 255, 255, 0.15);
+ color: #eff6ff;
+}
+
+.board-header-search .form-control {
+ border-color: rgba(214, 229, 250, 0.7);
+ background: rgba(255, 255, 255, 0.17);
+ color: #f5f9ff;
+}
+
+.board-header-search .form-control::placeholder {
+ color: rgba(235, 243, 255, 0.72);
+}
+
+.board-header-search .form-control:focus {
+ border-color: rgba(235, 245, 255, 0.95);
+ box-shadow: 0 0 0 0.2rem rgba(157, 194, 245, 0.25);
+ background: rgba(255, 255, 255, 0.23);
+}
+
.kanban-board-title {
display: block;
min-width: 0;
@@ -67,21 +93,27 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
width: 100%;
margin: 0 auto;
height: calc(100vh - 65px);
- overflow: auto;
+ overflow-x: auto;
+ overflow-y: auto;
padding: 0.9rem 1rem 1.1rem;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
touch-action: pan-x pan-y;
+ overscroll-behavior: contain;
+ scrollbar-width: thin;
+ scrollbar-color: #8fb0e0 #dce8f8;
+ scrollbar-gutter: stable;
}
.kanban-grid {
display: grid;
- min-width: max(75vw, max-content);
+ width: max-content;
+ min-width: 100%;
border: 1px solid var(--line, #d9e3f5);
border-radius: 14px;
background: rgba(255, 255, 255, 0.72);
box-shadow: 0 12px 34px rgba(22, 48, 92, 0.12);
- overflow: hidden;
+ overflow: clip;
}
/* Sticky corner and headers */
@@ -121,6 +153,9 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
left: 0;
z-index: 20;
min-width: 240px;
+ display: flex;
+ align-items: center;
+ gap: 0.42rem;
padding: 0.85rem 0.82rem;
font-size: clamp(0.7rem, 0.14vw + 0.66rem, 0.78rem);
font-weight: 700;
@@ -139,6 +174,33 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
overflow-wrap: anywhere;
}
+.kanban-lane-header .lane-toggle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.34rem;
+ height: 1.34rem;
+ padding: 0;
+ border: 0;
+ border-radius: 999px;
+ background: rgba(26, 74, 145, 0.12);
+ color: #1f4f96;
+ cursor: pointer;
+ flex: 0 0 auto;
+}
+
+.kanban-lane-header .lane-toggle:hover {
+ background: rgba(26, 74, 145, 0.22);
+}
+
+.kanban-lane-header .lane-toggle i {
+ transition: transform 140ms ease;
+}
+
+.kanban-lane-header.lane-collapsed .lane-toggle i {
+ transform: rotate(-90deg);
+}
+
/* Cells */
.kanban-cell {
min-width: 230px;
@@ -154,6 +216,20 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(244, 248, 255, 0.93) 100%);
}
+.kanban-cell.lane-collapsed {
+ min-height: 0;
+ max-height: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ border-top-color: transparent;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.kanban-cell.lane-collapsed .kanban-card {
+ display: none !important;
+}
+
.kanban-cell.drag-over {
background: linear-gradient(180deg, #e9f2ff 0%, #deecff 100%);
box-shadow: inset 0 0 0 2px rgba(19, 99, 223, 0.24);
@@ -187,9 +263,19 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
box-shadow: 0 14px 30px rgba(17, 46, 94, 0.22);
}
+.kanban-card-hidden {
+ display: none !important;
+}
+
+.card-headline {
+ display: flex;
+ align-items: center;
+ gap: 0.38rem;
+ min-width: 0;
+}
+
.card-job-number {
display: inline-block;
- margin-bottom: 0.36rem;
padding: 0.08rem 0.42rem;
border-radius: 999px;
font-size: 0.66rem;
@@ -198,12 +284,45 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
text-transform: uppercase;
color: #0e4fae;
background: #e7f0ff;
+ flex: 0 0 auto;
+}
+
+.card-customer {
+ font-size: 0.78rem;
+ font-weight: 600;
+ color: #2b4a80;
+ min-width: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
-.card-job-name {
- color: #1f2b43;
- font-size: 0.86rem;
- line-height: 1.32;
+.card-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+.card-meta-label {
+ font-size: 0.63rem;
+ font-weight: 700;
+ color: #7a90b2;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.card-delivery,
+.card-qty {
+ font-size: 0.73rem;
+ color: #3a5080;
+}
+
+.card-notes {
+ margin-top: 0.22rem;
+ font-size: 0.71rem;
+ color: #647899;
+ line-height: 1.3;
}
/* Settings panel */
@@ -294,25 +413,30 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
/* Scrollbars */
.kanban-wrapper::-webkit-scrollbar,
.settings-body::-webkit-scrollbar {
- width: 10px;
- height: 10px;
+ width: 12px;
+ height: 12px;
}
.kanban-wrapper::-webkit-scrollbar-track,
.settings-body::-webkit-scrollbar-track {
- background: #eaf0fb;
+ background: #dce8f8;
+ border-radius: 999px;
}
.kanban-wrapper::-webkit-scrollbar-thumb,
.settings-body::-webkit-scrollbar-thumb {
- background: #b8c9e6;
+ background: #8fb0e0;
border-radius: 999px;
- border: 2px solid #eaf0fb;
+ border: 2px solid #dce8f8;
}
.kanban-wrapper::-webkit-scrollbar-thumb:hover,
.settings-body::-webkit-scrollbar-thumb:hover {
- background: #97afd8;
+ background: #5e8ecb;
+}
+
+.kanban-wrapper::-webkit-scrollbar-corner {
+ background: #dce8f8;
}
/* Small screens */
@@ -340,6 +464,11 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
gap: 0.35rem !important;
}
+ .board-header-search {
+ width: min(270px, 44vw);
+ margin: 0 0.35rem;
+ }
+
.board-header-actions .btn {
padding-left: 0.48rem;
padding-right: 0.48rem;
@@ -347,6 +476,25 @@ body.kanban-page .navbar .btn-outline-secondary:hover {
}
@media (max-width: 640px) {
+ .board-header-search {
+ width: 100%;
+ margin: 0.5rem 0 0;
+ order: 3;
+ }
+
+ body.kanban-page .navbar {
+ flex-wrap: wrap;
+ align-items: flex-start !important;
+ }
+
+ .board-header-main {
+ flex: 1 1 auto;
+ }
+
+ .board-header-actions {
+ flex: 0 0 auto;
+ }
+
.kanban-settings-panel {
width: 100vw;
border-left: 0;
diff --git a/public/js/kanban-board.js b/public/js/kanban-board.js
index 24b2f73..64f4a67 100644
--- a/public/js/kanban-board.js
+++ b/public/js/kanban-board.js
@@ -3,6 +3,11 @@
'use strict';
var boardId = KANBAN.boardId;
+ var laneCollapseStorageKey = 'kanban_lane_collapsed_' + String(boardId);
+ var collapsedLaneIds = loadCollapsedLaneIds();
+ var searchState = {
+ query: ''
+ };
var dragState = {
active: false,
x: 0,
@@ -35,27 +40,123 @@
var grid = document.getElementById('kanban-grid');
var colHs = grid.querySelectorAll('.kanban-col-header');
var cols = '240px';
- colHs.forEach(function () { cols += ' 220px'; });
+ colHs.forEach(function () { cols += ' 230px'; });
grid.style.gridTemplateColumns = cols;
}
+ function loadCollapsedLaneIds() {
+ var laneMap = {};
+ try {
+ var raw = window.localStorage.getItem(laneCollapseStorageKey);
+ if (!raw) return laneMap;
+ var arr = JSON.parse(raw);
+ if (!Array.isArray(arr)) return laneMap;
+ arr.forEach(function (laneId) {
+ laneMap[String(laneId)] = true;
+ });
+ } catch (e) {
+ console.warn('Failed to load lane collapse state', e);
+ }
+ return laneMap;
+ }
+
+ function saveCollapsedLaneIds() {
+ try {
+ window.localStorage.setItem(laneCollapseStorageKey, JSON.stringify(Object.keys(collapsedLaneIds)));
+ } catch (e) {
+ console.warn('Failed to save lane collapse state', e);
+ }
+ }
+
+ function setLaneCollapsed(laneId, isCollapsed) {
+ var laneKey = String(laneId);
+ var header = document.querySelector('.kanban-lane-header[data-lane-id="' + laneKey + '"]');
+ if (!header) return;
+
+ var laneCells = document.querySelectorAll('.kanban-cell[data-lane-id="' + laneKey + '"]');
+ header.classList.toggle('lane-collapsed', isCollapsed);
+ laneCells.forEach(function (cell) {
+ cell.classList.toggle('lane-collapsed', isCollapsed);
+ });
+
+ var toggleBtn = header.querySelector('.lane-toggle');
+ if (toggleBtn) {
+ toggleBtn.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true');
+ toggleBtn.title = isCollapsed ? 'Expand swim lane' : 'Collapse swim lane';
+ toggleBtn.setAttribute('aria-label', toggleBtn.title);
+ }
+
+ if (isCollapsed) {
+ collapsedLaneIds[laneKey] = true;
+ } else {
+ delete collapsedLaneIds[laneKey];
+ }
+ saveCollapsedLaneIds();
+ }
+
+ function toggleLaneCollapsed(laneId) {
+ var laneKey = String(laneId);
+ setLaneCollapsed(laneKey, !collapsedLaneIds[laneKey]);
+ }
+
+ function bindLaneHeaderToggle(headerEl) {
+ if (!headerEl) return;
+ var toggleBtn = headerEl.querySelector('.lane-toggle');
+ if (!toggleBtn) return;
+
+ if (!toggleBtn.dataset.boundToggle) {
+ toggleBtn.addEventListener('click', function (evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ toggleLaneCollapsed(headerEl.dataset.laneId);
+ });
+ toggleBtn.dataset.boundToggle = '1';
+ }
+ }
+
+ function initLaneHeaderToggles() {
+ document.querySelectorAll('.kanban-lane-header').forEach(function (headerEl) {
+ bindLaneHeaderToggle(headerEl);
+ if (collapsedLaneIds[String(headerEl.dataset.laneId)]) {
+ setLaneCollapsed(headerEl.dataset.laneId, true);
+ }
+ });
+ }
+
+ function cardBodyHtml(card) {
+ var html = '' +
+ '' + esc(card.job_number || '') + '';
+
+ if (card.customer_name) {
+ html += '' + esc(card.customer_name) + '';
+ }
+
+ html += '
';
+
+ return html;
+ }
+
+ function buildCardSearchText(card) {
+ return [
+ card.job_number || '',
+ card.job_name || '',
+ card.customer_name || '',
+ card.notes || ''
+ ].join(' ').toLowerCase();
+ }
+
function buildCardEl(card) {
var div = document.createElement('div');
div.className = 'kanban-card';
div.dataset.id = card.id;
div.dataset.columnId = card.column_id;
div.dataset.laneId = card.swim_lane_id;
- div.innerHTML =
- '' + esc(card.job_number || '') + '
' +
- '' + esc(card.job_name || '') + '
';
+ div.dataset.searchText = buildCardSearchText(card);
+ div.innerHTML = cardBodyHtml(card);
div.addEventListener('click', function () {
- window.KanbanModal.openEdit(
- card.id,
- card.column_id,
- card.swim_lane_id,
- card.job_number,
- card.job_name
- );
+ var c = KANBAN.cards.find(function (x) { return String(x.id) === String(div.dataset.id); });
+ if (!c) return;
+ window.KanbanModal.openEdit(c.id, c.column_id, c.swim_lane_id, c.job_number, c.job_name, c.customer_name, c.delivery_date, c.quantity, c.notes, c.full_note);
});
return div;
}
@@ -69,6 +170,26 @@
cell.appendChild(buildCardEl(card));
}
});
+ applyCardFilter();
+ }
+
+ function applyCardFilter() {
+ var activeQuery = searchState.query;
+ document.querySelectorAll('.kanban-card').forEach(function (el) {
+ var searchableText = (el.dataset.searchText || '').toLowerCase();
+ var isMatch = activeQuery === '' || searchableText.indexOf(activeQuery) > -1;
+ el.classList.toggle('kanban-card-hidden', !isMatch);
+ });
+ }
+
+ function initJobSearch() {
+ var searchInput = document.getElementById('job-search-input');
+ if (!searchInput) return;
+
+ searchInput.addEventListener('input', function () {
+ searchState.query = String(searchInput.value || '').toLowerCase().trim();
+ applyCardFilter();
+ });
}
function handleDragEnd(evt) {
@@ -206,23 +327,31 @@
if (cell) {
cell.appendChild(buildCardEl(card));
}
+ applyCardFilter();
},
- onCardUpdated: function (id, jobNumber, jobName) {
+ onCardUpdated: function (id, data) {
var card = KANBAN.cards.find(function (c) { return String(c.id) === String(id); });
if (card) {
- card.job_number = jobNumber;
- card.job_name = jobName;
+ card.job_number = data.job_number || '';
+ card.job_name = data.job_name || '';
+ card.customer_name = data.customer_name || '';
+ card.delivery_date = data.delivery_date || null;
+ card.quantity = data.quantity || '';
+ card.notes = data.notes || '';
+ card.full_note = data.full_note !== undefined ? data.full_note : (card.full_note || '');
}
var el = document.querySelector('.kanban-card[data-id="' + id + '"]');
- if (el) {
- el.querySelector('.card-job-number').textContent = jobNumber;
- el.querySelector('.card-job-name').textContent = jobName;
+ if (el && card) {
+ el.innerHTML = cardBodyHtml(card);
+ el.dataset.searchText = buildCardSearchText(card);
}
+ applyCardFilter();
},
onCardDeleted: function (id) {
KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.id) !== String(id); });
var el = document.querySelector('.kanban-card[data-id="' + id + '"]');
if (el) el.remove();
+ applyCardFilter();
},
addColumn: function (col) {
var grid = document.getElementById('kanban-grid');
@@ -263,8 +392,13 @@
var lh = document.createElement('div');
lh.className = 'kanban-lane-header';
lh.dataset.laneId = lane.id;
- lh.innerHTML = '' + esc(lane.name) + '';
+ lh.innerHTML =
+ '' +
+ '' + esc(lane.name) + '';
grid.appendChild(lh);
+ bindLaneHeaderToggle(lh);
colHeaders.forEach(function (ch) {
var cell = document.createElement('div');
@@ -275,12 +409,20 @@
createCellSortable(cell);
});
+ if (collapsedLaneIds[String(lane.id)]) {
+ setLaneCollapsed(lane.id, true);
+ }
+
applyGridTemplate();
},
removeLane: function (laneId) {
document.querySelector('.kanban-lane-header[data-lane-id="' + laneId + '"]').remove();
document.querySelectorAll('.kanban-cell[data-lane-id="' + laneId + '"]').forEach(function (el) { el.remove(); });
KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.swim_lane_id) !== String(laneId); });
+ if (collapsedLaneIds[String(laneId)]) {
+ delete collapsedLaneIds[String(laneId)];
+ saveCollapsedLaneIds();
+ }
},
renameColumn: function (colId, name) {
var hdr = document.querySelector('.kanban-col-header[data-col-id="' + colId + '"] .col-label');
@@ -295,4 +437,6 @@
applyGridTemplate();
renderCards();
initSortables();
+ initJobSearch();
+ initLaneHeaderToggles();
})();
diff --git a/public/js/kanban-modal.js b/public/js/kanban-modal.js
index 46a8e5e..07300e7 100644
--- a/public/js/kanban-modal.js
+++ b/public/js/kanban-modal.js
@@ -14,6 +14,12 @@
var btnSave = document.getElementById('btn-save-card');
var btnDelete = document.getElementById('btn-delete-card');
+ var custNameEl = document.getElementById('card-customer-name');
+ var delivDateEl = document.getElementById('card-delivery-date');
+ var qtyEl = document.getElementById('card-quantity');
+ var notesEl = document.getElementById('card-notes');
+ var fullNoteEl = document.getElementById('card-full-note');
+
var boardId = KANBAN.boardId;
/* ── Helpers ─────────────────────────────────────────────── */
@@ -44,6 +50,11 @@
laneIdEl.value = laneId || '';
jobNumEl.value = '';
jobNameEl.value = '';
+ custNameEl.value = '';
+ delivDateEl.value = '';
+ qtyEl.value = '';
+ notesEl.value = '';
+ fullNoteEl.value = '';
btnDelete.classList.add('d-none');
clearError();
bsModal.show();
@@ -51,13 +62,18 @@
}
/* ── Open for edit ───────────────────────────────────────── */
- function openEdit(id, colId, laneId, jobNum, jobName) {
+ function openEdit(id, colId, laneId, jobNum, jobName, custName, delivDate, qty, notes, fullNote) {
titleEl.textContent = 'Edit Card';
cardIdEl.value = id;
colIdEl.value = colId;
laneIdEl.value = laneId;
- jobNumEl.value = jobNum || '';
- jobNameEl.value = jobName || '';
+ jobNumEl.value = jobNum || '';
+ jobNameEl.value = jobName || '';
+ custNameEl.value = custName || '';
+ delivDateEl.value = delivDate || '';
+ qtyEl.value = qty || '';
+ notesEl.value = notes || '';
+ fullNoteEl.value = fullNote || '';
btnDelete.classList.remove('d-none');
clearError();
bsModal.show();
@@ -72,6 +88,11 @@
var laneId = laneIdEl.value;
var jNum = jobNumEl.value.trim();
var jName = jobNameEl.value.trim();
+ var cust = custNameEl.value.trim();
+ var dDate = delivDateEl.value;
+ var qty = qtyEl.value.trim();
+ var notes = notesEl.value.trim();
+ var fullNote = fullNoteEl.value;
if (!jNum && !jName) {
showError('Enter at least a job number or job name.');
@@ -80,10 +101,10 @@
if (id) {
// Update existing
- post('/cards/' + id, { job_number: jNum, job_name: jName }, function (res) {
+ post('/cards/' + id, { job_number: jNum, job_name: jName, customer_name: cust, delivery_date: dDate, quantity: qty, notes: notes, full_note: fullNote }, function (res) {
if (res.ok) {
bsModal.hide();
- window.KanbanBoard.onCardUpdated(id, res.job_number, res.job_name);
+ window.KanbanBoard.onCardUpdated(id, res);
} else {
showError(res.error || 'Save failed.');
}
@@ -95,11 +116,16 @@
return;
}
post('/cards', {
- board_id: boardId,
- column_id: colId,
+ board_id: boardId,
+ column_id: colId,
swim_lane_id: laneId,
- job_number: jNum,
- job_name: jName
+ job_number: jNum,
+ job_name: jName,
+ customer_name: cust,
+ delivery_date: dDate,
+ quantity: qty,
+ notes: notes,
+ full_note: fullNote
}, function (res) {
if (res.ok) {
bsModal.hide();
diff --git a/scripts/importPrintStreamJobs.vbs b/scripts/importPrintStreamJobs.vbs
new file mode 100644
index 0000000..69f682a
Binary files /dev/null and b/scripts/importPrintStreamJobs.vbs differ
diff --git a/scripts/migrate_isbusiness_to_households.vbs b/scripts/migrate_isbusiness_to_households.vbs
deleted file mode 100644
index 5f0a8b9..0000000
--- a/scripts/migrate_isbusiness_to_households.vbs
+++ /dev/null
@@ -1,90 +0,0 @@
-' migrate_isbusiness_to_households.vbs
-' Moves IsBusiness from HouseholderNames to Households.
-'
-' Usage:
-' cscript //nologo scripts\migrate_isbusiness_to_households.vbs "C:\path\to\myAccessFile.accdb"
-'
-' What it does:
-' 1) Adds Households.IsBusiness (SMALLINT) if missing
-' 2) Copies data: sets Households.IsBusiness=1 if any related HouseholderNames.IsBusiness<>0
-' 3) Sets NULLs to 0
-' 4) Drops HouseholderNames.IsBusiness if present
-'
-Option Explicit
-
-Dim dbPath
-If WScript.Arguments.Count < 1 Then
- WScript.Echo "ERROR: missing db path."
- WScript.Echo "Usage: cscript //nologo scripts\migrate_isbusiness_to_households.vbs ""C:\path\to\db.accdb"""
- WScript.Quit 1
-End If
-
-dbPath = WScript.Arguments(0)
-
-Dim conn
-Set conn = CreateObject("ADODB.Connection")
-conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath & ";Persist Security Info=False;"
-
-On Error Resume Next
-
-If Not ColumnExists(conn, "Households", "IsBusiness") Then
- Exec conn, "ALTER TABLE [Households] ADD COLUMN [IsBusiness] SMALLINT"
- If Err.Number <> 0 Then
- WScript.Echo "ERROR adding Households.IsBusiness: " & Err.Description
- WScript.Quit 1
- End If
- WScript.Echo "Added Households.IsBusiness"
-Else
- WScript.Echo "Households.IsBusiness already exists"
-End If
-
-' Copy data (only if the old column exists)
-If ColumnExists(conn, "HouseholderNames", "IsBusiness") Then
- ' Normalize all existing households first so the column is never left NULL.
- Exec conn, "UPDATE [Households] SET [IsBusiness]=0"
- If Err.Number <> 0 Then
- WScript.Echo "ERROR initializing Households.IsBusiness: " & Err.Description
- WScript.Quit 1
- End If
-
- ' Promote households to business when any related name was previously marked as a business.
- Exec conn, "UPDATE [Households] SET [IsBusiness]=1 WHERE [Id] IN (SELECT [HouseholdId] FROM [HouseholderNames] WHERE [IsBusiness]<>0)"
- If Err.Number <> 0 Then
- WScript.Echo "ERROR copying IsBusiness to Households: " & Err.Description
- WScript.Quit 1
- End If
- WScript.Echo "Copied IsBusiness values to Households"
-
- Exec conn, "ALTER TABLE [HouseholderNames] DROP COLUMN [IsBusiness]"
- If Err.Number <> 0 Then
- WScript.Echo "ERROR dropping HouseholderNames.IsBusiness: " & Err.Description
- WScript.Quit 1
- End If
- WScript.Echo "Dropped HouseholderNames.IsBusiness"
-Else
- WScript.Echo "HouseholderNames.IsBusiness does not exist; nothing to drop"
-End If
-
-conn.Close
-Set conn = Nothing
-WScript.Echo "Done."
-
-' --- helpers ---
-Sub Exec(c, sql)
- Err.Clear
- c.Execute sql
-End Sub
-
-Function ColumnExists(c, tableName, colName)
- Dim rs
- ColumnExists = False
- Err.Clear
- Set rs = c.OpenSchema(4, Array(Empty, Empty, tableName, colName)) ' adSchemaColumns=4
- If Err.Number <> 0 Then
- Err.Clear
- Exit Function
- End If
- If Not rs.EOF Then ColumnExists = True
- rs.Close
- Set rs = Nothing
-End Function