- remove domain-specific controllers, models, repositories, and views - keep only Home/Error starter routing and controller registry entries - add IIS Express startup support with run_site.cmd and applicationhost.config Co-Authored-By: Abacus.AI CLI <agent@abacus.ai>master
| @@ -1,397 +0,0 @@ | |||||
| <% | |||||
| ' HouseholdController - CRUD controller for Households | |||||
| ' NorthTerritory app | |||||
| ' | |||||
| ' Dependencies: | |||||
| ' - app/models/POBO_Households.asp | |||||
| ' - app/models/HouseholdsRepository.asp | |||||
| ' - app/models/POBO_HouseholderNames.asp | |||||
| ' - app/models/HouseholderNamesRepository.asp | |||||
| %> | |||||
| <!--#include file="../models/POBO_Households.asp" --> | |||||
| <!--#include file="../models/HouseholdsRepository.asp" --> | |||||
| <!--#include file="../models/POBO_HouseholderNames.asp" --> | |||||
| <!--#include file="../models/HouseholderNamesRepository.asp" --> | |||||
| <% | |||||
| Class HouseholdController_Class | |||||
| Private m_useLayout | |||||
| Private m_title | |||||
| ' Public properties for views | |||||
| Public households ' LinkedList for Index | |||||
| Public household ' Single POBO for Show/Edit | |||||
| Public territoriesList ' For dropdown | |||||
| Public territoryNamesById ' Dictionary for territory labels | |||||
| Public householderNames ' LinkedList for Show | |||||
| ' Pagination properties | |||||
| Public currentPage | |||||
| Public pageCount | |||||
| Public recordCount | |||||
| Public perPage | |||||
| Public searchTerm | |||||
| Public filterTerritoryId | |||||
| Public filterDoNotCall | |||||
| Private Sub Class_Initialize() | |||||
| m_useLayout = True | |||||
| m_title = "Households" | |||||
| currentPage = 1 | |||||
| pageCount = 0 | |||||
| recordCount = 0 | |||||
| perPage = 25 | |||||
| searchTerm = "" | |||||
| filterTerritoryId = 0 | |||||
| filterDoNotCall = -1 | |||||
| End Sub | |||||
| Public Property Get useLayout | |||||
| useLayout = m_useLayout | |||||
| End Property | |||||
| Public Property Let useLayout(v) | |||||
| m_useLayout = v | |||||
| End Property | |||||
| Public Property Get Title | |||||
| Title = m_title | |||||
| End Property | |||||
| Public Property Let Title(v) | |||||
| m_title = v | |||||
| End Property | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Index - List all households with pagination, search, and territory filter | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Index() | |||||
| ' Get pagination params | |||||
| If Request.QueryString("page") <> "" And IsNumeric(Request.QueryString("page")) Then | |||||
| currentPage = CInt(Request.QueryString("page")) | |||||
| If currentPage < 1 Then currentPage = 1 | |||||
| End If | |||||
| ' Get search param | |||||
| searchTerm = Trim(Request.QueryString("q") & "") | |||||
| ' Get territory filter | |||||
| If Request.QueryString("territory") <> "" And IsNumeric(Request.QueryString("territory")) Then | |||||
| filterTerritoryId = CInt(Request.QueryString("territory")) | |||||
| End If | |||||
| If Request.QueryString("dnc") <> "" Then | |||||
| If Request.QueryString("dnc") = "1" Then | |||||
| filterDoNotCall = 1 | |||||
| ElseIf Request.QueryString("dnc") = "0" Then | |||||
| filterDoNotCall = 0 | |||||
| End If | |||||
| End If | |||||
| ' Load territories for filter dropdown | |||||
| Set territoriesList = TerritoriesRepository.GetAll(Empty) | |||||
| Set territoryNamesById = BuildTerritoryNamesById(territoriesList) | |||||
| ' Fetch households with pagination | |||||
| If searchTerm <> "" And filterDoNotCall <> -1 Then | |||||
| Set households = HouseholdsRepository.SearchTablePagedByDoNotCall( _ | |||||
| Array("Address", "StreetName"), _ | |||||
| searchTerm, _ | |||||
| filterDoNotCall, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ElseIf searchTerm <> "" Then | |||||
| ' Search in Address and StreetName columns | |||||
| Set households = HouseholdsRepository.SearchTablePaged( _ | |||||
| Array("Address", "StreetName"), _ | |||||
| searchTerm, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ElseIf filterTerritoryId > 0 And filterDoNotCall <> -1 Then | |||||
| Set households = HouseholdsRepository.FindPagedByTerritoryAndDoNotCall( _ | |||||
| filterTerritoryId, _ | |||||
| filterDoNotCall, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ElseIf filterTerritoryId > 0 Then | |||||
| ' Filter by territory | |||||
| Set households = HouseholdsRepository.FindPaged( _ | |||||
| Array("TerritoryId", filterTerritoryId), _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ElseIf filterDoNotCall <> -1 Then | |||||
| Set households = HouseholdsRepository.FindPaged( _ | |||||
| Array("DoNotCall", filterDoNotCall), _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| Else | |||||
| Set households = HouseholdsRepository.FindPaged( _ | |||||
| Empty, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| End If | |||||
| %> <!--#include file="../views/Household/index.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Show - Display a single household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Show(id) | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or household Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Household not found." | |||||
| Response.Redirect "/households" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Load householder names for this household | |||||
| Set householderNames = HouseholderNamesRepository.Find( _ | |||||
| Array("HouseholdId", id), _ | |||||
| "Name" _ | |||||
| ) | |||||
| %> <!--#include file="../views/Household/show.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' MarkReturned - Quick action to mark a householder name as returned | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub MarkReturned(id) | |||||
| Dim householderId, hn, householdId | |||||
| householderId = Request.Form("householder_id") | |||||
| If householderId = "" Or Not IsNumeric(householderId) Then | |||||
| Flash().Error = "Invalid householder ID." | |||||
| Response.Redirect "/households/" & id | |||||
| Exit Sub | |||||
| End If | |||||
| On Error Resume Next | |||||
| Set hn = HouseholderNamesRepository.FindByID(CLng(householderId)) | |||||
| If Err.Number <> 0 Or hn Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().Error = "Householder name not found." | |||||
| Response.Redirect "/households/" & id | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Toggle the returned status | |||||
| If hn.LetterReturned = 1 Then | |||||
| hn.LetterReturned = 0 | |||||
| hn.ReturnDate = #1/1/1970# | |||||
| Else | |||||
| hn.LetterReturned = 1 | |||||
| hn.ReturnDate = Now() | |||||
| End If | |||||
| HouseholderNamesRepository.Update hn | |||||
| If hn.LetterReturned = 1 Then | |||||
| Flash().Success = "Marked as returned." | |||||
| Else | |||||
| Flash().Success = "Marked as not returned." | |||||
| End If | |||||
| Response.Redirect "/households/" & id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Create - Display form for new household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Create() | |||||
| Set household = New POBO_Households | |||||
| ' Pre-fill territory if passed in query string | |||||
| If Request.QueryString("territory") <> "" And IsNumeric(Request.QueryString("territory")) Then | |||||
| household.TerritoryId = CInt(Request.QueryString("territory")) | |||||
| End If | |||||
| ' Load territories for dropdown | |||||
| Set territoriesList = TerritoriesRepository.GetAll(Empty) | |||||
| %> <!--#include file="../views/Household/create.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Store - Save new household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Store() | |||||
| Set household = New POBO_Households | |||||
| household.Address = Trim(Request.Form("Address")) | |||||
| household.StreetNumber = Request.Form("StreetNumber") | |||||
| household.StreetName = Trim(Request.Form("StreetName")) | |||||
| household.Latitude = Trim(Request.Form("Latitude")) | |||||
| household.Longitude = Trim(Request.Form("Longitude")) | |||||
| household.IsBusiness = IIf(Request.Form("IsBusiness") = "1", 1, 0) | |||||
| household.DoNotCall = IIf(Request.Form("DoNotCall") = "1", 1, 0) | |||||
| household.DoNotCallNotes = Trim(Request.Form("DoNotCallNotes")) | |||||
| household.DoNotCallPrivateNotes = Trim(Request.Form("DoNotCallPrivateNotes")) | |||||
| household.TerritoryId = Request.Form("TerritoryId") | |||||
| If Request.Form("DoNotCallDate") <> "" Then | |||||
| household.DoNotCallDate = CDate(Request.Form("DoNotCallDate")) | |||||
| ElseIf household.DoNotCall = 1 Then | |||||
| household.DoNotCallDate = Date() | |||||
| Else | |||||
| household.DoNotCallDate = Null | |||||
| End If | |||||
| ' Validation | |||||
| If household.Address = "" Then | |||||
| Flash().AddError "Address is required." | |||||
| Response.Redirect "/households/new" | |||||
| Exit Sub | |||||
| End If | |||||
| If Not IsNumeric(household.TerritoryId) Or CInt(household.TerritoryId) < 1 Then | |||||
| Flash().AddError "Please select a territory." | |||||
| Response.Redirect "/households/new" | |||||
| Exit Sub | |||||
| End If | |||||
| HouseholdsRepository.AddNew household | |||||
| Flash().Success = "Household created successfully." | |||||
| Response.Redirect "/households/" & household.Id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Edit - Display form to edit household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Edit(id) | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or household Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Household not found." | |||||
| Response.Redirect "/households" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Load territories for dropdown | |||||
| Set territoriesList = TerritoriesRepository.GetAll(Empty) | |||||
| %> <!--#include file="../views/Household/edit.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Update - Save changes to household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Update(id) | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or household Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Household not found." | |||||
| Response.Redirect "/households" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| household.Address = Trim(Request.Form("Address")) | |||||
| household.StreetNumber = Request.Form("StreetNumber") | |||||
| household.StreetName = Trim(Request.Form("StreetName")) | |||||
| household.Latitude = Trim(Request.Form("Latitude")) | |||||
| household.Longitude = Trim(Request.Form("Longitude")) | |||||
| household.IsBusiness = IIf(Request.Form("IsBusiness") = "1", 1, 0) | |||||
| household.DoNotCall = IIf(Request.Form("DoNotCall") = "1", 1, 0) | |||||
| household.DoNotCallNotes = Trim(Request.Form("DoNotCallNotes")) | |||||
| household.DoNotCallPrivateNotes = Trim(Request.Form("DoNotCallPrivateNotes")) | |||||
| household.TerritoryId = Request.Form("TerritoryId") | |||||
| If Request.Form("DoNotCallDate") <> "" Then | |||||
| household.DoNotCallDate = CDate(Request.Form("DoNotCallDate")) | |||||
| ElseIf household.DoNotCall = 1 Then | |||||
| If IsNull(household.DoNotCallDate) Then | |||||
| household.DoNotCallDate = Date() | |||||
| ElseIf Trim(CStr(household.DoNotCallDate)) = "" Then | |||||
| household.DoNotCallDate = Date() | |||||
| End If | |||||
| Else | |||||
| household.DoNotCallDate = Null | |||||
| End If | |||||
| ' Validation | |||||
| If household.Address = "" Then | |||||
| Flash().AddError "Address is required." | |||||
| Response.Redirect "/households/" & id & "/edit" | |||||
| Exit Sub | |||||
| End If | |||||
| HouseholdsRepository.Update household | |||||
| Flash().Success = "Household updated successfully." | |||||
| Response.Redirect "/households/" & id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Delete - Remove a household | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Delete(id) | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or household Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Household not found." | |||||
| Response.Redirect "/households" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| HouseholdsRepository.Delete id | |||||
| Flash().Success = "Household deleted successfully." | |||||
| Response.Redirect "/households" | |||||
| End Sub | |||||
| Private Function BuildTerritoryNamesById(territories) | |||||
| Dim dict : Set dict = Server.CreateObject("Scripting.Dictionary") | |||||
| Dim iter, territory | |||||
| Set iter = territories.Iterator() | |||||
| Do While iter.HasNext() | |||||
| Set territory = iter.GetNext() | |||||
| dict(CStr(territory.Id)) = territory.Name & "" | |||||
| Loop | |||||
| Set BuildTerritoryNamesById = dict | |||||
| End Function | |||||
| End Class | |||||
| ' Singleton instance | |||||
| Dim HouseholdController_Class__Singleton | |||||
| Function HouseholdController() | |||||
| If IsEmpty(HouseholdController_Class__Singleton) Then | |||||
| Set HouseholdController_Class__Singleton = New HouseholdController_Class | |||||
| End If | |||||
| Set HouseholdController = HouseholdController_Class__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,284 +0,0 @@ | |||||
| <% | |||||
| ' HouseholderNameController - CRUD controller for HouseholderNames | |||||
| ' NorthTerritory app | |||||
| ' | |||||
| ' Dependencies (all included via HouseholdController which loads first): | |||||
| ' - app/models/POBO_HouseholderNames.asp | |||||
| ' - app/models/HouseholderNamesRepository.asp | |||||
| ' - app/models/POBO_Households.asp | |||||
| ' - app/models/HouseholdsRepository.asp | |||||
| Class HouseholderNameController_Class | |||||
| Private m_useLayout | |||||
| Private m_title | |||||
| ' Public properties for views | |||||
| Public householderNames ' LinkedList for Index | |||||
| Public householderName ' Single POBO for Show/Edit | |||||
| Public household ' Parent household | |||||
| Public householdsList ' For dropdown | |||||
| ' Pagination properties | |||||
| Public currentPage | |||||
| Public pageCount | |||||
| Public recordCount | |||||
| Public perPage | |||||
| Public searchTerm | |||||
| Public filterHouseholdId | |||||
| Private Sub Class_Initialize() | |||||
| m_useLayout = True | |||||
| m_title = "Householder Names" | |||||
| currentPage = 1 | |||||
| pageCount = 0 | |||||
| recordCount = 0 | |||||
| perPage = 25 | |||||
| searchTerm = "" | |||||
| filterHouseholdId = 0 | |||||
| End Sub | |||||
| Public Property Get useLayout | |||||
| useLayout = m_useLayout | |||||
| End Property | |||||
| Public Property Let useLayout(v) | |||||
| m_useLayout = v | |||||
| End Property | |||||
| Public Property Get Title | |||||
| Title = m_title | |||||
| End Property | |||||
| Public Property Let Title(v) | |||||
| m_title = v | |||||
| End Property | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Index - List all householder names with pagination, search, and household filter | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Index() | |||||
| ' Get pagination params | |||||
| If Request.QueryString("page") <> "" And IsNumeric(Request.QueryString("page")) Then | |||||
| currentPage = CInt(Request.QueryString("page")) | |||||
| If currentPage < 1 Then currentPage = 1 | |||||
| End If | |||||
| ' Get search param | |||||
| searchTerm = Trim(Request.QueryString("q") & "") | |||||
| ' Get household filter | |||||
| If Request.QueryString("household") <> "" And IsNumeric(Request.QueryString("household")) Then | |||||
| filterHouseholdId = CLng(Request.QueryString("household")) | |||||
| End If | |||||
| ' Fetch householder names with pagination | |||||
| If searchTerm <> "" Then | |||||
| ' Search in Name column | |||||
| Set householderNames = HouseholderNamesRepository.SearchTablePaged( _ | |||||
| Array("Name"), _ | |||||
| searchTerm, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ElseIf filterHouseholdId > 0 Then | |||||
| ' Filter by household | |||||
| Set householderNames = HouseholderNamesRepository.FindPaged( _ | |||||
| Array("HouseholdId", filterHouseholdId), _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| ' Load parent household for context | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(filterHouseholdId) | |||||
| On Error GoTo 0 | |||||
| Else | |||||
| Set householderNames = HouseholderNamesRepository.FindPaged( _ | |||||
| Empty, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| End If | |||||
| %> <!--#include file="../views/HouseholderName/index.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Show - Display a single householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Show(id) | |||||
| On Error Resume Next | |||||
| Set householderName = HouseholderNamesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or householderName Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().Error = "Householder name not found." | |||||
| Response.Redirect "/householder-names" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Load parent household | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(householderName.HouseholdId) | |||||
| On Error GoTo 0 | |||||
| %> <!--#include file="../views/HouseholderName/show.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Create - Display form for new householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Create() | |||||
| Set householderName = New POBO_HouseholderNames | |||||
| householderName.Created = Now() | |||||
| householderName.LetterReturned = 0 | |||||
| ' Pre-fill household if passed in query string | |||||
| If Request.QueryString("household") <> "" And IsNumeric(Request.QueryString("household")) Then | |||||
| householderName.HouseholdId = CLng(Request.QueryString("household")) | |||||
| ' Load parent household for context | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(householderName.HouseholdId) | |||||
| On Error GoTo 0 | |||||
| End If | |||||
| %> <!--#include file="../views/HouseholderName/create.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Store - Save new householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Store() | |||||
| Set householderName = New POBO_HouseholderNames | |||||
| householderName.Name = Trim(Request.Form("Name")) | |||||
| householderName.HouseholdId = Request.Form("HouseholdId") | |||||
| householderName.LetterReturned = IIf(Request.Form("LetterReturned") = "1", 1, 0) | |||||
| householderName.Created = Now() | |||||
| If Request.Form("ReturnDate") <> "" Then | |||||
| On Error Resume Next | |||||
| householderName.ReturnDate = CDate(Request.Form("ReturnDate")) | |||||
| On Error GoTo 0 | |||||
| End If | |||||
| ' Validation | |||||
| If householderName.Name = "" Then | |||||
| Flash().Error = "Name is required." | |||||
| Response.Redirect "/householder-names/new?household=" & householderName.HouseholdId | |||||
| Exit Sub | |||||
| End If | |||||
| If Not IsNumeric(householderName.HouseholdId) Or CLng(householderName.HouseholdId) < 1 Then | |||||
| Flash().Error = "Please select a household." | |||||
| Response.Redirect "/householder-names/new" | |||||
| Exit Sub | |||||
| End If | |||||
| HouseholderNamesRepository.AddNew householderName | |||||
| Flash().Success = "Householder name created successfully." | |||||
| Response.Redirect "/householder-names/" & householderName.Id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Edit - Display form to edit householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Edit(id) | |||||
| On Error Resume Next | |||||
| Set householderName = HouseholderNamesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or householderName Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().Error = "Householder name not found." | |||||
| Response.Redirect "/householder-names" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Load parent household for context | |||||
| On Error Resume Next | |||||
| Set household = HouseholdsRepository.FindByID(householderName.HouseholdId) | |||||
| On Error GoTo 0 | |||||
| %> <!--#include file="../views/HouseholderName/edit.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Update - Save changes to householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Update(id) | |||||
| On Error Resume Next | |||||
| Set householderName = HouseholderNamesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or householderName Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().Error = "Householder name not found." | |||||
| Response.Redirect "/householder-names" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| householderName.Name = Trim(Request.Form("Name")) | |||||
| householderName.LetterReturned = IIf(Request.Form("LetterReturned") = "1", 1, 0) | |||||
| If Request.Form("ReturnDate") <> "" Then | |||||
| On Error Resume Next | |||||
| householderName.ReturnDate = CDate(Request.Form("ReturnDate")) | |||||
| On Error GoTo 0 | |||||
| Else | |||||
| householderName.ReturnDate = #1/1/1970# | |||||
| End If | |||||
| ' Validation | |||||
| If householderName.Name = "" Then | |||||
| Flash().Error = "Name is required." | |||||
| Response.Redirect "/householder-names/" & id & "/edit" | |||||
| Exit Sub | |||||
| End If | |||||
| HouseholderNamesRepository.Update householderName | |||||
| Flash().Success = "Householder name updated successfully." | |||||
| Response.Redirect "/householder-names/" & id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Delete - Remove a householder name | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Delete(id) | |||||
| On Error Resume Next | |||||
| Set householderName = HouseholderNamesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or householderName Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().Error = "Householder name not found." | |||||
| Response.Redirect "/householder-names" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| Dim householdId | |||||
| householdId = householderName.HouseholdId | |||||
| HouseholderNamesRepository.Delete id | |||||
| Flash().Success = "Householder name deleted successfully." | |||||
| Response.Redirect "/householder-names?household=" & householdId | |||||
| End Sub | |||||
| End Class | |||||
| ' Singleton instance | |||||
| Dim HouseholderNameController_Class__Singleton | |||||
| Function HouseholderNameController() | |||||
| If IsEmpty(HouseholderNameController_Class__Singleton) Then | |||||
| Set HouseholderNameController_Class__Singleton = New HouseholderNameController_Class | |||||
| End If | |||||
| Set HouseholderNameController = HouseholderNameController_Class__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,226 +0,0 @@ | |||||
| <% | |||||
| ' TerritoryController - CRUD controller for Territories | |||||
| ' Generated for NorthTerritory app | |||||
| ' | |||||
| ' Dependencies: | |||||
| ' - app/models/POBO_Territories.asp | |||||
| ' - app/models/TerritoriesRepository.asp | |||||
| %> | |||||
| <!--#include file="../models/POBO_Territories.asp" --> | |||||
| <!--#include file="../models/TerritoriesRepository.asp" --> | |||||
| <% | |||||
| Class TerritoryController_Class | |||||
| Private m_useLayout | |||||
| Private m_title | |||||
| ' Public properties for views | |||||
| Public territories ' LinkedList for Index | |||||
| Public territory ' Single POBO for Show/Edit | |||||
| Public territoryHouseholdCounts | |||||
| Public territoryStreets ' LinkedList of street names for Show | |||||
| ' Pagination properties | |||||
| Public currentPage | |||||
| Public pageCount | |||||
| Public recordCount | |||||
| Public perPage | |||||
| Public searchTerm | |||||
| Private Sub Class_Initialize() | |||||
| m_useLayout = True | |||||
| m_title = "Territories" | |||||
| currentPage = 1 | |||||
| pageCount = 0 | |||||
| recordCount = 0 | |||||
| perPage = 20 | |||||
| searchTerm = "" | |||||
| Set territoryHouseholdCounts = Nothing | |||||
| End Sub | |||||
| Public Property Get useLayout | |||||
| useLayout = m_useLayout | |||||
| End Property | |||||
| Public Property Let useLayout(v) | |||||
| m_useLayout = v | |||||
| End Property | |||||
| Public Property Get Title | |||||
| Title = m_title | |||||
| End Property | |||||
| Public Property Let Title(v) | |||||
| m_title = v | |||||
| End Property | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Index - List all territories with pagination and search | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Index() | |||||
| ' Get pagination params | |||||
| If Request.QueryString("page") <> "" And IsNumeric(Request.QueryString("page")) Then | |||||
| currentPage = CInt(Request.QueryString("page")) | |||||
| If currentPage < 1 Then currentPage = 1 | |||||
| End If | |||||
| ' Get search param | |||||
| searchTerm = Trim(Request.QueryString("q") & "") | |||||
| ' Fetch territories with pagination | |||||
| If searchTerm <> "" Then | |||||
| ' Search in Name and Description columns | |||||
| Set territories = TerritoriesRepository.SearchTablePaged( _ | |||||
| Array("Name", "Description"), _ | |||||
| searchTerm, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| Else | |||||
| Set territories = TerritoriesRepository.FindPaged( _ | |||||
| Empty, _ | |||||
| Empty, _ | |||||
| perPage, _ | |||||
| currentPage, _ | |||||
| pageCount, _ | |||||
| recordCount _ | |||||
| ) | |||||
| End If | |||||
| Set territoryHouseholdCounts = HouseholdsRepository.GetCountsByTerritory() | |||||
| %> <!--#include file="../views/Territory/index.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Show - Display a single territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Show(id) | |||||
| On Error Resume Next | |||||
| Set territory = TerritoriesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or territory Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Territory not found." | |||||
| Response.Redirect "/territories" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| ' Load distinct street names for this territory | |||||
| Set territoryStreets = HouseholdsRepository.GetDistinctStreetsByTerritory(id) | |||||
| %> <!--#include file="../views/Territory/show.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Create - Display form for new territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Create() | |||||
| Set territory = New POBO_Territories | |||||
| %> <!--#include file="../views/Territory/create.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Store - Save new territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Store() | |||||
| Set territory = New POBO_Territories | |||||
| territory.Name = Trim(Request.Form("Name")) | |||||
| territory.Description = Trim(Request.Form("Description")) | |||||
| territory.Coordinates = Trim(Request.Form("Coordinates")) | |||||
| ' Validation | |||||
| If territory.Name = "" Then | |||||
| Flash().AddError "Name is required." | |||||
| Response.Redirect "/territories/new" | |||||
| Exit Sub | |||||
| End If | |||||
| TerritoriesRepository.AddNew territory | |||||
| Flash().Success = "Territory created successfully." | |||||
| Response.Redirect "/territories/" & territory.Id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Edit - Display form to edit territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Edit(id) | |||||
| On Error Resume Next | |||||
| Set territory = TerritoriesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or territory Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Territory not found." | |||||
| Response.Redirect "/territories" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| %> <!--#include file="../views/Territory/edit.asp" --> <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Update - Save changes to territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Update(id) | |||||
| On Error Resume Next | |||||
| Set territory = TerritoriesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or territory Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Territory not found." | |||||
| Response.Redirect "/territories" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| territory.Name = Trim(Request.Form("Name")) | |||||
| territory.Description = Trim(Request.Form("Description")) | |||||
| territory.Coordinates = Trim(Request.Form("Coordinates")) | |||||
| ' Validation | |||||
| If territory.Name = "" Then | |||||
| Flash().AddError "Name is required." | |||||
| Response.Redirect "/territories/" & id & "/edit" | |||||
| Exit Sub | |||||
| End If | |||||
| TerritoriesRepository.Update territory | |||||
| Flash().Success = "Territory updated successfully." | |||||
| Response.Redirect "/territories/" & id | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Delete - Remove a territory | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub Delete(id) | |||||
| On Error Resume Next | |||||
| Set territory = TerritoriesRepository.FindByID(id) | |||||
| If Err.Number <> 0 Or territory Is Nothing Then | |||||
| On Error GoTo 0 | |||||
| Flash().AddError "Territory not found." | |||||
| Response.Redirect "/territories" | |||||
| Exit Sub | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| TerritoriesRepository.Delete id | |||||
| Flash().Success = "Territory deleted successfully." | |||||
| Response.Redirect "/territories" | |||||
| End Sub | |||||
| End Class | |||||
| ' Singleton instance | |||||
| Dim TerritoryController_Class__Singleton | |||||
| Function TerritoryController() | |||||
| If IsEmpty(TerritoryController_Class__Singleton) Then | |||||
| Set TerritoryController_Class__Singleton = New TerritoryController_Class | |||||
| End If | |||||
| Set TerritoryController = TerritoryController_Class__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,5 +1,2 @@ | |||||
| <!--#include file="ErrorController.asp" --> | |||||
| <!--#include file="HomeController.asp" --> | <!--#include file="HomeController.asp" --> | ||||
| <!--#include file="TerritoryController.asp" --> | |||||
| <!--#include file="HouseholdController.asp" --> | |||||
| <!--#include file="HouseholderNameController.asp" --> | |||||
| <!--#include file="ErrorController.asp" --> | |||||
| @@ -1,176 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated Repository for table [HouseholderNames] | |||||
| ' Generated on 1/17/2026 7:59:02 PM | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: | |||||
| ' - core/lib.DAL.asp (DAL singleton for database access) | |||||
| ' - core/lib.AutoMapper.asp (Automapper for object mapping) | |||||
| ' - core/lib.Collections.asp (LinkedList_Class) | |||||
| ' - core/lib.helpers.asp (KVUnzip, BuildOrderBy, QI, Destroy) | |||||
| Class HouseholderNamesRepository_Class | |||||
| Public Function FindByID(id) | |||||
| Dim sql : sql = "Select [Created], [HouseholdId], [Id], [LetterReturned], [Name], [ReturnDate] FROM [HouseholderNames] WHERE [Id] = ?" | |||||
| Dim rs : Set rs = DAL.Query(sql, Array(id)) | |||||
| If rs.EOF Then | |||||
| Err.Raise 1, "HouseholderNamesRepository_Class", RecordNotFoundException("Id", id) | |||||
| Else | |||||
| Set FindByID = Automapper.AutoMap(rs, "POBO_HouseholderNames") | |||||
| End If | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function GetAll(orderBy) | |||||
| Set GetAll = Find(Empty, orderBy) | |||||
| End Function | |||||
| Public Function Find(where_kvarray, order_string_or_array) | |||||
| Dim sql : sql = "Select [Created], [HouseholdId], [Id], [LetterReturned], [Name], [ReturnDate] FROM [HouseholderNames]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.Query(sql, where_values) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Do Until rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_HouseholderNames") | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set Find = list | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function FindPaged(where_kvarray, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Created], [HouseholdId], [Id], [LetterReturned], [Name], [ReturnDate] FROM [HouseholderNames]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, where_values, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set FindPaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function SearchTablePaged(columns_array, search_value, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Created], [HouseholdId], [Id], [LetterReturned], [Name], [ReturnDate] FROM [HouseholderNames]" | |||||
| Dim i, params() | |||||
| If IsArray(columns_array) And UBound(columns_array) >= 0 Then | |||||
| sql = sql & " WHERE " | |||||
| ReDim params(UBound(columns_array)) | |||||
| For i = 0 To UBound(columns_array) | |||||
| If i > 0 Then sql = sql & " OR " | |||||
| sql = sql & " " & QI(columns_array(i)) & " LIKE ?" | |||||
| params(i) = "%" & search_value & "%" | |||||
| Next | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, params, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set SearchTablePaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Private Function PagedList(rs, per_page) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Dim x : x = 0 | |||||
| Do While (per_page <= 0 Or x < per_page) And Not rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_HouseholderNames") | |||||
| x = x + 1 | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set PagedList = list | |||||
| End Function | |||||
| Public Sub AddNew(ByRef model) | |||||
| Dim sql : sql = "INSERT INTO [HouseholderNames] ([Created], [HouseholdId], [LetterReturned], [Name], [ReturnDate]) VALUES (?, ?, ?, ?, ?)" | |||||
| DAL.[Execute] sql, Array(model.Created, model.HouseholdId, model.LetterReturned, model.Name, model.ReturnDate) | |||||
| ' Retrieve the newly inserted ID | |||||
| On Error Resume Next | |||||
| Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty) | |||||
| If Err.Number <> 0 Then | |||||
| ' Fallback for Access databases | |||||
| Err.Clear | |||||
| Set rsId = DAL.Query("SELECT TOP 1 [Id] FROM [HouseholderNames] ORDER BY [Id] DESC", Empty) | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| If Not rsId.EOF Then | |||||
| If Not IsNull(rsId(0)) Then model.Id = rsId(0) | |||||
| End If | |||||
| Destroy rsId | |||||
| End Sub | |||||
| Public Sub Update(model) | |||||
| Dim sql : sql = "UPDATE [HouseholderNames] SET [Created] = ?, [HouseholdId] = ?, [LetterReturned] = ?, [Name] = ?, [ReturnDate] = ? WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(model.Created, model.HouseholdId, model.LetterReturned, model.Name, model.ReturnDate, model.Id) | |||||
| End Sub | |||||
| Public Sub Delete(id) | |||||
| Dim sql : sql = "DELETE FROM [HouseholderNames] WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(id) | |||||
| End Sub | |||||
| Private Function RecordNotFoundException(ByVal field_name, ByVal field_val) | |||||
| RecordNotFoundException = "HouseholderNames record was not found with " & field_name & " = '" & field_val & "'." | |||||
| End Function | |||||
| Private Function QI(name) | |||||
| QI = "[" & Replace(CStr(name), "]", "]]") & "]" | |||||
| End Function | |||||
| Private Function BuildOrderBy(orderArg, defaultCol) | |||||
| Dim s : s = "" | |||||
| If IsEmpty(orderArg) Or IsNull(orderArg) Or orderArg = "" Then | |||||
| s = " ORDER BY " & defaultCol & " ASC" | |||||
| ElseIf IsArray(orderArg) Then | |||||
| Dim i : s = " ORDER BY " | |||||
| For i = 0 To UBound(orderArg) | |||||
| If i > 0 Then s = s & ", " | |||||
| s = s & QI(orderArg(i)) | |||||
| Next | |||||
| Else | |||||
| s = " ORDER BY " & QI(orderArg) | |||||
| End If | |||||
| BuildOrderBy = s | |||||
| End Function | |||||
| End Class | |||||
| Dim HouseholderNamesRepository__Singleton | |||||
| Function HouseholderNamesRepository() | |||||
| If IsEmpty(HouseholderNamesRepository__Singleton) Then | |||||
| Set HouseholderNamesRepository__Singleton = new HouseholderNamesRepository_Class | |||||
| End If | |||||
| Set HouseholderNamesRepository = HouseholderNamesRepository__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,243 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated Repository for table [Households] | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: | |||||
| ' - core/lib.DAL.asp (DAL singleton for database access) | |||||
| ' - core/lib.AutoMapper.asp (Automapper for object mapping) | |||||
| ' - core/lib.Collections.asp (LinkedList_Class) | |||||
| ' - core/lib.helpers.asp (KVUnzip, BuildOrderBy, QI, Destroy) | |||||
| Class HouseholdsRepository_Class | |||||
| Public Function FindByID(id) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households] WHERE [Id] = ?" | |||||
| Dim rs : Set rs = DAL.Query(sql, Array(id)) | |||||
| If rs.EOF Then | |||||
| Err.Raise 1, "HouseholdsRepository_Class", RecordNotFoundException("Id", id) | |||||
| Else | |||||
| Set FindByID = Automapper.AutoMap(rs, "POBO_Households") | |||||
| End If | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function GetAll(orderBy) | |||||
| Set GetAll = Find(Empty, orderBy) | |||||
| End Function | |||||
| Public Function Find(where_kvarray, order_string_or_array) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.Query(sql, where_values) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Do Until rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_Households") | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set Find = list | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function FindPaged(where_kvarray, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, where_values, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set FindPaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function SearchTablePaged(columns_array, search_value, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households]" | |||||
| Dim i, params() | |||||
| If IsArray(columns_array) And UBound(columns_array) >= 0 Then | |||||
| sql = sql & " WHERE " | |||||
| ReDim params(UBound(columns_array)) | |||||
| For i = 0 To UBound(columns_array) | |||||
| If i > 0 Then sql = sql & " OR " | |||||
| sql = sql & " " & QI(columns_array(i)) & " LIKE ?" | |||||
| params(i) = "%" & search_value & "%" | |||||
| Next | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, params, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set SearchTablePaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function SearchTablePagedByDoNotCall(columns_array, search_value, doNotCallValue, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households] WHERE [DoNotCall] = ?" | |||||
| Dim i, params() | |||||
| If IsArray(columns_array) And UBound(columns_array) >= 0 Then | |||||
| ReDim params(UBound(columns_array) + 1) | |||||
| params(0) = doNotCallValue | |||||
| sql = sql & " AND (" | |||||
| For i = 0 To UBound(columns_array) | |||||
| If i > 0 Then sql = sql & " OR " | |||||
| sql = sql & " " & QI(columns_array(i)) & " LIKE ?" | |||||
| params(i + 1) = "%" & search_value & "%" | |||||
| Next | |||||
| sql = sql & ")" | |||||
| Else | |||||
| ReDim params(0) | |||||
| params(0) = doNotCallValue | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, params, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set SearchTablePagedByDoNotCall = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function FindPagedByTerritoryAndDoNotCall(territoryId, doNotCallValue, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [Id], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId] FROM [Households] WHERE [TerritoryId] = ? AND [DoNotCall] = ?" | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql & BuildOrderBy(order_string_or_array, "[Id]"), Array(territoryId, doNotCallValue), per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set FindPagedByTerritoryAndDoNotCall = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Private Function PagedList(rs, per_page) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Dim x : x = 0 | |||||
| Do While (per_page <= 0 Or x < per_page) And Not rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_Households") | |||||
| x = x + 1 | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set PagedList = list | |||||
| End Function | |||||
| Public Function GetCountsByTerritory() | |||||
| Dim sql : sql = "SELECT [TerritoryId], COUNT(*) AS [HouseholdCount] FROM [Households] GROUP BY [TerritoryId]" | |||||
| Dim rs : Set rs = DAL.Query(sql, Empty) | |||||
| Dim dict : Set dict = Server.CreateObject("Scripting.Dictionary") | |||||
| Do Until rs.EOF | |||||
| If Not IsNull(rs("TerritoryId")) Then | |||||
| dict(CLng(rs("TerritoryId"))) = CLng(rs("HouseholdCount")) | |||||
| End If | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set GetCountsByTerritory = dict | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function GetDistinctStreetsByTerritory(territoryId) | |||||
| Dim sql : sql = "SELECT DISTINCT [StreetName] FROM [Households] WHERE [TerritoryId] = ? AND [StreetName] IS NOT NULL AND [StreetName] <> '' ORDER BY [StreetName]" | |||||
| Dim rs : Set rs = DAL.Query(sql, Array(territoryId)) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Do Until rs.EOF | |||||
| list.Push rs("StreetName") & "" | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set GetDistinctStreetsByTerritory = list | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Sub AddNew(ByRef model) | |||||
| Dim sql : sql = "INSERT INTO [Households] ([Address], [DoNotCall], [DoNotCallDate], [DoNotCallNotes], [DoNotCallPrivateNotes], [IsBusiness], [Latitude], [Longitude], [StreetName], [StreetNumber], [TerritoryId]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" | |||||
| DAL.[Execute] sql, Array(model.Address, model.DoNotCall, model.DoNotCallDate, model.DoNotCallNotes, model.DoNotCallPrivateNotes, model.IsBusiness, model.Latitude, model.Longitude, model.StreetName, model.StreetNumber, model.TerritoryId) | |||||
| ' Retrieve the newly inserted ID | |||||
| On Error Resume Next | |||||
| Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty) | |||||
| If Err.Number <> 0 Then | |||||
| ' Fallback for Access databases | |||||
| Err.Clear | |||||
| Set rsId = DAL.Query("SELECT TOP 1 [Id] FROM [Households] ORDER BY [Id] DESC", Empty) | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| If Not rsId.EOF Then | |||||
| If Not IsNull(rsId(0)) Then model.Id = rsId(0) | |||||
| End If | |||||
| Destroy rsId | |||||
| End Sub | |||||
| Public Sub Update(model) | |||||
| Dim sql : sql = "UPDATE [Households] SET [Address] = ?, [DoNotCall] = ?, [DoNotCallDate] = ?, [DoNotCallNotes] = ?, [DoNotCallPrivateNotes] = ?, [IsBusiness] = ?, [Latitude] = ?, [Longitude] = ?, [StreetName] = ?, [StreetNumber] = ?, [TerritoryId] = ? WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(model.Address, model.DoNotCall, model.DoNotCallDate, model.DoNotCallNotes, model.DoNotCallPrivateNotes, model.IsBusiness, model.Latitude, model.Longitude, model.StreetName, model.StreetNumber, model.TerritoryId, model.Id) | |||||
| End Sub | |||||
| Public Sub Delete(id) | |||||
| Dim sql : sql = "DELETE FROM [Households] WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(id) | |||||
| End Sub | |||||
| Private Function RecordNotFoundException(ByVal field_name, ByVal field_val) | |||||
| RecordNotFoundException = "Households record was not found with " & field_name & " = '" & field_val & "'." | |||||
| End Function | |||||
| Private Function QI(name) | |||||
| QI = "[" & Replace(CStr(name), "]", "]]") & "]" | |||||
| End Function | |||||
| Private Function BuildOrderBy(orderArg, defaultCol) | |||||
| Dim s : s = "" | |||||
| If IsEmpty(orderArg) Or IsNull(orderArg) Or orderArg = "" Then | |||||
| s = " ORDER BY " & defaultCol & " ASC" | |||||
| ElseIf IsArray(orderArg) Then | |||||
| Dim i : s = " ORDER BY " | |||||
| For i = 0 To UBound(orderArg) | |||||
| If i > 0 Then s = s & ", " | |||||
| s = s & QI(orderArg(i)) | |||||
| Next | |||||
| Else | |||||
| s = " ORDER BY " & QI(orderArg) | |||||
| End If | |||||
| BuildOrderBy = s | |||||
| End Function | |||||
| End Class | |||||
| Dim HouseholdsRepository__Singleton | |||||
| Function HouseholdsRepository() | |||||
| If IsEmpty(HouseholdsRepository__Singleton) Then | |||||
| Set HouseholdsRepository__Singleton = new HouseholdsRepository_Class | |||||
| End If | |||||
| Set HouseholdsRepository = HouseholdsRepository__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,133 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated POBO for table [HouseholderNames] | |||||
| ' Generated on 1/17/2026 7:59:02 PM | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: core/helpers.asp (QuoteValue function) | |||||
| Class POBO_HouseholderNames | |||||
| ' Public array of all property names | |||||
| Public Properties | |||||
| Private pCreated | |||||
| Private pHouseholdId | |||||
| Private pId | |||||
| Private pLetterReturned | |||||
| Private pName | |||||
| Private pReturnDate | |||||
| Private Sub Class_Initialize() | |||||
| pCreated = #1/1/1970# | |||||
| pHouseholdId = 0 | |||||
| pId = 0 | |||||
| pLetterReturned = 0 | |||||
| pName = Null | |||||
| pReturnDate = #1/1/1970# | |||||
| Properties = Array("Created","HouseholdId","Id","LetterReturned","Name","ReturnDate") | |||||
| End Sub | |||||
| Public Property Get PrimaryKey() | |||||
| PrimaryKey = "Id" | |||||
| End Property | |||||
| Public Property Get TableName() | |||||
| TableName = "HouseholderNames" | |||||
| End Property | |||||
| Public Property Get Created() | |||||
| Created = pCreated | |||||
| End Property | |||||
| Public Property Let Created(val) | |||||
| On Error Resume Next | |||||
| pCreated = CDate(val) | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.Created", "Invalid value for Created: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get HouseholdId() | |||||
| HouseholdId = pHouseholdId | |||||
| End Property | |||||
| Public Property Let HouseholdId(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pHouseholdId = CDbl(val) | |||||
| Else | |||||
| pHouseholdId = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.HouseholdId", "Invalid value for HouseholdId: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get Id() | |||||
| Id = pId | |||||
| End Property | |||||
| Public Property Let Id(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pId = CDbl(val) | |||||
| Else | |||||
| pId = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.Id", "Invalid value for Id: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get LetterReturned() | |||||
| LetterReturned = pLetterReturned | |||||
| End Property | |||||
| Public Property Let LetterReturned(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pLetterReturned = CDbl(val) | |||||
| Else | |||||
| pLetterReturned = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.LetterReturned", "Invalid value for LetterReturned: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get Name() | |||||
| Name = pName | |||||
| End Property | |||||
| Public Property Let Name(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pName = CDbl(val) | |||||
| Else | |||||
| pName = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.Name", "Invalid value for Name: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get ReturnDate() | |||||
| ReturnDate = pReturnDate | |||||
| End Property | |||||
| Public Property Let ReturnDate(val) | |||||
| On Error Resume Next | |||||
| pReturnDate = CDate(val) | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_HouseholderNames.ReturnDate", "Invalid value for ReturnDate: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| End Class | |||||
| %> | |||||
| @@ -1,172 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated POBO for table [Households] | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: core/helpers.asp (QuoteValue function) | |||||
| Class POBO_Households | |||||
| ' Public array of all property names | |||||
| Public Properties | |||||
| Private pAddress | |||||
| Private pDoNotCall | |||||
| Private pDoNotCallDate | |||||
| Private pDoNotCallNotes | |||||
| Private pDoNotCallPrivateNotes | |||||
| Private pId | |||||
| Private pIsBusiness | |||||
| Private pLatitude | |||||
| Private pLongitude | |||||
| Private pStreetName | |||||
| Private pStreetNumber | |||||
| Private pTerritoryId | |||||
| Private Sub Class_Initialize() | |||||
| pAddress = Null | |||||
| pDoNotCall = 0 | |||||
| pDoNotCallDate = Null | |||||
| pDoNotCallNotes = Null | |||||
| pDoNotCallPrivateNotes = Null | |||||
| pId = 0 | |||||
| pIsBusiness = 0 | |||||
| pLatitude = Null | |||||
| pLongitude = Null | |||||
| pStreetName = Null | |||||
| pStreetNumber = 0 | |||||
| pTerritoryId = 0 | |||||
| Properties = Array("Address","DoNotCall","DoNotCallDate","DoNotCallNotes","DoNotCallPrivateNotes","Id","IsBusiness","Latitude","Longitude","StreetName","StreetNumber","TerritoryId") | |||||
| End Sub | |||||
| Public Property Get PrimaryKey() | |||||
| PrimaryKey = "Id" | |||||
| End Property | |||||
| Public Property Get TableName() | |||||
| TableName = "Households" | |||||
| End Property | |||||
| Public Property Get Address() | |||||
| Address = pAddress | |||||
| End Property | |||||
| Public Property Let Address(val) | |||||
| pAddress = val | |||||
| End Property | |||||
| Public Property Get DoNotCall() | |||||
| DoNotCall = pDoNotCall | |||||
| End Property | |||||
| Public Property Let DoNotCall(val) | |||||
| If IsNumeric(val) Then | |||||
| pDoNotCall = CLng(val) | |||||
| Else | |||||
| pDoNotCall = val | |||||
| End If | |||||
| End Property | |||||
| Public Property Get DoNotCallDate() | |||||
| DoNotCallDate = pDoNotCallDate | |||||
| End Property | |||||
| Public Property Let DoNotCallDate(val) | |||||
| If IsNull(val) Then | |||||
| pDoNotCallDate = Null | |||||
| ElseIf Trim(CStr(val)) = "" Then | |||||
| pDoNotCallDate = Null | |||||
| Else | |||||
| pDoNotCallDate = CDate(val) | |||||
| End If | |||||
| End Property | |||||
| Public Property Get DoNotCallNotes() | |||||
| DoNotCallNotes = pDoNotCallNotes | |||||
| End Property | |||||
| Public Property Let DoNotCallNotes(val) | |||||
| pDoNotCallNotes = val | |||||
| End Property | |||||
| Public Property Get DoNotCallPrivateNotes() | |||||
| DoNotCallPrivateNotes = pDoNotCallPrivateNotes | |||||
| End Property | |||||
| Public Property Let DoNotCallPrivateNotes(val) | |||||
| pDoNotCallPrivateNotes = val | |||||
| End Property | |||||
| Public Property Get Id() | |||||
| Id = pId | |||||
| End Property | |||||
| Public Property Let Id(val) | |||||
| If IsNumeric(val) Then | |||||
| pId = CLng(val) | |||||
| Else | |||||
| pId = val | |||||
| End If | |||||
| End Property | |||||
| Public Property Get IsBusiness() | |||||
| IsBusiness = pIsBusiness | |||||
| End Property | |||||
| Public Property Let IsBusiness(val) | |||||
| If IsNumeric(val) Then | |||||
| pIsBusiness = CLng(val) | |||||
| Else | |||||
| pIsBusiness = val | |||||
| End If | |||||
| End Property | |||||
| Public Property Get Latitude() | |||||
| Latitude = pLatitude | |||||
| End Property | |||||
| Public Property Let Latitude(val) | |||||
| pLatitude = val | |||||
| End Property | |||||
| Public Property Get Longitude() | |||||
| Longitude = pLongitude | |||||
| End Property | |||||
| Public Property Let Longitude(val) | |||||
| pLongitude = val | |||||
| End Property | |||||
| Public Property Get StreetName() | |||||
| StreetName = pStreetName | |||||
| End Property | |||||
| Public Property Let StreetName(val) | |||||
| pStreetName = val | |||||
| End Property | |||||
| Public Property Get StreetNumber() | |||||
| StreetNumber = pStreetNumber | |||||
| End Property | |||||
| Public Property Let StreetNumber(val) | |||||
| If IsNumeric(val) Then | |||||
| pStreetNumber = CLng(val) | |||||
| Else | |||||
| pStreetNumber = val | |||||
| End If | |||||
| End Property | |||||
| Public Property Get TerritoryId() | |||||
| TerritoryId = pTerritoryId | |||||
| End Property | |||||
| Public Property Let TerritoryId(val) | |||||
| If IsNumeric(val) Then | |||||
| pTerritoryId = CLng(val) | |||||
| Else | |||||
| pTerritoryId = val | |||||
| End If | |||||
| End Property | |||||
| End Class | |||||
| %> | |||||
| @@ -1,103 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated POBO for table [Territories] | |||||
| ' Generated on 1/17/2026 2:53:15 PM | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: core/helpers.asp (QuoteValue function) | |||||
| Class POBO_Territories | |||||
| ' Public array of all property names | |||||
| Public Properties | |||||
| Private pCoordinates | |||||
| Private pDescription | |||||
| Private pId | |||||
| Private pName | |||||
| Private Sub Class_Initialize() | |||||
| pCoordinates = Null | |||||
| pDescription = Null | |||||
| pId = 0 | |||||
| pName = Null | |||||
| Properties = Array("Coordinates","Description","Id","Name") | |||||
| End Sub | |||||
| Public Property Get PrimaryKey() | |||||
| PrimaryKey = "Id" | |||||
| End Property | |||||
| Public Property Get TableName() | |||||
| TableName = "Territories" | |||||
| End Property | |||||
| Public Property Get Coordinates() | |||||
| Coordinates = pCoordinates | |||||
| End Property | |||||
| Public Property Let Coordinates(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pCoordinates = CDbl(val) | |||||
| Else | |||||
| pCoordinates = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_Territories.Coordinates", "Invalid value for Coordinates: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get Description() | |||||
| Description = pDescription | |||||
| End Property | |||||
| Public Property Let Description(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pDescription = CDbl(val) | |||||
| Else | |||||
| pDescription = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_Territories.Description", "Invalid value for Description: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get Id() | |||||
| Id = pId | |||||
| End Property | |||||
| Public Property Let Id(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pId = CDbl(val) | |||||
| Else | |||||
| pId = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_Territories.Id", "Invalid value for Id: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| Public Property Get Name() | |||||
| Name = pName | |||||
| End Property | |||||
| Public Property Let Name(val) | |||||
| On Error Resume Next | |||||
| If IsNumeric(val) Then | |||||
| pName = CDbl(val) | |||||
| Else | |||||
| pName = val | |||||
| End If | |||||
| If Err.Number <> 0 Then | |||||
| Err.Raise Err.Number, "POBO_Territories.Name", "Invalid value for Name: " & Err.Description | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| End Property | |||||
| End Class | |||||
| %> | |||||
| @@ -1,176 +0,0 @@ | |||||
| <% | |||||
| ' Auto-generated Repository for table [Territories] | |||||
| ' Generated on 1/17/2026 2:53:15 PM | |||||
| ' Generator: GenerateRepo.vbs v1.0 | |||||
| ' | |||||
| ' Dependencies: | |||||
| ' - core/lib.DAL.asp (DAL singleton for database access) | |||||
| ' - core/lib.AutoMapper.asp (Automapper for object mapping) | |||||
| ' - core/lib.Collections.asp (LinkedList_Class) | |||||
| ' - core/lib.helpers.asp (KVUnzip, BuildOrderBy, QI, Destroy) | |||||
| Class TerritoriesRepository_Class | |||||
| Public Function FindByID(id) | |||||
| Dim sql : sql = "Select [Coordinates], [Description], [Id], [Name] FROM [Territories] WHERE [Id] = ?" | |||||
| Dim rs : Set rs = DAL.Query(sql, Array(id)) | |||||
| If rs.EOF Then | |||||
| Err.Raise 1, "TerritoriesRepository_Class", RecordNotFoundException("Id", id) | |||||
| Else | |||||
| Set FindByID = Automapper.AutoMap(rs, "POBO_Territories") | |||||
| End If | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function GetAll(orderBy) | |||||
| Set GetAll = Find(Empty, orderBy) | |||||
| End Function | |||||
| Public Function Find(where_kvarray, order_string_or_array) | |||||
| Dim sql : sql = "Select [Coordinates], [Description], [Id], [Name] FROM [Territories]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.Query(sql, where_values) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Do Until rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_Territories") | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set Find = list | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function FindPaged(where_kvarray, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Coordinates], [Description], [Id], [Name] FROM [Territories]" | |||||
| Dim where_keys, where_values, i | |||||
| If Not IsEmpty(where_kvarray) Then | |||||
| KVUnzip where_kvarray, where_keys, where_values | |||||
| If Not IsEmpty(where_keys) Then | |||||
| sql = sql & " WHERE " | |||||
| For i = 0 To UBound(where_keys) | |||||
| If i > 0 Then sql = sql & " AND " | |||||
| sql = sql & " " & QI(where_keys(i)) & " = ?" | |||||
| Next | |||||
| End If | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, where_values, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set FindPaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Public Function SearchTablePaged(columns_array, search_value, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count) | |||||
| Dim sql : sql = "Select [Coordinates], [Description], [Id], [Name] FROM [Territories]" | |||||
| Dim i, params() | |||||
| If IsArray(columns_array) And UBound(columns_array) >= 0 Then | |||||
| sql = sql & " WHERE " | |||||
| ReDim params(UBound(columns_array)) | |||||
| For i = 0 To UBound(columns_array) | |||||
| If i > 0 Then sql = sql & " OR " | |||||
| sql = sql & " " & QI(columns_array(i)) & " LIKE ?" | |||||
| params(i) = "%" & search_value & "%" | |||||
| Next | |||||
| End If | |||||
| sql = sql & BuildOrderBy(order_string_or_array, "[Id]") | |||||
| Dim rs : Set rs = DAL.PagedQuery(sql, params, per_page, page_num) | |||||
| If Not rs.EOF Then | |||||
| rs.PageSize = per_page | |||||
| rs.AbsolutePage = page_num | |||||
| page_count = rs.PageCount | |||||
| record_count = rs.RecordCount | |||||
| End If | |||||
| Set SearchTablePaged = PagedList(rs, per_page) | |||||
| Destroy rs | |||||
| End Function | |||||
| Private Function PagedList(rs, per_page) | |||||
| Dim list : Set list = new LinkedList_Class | |||||
| Dim x : x = 0 | |||||
| Do While (per_page <= 0 Or x < per_page) And Not rs.EOF | |||||
| list.Push Automapper.AutoMap(rs, "POBO_Territories") | |||||
| x = x + 1 | |||||
| rs.MoveNext | |||||
| Loop | |||||
| Set PagedList = list | |||||
| End Function | |||||
| Public Sub AddNew(ByRef model) | |||||
| Dim sql : sql = "INSERT INTO [Territories] ([Coordinates], [Description], [Name]) VALUES (?, ?, ?)" | |||||
| DAL.[Execute] sql, Array(model.Coordinates, model.Description, model.Name) | |||||
| ' Retrieve the newly inserted ID | |||||
| On Error Resume Next | |||||
| Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty) | |||||
| If Err.Number <> 0 Then | |||||
| ' Fallback for Access databases | |||||
| Err.Clear | |||||
| Set rsId = DAL.Query("SELECT TOP 1 [Id] FROM [Territories] ORDER BY [Id] DESC", Empty) | |||||
| End If | |||||
| On Error GoTo 0 | |||||
| If Not rsId.EOF Then | |||||
| If Not IsNull(rsId(0)) Then model.Id = rsId(0) | |||||
| End If | |||||
| Destroy rsId | |||||
| End Sub | |||||
| Public Sub Update(model) | |||||
| Dim sql : sql = "UPDATE [Territories] SET [Coordinates] = ?, [Description] = ?, [Name] = ? WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(model.Coordinates, model.Description, model.Name, model.Id) | |||||
| End Sub | |||||
| Public Sub Delete(id) | |||||
| Dim sql : sql = "DELETE FROM [Territories] WHERE [Id] = ?" | |||||
| DAL.[Execute] sql, Array(id) | |||||
| End Sub | |||||
| Private Function RecordNotFoundException(ByVal field_name, ByVal field_val) | |||||
| RecordNotFoundException = "Territories record was not found with " & field_name & " = '" & field_val & "'." | |||||
| End Function | |||||
| Private Function QI(name) | |||||
| QI = "[" & Replace(CStr(name), "]", "]]") & "]" | |||||
| End Function | |||||
| Private Function BuildOrderBy(orderArg, defaultCol) | |||||
| Dim s : s = "" | |||||
| If IsEmpty(orderArg) Or IsNull(orderArg) Or orderArg = "" Then | |||||
| s = " ORDER BY " & defaultCol & " ASC" | |||||
| ElseIf IsArray(orderArg) Then | |||||
| Dim i : s = " ORDER BY " | |||||
| For i = 0 To UBound(orderArg) | |||||
| If i > 0 Then s = s & ", " | |||||
| s = s & QI(orderArg(i)) | |||||
| Next | |||||
| Else | |||||
| s = " ORDER BY " & QI(orderArg) | |||||
| End If | |||||
| BuildOrderBy = s | |||||
| End Function | |||||
| End Class | |||||
| Dim TerritoriesRepository__Singleton | |||||
| Function TerritoriesRepository() | |||||
| If IsEmpty(TerritoriesRepository__Singleton) Then | |||||
| Set TerritoriesRepository__Singleton = new TerritoriesRepository_Class | |||||
| End If | |||||
| Set TerritoriesRepository = TerritoriesRepository__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -1,153 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/households">Households</a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page">New Household</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1>New Household</h1> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/households"> | |||||
| <div class="row"> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Address" class="form-label">Address <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Address" name="Address" required maxlength="255"> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-md-4"> | |||||
| <div class="mb-3"> | |||||
| <label for="StreetNumber" class="form-label">Street Number</label> | |||||
| <input type="number" class="form-control" id="StreetNumber" name="StreetNumber" value="0"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-8"> | |||||
| <div class="mb-3"> | |||||
| <label for="StreetName" class="form-label">Street Name</label> | |||||
| <input type="text" class="form-control" id="StreetName" name="StreetName" maxlength="255"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="TerritoryId" class="form-label">Territory <span class="text-danger">*</span></label> | |||||
| <select class="form-select" id="TerritoryId" name="TerritoryId" required> | |||||
| <option value="">Select a territory...</option> | |||||
| <% | |||||
| Dim tIter, tItem | |||||
| Set tIter = HouseholdController.territoriesList.Iterator() | |||||
| Do While tIter.HasNext() | |||||
| Set tItem = tIter.GetNext() | |||||
| %> | |||||
| <option value="<%= tItem.Id %>" <% If tItem.Id = HouseholdController.household.TerritoryId Then Response.Write "selected" End If %>><%= Server.HTMLEncode(tItem.Name) %></option> | |||||
| <% | |||||
| Loop | |||||
| %> | |||||
| </select> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <div class="form-check"> | |||||
| <input class="form-check-input" type="checkbox" id="IsBusiness" name="IsBusiness" value="1"> | |||||
| <label class="form-check-label" for="IsBusiness"> | |||||
| This household is a business | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="card border-danger-subtle mb-3"> | |||||
| <div class="card-body"> | |||||
| <div class="form-check mb-3"> | |||||
| <input class="form-check-input" type="checkbox" id="DoNotCall" name="DoNotCall" value="1"> | |||||
| <label class="form-check-label" for="DoNotCall"> | |||||
| Mark this household as Do Not Call | |||||
| </label> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="DoNotCallDate" class="form-label">Do Not Call Date</label> | |||||
| <input type="date" class="form-control" id="DoNotCallDate" name="DoNotCallDate"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="DoNotCallNotes" class="form-label">Do Not Call Notes</label> | |||||
| <textarea class="form-control" id="DoNotCallNotes" name="DoNotCallNotes" rows="3"></textarea> | |||||
| </div> | |||||
| <div class="mb-0"> | |||||
| <label for="DoNotCallPrivateNotes" class="form-label">Private Do Not Call Notes</label> | |||||
| <textarea class="form-control" id="DoNotCallPrivateNotes" name="DoNotCallPrivateNotes" rows="3"></textarea> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Latitude" class="form-label">Latitude</label> | |||||
| <input type="text" class="form-control" id="Latitude" name="Latitude" placeholder="-33.8688"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Longitude" class="form-label">Longitude</label> | |||||
| <input type="text" class="form-control" id="Longitude" name="Longitude" placeholder="151.2093"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Create Household</button> | |||||
| <a href="/households" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-6"> | |||||
| <label class="form-label">Location (click to set coordinates)</label> | |||||
| <div id="map" style="height: 350px; width: 100%; border-radius: 8px;"></div> | |||||
| <small class="text-muted">Click on the map to set latitude and longitude.</small> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(GetAppSetting("GoogleMapsApiKey")) %>&callback=initMap" async defer></script> | |||||
| <script> | |||||
| var map, marker; | |||||
| function initMap() { | |||||
| var defaultCenter = { lat: -33.8688, lng: 151.2093 }; | |||||
| map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: 12, | |||||
| center: defaultCenter, | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| // Click to place marker and set coordinates | |||||
| map.addListener('click', function(event) { | |||||
| placeMarker(event.latLng); | |||||
| }); | |||||
| } | |||||
| function placeMarker(location) { | |||||
| if (marker) { | |||||
| marker.setPosition(location); | |||||
| } else { | |||||
| marker = new google.maps.Marker({ | |||||
| position: location, | |||||
| map: map | |||||
| }); | |||||
| } | |||||
| document.getElementById('Latitude').value = location.lat().toFixed(6); | |||||
| document.getElementById('Longitude').value = location.lng().toFixed(6); | |||||
| } | |||||
| </script> | |||||
| @@ -1,190 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/households">Households</a></li> | |||||
| <li class="breadcrumb-item"><a href="/households/<%= HouseholdController.household.Id %>"><%= Server.HTMLEncode(HouseholdController.household.Address) %></a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page">Edit</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1>Edit Household</h1> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/households/<%= HouseholdController.household.Id %>"> | |||||
| <div class="row"> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Address" class="form-label">Address <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Address" name="Address" required maxlength="255" value="<%= Server.HTMLEncode(HouseholdController.household.Address) %>"> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-md-4"> | |||||
| <div class="mb-3"> | |||||
| <label for="StreetNumber" class="form-label">Street Number</label> | |||||
| <input type="number" class="form-control" id="StreetNumber" name="StreetNumber" value="<%= HouseholdController.household.StreetNumber %>"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-8"> | |||||
| <div class="mb-3"> | |||||
| <label for="StreetName" class="form-label">Street Name</label> | |||||
| <input type="text" class="form-control" id="StreetName" name="StreetName" maxlength="255" value="<%= Server.HTMLEncode(HouseholdController.household.StreetName & "") %>"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="TerritoryId" class="form-label">Territory <span class="text-danger">*</span></label> | |||||
| <select class="form-select" id="TerritoryId" name="TerritoryId" required> | |||||
| <option value="">Select a territory...</option> | |||||
| <% | |||||
| Dim tIter, tItem | |||||
| Set tIter = HouseholdController.territoriesList.Iterator() | |||||
| Do While tIter.HasNext() | |||||
| Set tItem = tIter.GetNext() | |||||
| %> | |||||
| <option value="<%= tItem.Id %>" <% If tItem.Id = HouseholdController.household.TerritoryId Then Response.Write "selected" End If %>><%= Server.HTMLEncode(tItem.Name) %></option> | |||||
| <% | |||||
| Loop | |||||
| %> | |||||
| </select> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <div class="form-check"> | |||||
| <input class="form-check-input" type="checkbox" id="IsBusiness" name="IsBusiness" value="1" | |||||
| <% If HouseholdController.household.IsBusiness = 1 Then Response.Write "checked" End If %>> | |||||
| <label class="form-check-label" for="IsBusiness"> | |||||
| This household is a business | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <% | |||||
| Dim doNotCallDateVal, doNotCallNotesVal, doNotCallPrivateNotesVal | |||||
| doNotCallDateVal = "" | |||||
| doNotCallNotesVal = "" | |||||
| doNotCallPrivateNotesVal = "" | |||||
| If IsDate(HouseholdController.household.DoNotCallDate) Then | |||||
| doNotCallDateVal = Year(HouseholdController.household.DoNotCallDate) & "-" & _ | |||||
| Right("0" & Month(HouseholdController.household.DoNotCallDate), 2) & "-" & _ | |||||
| Right("0" & Day(HouseholdController.household.DoNotCallDate), 2) | |||||
| End If | |||||
| If Not IsNull(HouseholdController.household.DoNotCallNotes) Then | |||||
| doNotCallNotesVal = HouseholdController.household.DoNotCallNotes | |||||
| End If | |||||
| If Not IsNull(HouseholdController.household.DoNotCallPrivateNotes) Then | |||||
| doNotCallPrivateNotesVal = HouseholdController.household.DoNotCallPrivateNotes | |||||
| End If | |||||
| %> | |||||
| <div class="card border-danger-subtle mb-3"> | |||||
| <div class="card-body"> | |||||
| <div class="form-check mb-3"> | |||||
| <input class="form-check-input" type="checkbox" id="DoNotCall" name="DoNotCall" value="1" | |||||
| <% If HouseholdController.household.DoNotCall = 1 Then Response.Write "checked" End If %>> | |||||
| <label class="form-check-label" for="DoNotCall"> | |||||
| Mark this household as Do Not Call | |||||
| </label> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="DoNotCallDate" class="form-label">Do Not Call Date</label> | |||||
| <input type="date" class="form-control" id="DoNotCallDate" name="DoNotCallDate" value="<%= doNotCallDateVal %>"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="DoNotCallNotes" class="form-label">Do Not Call Notes</label> | |||||
| <textarea class="form-control" id="DoNotCallNotes" name="DoNotCallNotes" rows="3"><%= Server.HTMLEncode(doNotCallNotesVal) %></textarea> | |||||
| </div> | |||||
| <div class="mb-0"> | |||||
| <label for="DoNotCallPrivateNotes" class="form-label">Private Do Not Call Notes</label> | |||||
| <textarea class="form-control" id="DoNotCallPrivateNotes" name="DoNotCallPrivateNotes" rows="3"><%= Server.HTMLEncode(doNotCallPrivateNotesVal) %></textarea> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Latitude" class="form-label">Latitude</label> | |||||
| <input type="text" class="form-control" id="Latitude" name="Latitude" value="<%= Server.HTMLEncode(HouseholdController.household.Latitude & "") %>"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-6"> | |||||
| <div class="mb-3"> | |||||
| <label for="Longitude" class="form-label">Longitude</label> | |||||
| <input type="text" class="form-control" id="Longitude" name="Longitude" value="<%= Server.HTMLEncode(HouseholdController.household.Longitude & "") %>"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Update Household</button> | |||||
| <a href="/households/<%= HouseholdController.household.Id %>" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-6"> | |||||
| <label class="form-label">Location (click to update coordinates)</label> | |||||
| <div id="map" style="height: 350px; width: 100%; border-radius: 8px;"></div> | |||||
| <small class="text-muted">Click on the map to update latitude and longitude.</small> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <script> | |||||
| var existingLat = <%= IIf(HouseholdController.household.Latitude & "" <> "", HouseholdController.household.Latitude, "null") %>; | |||||
| var existingLng = <%= IIf(HouseholdController.household.Longitude & "" <> "", HouseholdController.household.Longitude, "null") %>; | |||||
| </script> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(GetAppSetting("GoogleMapsApiKey")) %>&callback=initMap" async defer></script> | |||||
| <script> | |||||
| var map, marker; | |||||
| function initMap() { | |||||
| var defaultCenter = { lat: -33.8688, lng: 151.2093 }; | |||||
| // Use existing coordinates if available | |||||
| if (existingLat !== null && existingLng !== null) { | |||||
| defaultCenter = { lat: existingLat, lng: existingLng }; | |||||
| } | |||||
| map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: existingLat !== null ? 17 : 12, | |||||
| center: defaultCenter, | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| // Place marker if coordinates exist | |||||
| if (existingLat !== null && existingLng !== null) { | |||||
| marker = new google.maps.Marker({ | |||||
| position: defaultCenter, | |||||
| map: map | |||||
| }); | |||||
| } | |||||
| // Click to place/move marker and set coordinates | |||||
| map.addListener('click', function(event) { | |||||
| placeMarker(event.latLng); | |||||
| }); | |||||
| } | |||||
| function placeMarker(location) { | |||||
| if (marker) { | |||||
| marker.setPosition(location); | |||||
| } else { | |||||
| marker = new google.maps.Marker({ | |||||
| position: location, | |||||
| map: map | |||||
| }); | |||||
| } | |||||
| document.getElementById('Latitude').value = location.lat().toFixed(6); | |||||
| document.getElementById('Longitude').value = location.lng().toFixed(6); | |||||
| } | |||||
| </script> | |||||
| @@ -1,187 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <div class="d-flex justify-content-between align-items-center mb-4"> | |||||
| <h1>Households</h1> | |||||
| <a href="/households/new" class="btn btn-primary">New Household</a> | |||||
| </div> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <!-- Search and Filter Form --> | |||||
| <div class="card mb-4"> | |||||
| <div class="card-body"> | |||||
| <form method="get" action="/households" class="row g-3"> | |||||
| <div class="col-md-5"> | |||||
| <div class="input-group"> | |||||
| <input type="text" class="form-control" name="q" placeholder="Search by address or street name..." value="<%= Server.HTMLEncode(HouseholdController.searchTerm) %>"> | |||||
| <button class="btn btn-outline-primary" type="submit">Search</button> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-4"> | |||||
| <select name="territory" class="form-select" onchange="this.form.submit()"> | |||||
| <option value="">All Territories</option> | |||||
| <% | |||||
| Dim tIter, tItem | |||||
| Set tIter = HouseholdController.territoriesList.Iterator() | |||||
| Do While tIter.HasNext() | |||||
| Set tItem = tIter.GetNext() | |||||
| %> | |||||
| <option value="<%= tItem.Id %>" <% If tItem.Id = HouseholdController.filterTerritoryId Then Response.Write "selected" End If %>><%= Server.HTMLEncode(tItem.Name) %></option> | |||||
| <% | |||||
| Loop | |||||
| %> | |||||
| </select> | |||||
| </div> | |||||
| <div class="col-md-2"> | |||||
| <select name="dnc" class="form-select" onchange="this.form.submit()"> | |||||
| <option value="" <% If HouseholdController.filterDoNotCall = -1 Then Response.Write "selected" End If %>>All DNC Status</option> | |||||
| <option value="1" <% If HouseholdController.filterDoNotCall = 1 Then Response.Write "selected" End If %>>Do Not Call</option> | |||||
| <option value="0" <% If HouseholdController.filterDoNotCall = 0 Then Response.Write "selected" End If %>>Callable</option> | |||||
| </select> | |||||
| </div> | |||||
| <div class="col-md-1 text-end"> | |||||
| <span class="text-muted"> | |||||
| <%= HouseholdController.recordCount %> household(s) | |||||
| </span> | |||||
| <% If HouseholdController.searchTerm <> "" Or HouseholdController.filterTerritoryId > 0 Or HouseholdController.filterDoNotCall > -1 Then %> | |||||
| <a href="/households" class="btn btn-sm btn-outline-secondary ms-2">Clear</a> | |||||
| <% End If %> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| <div class="table-responsive"> | |||||
| <table class="table table-striped table-hover"> | |||||
| <thead class="table-dark"> | |||||
| <tr> | |||||
| <th>ID</th> | |||||
| <th>Address</th> | |||||
| <th>Type</th> | |||||
| <th>DNC</th> | |||||
| <th>Street</th> | |||||
| <th>Territory</th> | |||||
| <th>Actions</th> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <% | |||||
| Dim iter, h | |||||
| Set iter = HouseholdController.households.Iterator() | |||||
| If Not iter.HasNext() Then | |||||
| %> | |||||
| <tr> | |||||
| <td colspan="7" class="text-center text-muted py-4"> | |||||
| <% If HouseholdController.searchTerm <> "" Then %> | |||||
| No households found matching "<%= Server.HTMLEncode(HouseholdController.searchTerm) %>" | |||||
| <% ElseIf HouseholdController.filterTerritoryId > 0 Then %> | |||||
| No households found in this territory. | |||||
| <% Else %> | |||||
| No households found. <a href="/households/new">Create one</a> | |||||
| <% End If %> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Else | |||||
| Do While iter.HasNext() | |||||
| Set h = iter.GetNext() | |||||
| %> | |||||
| <tr> | |||||
| <td><%= h.Id %></td> | |||||
| <td><%= Server.HTMLEncode(h.Address) %></td> | |||||
| <td> | |||||
| <% If h.IsBusiness = 1 Then %> | |||||
| <span class="badge bg-info">Business</span> | |||||
| <% Else %> | |||||
| <span class="badge bg-secondary">Residential</span> | |||||
| <% End If %> | |||||
| </td> | |||||
| <td> | |||||
| <% If h.DoNotCall = 1 Then %> | |||||
| <span class="badge bg-danger">Do Not Call</span> | |||||
| <% If IsDate(h.DoNotCallDate) Then %> | |||||
| <div><small class="text-muted"><%= FormatDateTime(h.DoNotCallDate, 2) %></small></div> | |||||
| <% End If %> | |||||
| <% Else %> | |||||
| <span class="text-muted">No</span> | |||||
| <% End If %> | |||||
| </td> | |||||
| <td><%= h.StreetNumber %> <%= Server.HTMLEncode(h.StreetName) %></td> | |||||
| <td> | |||||
| <a href="/territories/<%= h.TerritoryId %>"> | |||||
| <% | |||||
| If HouseholdController.territoryNamesById.Exists(CStr(h.TerritoryId)) Then | |||||
| Response.Write Server.HTMLEncode(HouseholdController.territoryNamesById(CStr(h.TerritoryId))) | |||||
| Else | |||||
| Response.Write "Territory " & h.TerritoryId | |||||
| End If | |||||
| %> | |||||
| </a> | |||||
| </td> | |||||
| <td> | |||||
| <a href="/households/<%= h.Id %>" class="btn btn-sm btn-info">View</a> | |||||
| <a href="/households/<%= h.Id %>/edit" class="btn btn-sm btn-warning">Edit</a> | |||||
| <form method="post" action="/households/<%= h.Id %>/delete" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this household?');"> | |||||
| <button type="submit" class="btn btn-sm btn-danger">Delete</button> | |||||
| </form> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Loop | |||||
| End If | |||||
| %> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <!-- Pagination --> | |||||
| <% If HouseholdController.pageCount > 1 Then %> | |||||
| <% | |||||
| Dim baseUrl, pg, startPage, endPage | |||||
| baseUrl = "/households?" | |||||
| If HouseholdController.searchTerm <> "" Then | |||||
| baseUrl = baseUrl & "q=" & Server.URLEncode(HouseholdController.searchTerm) & "&" | |||||
| End If | |||||
| If HouseholdController.filterTerritoryId > 0 Then | |||||
| baseUrl = baseUrl & "territory=" & HouseholdController.filterTerritoryId & "&" | |||||
| End If | |||||
| If HouseholdController.filterDoNotCall > -1 Then | |||||
| baseUrl = baseUrl & "dnc=" & HouseholdController.filterDoNotCall & "&" | |||||
| End If | |||||
| ' Calculate pagination range (show 5 pages at a time) | |||||
| startPage = HouseholdController.currentPage - 2 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| endPage = startPage + 4 | |||||
| If endPage > HouseholdController.pageCount Then | |||||
| endPage = HouseholdController.pageCount | |||||
| startPage = endPage - 4 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| End If | |||||
| %> | |||||
| <nav aria-label="Household pagination"> | |||||
| <ul class="pagination justify-content-center"> | |||||
| <li class="page-item <% If HouseholdController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=1">« First</a> | |||||
| </li> | |||||
| <li class="page-item <% If HouseholdController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholdController.currentPage - 1 %>">‹ Prev</a> | |||||
| </li> | |||||
| <% For pg = startPage To endPage %> | |||||
| <li class="page-item <% If pg = HouseholdController.currentPage Then Response.Write "active" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= pg %>"><%= pg %></a> | |||||
| </li> | |||||
| <% Next %> | |||||
| <li class="page-item <% If HouseholdController.currentPage >= HouseholdController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholdController.currentPage + 1 %>">Next ›</a> | |||||
| </li> | |||||
| <li class="page-item <% If HouseholdController.currentPage >= HouseholdController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholdController.pageCount %>">Last »</a> | |||||
| </li> | |||||
| </ul> | |||||
| </nav> | |||||
| <p class="text-center text-muted"> | |||||
| Page <%= HouseholdController.currentPage %> of <%= HouseholdController.pageCount %> | |||||
| </p> | |||||
| <% End If %> | |||||
| </div> | |||||
| @@ -1,291 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <% | |||||
| Dim dncNotesVal, dncPrivateNotesVal | |||||
| dncNotesVal = "" | |||||
| dncPrivateNotesVal = "" | |||||
| If Not IsNull(HouseholdController.household.DoNotCallNotes) Then dncNotesVal = HouseholdController.household.DoNotCallNotes | |||||
| If Not IsNull(HouseholdController.household.DoNotCallPrivateNotes) Then dncPrivateNotesVal = HouseholdController.household.DoNotCallPrivateNotes | |||||
| %> | |||||
| <nav aria-label="breadcrumb"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/households">Households</a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page"><%= Server.HTMLEncode(HouseholdController.household.Address) %></li> | |||||
| </ol> | |||||
| </nav> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-header d-flex justify-content-between align-items-center"> | |||||
| <h2 class="mb-0"><%= Server.HTMLEncode(HouseholdController.household.Address) %></h2> | |||||
| <div> | |||||
| <a href="/households/<%= HouseholdController.household.Id %>/edit" class="btn btn-warning">Edit</a> | |||||
| <form method="post" action="/households/<%= HouseholdController.household.Id %>/delete" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this household?');"> | |||||
| <button type="submit" class="btn btn-danger">Delete</button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <div class="row"> | |||||
| <div class="col-md-6"> | |||||
| <dl> | |||||
| <dt>ID</dt> | |||||
| <dd><%= HouseholdController.household.Id %></dd> | |||||
| <dt>Address</dt> | |||||
| <dd><%= Server.HTMLEncode(HouseholdController.household.Address) %></dd> | |||||
| <dt>Street Number</dt> | |||||
| <dd><%= HouseholdController.household.StreetNumber %></dd> | |||||
| <dt>Street Name</dt> | |||||
| <dd><%= Server.HTMLEncode(HouseholdController.household.StreetName & "") %></dd> | |||||
| <dt>Type</dt> | |||||
| <dd> | |||||
| <% If HouseholdController.household.IsBusiness = 1 Then %> | |||||
| <span class="badge bg-info">Business</span> | |||||
| <% Else %> | |||||
| <span class="badge bg-secondary">Residential</span> | |||||
| <% End If %> | |||||
| <% If HouseholdController.household.DoNotCall = 1 Then %> | |||||
| <span class="badge bg-danger ms-2">Do Not Call</span> | |||||
| <% End If %> | |||||
| </dd> | |||||
| <dt>Do Not Call Date</dt> | |||||
| <dd> | |||||
| <% If IsDate(HouseholdController.household.DoNotCallDate) Then %> | |||||
| <%= FormatDateTime(HouseholdController.household.DoNotCallDate, 2) %> | |||||
| <% Else %> | |||||
| <span class="text-muted">Not set</span> | |||||
| <% End If %> | |||||
| </dd> | |||||
| <dt>Do Not Call Notes</dt> | |||||
| <dd> | |||||
| <% If Trim(dncNotesVal) <> "" Then %> | |||||
| <%= Server.HTMLEncode(dncNotesVal) %> | |||||
| <% Else %> | |||||
| <span class="text-muted">None</span> | |||||
| <% End If %> | |||||
| </dd> | |||||
| <dt>Private DNC Notes</dt> | |||||
| <dd> | |||||
| <% If Trim(dncPrivateNotesVal) <> "" Then %> | |||||
| <%= Server.HTMLEncode(dncPrivateNotesVal) %> | |||||
| <% Else %> | |||||
| <span class="text-muted">None</span> | |||||
| <% End If %> | |||||
| </dd> | |||||
| <dt>Territory</dt> | |||||
| <dd><a href="/territories/<%= HouseholdController.household.TerritoryId %>">Territory <%= HouseholdController.household.TerritoryId %></a></dd> | |||||
| </dl> | |||||
| </div> | |||||
| <div class="col-md-6"> | |||||
| <dl> | |||||
| <dt>Latitude</dt> | |||||
| <dd><%= Server.HTMLEncode(HouseholdController.household.Latitude & "") %></dd> | |||||
| <dt>Longitude</dt> | |||||
| <dd><%= Server.HTMLEncode(HouseholdController.household.Longitude & "") %></dd> | |||||
| </dl> | |||||
| <% If HouseholdController.household.Latitude <> "" And HouseholdController.household.Longitude <> "" Then %> | |||||
| <div id="map" style="height: 250px; width: 100%; border-radius: 8px;"></div> | |||||
| <% End If %> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- Householder Names Section --> | |||||
| <div class="card mt-4"> | |||||
| <div class="card-header d-flex justify-content-between align-items-center"> | |||||
| <h5 class="mb-0">Householder Names</h5> | |||||
| <a href="/householder-names/new?household=<%= HouseholdController.household.Id %>" class="btn btn-sm btn-success">Add Name</a> | |||||
| </div> | |||||
| <div class="card-body p-0"> | |||||
| <% | |||||
| Dim hnIter, hn | |||||
| Set hnIter = HouseholdController.householderNames.Iterator() | |||||
| If Not hnIter.HasNext() Then | |||||
| %> | |||||
| <div class="p-3 text-center text-muted"> | |||||
| No householder names recorded. <a href="/householder-names/new?household=<%= HouseholdController.household.Id %>">Add one</a> | |||||
| </div> | |||||
| <% | |||||
| Else | |||||
| %> | |||||
| <table class="table table-hover mb-0"> | |||||
| <thead class="table-light"> | |||||
| <tr> | |||||
| <th>Name</th> | |||||
| <th>Type</th> | |||||
| <th style="width: 180px;">Letter Status</th> | |||||
| <th style="width: 100px;">Actions</th> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <% | |||||
| Do While hnIter.HasNext() | |||||
| Set hn = hnIter.GetNext() | |||||
| %> | |||||
| <tr> | |||||
| <td> | |||||
| <a href="/householder-names/<%= hn.Id %>"><%= Server.HTMLEncode(hn.Name & "") %></a> | |||||
| </td> | |||||
| <td> | |||||
| <% If HouseholdController.household.IsBusiness = 1 Then %> | |||||
| <span class="badge bg-info">Business</span> | |||||
| <% Else %> | |||||
| <span class="badge bg-secondary">Residential</span> | |||||
| <% End If %> | |||||
| </td> | |||||
| <td> | |||||
| <form method="post" action="/households/<%= HouseholdController.household.Id %>/mark-returned" style="display:inline;"> | |||||
| <input type="hidden" name="householder_id" value="<%= hn.Id %>"> | |||||
| <% If hn.LetterReturned = 1 Then %> | |||||
| <button type="submit" class="btn btn-sm btn-warning" title="Click to mark as NOT returned"> | |||||
| Returned | |||||
| <% If Year(hn.ReturnDate) > 1970 Then %> | |||||
| <small>(<%= FormatDateTime(hn.ReturnDate, 2) %>)</small> | |||||
| <% End If %> | |||||
| </button> | |||||
| <% Else %> | |||||
| <button type="submit" class="btn btn-sm btn-outline-success" title="Click to mark as returned"> | |||||
| Not Returned | |||||
| </button> | |||||
| <% End If %> | |||||
| </form> | |||||
| </td> | |||||
| <td> | |||||
| <a href="/householder-names/<%= hn.Id %>/edit" class="btn btn-sm btn-outline-warning" title="Edit">Edit</a> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Loop | |||||
| %> | |||||
| </tbody> | |||||
| </table> | |||||
| <% | |||||
| End If | |||||
| %> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mt-3"> | |||||
| <a href="/households" class="btn btn-secondary">Back to List</a> | |||||
| <a href="/households?territory=<%= HouseholdController.household.TerritoryId %>" class="btn btn-outline-primary">View Territory Households</a> | |||||
| </div> | |||||
| </div> | |||||
| <% If HouseholdController.household.Latitude <> "" And HouseholdController.household.Longitude <> "" Then %> | |||||
| <% | |||||
| Dim mapProvider, googleMapsKey, mapTilerKey, mapTilerStyle, mapTilerSdkJsUrl, mapTilerSdkCssUrl | |||||
| mapProvider = LCase(Trim(GetAppSetting("MapProvider") & "")) | |||||
| If mapProvider <> "maptiler" Then mapProvider = "google" | |||||
| googleMapsKey = Trim(GetAppSetting("GoogleMapsApiKey") & "") | |||||
| mapTilerKey = Trim(GetAppSetting("MapTilerApiKey") & "") | |||||
| mapTilerStyle = Trim(GetAppSetting("MapTilerStyle") & "") | |||||
| If mapTilerStyle = "" Or LCase(mapTilerStyle) = "nothing" Then mapTilerStyle = "streets-v2" | |||||
| mapTilerSdkJsUrl = Trim(GetAppSetting("MapTilerSdkJsUrl") & "") | |||||
| mapTilerSdkCssUrl = Trim(GetAppSetting("MapTilerSdkCssUrl") & "") | |||||
| If mapTilerSdkJsUrl = "" Or LCase(mapTilerSdkJsUrl) = "nothing" Then mapTilerSdkJsUrl = "https://cdn.maptiler.com/maptiler-sdk-js/v3.0.0/maptiler-sdk.umd.min.js" | |||||
| If mapTilerSdkCssUrl = "" Or LCase(mapTilerSdkCssUrl) = "nothing" Then mapTilerSdkCssUrl = "https://cdn.maptiler.com/maptiler-sdk-js/v3.0.0/maptiler-sdk.css" | |||||
| %> | |||||
| <script> | |||||
| var householdLat = <%= HouseholdController.household.Latitude %>; | |||||
| var householdLng = <%= HouseholdController.household.Longitude %>; | |||||
| var mapProvider = "<%= Replace(mapProvider, """", "\""") %>"; | |||||
| var googleMapsKey = "<%= Replace(googleMapsKey, """", "\""") %>"; | |||||
| var mapTilerKey = "<%= Replace(mapTilerKey, """", "\""") %>"; | |||||
| var mapTilerStyle = "<%= Replace(mapTilerStyle, """", "\""") %>"; | |||||
| </script> | |||||
| <% If mapProvider = "maptiler" Then %> | |||||
| <link rel="stylesheet" href="<%= Server.HTMLEncode(mapTilerSdkCssUrl) %>" /> | |||||
| <script src="<%= Server.HTMLEncode(mapTilerSdkJsUrl) %>"></script> | |||||
| <% Else %> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(googleMapsKey) %>&callback=initMap" async defer></script> | |||||
| <% End If %> | |||||
| <script> | |||||
| function setMapMessage(message) { | |||||
| var mapEl = document.getElementById('map'); | |||||
| if (mapEl) { | |||||
| mapEl.innerHTML = '<div class="alert alert-info">' + message + '</div>'; | |||||
| } | |||||
| } | |||||
| function initMap() { | |||||
| try { | |||||
| if (mapProvider === 'maptiler') { | |||||
| if (!mapTilerKey || mapTilerKey === 'nothing') { | |||||
| setMapMessage('MapTiler API key is missing.'); | |||||
| return; | |||||
| } | |||||
| if (typeof maptilersdk === 'undefined') { | |||||
| setMapMessage('MapTiler SDK failed to load.'); | |||||
| return; | |||||
| } | |||||
| maptilersdk.config.apiKey = mapTilerKey; | |||||
| var styleUrl = 'https://api.maptiler.com/maps/' + encodeURIComponent(mapTilerStyle) + '/style.json?key=' + encodeURIComponent(mapTilerKey); | |||||
| var location = [parseFloat(householdLng), parseFloat(householdLat)]; | |||||
| var map = new maptilersdk.Map({ | |||||
| container: 'map', | |||||
| style: styleUrl, | |||||
| center: location, | |||||
| zoom: 17 | |||||
| }); | |||||
| new maptilersdk.Marker() | |||||
| .setLngLat(location) | |||||
| .addTo(map); | |||||
| return; | |||||
| } | |||||
| var location = { lat: householdLat, lng: householdLng }; | |||||
| var map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: 17, | |||||
| center: location, | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| var marker = new google.maps.Marker({ | |||||
| position: location, | |||||
| map: map, | |||||
| title: '<%= Replace(HouseholdController.household.Address, "'", "\'") %>' | |||||
| }); | |||||
| } catch (e) { | |||||
| setMapMessage('Map error: ' + e.message); | |||||
| } | |||||
| } | |||||
| if (mapProvider === 'maptiler') { | |||||
| if (document.readyState === 'loading') { | |||||
| document.addEventListener('DOMContentLoaded', initMap); | |||||
| } else { | |||||
| initMap(); | |||||
| } | |||||
| setTimeout(function() { | |||||
| if (typeof maptilersdk === 'undefined') { | |||||
| setMapMessage('MapTiler SDK failed to load.'); | |||||
| } | |||||
| }, 2000); | |||||
| } else { | |||||
| setTimeout(function() { | |||||
| if (typeof google === 'undefined') { | |||||
| setMapMessage('Google Maps failed to load.'); | |||||
| } | |||||
| }, 2000); | |||||
| } | |||||
| </script> | |||||
| <% End If %> | |||||
| @@ -1,86 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb" class="mb-3"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/householder-names">Householder Names</a></li> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <li class="breadcrumb-item"><a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a></li> | |||||
| <% End If %> | |||||
| <li class="breadcrumb-item active">New</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1 class="mb-4"> | |||||
| New Householder Name | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <small class="text-muted">for <%= Server.HTMLEncode(HouseholderNameController.household.Address) %></small> | |||||
| <% End If %> | |||||
| </h1> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="row"> | |||||
| <div class="col-md-8"> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/householder-names"> | |||||
| <div class="mb-3"> | |||||
| <label for="Name" class="form-label">Name <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Name" name="Name" required autofocus | |||||
| value="<%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %>"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="HouseholdId" class="form-label">Household <span class="text-danger">*</span></label> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <input type="hidden" name="HouseholdId" value="<%= HouseholderNameController.household.Id %>"> | |||||
| <input type="text" class="form-control" readonly value="<%= Server.HTMLEncode(HouseholderNameController.household.Address) %>"> | |||||
| <% Else %> | |||||
| <input type="number" class="form-control" id="HouseholdId" name="HouseholdId" required | |||||
| value="<%= HouseholderNameController.householderName.HouseholdId %>" | |||||
| placeholder="Enter Household ID"> | |||||
| <div class="form-text">Enter the ID of the household this name belongs to.</div> | |||||
| <% End If %> | |||||
| </div> | |||||
| <!-- IsBusiness moved to Household --> | |||||
| <div class="mb-3"> | |||||
| <div class="form-check"> | |||||
| <input class="form-check-input" type="checkbox" id="LetterReturned" name="LetterReturned" value="1" | |||||
| <% If HouseholderNameController.householderName.LetterReturned = 1 Then Response.Write "checked" End If %>> | |||||
| <label class="form-check-label" for="LetterReturned"> | |||||
| Letter was returned | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="ReturnDate" class="form-label">Return Date</label> | |||||
| <input type="date" class="form-control" id="ReturnDate" name="ReturnDate"> | |||||
| <div class="form-text">Only fill in if letter was returned.</div> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Create Householder Name</button> | |||||
| <a href="/householder-names<% If HouseholderNameController.householderName.HouseholdId > 0 Then Response.Write "?household=" & HouseholderNameController.householderName.HouseholdId End If %>" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-4"> | |||||
| <div class="card"> | |||||
| <div class="card-header"> | |||||
| <h5 class="mb-0">Help</h5> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <p class="mb-2"><strong>Name:</strong> The name of the person or business at this address.</p> | |||||
| <p class="mb-2"><strong>Type:</strong> Business/Residential is now stored on the household, not the name.</p> | |||||
| <p class="mb-0"><strong>Letter Returned:</strong> Check if a letter sent to this name was returned as undeliverable.</p> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| @@ -1,95 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb" class="mb-3"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/householder-names">Householder Names</a></li> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <li class="breadcrumb-item"><a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a></li> | |||||
| <% End If %> | |||||
| <li class="breadcrumb-item"><a href="/householder-names/<%= HouseholderNameController.householderName.Id %>"><%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %></a></li> | |||||
| <li class="breadcrumb-item active">Edit</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1 class="mb-4">Edit Householder Name</h1> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="row"> | |||||
| <div class="col-md-8"> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/householder-names/<%= HouseholderNameController.householderName.Id %>"> | |||||
| <div class="mb-3"> | |||||
| <label for="Name" class="form-label">Name <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Name" name="Name" required autofocus | |||||
| value="<%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %>"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label class="form-label">Household</label> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <p class="form-control-plaintext"> | |||||
| <a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a> | |||||
| </p> | |||||
| <% Else %> | |||||
| <p class="form-control-plaintext">ID: <%= HouseholderNameController.householderName.HouseholdId %></p> | |||||
| <% End If %> | |||||
| </div> | |||||
| <!-- IsBusiness moved to Household --> | |||||
| <div class="mb-3"> | |||||
| <div class="form-check"> | |||||
| <input class="form-check-input" type="checkbox" id="LetterReturned" name="LetterReturned" value="1" | |||||
| <% If HouseholderNameController.householderName.LetterReturned = 1 Then Response.Write "checked" End If %>> | |||||
| <label class="form-check-label" for="LetterReturned"> | |||||
| Letter was returned | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="ReturnDate" class="form-label">Return Date</label> | |||||
| <% | |||||
| Dim returnDateVal | |||||
| returnDateVal = "" | |||||
| If Year(HouseholderNameController.householderName.ReturnDate) > 1970 Then | |||||
| returnDateVal = Year(HouseholderNameController.householderName.ReturnDate) & "-" & _ | |||||
| Right("0" & Month(HouseholderNameController.householderName.ReturnDate), 2) & "-" & _ | |||||
| Right("0" & Day(HouseholderNameController.householderName.ReturnDate), 2) | |||||
| End If | |||||
| %> | |||||
| <input type="date" class="form-control" id="ReturnDate" name="ReturnDate" value="<%= returnDateVal %>"> | |||||
| <div class="form-text">Only fill in if letter was returned.</div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label class="form-label">Created</label> | |||||
| <p class="form-control-plaintext"><%= FormatDateTime(HouseholderNameController.householderName.Created, 2) %></p> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Update Householder Name</button> | |||||
| <a href="/householder-names/<%= HouseholderNameController.householderName.Id %>" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-4"> | |||||
| <div class="card border-danger"> | |||||
| <div class="card-header bg-danger text-white"> | |||||
| <h5 class="mb-0">Danger Zone</h5> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <p>Permanently delete this householder name.</p> | |||||
| <form method="post" action="/householder-names/<%= HouseholderNameController.householderName.Id %>/delete" onsubmit="return confirm('Are you sure you want to delete this householder name? This action cannot be undone.');"> | |||||
| <button type="submit" class="btn btn-danger w-100">Delete Householder Name</button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| @@ -1,171 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <div class="d-flex justify-content-between align-items-center mb-4"> | |||||
| <h1> | |||||
| Householder Names | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <small class="text-muted">for <%= Server.HTMLEncode(HouseholderNameController.household.Address) %></small> | |||||
| <% End If %> | |||||
| </h1> | |||||
| <a href="/householder-names/new<% If HouseholderNameController.filterHouseholdId > 0 Then Response.Write "?household=" & HouseholderNameController.filterHouseholdId End If %>" class="btn btn-primary">New Householder Name</a> | |||||
| </div> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <!-- Search Form --> | |||||
| <div class="card mb-4"> | |||||
| <div class="card-body"> | |||||
| <form method="get" action="/householder-names" class="row g-3"> | |||||
| <div class="col-md-6"> | |||||
| <div class="input-group"> | |||||
| <input type="text" class="form-control" name="q" placeholder="Search by name..." value="<%= Server.HTMLEncode(HouseholderNameController.searchTerm) %>"> | |||||
| <button class="btn btn-outline-primary" type="submit">Search</button> | |||||
| <% If HouseholderNameController.searchTerm <> "" Or HouseholderNameController.filterHouseholdId > 0 Then %> | |||||
| <a href="/householder-names" class="btn btn-outline-secondary">Clear</a> | |||||
| <% End If %> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-3 text-end"> | |||||
| <span class="text-muted"> | |||||
| <% If HouseholderNameController.searchTerm <> "" Then %> | |||||
| Found <%= HouseholderNameController.recordCount %> result(s) | |||||
| <% Else %> | |||||
| <%= HouseholderNameController.recordCount %> total | |||||
| <% End If %> | |||||
| </span> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <div class="alert alert-info"> | |||||
| Showing householder names for: <strong><a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a></strong> | |||||
| <a href="/householder-names" class="btn btn-sm btn-outline-info ms-2">Show All</a> | |||||
| </div> | |||||
| <% End If %> | |||||
| <div class="table-responsive"> | |||||
| <table class="table table-striped table-hover"> | |||||
| <thead class="table-dark"> | |||||
| <tr> | |||||
| <th>ID</th> | |||||
| <th>Name</th> | |||||
| <th>Type</th> | |||||
| <th>Letter Returned</th> | |||||
| <th>Created</th> | |||||
| <th>Actions</th> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <% | |||||
| Dim iter, hn | |||||
| Set iter = HouseholderNameController.householderNames.Iterator() | |||||
| If Not iter.HasNext() Then | |||||
| %> | |||||
| <tr> | |||||
| <td colspan="6" class="text-center text-muted py-4"> | |||||
| <% If HouseholderNameController.searchTerm <> "" Then %> | |||||
| No householder names found matching "<%= Server.HTMLEncode(HouseholderNameController.searchTerm) %>" | |||||
| <% ElseIf HouseholderNameController.filterHouseholdId > 0 Then %> | |||||
| No householder names for this household. <a href="/householder-names/new?household=<%= HouseholderNameController.filterHouseholdId %>">Add one</a> | |||||
| <% Else %> | |||||
| No householder names found. <a href="/householder-names/new">Create one</a> | |||||
| <% End If %> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Else | |||||
| Do While iter.HasNext() | |||||
| Set hn = iter.GetNext() | |||||
| %> | |||||
| <tr> | |||||
| <td><%= hn.Id %></td> | |||||
| <td><%= Server.HTMLEncode(hn.Name & "") %></td> | |||||
| <td> | |||||
| <span class="badge bg-secondary">Household</span> | |||||
| </td> | |||||
| <td> | |||||
| <% If hn.LetterReturned = 1 Then %> | |||||
| <span class="badge bg-warning text-dark">Returned</span> | |||||
| <% If Year(hn.ReturnDate) > 1970 Then %> | |||||
| <small class="text-muted"><%= FormatDateTime(hn.ReturnDate, 2) %></small> | |||||
| <% End If %> | |||||
| <% Else %> | |||||
| <span class="badge bg-success">No</span> | |||||
| <% End If %> | |||||
| </td> | |||||
| <td><%= FormatDateTime(hn.Created, 2) %></td> | |||||
| <td> | |||||
| <a href="/householder-names/<%= hn.Id %>" class="btn btn-sm btn-info">View</a> | |||||
| <a href="/householder-names/<%= hn.Id %>/edit" class="btn btn-sm btn-warning">Edit</a> | |||||
| <form method="post" action="/householder-names/<%= hn.Id %>/delete" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this householder name?');"> | |||||
| <button type="submit" class="btn btn-sm btn-danger">Delete</button> | |||||
| </form> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Loop | |||||
| End If | |||||
| %> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <!-- Pagination --> | |||||
| <% If HouseholderNameController.pageCount > 1 Then %> | |||||
| <% | |||||
| Dim baseUrl, pg, startPage, endPage | |||||
| baseUrl = "/householder-names?" | |||||
| If HouseholderNameController.searchTerm <> "" Then | |||||
| baseUrl = baseUrl & "q=" & Server.URLEncode(HouseholderNameController.searchTerm) & "&" | |||||
| End If | |||||
| If HouseholderNameController.filterHouseholdId > 0 Then | |||||
| baseUrl = baseUrl & "household=" & HouseholderNameController.filterHouseholdId & "&" | |||||
| End If | |||||
| ' Calculate pagination range (show 5 pages at a time) | |||||
| startPage = HouseholderNameController.currentPage - 2 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| endPage = startPage + 4 | |||||
| If endPage > HouseholderNameController.pageCount Then | |||||
| endPage = HouseholderNameController.pageCount | |||||
| startPage = endPage - 4 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| End If | |||||
| %> | |||||
| <nav aria-label="Householder names pagination"> | |||||
| <ul class="pagination justify-content-center"> | |||||
| <!-- First page --> | |||||
| <li class="page-item <% If HouseholderNameController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=1">« First</a> | |||||
| </li> | |||||
| <!-- Previous page --> | |||||
| <li class="page-item <% If HouseholderNameController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholderNameController.currentPage - 1 %>">‹ Prev</a> | |||||
| </li> | |||||
| <!-- Page numbers --> | |||||
| <% For pg = startPage To endPage %> | |||||
| <li class="page-item <% If pg = HouseholderNameController.currentPage Then Response.Write "active" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= pg %>"><%= pg %></a> | |||||
| </li> | |||||
| <% Next %> | |||||
| <!-- Next page --> | |||||
| <li class="page-item <% If HouseholderNameController.currentPage >= HouseholderNameController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholderNameController.currentPage + 1 %>">Next ›</a> | |||||
| </li> | |||||
| <!-- Last page --> | |||||
| <li class="page-item <% If HouseholderNameController.currentPage >= HouseholderNameController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= HouseholderNameController.pageCount %>">Last »</a> | |||||
| </li> | |||||
| </ul> | |||||
| </nav> | |||||
| <p class="text-center text-muted"> | |||||
| Page <%= HouseholderNameController.currentPage %> of <%= HouseholderNameController.pageCount %> | |||||
| </p> | |||||
| <% End If %> | |||||
| </div> | |||||
| @@ -1,90 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb" class="mb-3"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/householder-names">Householder Names</a></li> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <li class="breadcrumb-item"><a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a></li> | |||||
| <% End If %> | |||||
| <li class="breadcrumb-item active"><%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %></li> | |||||
| </ol> | |||||
| </nav> | |||||
| <div class="d-flex justify-content-between align-items-center mb-4"> | |||||
| <h1><%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %></h1> | |||||
| <div> | |||||
| <a href="/householder-names/<%= HouseholderNameController.householderName.Id %>/edit" class="btn btn-warning">Edit</a> | |||||
| <a href="/householder-names?household=<%= HouseholderNameController.householderName.HouseholdId %>" class="btn btn-secondary">Back to List</a> | |||||
| </div> | |||||
| </div> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="row"> | |||||
| <div class="col-md-8"> | |||||
| <div class="card"> | |||||
| <div class="card-header"> | |||||
| <h5 class="mb-0">Householder Details</h5> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <dl class="row mb-0"> | |||||
| <dt class="col-sm-4">ID</dt> | |||||
| <dd class="col-sm-8"><%= HouseholderNameController.householderName.Id %></dd> | |||||
| <dt class="col-sm-4">Name</dt> | |||||
| <dd class="col-sm-8"><%= Server.HTMLEncode(HouseholderNameController.householderName.Name & "") %></dd> | |||||
| <dt class="col-sm-4">Type</dt> | |||||
| <dd class="col-sm-8"> | |||||
| <span class="badge bg-secondary">Household</span> | |||||
| </dd> | |||||
| <dt class="col-sm-4">Letter Returned</dt> | |||||
| <dd class="col-sm-8"> | |||||
| <% If HouseholderNameController.householderName.LetterReturned = 1 Then %> | |||||
| <span class="badge bg-warning text-dark">Yes - Returned</span> | |||||
| <% If Year(HouseholderNameController.householderName.ReturnDate) > 1970 Then %> | |||||
| <br><small class="text-muted">Return Date: <%= FormatDateTime(HouseholderNameController.householderName.ReturnDate, 2) %></small> | |||||
| <% End If %> | |||||
| <% Else %> | |||||
| <span class="badge bg-success">No</span> | |||||
| <% End If %> | |||||
| </dd> | |||||
| <dt class="col-sm-4">Created</dt> | |||||
| <dd class="col-sm-8"><%= FormatDateTime(HouseholderNameController.householderName.Created, 2) %></dd> | |||||
| <dt class="col-sm-4">Household</dt> | |||||
| <dd class="col-sm-8"> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <a href="/households/<%= HouseholderNameController.household.Id %>"><%= Server.HTMLEncode(HouseholderNameController.household.Address) %></a> | |||||
| <% Else %> | |||||
| ID: <%= HouseholderNameController.householderName.HouseholdId %> | |||||
| <% End If %> | |||||
| </dd> | |||||
| </dl> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-4"> | |||||
| <div class="card"> | |||||
| <div class="card-header"> | |||||
| <h5 class="mb-0">Actions</h5> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <div class="d-grid gap-2"> | |||||
| <a href="/householder-names/<%= HouseholderNameController.householderName.Id %>/edit" class="btn btn-warning">Edit</a> | |||||
| <% If IsObject(HouseholderNameController.household) Then %> | |||||
| <a href="/households/<%= HouseholderNameController.household.Id %>" class="btn btn-outline-primary">View Household</a> | |||||
| <a href="/householder-names/new?household=<%= HouseholderNameController.householderName.HouseholdId %>" class="btn btn-outline-success">Add Another Name</a> | |||||
| <% End If %> | |||||
| <form method="post" action="/householder-names/<%= HouseholderNameController.householderName.Id %>/delete" onsubmit="return confirm('Are you sure you want to delete this householder name?');"> | |||||
| <button type="submit" class="btn btn-danger w-100">Delete</button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| @@ -1,134 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/territories">Territories</a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page">New Territory</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1>New Territory</h1> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/territories"> | |||||
| <div class="row"> | |||||
| <div class="col-md-4"> | |||||
| <div class="mb-3"> | |||||
| <label for="Name" class="form-label">Name <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Name" name="Name" required maxlength="255"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="Description" class="form-label">Description</label> | |||||
| <textarea class="form-control" id="Description" name="Description" rows="2"></textarea> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="Coordinates" class="form-label">Coordinates (JSON)</label> | |||||
| <textarea class="form-control" id="Coordinates" name="Coordinates" rows="4" style="font-family: monospace; font-size: 12px;"></textarea> | |||||
| <small class="text-muted">Click on the map to draw the territory boundary.</small> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Create Territory</button> | |||||
| <a href="/territories" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-8"> | |||||
| <label class="form-label">Territory Boundary</label> | |||||
| <div id="map" style="height: 400px; width: 100%; border-radius: 8px;"></div> | |||||
| <div class="mt-2"> | |||||
| <button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearPolygon()">Clear Polygon</button> | |||||
| <button type="button" class="btn btn-sm btn-outline-secondary" onclick="undoLastPoint()">Undo Last Point</button> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(GetAppSetting("GoogleMapsApiKey")) %>&callback=initMap" async defer></script> | |||||
| <script> | |||||
| var map, polygon; | |||||
| function initMap() { | |||||
| var defaultCenter = { lat: -33.8688, lng: 151.2093 }; // Sydney default | |||||
| map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: 12, | |||||
| center: defaultCenter, | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| // Create editable polygon (empty initially) | |||||
| polygon = new google.maps.Polygon({ | |||||
| paths: [], | |||||
| strokeColor: '#FF0000', | |||||
| strokeOpacity: 0.8, | |||||
| strokeWeight: 2, | |||||
| fillColor: '#FF0000', | |||||
| fillOpacity: 0.35, | |||||
| editable: true, | |||||
| draggable: true | |||||
| }); | |||||
| polygon.setMap(map); | |||||
| // Click to add points | |||||
| map.addListener('click', function(event) { | |||||
| var path = polygon.getPath(); | |||||
| path.push(event.latLng); | |||||
| updateCoordinatesField(); | |||||
| }); | |||||
| // Update field when polygon is edited | |||||
| google.maps.event.addListener(polygon.getPath(), 'set_at', updateCoordinatesField); | |||||
| google.maps.event.addListener(polygon.getPath(), 'insert_at', updateCoordinatesField); | |||||
| google.maps.event.addListener(polygon.getPath(), 'remove_at', updateCoordinatesField); | |||||
| // Also update when dragged | |||||
| google.maps.event.addListener(polygon, 'dragend', updateCoordinatesField); | |||||
| } | |||||
| function updateCoordinatesField() { | |||||
| var path = polygon.getPath(); | |||||
| var coords = []; | |||||
| for (var i = 0; i < path.getLength(); i++) { | |||||
| var point = path.getAt(i); | |||||
| coords.push({ lat: point.lat(), lng: point.lng() }); | |||||
| } | |||||
| document.getElementById('Coordinates').value = JSON.stringify(coords, null, 2); | |||||
| } | |||||
| function clearPolygon() { | |||||
| polygon.getPath().clear(); | |||||
| updateCoordinatesField(); | |||||
| } | |||||
| function undoLastPoint() { | |||||
| var path = polygon.getPath(); | |||||
| if (path.getLength() > 0) { | |||||
| path.pop(); | |||||
| updateCoordinatesField(); | |||||
| } | |||||
| } | |||||
| // Allow manual JSON editing | |||||
| document.getElementById('Coordinates').addEventListener('change', function() { | |||||
| try { | |||||
| var coords = JSON.parse(this.value); | |||||
| if (Array.isArray(coords)) { | |||||
| var path = polygon.getPath(); | |||||
| path.clear(); | |||||
| coords.forEach(function(coord) { | |||||
| path.push(new google.maps.LatLng(coord.lat || coord[0], coord.lng || coord[1])); | |||||
| }); | |||||
| } | |||||
| } catch (e) { | |||||
| // Invalid JSON, ignore | |||||
| } | |||||
| }); | |||||
| </script> | |||||
| @@ -1,164 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/territories">Territories</a></li> | |||||
| <li class="breadcrumb-item"><a href="/territories/<%= TerritoryController.territory.Id %>"><%= Server.HTMLEncode(TerritoryController.territory.Name) %></a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page">Edit</li> | |||||
| </ol> | |||||
| </nav> | |||||
| <h1>Edit Territory</h1> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-body"> | |||||
| <form method="post" action="/territories/<%= TerritoryController.territory.Id %>"> | |||||
| <div class="row"> | |||||
| <div class="col-md-4"> | |||||
| <div class="mb-3"> | |||||
| <label for="Name" class="form-label">Name <span class="text-danger">*</span></label> | |||||
| <input type="text" class="form-control" id="Name" name="Name" required maxlength="255" value="<%= Server.HTMLEncode(TerritoryController.territory.Name) %>"> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="Description" class="form-label">Description</label> | |||||
| <textarea class="form-control" id="Description" name="Description" rows="2"><%= Server.HTMLEncode(TerritoryController.territory.Description & "") %></textarea> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="Coordinates" class="form-label">Coordinates (JSON)</label> | |||||
| <textarea class="form-control" id="Coordinates" name="Coordinates" rows="4" style="font-family: monospace; font-size: 12px;"><%= Server.HTMLEncode(TerritoryController.territory.Coordinates & "") %></textarea> | |||||
| <small class="text-muted">Click on the map to add points, or edit JSON directly.</small> | |||||
| </div> | |||||
| <div class="d-flex gap-2"> | |||||
| <button type="submit" class="btn btn-primary">Update Territory</button> | |||||
| <a href="/territories/<%= TerritoryController.territory.Id %>" class="btn btn-secondary">Cancel</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-8"> | |||||
| <label class="form-label">Territory Boundary</label> | |||||
| <div id="map" style="height: 400px; width: 100%; border-radius: 8px;"></div> | |||||
| <div class="mt-2"> | |||||
| <button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearPolygon()">Clear Polygon</button> | |||||
| <button type="button" class="btn btn-sm btn-outline-secondary" onclick="undoLastPoint()">Undo Last Point</button> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <script> | |||||
| var existingCoordinates = <%= TerritoryController.territory.Coordinates & "" %>; | |||||
| </script> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(GetAppSetting("GoogleMapsApiKey")) %>&callback=initMap" async defer></script> | |||||
| <script> | |||||
| var map, polygon, polygonCoords = []; | |||||
| function initMap() { | |||||
| var defaultCenter = { lat: -33.8688, lng: 151.2093 }; // Sydney default | |||||
| // Parse existing coordinates | |||||
| if (existingCoordinates && Array.isArray(existingCoordinates) && existingCoordinates.length > 0) { | |||||
| polygonCoords = existingCoordinates.map(function(coord) { | |||||
| return { lat: coord.lat || coord[0], lng: coord.lng || coord[1] }; | |||||
| }); | |||||
| } | |||||
| // Calculate center from existing coords or use default | |||||
| var center = defaultCenter; | |||||
| if (polygonCoords.length > 0) { | |||||
| var bounds = new google.maps.LatLngBounds(); | |||||
| polygonCoords.forEach(function(coord) { | |||||
| bounds.extend(coord); | |||||
| }); | |||||
| center = bounds.getCenter(); | |||||
| } | |||||
| map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: 14, | |||||
| center: center, | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| // Create editable polygon | |||||
| polygon = new google.maps.Polygon({ | |||||
| paths: polygonCoords, | |||||
| strokeColor: '#FF0000', | |||||
| strokeOpacity: 0.8, | |||||
| strokeWeight: 2, | |||||
| fillColor: '#FF0000', | |||||
| fillOpacity: 0.35, | |||||
| editable: true, | |||||
| draggable: true | |||||
| }); | |||||
| polygon.setMap(map); | |||||
| // Fit bounds if we have coordinates | |||||
| if (polygonCoords.length > 0) { | |||||
| var bounds = new google.maps.LatLngBounds(); | |||||
| polygonCoords.forEach(function(coord) { | |||||
| bounds.extend(coord); | |||||
| }); | |||||
| map.fitBounds(bounds); | |||||
| } | |||||
| // Click to add points | |||||
| map.addListener('click', function(event) { | |||||
| var path = polygon.getPath(); | |||||
| path.push(event.latLng); | |||||
| updateCoordinatesField(); | |||||
| }); | |||||
| // Update field when polygon is edited | |||||
| google.maps.event.addListener(polygon.getPath(), 'set_at', updateCoordinatesField); | |||||
| google.maps.event.addListener(polygon.getPath(), 'insert_at', updateCoordinatesField); | |||||
| google.maps.event.addListener(polygon.getPath(), 'remove_at', updateCoordinatesField); | |||||
| // Also update when dragged | |||||
| google.maps.event.addListener(polygon, 'dragend', updateCoordinatesField); | |||||
| } | |||||
| function updateCoordinatesField() { | |||||
| var path = polygon.getPath(); | |||||
| var coords = []; | |||||
| for (var i = 0; i < path.getLength(); i++) { | |||||
| var point = path.getAt(i); | |||||
| coords.push({ lat: point.lat(), lng: point.lng() }); | |||||
| } | |||||
| document.getElementById('Coordinates').value = JSON.stringify(coords, null, 2); | |||||
| } | |||||
| function clearPolygon() { | |||||
| polygon.getPath().clear(); | |||||
| updateCoordinatesField(); | |||||
| } | |||||
| function undoLastPoint() { | |||||
| var path = polygon.getPath(); | |||||
| if (path.getLength() > 0) { | |||||
| path.pop(); | |||||
| updateCoordinatesField(); | |||||
| } | |||||
| } | |||||
| // Allow manual JSON editing | |||||
| document.getElementById('Coordinates').addEventListener('change', function() { | |||||
| try { | |||||
| var coords = JSON.parse(this.value); | |||||
| if (Array.isArray(coords)) { | |||||
| var path = polygon.getPath(); | |||||
| path.clear(); | |||||
| coords.forEach(function(coord) { | |||||
| path.push(new google.maps.LatLng(coord.lat || coord[0], coord.lng || coord[1])); | |||||
| }); | |||||
| } | |||||
| } catch (e) { | |||||
| // Invalid JSON, ignore | |||||
| } | |||||
| }); | |||||
| </script> | |||||
| @@ -1,150 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <div class="d-flex justify-content-between align-items-center mb-4"> | |||||
| <h1>Territories</h1> | |||||
| <a href="/territories/new" class="btn btn-primary">New Territory</a> | |||||
| </div> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <!-- Search Form --> | |||||
| <div class="card mb-4"> | |||||
| <div class="card-body"> | |||||
| <form method="get" action="/territories" class="row g-3"> | |||||
| <div class="col-md-8"> | |||||
| <div class="input-group"> | |||||
| <input type="text" class="form-control" name="q" placeholder="Search by name or description..." value="<%= Server.HTMLEncode(TerritoryController.searchTerm) %>"> | |||||
| <button class="btn btn-outline-primary" type="submit">Search</button> | |||||
| <% If TerritoryController.searchTerm <> "" Then %> | |||||
| <a href="/territories" class="btn btn-outline-secondary">Clear</a> | |||||
| <% End If %> | |||||
| </div> | |||||
| </div> | |||||
| <div class="col-md-4 text-end"> | |||||
| <span class="text-muted"> | |||||
| <% If TerritoryController.searchTerm <> "" Then %> | |||||
| Found <%= TerritoryController.recordCount %> result(s) | |||||
| <% Else %> | |||||
| <%= TerritoryController.recordCount %> total territories | |||||
| <% End If %> | |||||
| </span> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| <div class="table-responsive"> | |||||
| <table class="table table-striped table-hover"> | |||||
| <thead class="table-dark"> | |||||
| <tr> | |||||
| <th>ID</th> | |||||
| <th>Name</th> | |||||
| <th>Description</th> | |||||
| <th>Households</th> | |||||
| <th>Actions</th> | |||||
| </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <% | |||||
| Dim iter, t | |||||
| Set iter = TerritoryController.territories.Iterator() | |||||
| If Not iter.HasNext() Then | |||||
| %> | |||||
| <tr> | |||||
| <td colspan="5" class="text-center text-muted py-4"> | |||||
| <% If TerritoryController.searchTerm <> "" Then %> | |||||
| No territories found matching "<%= Server.HTMLEncode(TerritoryController.searchTerm) %>" | |||||
| <% Else %> | |||||
| No territories found. <a href="/territories/new">Create one</a> | |||||
| <% End If %> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Else | |||||
| Do While iter.HasNext() | |||||
| Set t = iter.GetNext() | |||||
| %> | |||||
| <tr> | |||||
| <td><%= t.Id %></td> | |||||
| <td><%= Server.HTMLEncode(t.Name) %></td> | |||||
| <td><%= Server.HTMLEncode(Left(t.Description & "", 50)) %><% If Len(t.Description & "") > 50 Then Response.Write "..." End If %></td> | |||||
| <% | |||||
| Dim householdCount | |||||
| householdCount = 0 | |||||
| If IsObject(TerritoryController.territoryHouseholdCounts) Then | |||||
| If TerritoryController.territoryHouseholdCounts.Exists(CLng(t.Id)) Then | |||||
| householdCount = TerritoryController.territoryHouseholdCounts(CLng(t.Id)) | |||||
| End If | |||||
| End If | |||||
| %> | |||||
| <td><%= householdCount %></td> | |||||
| <td> | |||||
| <a href="/territories/<%= t.Id %>" class="btn btn-sm btn-info">View</a> | |||||
| <a href="/territories/<%= t.Id %>/edit" class="btn btn-sm btn-warning">Edit</a> | |||||
| <form method="post" action="/territories/<%= t.Id %>/delete" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this territory?');"> | |||||
| <button type="submit" class="btn btn-sm btn-danger">Delete</button> | |||||
| </form> | |||||
| </td> | |||||
| </tr> | |||||
| <% | |||||
| Loop | |||||
| End If | |||||
| %> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <!-- Pagination --> | |||||
| <% If TerritoryController.pageCount > 1 Then %> | |||||
| <% | |||||
| Dim baseUrl, pg, startPage, endPage | |||||
| baseUrl = "/territories?" | |||||
| If TerritoryController.searchTerm <> "" Then | |||||
| baseUrl = baseUrl & "q=" & Server.URLEncode(TerritoryController.searchTerm) & "&" | |||||
| End If | |||||
| ' Calculate pagination range (show 5 pages at a time) | |||||
| startPage = TerritoryController.currentPage - 2 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| endPage = startPage + 4 | |||||
| If endPage > TerritoryController.pageCount Then | |||||
| endPage = TerritoryController.pageCount | |||||
| startPage = endPage - 4 | |||||
| If startPage < 1 Then startPage = 1 | |||||
| End If | |||||
| %> | |||||
| <nav aria-label="Territory pagination"> | |||||
| <ul class="pagination justify-content-center"> | |||||
| <!-- First page --> | |||||
| <li class="page-item <% If TerritoryController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=1">« First</a> | |||||
| </li> | |||||
| <!-- Previous page --> | |||||
| <li class="page-item <% If TerritoryController.currentPage = 1 Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= TerritoryController.currentPage - 1 %>">‹ Prev</a> | |||||
| </li> | |||||
| <!-- Page numbers --> | |||||
| <% For pg = startPage To endPage %> | |||||
| <li class="page-item <% If pg = TerritoryController.currentPage Then Response.Write "active" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= pg %>"><%= pg %></a> | |||||
| </li> | |||||
| <% Next %> | |||||
| <!-- Next page --> | |||||
| <li class="page-item <% If TerritoryController.currentPage >= TerritoryController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= TerritoryController.currentPage + 1 %>">Next ›</a> | |||||
| </li> | |||||
| <!-- Last page --> | |||||
| <li class="page-item <% If TerritoryController.currentPage >= TerritoryController.pageCount Then Response.Write "disabled" End If %>"> | |||||
| <a class="page-link" href="<%= baseUrl %>page=<%= TerritoryController.pageCount %>">Last »</a> | |||||
| </li> | |||||
| </ul> | |||||
| </nav> | |||||
| <p class="text-center text-muted"> | |||||
| Page <%= TerritoryController.currentPage %> of <%= TerritoryController.pageCount %> | |||||
| </p> | |||||
| <% End If %> | |||||
| </div> | |||||
| @@ -1,813 +0,0 @@ | |||||
| <div class="container mt-4"> | |||||
| <nav aria-label="breadcrumb" class="d-print-none"> | |||||
| <ol class="breadcrumb"> | |||||
| <li class="breadcrumb-item"><a href="/territories">Territories</a></li> | |||||
| <li class="breadcrumb-item active" aria-current="page"><%= Server.HTMLEncode(TerritoryController.territory.Name) %></li> | |||||
| </ol> | |||||
| </nav> | |||||
| <% Flash().ShowSuccessIfPresent %> | |||||
| <% Flash().ShowErrorsIfPresent %> | |||||
| <div class="card"> | |||||
| <div class="card-header d-flex justify-content-between align-items-center"> | |||||
| <h2 class="mb-0"><%= Server.HTMLEncode(TerritoryController.territory.Name) %></h2> | |||||
| <div class="d-print-none"> | |||||
| <button type="button" class="btn btn-secondary" onclick="printTerritory()"><i class="bi bi-printer"></i> Print</button> | |||||
| <a href="/territories/<%= TerritoryController.territory.Id %>/edit" class="btn btn-warning">Edit</a> | |||||
| <form method="post" action="/territories/<%= TerritoryController.territory.Id %>/delete" style="display:inline;" onsubmit="return confirm('Are you sure you want to delete this territory?');"> | |||||
| <button type="submit" class="btn btn-danger">Delete</button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| <div class="card-body"> | |||||
| <div class="row"> | |||||
| <div class="col-md-4 territory-details"> | |||||
| <dl> | |||||
| <dt>ID</dt> | |||||
| <dd><%= TerritoryController.territory.Id %></dd> | |||||
| <dt>Name</dt> | |||||
| <dd><%= Server.HTMLEncode(TerritoryController.territory.Name) %></dd> | |||||
| <dt>Description</dt> | |||||
| <dd><%= Server.HTMLEncode(TerritoryController.territory.Description & "") %></dd> | |||||
| </dl> | |||||
| </div> | |||||
| <div class="col-md-8 map-container"> | |||||
| <div id="map" style="height: 400px; width: 100%; border-radius: 8px;"></div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mt-3 d-print-none"> | |||||
| <a href="/territories" class="btn btn-secondary">Back to List</a> | |||||
| </div> | |||||
| </div> | |||||
| <style> | |||||
| @media print { | |||||
| /* Hide navigation and non-essential elements */ | |||||
| .d-print-none, | |||||
| nav.navbar, | |||||
| footer, | |||||
| .breadcrumb { | |||||
| display: none !important; | |||||
| } | |||||
| /* Set page size and margins */ | |||||
| @page { | |||||
| size: letter portrait; | |||||
| margin: 0.5in; | |||||
| } | |||||
| /* Reset body for print */ | |||||
| body { | |||||
| margin: 0 !important; | |||||
| padding: 0 !important; | |||||
| } | |||||
| .container { | |||||
| max-width: 100% !important; | |||||
| padding: 0 !important; | |||||
| margin: 0 !important; | |||||
| } | |||||
| .card { | |||||
| border: none !important; | |||||
| box-shadow: none !important; | |||||
| } | |||||
| .card-header { | |||||
| background-color: transparent !important; | |||||
| border-bottom: 2px solid #000 !important; | |||||
| padding: 0 0 10px 0 !important; | |||||
| margin-bottom: 10px !important; | |||||
| } | |||||
| .card-body { | |||||
| padding: 0 !important; | |||||
| } | |||||
| /* Territory title styling for print */ | |||||
| h2 { | |||||
| font-size: 24pt !important; | |||||
| margin: 0 !important; | |||||
| text-align: center !important; | |||||
| } | |||||
| /* Hide details section in print, show only title and map */ | |||||
| .territory-details { | |||||
| display: none !important; | |||||
| } | |||||
| /* Map takes 3/4 of 8.5x11 page (11in - 1in margins = 10in, 3/4 = 7.5in) */ | |||||
| #map { | |||||
| height: 7.5in !important; | |||||
| width: 100% !important; | |||||
| page-break-inside: avoid; | |||||
| border: 1px solid #ccc !important; | |||||
| } | |||||
| .map-container { | |||||
| flex: 0 0 100% !important; | |||||
| max-width: 100% !important; | |||||
| } | |||||
| .row { | |||||
| display: block !important; | |||||
| } | |||||
| /* Ensure text is black for printing */ | |||||
| body, .card-body, dt, dd, h2 { | |||||
| color: #000 !important; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| <% | |||||
| Dim mapProvider, googleMapsKey, mapTilerKey, mapTilerStyle, mapTilerSdkJsUrl, mapTilerSdkCssUrl | |||||
| mapProvider = LCase(Trim(GetAppSetting("MapProvider") & "")) | |||||
| If mapProvider <> "maptiler" Then mapProvider = "google" | |||||
| googleMapsKey = Trim(GetAppSetting("GoogleMapsApiKey") & "") | |||||
| mapTilerKey = Trim(GetAppSetting("MapTilerApiKey") & "") | |||||
| mapTilerStyle = Trim(GetAppSetting("MapTilerStyle") & "") | |||||
| If mapTilerStyle = "" Or LCase(mapTilerStyle) = "nothing" Then mapTilerStyle = "streets-v2" | |||||
| mapTilerSdkJsUrl = Trim(GetAppSetting("MapTilerSdkJsUrl") & "") | |||||
| mapTilerSdkCssUrl = Trim(GetAppSetting("MapTilerSdkCssUrl") & "") | |||||
| If mapTilerSdkJsUrl = "" Or LCase(mapTilerSdkJsUrl) = "nothing" Then mapTilerSdkJsUrl = "https://cdn.maptiler.com/maptiler-sdk-js/v3.0.0/maptiler-sdk.umd.min.js" | |||||
| If mapTilerSdkCssUrl = "" Or LCase(mapTilerSdkCssUrl) = "nothing" Then mapTilerSdkCssUrl = "https://cdn.maptiler.com/maptiler-sdk-js/v3.0.0/maptiler-sdk.css" | |||||
| %> | |||||
| <% | |||||
| Dim coordsJson | |||||
| coordsJson = Trim(TerritoryController.territory.Coordinates & "") | |||||
| If coordsJson = "" Then coordsJson = "[]" | |||||
| ' Build street names array for JavaScript | |||||
| Dim streetIter, streetName, streetsJson, isFirst | |||||
| streetsJson = "[" | |||||
| isFirst = True | |||||
| If Not TerritoryController.territoryStreets Is Nothing Then | |||||
| Set streetIter = TerritoryController.territoryStreets.Iterator() | |||||
| Do While streetIter.HasNext() | |||||
| streetName = streetIter.GetNext() | |||||
| If Not isFirst Then streetsJson = streetsJson & "," | |||||
| streetsJson = streetsJson & """" & Replace(streetName, """", "\""") & """" | |||||
| isFirst = False | |||||
| Loop | |||||
| End If | |||||
| streetsJson = streetsJson & "]" | |||||
| ' Get border streets from the Description field | |||||
| Dim borderStreets | |||||
| borderStreets = Trim(TerritoryController.territory.Description & "") | |||||
| %> | |||||
| <script> | |||||
| var territoryCoordinates = <%= coordsJson %>; | |||||
| var territoryName = "<%= Replace(TerritoryController.territory.Name, """", "\""") %>"; | |||||
| var territoryBorderStreets = "<%= Replace(Replace(borderStreets, """", "\"""), vbCrLf, ", ") %>"; | |||||
| var territoryStreets = <%= streetsJson %>; | |||||
| var mapProvider = "<%= Replace(mapProvider, """", "\""") %>"; | |||||
| var googleMapsKey = "<%= Replace(googleMapsKey, """", "\""") %>"; | |||||
| var mapTilerKey = "<%= Replace(mapTilerKey, """", "\""") %>"; | |||||
| var mapTilerStyle = "<%= Replace(mapTilerStyle, """", "\""") %>"; | |||||
| </script> | |||||
| <% If mapProvider = "maptiler" Then %> | |||||
| <link rel="stylesheet" href="<%= Server.HTMLEncode(mapTilerSdkCssUrl) %>" /> | |||||
| <script src="<%= Server.HTMLEncode(mapTilerSdkJsUrl) %>"></script> | |||||
| <% Else %> | |||||
| <script src="https://maps.googleapis.com/maps/api/js?key=<%= Server.HTMLEncode(googleMapsKey) %>&callback=initMap" async defer></script> | |||||
| <% End If %> | |||||
| <script> | |||||
| var map, polygon; | |||||
| var mapIsReady = false; | |||||
| function setMapMessage(message) { | |||||
| var mapEl = document.getElementById('map'); | |||||
| if (mapEl) { | |||||
| mapEl.innerHTML = '<div class="alert alert-info">' + message + '</div>'; | |||||
| } | |||||
| } | |||||
| function getLatLng(coord) { | |||||
| var lat, lng; | |||||
| if (coord && typeof coord === 'object' && (coord.lat !== undefined || coord.lng !== undefined)) { | |||||
| lat = parseFloat(coord.lat); | |||||
| lng = parseFloat(coord.lng); | |||||
| } else if (Array.isArray(coord) && coord.length >= 2) { | |||||
| var a = parseFloat(coord[0]); | |||||
| var b = parseFloat(coord[1]); | |||||
| if (!isNaN(a) && !isNaN(b)) { | |||||
| if (Math.abs(a) > 90 && Math.abs(b) <= 90) { | |||||
| lng = a; | |||||
| lat = b; | |||||
| } else { | |||||
| lat = a; | |||||
| lng = b; | |||||
| } | |||||
| } | |||||
| } | |||||
| return { lat: lat, lng: lng }; | |||||
| } | |||||
| function initMap() { | |||||
| try { | |||||
| var coords = territoryCoordinates; | |||||
| if (!coords || !Array.isArray(coords) || coords.length === 0) { | |||||
| setMapMessage('No coordinates available for this territory.'); | |||||
| return; | |||||
| } | |||||
| if (mapProvider === 'maptiler') { | |||||
| if (!mapTilerKey || mapTilerKey === 'nothing') { | |||||
| setMapMessage('MapTiler API key is missing.'); | |||||
| return; | |||||
| } | |||||
| if (typeof maptilersdk === 'undefined') { | |||||
| setMapMessage('MapTiler SDK failed to load.'); | |||||
| return; | |||||
| } | |||||
| maptilersdk.config.apiKey = mapTilerKey; | |||||
| var polygonCoords = coords.map(function(coord) { | |||||
| var ll = getLatLng(coord); | |||||
| return [ll.lng, ll.lat]; | |||||
| }).filter(function(p) { | |||||
| return isFinite(p[0]) && isFinite(p[1]); | |||||
| }); | |||||
| if (polygonCoords.length === 0) { | |||||
| setMapMessage('No valid coordinates available for this territory.'); | |||||
| return; | |||||
| } | |||||
| if (polygonCoords.length > 0) { | |||||
| var firstCoord = polygonCoords[0]; | |||||
| var lastCoord = polygonCoords[polygonCoords.length - 1]; | |||||
| if (firstCoord[0] !== lastCoord[0] || firstCoord[1] !== lastCoord[1]) { | |||||
| polygonCoords.push([firstCoord[0], firstCoord[1]]); | |||||
| } | |||||
| } | |||||
| var bounds = new maptilersdk.LngLatBounds(); | |||||
| polygonCoords.forEach(function(coord) { | |||||
| bounds.extend(coord); | |||||
| }); | |||||
| var styleUrl = 'https://api.maptiler.com/maps/' + encodeURIComponent(mapTilerStyle) + '/style.json?key=' + encodeURIComponent(mapTilerKey); | |||||
| map = new maptilersdk.Map({ | |||||
| container: 'map', | |||||
| style: styleUrl, | |||||
| center: bounds.getCenter(), | |||||
| zoom: 14, | |||||
| preserveDrawingBuffer: true | |||||
| }); | |||||
| map.on('styleimagemissing', function(e) { | |||||
| if (map.hasImage(e.id)) return; | |||||
| var emptyCanvas = document.createElement('canvas'); | |||||
| emptyCanvas.width = 1; | |||||
| emptyCanvas.height = 1; | |||||
| map.addImage(e.id, emptyCanvas); | |||||
| }); | |||||
| map.on('load', function() { | |||||
| map.addSource('territory', { | |||||
| type: 'geojson', | |||||
| data: { | |||||
| type: 'Feature', | |||||
| geometry: { | |||||
| type: 'Polygon', | |||||
| coordinates: [polygonCoords] | |||||
| } | |||||
| } | |||||
| }); | |||||
| map.addLayer({ | |||||
| id: 'territory-fill', | |||||
| type: 'fill', | |||||
| source: 'territory', | |||||
| paint: { | |||||
| 'fill-color': '#ff0000', | |||||
| 'fill-opacity': 0.35 | |||||
| } | |||||
| }); | |||||
| map.addLayer({ | |||||
| id: 'territory-line', | |||||
| type: 'line', | |||||
| source: 'territory', | |||||
| paint: { | |||||
| 'line-color': '#ff0000', | |||||
| 'line-width': 2 | |||||
| } | |||||
| }); | |||||
| map.fitBounds(bounds, { padding: 20 }); | |||||
| }); | |||||
| map.on('idle', function() { | |||||
| mapIsReady = true; | |||||
| }); | |||||
| return; | |||||
| } | |||||
| // Convert coordinates to Google Maps format | |||||
| var polygonCoords = coords.map(function(coord) { | |||||
| var ll = getLatLng(coord); | |||||
| return { lat: ll.lat, lng: ll.lng }; | |||||
| }).filter(function(p) { | |||||
| return isFinite(p.lat) && isFinite(p.lng); | |||||
| }); | |||||
| if (polygonCoords.length === 0) { | |||||
| setMapMessage('No valid coordinates available for this territory.'); | |||||
| return; | |||||
| } | |||||
| // Calculate bounds to center the map | |||||
| var bounds = new google.maps.LatLngBounds(); | |||||
| polygonCoords.forEach(function(coord) { | |||||
| bounds.extend(coord); | |||||
| }); | |||||
| map = new google.maps.Map(document.getElementById('map'), { | |||||
| zoom: 14, | |||||
| center: bounds.getCenter(), | |||||
| mapTypeId: 'roadmap' | |||||
| }); | |||||
| // Draw the polygon | |||||
| polygon = new google.maps.Polygon({ | |||||
| paths: polygonCoords, | |||||
| strokeColor: '#FF0000', | |||||
| strokeOpacity: 0.8, | |||||
| strokeWeight: 2, | |||||
| fillColor: '#FF0000', | |||||
| fillOpacity: 0.35 | |||||
| }); | |||||
| polygon.setMap(map); | |||||
| map.fitBounds(bounds); | |||||
| } catch (e) { | |||||
| setMapMessage('Map error: ' + e.message); | |||||
| } | |||||
| } | |||||
| var printWindow = null; | |||||
| function printTerritory() { | |||||
| var coords = territoryCoordinates; | |||||
| if (!coords || !Array.isArray(coords) || coords.length === 0) { | |||||
| alert('No coordinates available to print.'); | |||||
| return; | |||||
| } | |||||
| if (mapProvider === 'maptiler') { | |||||
| function openPrintWindowShell() { | |||||
| // Build street lists HTML | |||||
| var borderStreetsHtml = ''; | |||||
| if (territoryBorderStreets && territoryBorderStreets.trim() !== '') { | |||||
| borderStreetsHtml = '<div class="streets-section"><strong>Border Streets:</strong> ' + territoryBorderStreets + '</div>'; | |||||
| } | |||||
| var insideStreetsHtml = ''; | |||||
| if (territoryStreets && territoryStreets.length > 0) { | |||||
| insideStreetsHtml = '<div class="streets-section"><strong>Streets Inside:</strong> ' + territoryStreets.join(', ') + '</div>'; | |||||
| } | |||||
| var printContent = '<!DOCTYPE html>' + | |||||
| '<html><head><title>Territory: ' + territoryName + '</title>' + | |||||
| '<style>' + | |||||
| '@page { size: letter portrait; margin: 0.25in; }' + | |||||
| 'body { margin: 0; padding: 0; font-family: Arial, sans-serif; }' + | |||||
| '.header { text-align: center; border-bottom: 2px solid #000; padding-bottom: 5px; margin-bottom: 10px; }' + | |||||
| '.header h1 { margin: 0; font-size: 20pt; }' + | |||||
| '.streets-section { font-size: 10pt; margin: 5px 0; text-align: left; }' + | |||||
| '.map-container { text-align: center; min-height: 200px; }' + | |||||
| '.map-container img { max-width: 100%; height: auto; max-height: 7in; }' + | |||||
| '.map-link { display: inline-block; margin-top: 10px; font-size: 12px; word-break: break-all; }' + | |||||
| '.print-btn { margin-top: 15px; padding: 10px 30px; font-size: 16px; cursor: pointer; }' + | |||||
| '.loading { color: #666; font-size: 14px; }' + | |||||
| '@media print { .no-print { display: none !important; } }' + | |||||
| '</style></head><body>' + | |||||
| '<div class="header"><h1>' + territoryName + '</h1></div>' + | |||||
| borderStreetsHtml + | |||||
| insideStreetsHtml + | |||||
| '<div class="map-container">' + | |||||
| '<div class="loading">Preparing map for print...</div>' + | |||||
| '<img id="print-map-img" src="" alt="Territory Map" style="display:none;">' + | |||||
| '<div class="map-link no-print"><a id="print-map-link" href="#" target="_blank" rel="noopener">Open image in new tab</a></div>' + | |||||
| '</div>' + | |||||
| '<div class="no-print" style="text-align: center; margin-top: 15px;">' + | |||||
| '<button class="print-btn" onclick="window.print();">Print</button> ' + | |||||
| '<button class="print-btn" onclick="window.close();">Close</button>' + | |||||
| '</div>' + | |||||
| '</body></html>'; | |||||
| printWindow = window.open('', '_blank', 'width=900,height=1000'); | |||||
| printWindow.document.write(printContent); | |||||
| printWindow.document.close(); | |||||
| } | |||||
| function setPrintWindowImage(src) { | |||||
| if (!printWindow || printWindow.closed) { | |||||
| alert('Print window was blocked. Please allow popups and try again.'); | |||||
| return; | |||||
| } | |||||
| var attempts = 0; | |||||
| function trySet() { | |||||
| attempts = attempts + 1; | |||||
| var doc = printWindow.document; | |||||
| if (!doc) { | |||||
| if (attempts < 20) return setTimeout(trySet, 100); | |||||
| alert('Print window did not finish loading.'); | |||||
| return; | |||||
| } | |||||
| var img = doc.getElementById('print-map-img'); | |||||
| var loading = doc.querySelector('.loading'); | |||||
| var link = doc.getElementById('print-map-link'); | |||||
| if (!img) { | |||||
| if (attempts < 20) return setTimeout(trySet, 100); | |||||
| alert('Print image element not found.'); | |||||
| return; | |||||
| } | |||||
| if (loading) loading.style.display = 'none'; | |||||
| img.style.display = 'block'; | |||||
| img.src = src; | |||||
| if (link) link.href = src; | |||||
| } | |||||
| trySet(); | |||||
| } | |||||
| openPrintWindowShell(); | |||||
| function loadImage(url) { | |||||
| return new Promise(function(resolve, reject) { | |||||
| var img = new Image(); | |||||
| img.crossOrigin = 'anonymous'; | |||||
| img.onload = function() { resolve(img); }; | |||||
| img.onerror = function() { reject(url); }; | |||||
| img.src = url; | |||||
| }); | |||||
| } | |||||
| function renderMapTilerImage() { | |||||
| return new Promise(function(resolve, reject) { | |||||
| if (!mapTilerKey || mapTilerKey === 'nothing') { | |||||
| reject('MapTiler API key is missing.'); | |||||
| return; | |||||
| } | |||||
| var tileCoordSize = 512; | |||||
| if (map && typeof map.getStyle === 'function') { | |||||
| var styleObj = map.getStyle(); | |||||
| if (styleObj && styleObj.sources) { | |||||
| for (var srcKey in styleObj.sources) { | |||||
| if (styleObj.sources.hasOwnProperty(srcKey)) { | |||||
| var src = styleObj.sources[srcKey]; | |||||
| if (src && src.tileSize) { | |||||
| tileCoordSize = src.tileSize; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| function buildTileUrl(z, x, y) { | |||||
| return 'https://api.maptiler.com/maps/' + | |||||
| encodeURIComponent(mapTilerStyle) + '/' + | |||||
| z + '/' + x + '/' + y + '.png?key=' + | |||||
| encodeURIComponent(mapTilerKey); | |||||
| } | |||||
| function buildImage(tileImageSize) { | |||||
| function lngToWorldX(lng, zoom) { | |||||
| var scale = tileCoordSize * Math.pow(2, zoom); | |||||
| return (lng + 180) / 360 * scale; | |||||
| } | |||||
| function latToWorldY(lat, zoom) { | |||||
| var rad = lat * Math.PI / 180; | |||||
| var scale = tileCoordSize * Math.pow(2, zoom); | |||||
| return (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2 * scale; | |||||
| } | |||||
| var minLat = Infinity, maxLat = -Infinity; | |||||
| var minLng = Infinity, maxLng = -Infinity; | |||||
| coords.forEach(function(coord) { | |||||
| var ll = getLatLng(coord); | |||||
| var lat = ll.lat; | |||||
| var lng = ll.lng; | |||||
| if (!isFinite(lat) || !isFinite(lng)) return; | |||||
| if (lat < minLat) minLat = lat; | |||||
| if (lat > maxLat) maxLat = lat; | |||||
| if (lng < minLng) minLng = lng; | |||||
| if (lng > maxLng) maxLng = lng; | |||||
| }); | |||||
| if (!isFinite(minLat) || !isFinite(minLng) || !isFinite(maxLat) || !isFinite(maxLng)) { | |||||
| reject('No valid coordinates available to print.'); | |||||
| return; | |||||
| } | |||||
| var centerLat = (minLat + maxLat) / 2; | |||||
| var centerLng = (minLng + maxLng) / 2; | |||||
| var isWide = (maxLng - minLng) > (maxLat - minLat) * 1.2; | |||||
| var baseWidth = isWide ? 800 : 640; | |||||
| var baseHeight = isWide ? 640 : 800; | |||||
| var outputScale = tileImageSize > 0 ? (tileImageSize / tileCoordSize) : 1; | |||||
| var width = baseWidth * outputScale; | |||||
| var height = baseHeight * outputScale; | |||||
| var x1 = lngToWorldX(minLng, 0); | |||||
| var x2 = lngToWorldX(maxLng, 0); | |||||
| var y1 = latToWorldY(maxLat, 0); | |||||
| var y2 = latToWorldY(minLat, 0); | |||||
| var spanX0 = Math.abs(x2 - x1); | |||||
| var spanY0 = Math.abs(y2 - y1); | |||||
| var paddingFactor = 1.02; | |||||
| var zoomX = spanX0 > 0 ? Math.log2(baseWidth / (spanX0 * paddingFactor)) : 20; | |||||
| var zoomY = spanY0 > 0 ? Math.log2(baseHeight / (spanY0 * paddingFactor)) : 20; | |||||
| var zoom = Math.min(zoomX, zoomY); | |||||
| if (!isFinite(zoom)) zoom = 1; | |||||
| if (zoom < 1) zoom = 1; | |||||
| if (zoom > 20) zoom = 20; | |||||
| var zInt = Math.floor(zoom); | |||||
| var zScale = Math.pow(2, zoom - zInt); | |||||
| var worldCenterXint = lngToWorldX(centerLng, zInt); | |||||
| var worldCenterYint = latToWorldY(centerLat, zInt); | |||||
| var worldWidth = width / (outputScale * zScale); | |||||
| var worldHeight = height / (outputScale * zScale); | |||||
| var topLeftXint = worldCenterXint - worldWidth / 2; | |||||
| var topLeftYint = worldCenterYint - worldHeight / 2; | |||||
| var topLeftX = topLeftXint * outputScale * zScale; | |||||
| var topLeftY = topLeftYint * outputScale * zScale; | |||||
| var n = Math.pow(2, zInt); | |||||
| var tileXStart = Math.floor(topLeftXint / tileCoordSize); | |||||
| var tileYStart = Math.floor(topLeftYint / tileCoordSize); | |||||
| var tileXEnd = Math.floor((topLeftXint + worldWidth) / tileCoordSize); | |||||
| var tileYEnd = Math.floor((topLeftYint + worldHeight) / tileCoordSize); | |||||
| var promises = []; | |||||
| var tiles = []; | |||||
| var tileX, tileY; | |||||
| for (tileY = tileYStart; tileY <= tileYEnd; tileY++) { | |||||
| if (tileY < 0 || tileY >= n) continue; | |||||
| for (tileX = tileXStart; tileX <= tileXEnd; tileX++) { | |||||
| var wrappedX = ((tileX % n) + n) % n; | |||||
| var url = buildTileUrl(zInt, wrappedX, tileY); | |||||
| (function(x, y, u) { | |||||
| var p = loadImage(u).then(function(img) { | |||||
| tiles.push({ x: x, y: y, img: img }); | |||||
| }); | |||||
| promises.push(p); | |||||
| })(tileX, tileY, url); | |||||
| } | |||||
| } | |||||
| Promise.all(promises).then(function() { | |||||
| var canvas = document.createElement('canvas'); | |||||
| canvas.width = width; | |||||
| canvas.height = height; | |||||
| var ctx = canvas.getContext('2d'); | |||||
| var tileDrawSize = tileCoordSize * zScale * outputScale; | |||||
| tiles.forEach(function(t) { | |||||
| var dx = (t.x * tileCoordSize * zScale * outputScale) - topLeftX; | |||||
| var dy = (t.y * tileCoordSize * zScale * outputScale) - topLeftY; | |||||
| ctx.drawImage(t.img, dx, dy, tileDrawSize, tileDrawSize); | |||||
| }); | |||||
| if (coords.length > 1) { | |||||
| ctx.save(); | |||||
| ctx.strokeStyle = 'rgba(255,0,0,0.8)'; | |||||
| ctx.fillStyle = 'rgba(255,0,0,0.25)'; | |||||
| ctx.lineWidth = 3; | |||||
| ctx.beginPath(); | |||||
| coords.forEach(function(coord, idx) { | |||||
| var ll = getLatLng(coord); | |||||
| var lat = ll.lat; | |||||
| var lng = ll.lng; | |||||
| if (!isFinite(lat) || !isFinite(lng)) return; | |||||
| var px = (lngToWorldX(lng, zInt) * zScale * outputScale) - topLeftX; | |||||
| var py = (latToWorldY(lat, zInt) * zScale * outputScale) - topLeftY; | |||||
| if (idx === 0) { | |||||
| ctx.moveTo(px, py); | |||||
| } else { | |||||
| ctx.lineTo(px, py); | |||||
| } | |||||
| }); | |||||
| ctx.closePath(); | |||||
| ctx.fill(); | |||||
| ctx.stroke(); | |||||
| ctx.restore(); | |||||
| } | |||||
| var dataUrl = ""; | |||||
| try { | |||||
| dataUrl = canvas.toDataURL('image/png'); | |||||
| } catch (e) { | |||||
| reject('Unable to generate print image.'); | |||||
| return; | |||||
| } | |||||
| if (!dataUrl || dataUrl.length < 1000) { | |||||
| reject('Generated print image is empty.'); | |||||
| return; | |||||
| } | |||||
| resolve(dataUrl); | |||||
| }).catch(function() { | |||||
| reject('Failed to load map tiles for printing.'); | |||||
| }); | |||||
| } | |||||
| var testUrl = buildTileUrl(0, 0, 0); | |||||
| loadImage(testUrl).then(function(img) { | |||||
| var tileImageSize = (img && img.width) ? img.width : 256; | |||||
| buildImage(tileImageSize); | |||||
| }).catch(function() { | |||||
| buildImage(256); | |||||
| }); | |||||
| }); | |||||
| } | |||||
| renderMapTilerImage().then(function(dataUrl) { | |||||
| setPrintWindowImage(dataUrl); | |||||
| }).catch(function(errMsg) { | |||||
| alert(errMsg); | |||||
| }); | |||||
| return; | |||||
| } | |||||
| // Build polygon path for Static Maps API | |||||
| var pathPoints = coords.map(function(coord) { | |||||
| var ll = getLatLng(coord); | |||||
| if (!isFinite(ll.lat) || !isFinite(ll.lng)) return null; | |||||
| return ll.lat + ',' + ll.lng; | |||||
| }).filter(function(p) { return p !== null; }); | |||||
| // Close the polygon | |||||
| pathPoints.push(pathPoints[0]); | |||||
| var pathStr = pathPoints.join('|'); | |||||
| // Calculate bounding box to determine optimal zoom | |||||
| var minLat = Infinity, maxLat = -Infinity; | |||||
| var minLng = Infinity, maxLng = -Infinity; | |||||
| coords.forEach(function(coord) { | |||||
| var ll = getLatLng(coord); | |||||
| var lat = ll.lat; | |||||
| var lng = ll.lng; | |||||
| if (!isFinite(lat) || !isFinite(lng)) return; | |||||
| if (lat < minLat) minLat = lat; | |||||
| if (lat > maxLat) maxLat = lat; | |||||
| if (lng < minLng) minLng = lng; | |||||
| if (lng > maxLng) maxLng = lng; | |||||
| }); | |||||
| var centerLat = (minLat + maxLat) / 2; | |||||
| var centerLng = (minLng + maxLng) / 2; | |||||
| // Calculate the span of the polygon | |||||
| var latSpan = maxLat - minLat; | |||||
| var lngSpan = maxLng - minLng; | |||||
| // Calculate optimal zoom level to maximize polygon size | |||||
| // Map dimensions: 640x800 (width x height for portrait letter) | |||||
| // Each zoom level doubles the scale | |||||
| var zoom = 20; // Start with max zoom | |||||
| // Degrees per pixel at zoom level 0 is approximately 360/256 for lng | |||||
| // For latitude it varies, but we use equirectangular approximation | |||||
| var mapWidth = 640; | |||||
| var mapHeight = 800; | |||||
| // Add 10% padding around the polygon | |||||
| var paddedLatSpan = latSpan * 1.1; | |||||
| var paddedLngSpan = lngSpan * 1.1; | |||||
| // Calculate zoom based on longitude span | |||||
| if (paddedLngSpan > 0) { | |||||
| var lngZoom = Math.floor(Math.log2(360 / paddedLngSpan * mapWidth / 256)); | |||||
| if (lngZoom < zoom) zoom = lngZoom; | |||||
| } | |||||
| // Calculate zoom based on latitude span | |||||
| if (paddedLatSpan > 0) { | |||||
| var latZoom = Math.floor(Math.log2(180 / paddedLatSpan * mapHeight / 256)); | |||||
| if (latZoom < zoom) zoom = latZoom; | |||||
| } | |||||
| // Clamp zoom between 1 and 20 | |||||
| if (zoom < 1) zoom = 1; | |||||
| if (zoom > 20) zoom = 20; | |||||
| var staticMapUrl; | |||||
| if (mapProvider === 'maptiler') { | |||||
| if (!mapTilerKey || mapTilerKey === 'nothing') { | |||||
| alert('MapTiler API key is missing.'); | |||||
| return; | |||||
| } | |||||
| var mapWidth = 640; | |||||
| var mapHeight = 800; | |||||
| var pathParam = 'stroke:#ff0000|width:3|fill:none|' + pathStr; | |||||
| staticMapUrl = 'https://api.maptiler.com/maps/' + | |||||
| encodeURIComponent(mapTilerStyle) + | |||||
| '/static/auto/' + | |||||
| mapWidth + 'x' + mapHeight + '@2x.png' + | |||||
| '?path=' + encodeURIComponent(pathParam) + | |||||
| '&key=' + encodeURIComponent(mapTilerKey); | |||||
| } else { | |||||
| // Build Google Static Maps URL with scale=2 for higher resolution (1280x1600 actual pixels) | |||||
| staticMapUrl = 'https://maps.googleapis.com/maps/api/staticmap?' + | |||||
| 'size=640x800' + | |||||
| '&scale=2' + | |||||
| '¢er=' + centerLat + ',' + centerLng + | |||||
| '&zoom=' + zoom + | |||||
| '&maptype=roadmap' + | |||||
| '&path=color:0xFF0000CC|weight:3|fillcolor:0xFF000044|' + pathStr + | |||||
| '&key=' + encodeURIComponent(googleMapsKey); | |||||
| } | |||||
| // Open print window with static map - landscape for wider polygons, portrait for taller | |||||
| var isWide = lngSpan > latSpan * 1.2; | |||||
| var pageSize = isWide ? 'letter landscape' : 'letter portrait'; | |||||
| var imgMaxHeight = isWide ? '5in' : '7in'; | |||||
| // Build street lists HTML | |||||
| var borderStreetsHtml = ''; | |||||
| if (territoryBorderStreets && territoryBorderStreets.trim() !== '') { | |||||
| borderStreetsHtml = '<div class="streets-section"><strong>Border Streets:</strong> ' + territoryBorderStreets + '</div>'; | |||||
| } | |||||
| var insideStreetsHtml = ''; | |||||
| if (territoryStreets && territoryStreets.length > 0) { | |||||
| insideStreetsHtml = '<div class="streets-section"><strong>Streets Inside:</strong> ' + territoryStreets.join(', ') + '</div>'; | |||||
| } | |||||
| var printContent = '<!DOCTYPE html>' + | |||||
| '<html><head><title>Territory: ' + territoryName + '</title>' + | |||||
| '<style>' + | |||||
| '@page { size: ' + pageSize + '; margin: 0.25in; }' + | |||||
| 'body { margin: 0; padding: 0; font-family: Arial, sans-serif; }' + | |||||
| '.header { text-align: center; border-bottom: 2px solid #000; padding-bottom: 5px; margin-bottom: 10px; }' + | |||||
| '.header h1 { margin: 0; font-size: 20pt; }' + | |||||
| '.streets-section { font-size: 10pt; margin: 5px 0; text-align: left; }' + | |||||
| '.map-container { text-align: center; }' + | |||||
| '.map-container img { max-width: 100%; height: auto; max-height: ' + imgMaxHeight + '; }' + | |||||
| '.print-btn { margin-top: 15px; padding: 10px 30px; font-size: 16px; cursor: pointer; }' + | |||||
| '@media print { .no-print { display: none !important; } }' + | |||||
| '</style></head><body>' + | |||||
| '<div class="header"><h1>' + territoryName + '</h1></div>' + | |||||
| borderStreetsHtml + | |||||
| insideStreetsHtml + | |||||
| '<div class="map-container">' + | |||||
| '<img src="' + staticMapUrl + '" alt="Territory Map" onerror="document.body.innerHTML=\'<p>Error loading map. Please try again.</p>\';">' + | |||||
| '</div>' + | |||||
| '<div class="no-print" style="text-align: center; margin-top: 15px;">' + | |||||
| '<button class="print-btn" onclick="window.print();">Print</button> ' + | |||||
| '<button class="print-btn" onclick="window.close();">Close</button>' + | |||||
| '</div>' + | |||||
| '</body></html>'; | |||||
| printWindow = window.open('', '_blank', 'width=900,height=1000'); | |||||
| printWindow.document.write(printContent); | |||||
| printWindow.document.close(); | |||||
| } | |||||
| if (mapProvider === 'maptiler') { | |||||
| if (document.readyState === 'loading') { | |||||
| document.addEventListener('DOMContentLoaded', initMap); | |||||
| } else { | |||||
| initMap(); | |||||
| } | |||||
| setTimeout(function() { | |||||
| if (!map) { | |||||
| setMapMessage('Map failed to load.'); | |||||
| } | |||||
| }, 2000); | |||||
| } else { | |||||
| setTimeout(function() { | |||||
| if (typeof google === 'undefined') { | |||||
| setMapMessage('Google Maps failed to load.'); | |||||
| } | |||||
| }, 2000); | |||||
| } | |||||
| </script> | |||||
| @@ -15,7 +15,7 @@ If IsObject(CurrentController) Then | |||||
| On Error GoTo 0 | On Error GoTo 0 | ||||
| End If | End If | ||||
| If Len(pageTitle) = 0 Then pageTitle = "Classic ASP Unified Framework" | |||||
| If Len(pageTitle) = 0 Then pageTitle = "Classic ASP Starter Template" | |||||
| %> | %> | ||||
| <html lang="en"> | <html lang="en"> | ||||
| <head> | <head> | ||||
| @@ -50,8 +50,8 @@ If Len(pageTitle) = 0 Then pageTitle = "Classic ASP Unified Framework" | |||||
| <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | ||||
| <div class="container-fluid"> | <div class="container-fluid"> | ||||
| <a class="navbar-brand rk-navbar-brand" href="/"> | <a class="navbar-brand rk-navbar-brand" href="/"> | ||||
| Classic ASP Unified | |||||
| <span class="text-secondary small">Framework</span> | |||||
| Classic ASP | |||||
| <span class="text-secondary small">Starter</span> | |||||
| </a> | </a> | ||||
| <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#rkMainNav" aria-controls="rkMainNav" aria-expanded="false" aria-label="Toggle navigation"> | <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#rkMainNav" aria-controls="rkMainNav" aria-expanded="false" aria-label="Toggle navigation"> | ||||
| @@ -61,13 +61,7 @@ If Len(pageTitle) = 0 Then pageTitle = "Classic ASP Unified Framework" | |||||
| <div class="collapse navbar-collapse" id="rkMainNav"> | <div class="collapse navbar-collapse" id="rkMainNav"> | ||||
| <ul class="navbar-nav me-auto mb-2 mb-lg-0"> | <ul class="navbar-nav me-auto mb-2 mb-lg-0"> | ||||
| <li class="nav-item"> | <li class="nav-item"> | ||||
| <a class="nav-link" href="/territories">Territories</a> | |||||
| </li> | |||||
| <li class="nav-item"> | |||||
| <a class="nav-link" href="/households">Households</a> | |||||
| </li> | |||||
| <li class="nav-item"> | |||||
| <a class="nav-link" href="/householder-names">Householder Names</a> | |||||
| <a class="nav-link" href="/home">Home</a> | |||||
| </li> | </li> | ||||
| </ul> | </ul> | ||||
| @@ -15,9 +15,6 @@ Class ControllerRegistry_Class | |||||
| ' Format: m_controllers.Add "controllername", True | ' Format: m_controllers.Add "controllername", True | ||||
| RegisterController "homecontroller" | RegisterController "homecontroller" | ||||
| RegisterController "errorcontroller" | RegisterController "errorcontroller" | ||||
| RegisterController "territorycontroller" | |||||
| RegisterController "householdcontroller" | |||||
| RegisterController "householdernamecontroller" | |||||
| End Sub | End Sub | ||||
| Private Sub Class_Terminate() | Private Sub Class_Terminate() | ||||
| @@ -1,40 +1,12 @@ | |||||
| <!--#include file="..\core\autoload_core.asp" --> | <!--#include file="..\core\autoload_core.asp" --> | ||||
| <% | <% | ||||
| ' Define application routes | |||||
| router.AddRoute "GET", "/home", "homeController", "Index" | |||||
| router.AddRoute "GET", "/", "TerritoryController", "Index" | |||||
| router.AddRoute "GET", "", "TerritoryController", "Index" | |||||
| ' Define starter application routes | |||||
| router.AddRoute "GET", "/home", "HomeController", "Index" | |||||
| router.AddRoute "GET", "/", "HomeController", "Index" | |||||
| router.AddRoute "GET", "", "HomeController", "Index" | |||||
| router.AddRoute "GET", "/404", "ErrorController", "NotFound" | router.AddRoute "GET", "/404", "ErrorController", "NotFound" | ||||
| ' Territory routes | |||||
| router.AddRoute "GET", "/territories", "TerritoryController", "Index" | |||||
| router.AddRoute "GET", "/territories/new", "TerritoryController", "Create" | |||||
| router.AddRoute "POST", "/territories", "TerritoryController", "Store" | |||||
| router.AddRoute "GET", "/territories/:id", "TerritoryController", "Show" | |||||
| router.AddRoute "GET", "/territories/:id/edit", "TerritoryController", "Edit" | |||||
| router.AddRoute "POST", "/territories/:id", "TerritoryController", "Update" | |||||
| router.AddRoute "POST", "/territories/:id/delete", "TerritoryController", "Delete" | |||||
| ' Household routes | |||||
| router.AddRoute "GET", "/households", "HouseholdController", "Index" | |||||
| router.AddRoute "GET", "/households/new", "HouseholdController", "Create" | |||||
| router.AddRoute "POST", "/households", "HouseholdController", "Store" | |||||
| router.AddRoute "GET", "/households/:id", "HouseholdController", "Show" | |||||
| router.AddRoute "GET", "/households/:id/edit", "HouseholdController", "Edit" | |||||
| router.AddRoute "POST", "/households/:id", "HouseholdController", "Update" | |||||
| router.AddRoute "POST", "/households/:id/delete", "HouseholdController", "Delete" | |||||
| router.AddRoute "POST", "/households/:id/mark-returned", "HouseholdController", "MarkReturned" | |||||
| ' Householder Name routes | |||||
| router.AddRoute "GET", "/householder-names", "HouseholderNameController", "Index" | |||||
| router.AddRoute "GET", "/householder-names/new", "HouseholderNameController", "Create" | |||||
| router.AddRoute "POST", "/householder-names", "HouseholderNameController", "Store" | |||||
| router.AddRoute "GET", "/householder-names/:id", "HouseholderNameController", "Show" | |||||
| router.AddRoute "GET", "/householder-names/:id/edit", "HouseholderNameController", "Edit" | |||||
| router.AddRoute "POST", "/householder-names/:id", "HouseholderNameController", "Update" | |||||
| router.AddRoute "POST", "/householder-names/:id/delete", "HouseholderNameController", "Delete" | |||||
| ' Dispatch the request (resolves route and executes controller action) | ' Dispatch the request (resolves route and executes controller action) | ||||
| MVC.DispatchRequest Request.ServerVariables("REQUEST_METHOD"), _ | MVC.DispatchRequest Request.ServerVariables("REQUEST_METHOD"), _ | ||||
| TrimQueryParams(Request.ServerVariables("HTTP_X_ORIGINAL_URL")) | TrimQueryParams(Request.ServerVariables("HTTP_X_ORIGINAL_URL")) | ||||
| @@ -0,0 +1,4 @@ | |||||
| @echo off | |||||
| setlocal | |||||
| set "ASPC_STARTER_ROOT=%~dp0" | |||||
| "C:\Program Files\IIS Express\iisexpress.exe" /config:"%~dp0applicationhost.config" | |||||
Powered by TurnKey Linux.