diff --git a/handlers/lots-get/edit.js b/handlers/lots-get/edit.js index 2fe0d0fa..f6285305 100644 --- a/handlers/lots-get/edit.js +++ b/handlers/lots-get/edit.js @@ -1,5 +1,5 @@ export const handler = (_request, response) => { - return response.render("licence-edit", { + return response.render("lot-edit", { headTitle: "Licence Update", isCreate: false }); diff --git a/handlers/lots-get/edit.ts b/handlers/lots-get/edit.ts index 5e06a462..6b1b6867 100644 --- a/handlers/lots-get/edit.ts +++ b/handlers/lots-get/edit.ts @@ -3,7 +3,7 @@ import type { RequestHandler } from "express"; export const handler: RequestHandler = (_request, response) => { - return response.render("licence-edit", { + return response.render("lot-edit", { headTitle: "Licence Update", isCreate: false }); diff --git a/handlers/lots-get/new.js b/handlers/lots-get/new.js index 589d5d76..b17bd41b 100644 --- a/handlers/lots-get/new.js +++ b/handlers/lots-get/new.js @@ -1,5 +1,5 @@ export const handler = (_request, response) => { - response.render("licence-edit", { + response.render("lot-edit", { headTitle: "Licence Create", isCreate: true }); diff --git a/handlers/lots-get/new.ts b/handlers/lots-get/new.ts index c2545b57..774578da 100644 --- a/handlers/lots-get/new.ts +++ b/handlers/lots-get/new.ts @@ -3,7 +3,7 @@ import type { RequestHandler } from "express"; export const handler: RequestHandler = (_request, response) => { - response.render("licence-edit", { + response.render("lot-edit", { headTitle: "Licence Create", isCreate: true }); diff --git a/handlers/lots-get/search.d.ts b/handlers/lots-get/search.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lots-get/search.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lots-get/search.js b/handlers/lots-get/search.js new file mode 100644 index 00000000..fd6b70ed --- /dev/null +++ b/handlers/lots-get/search.js @@ -0,0 +1,13 @@ +import * as configFunctions from "../../helpers/functions.config.js"; +import { getMaps } from "../../helpers/lotOccupancyDB/getMaps.js"; +import { getLotTypes } from "../../helpers/lotOccupancyDB/getLotTypes.js"; +export const handler = (_request, response) => { + const maps = getMaps(); + const lotTypes = getLotTypes(); + response.render("lot-search", { + headTitle: configFunctions.getProperty("aliases.lot") + " Search", + maps, + lotTypes + }); +}; +export default handler; diff --git a/handlers/lots-get/search.ts b/handlers/lots-get/search.ts new file mode 100644 index 00000000..f4875212 --- /dev/null +++ b/handlers/lots-get/search.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import * as configFunctions from "../../helpers/functions.config.js"; + +import { + getMaps +} from "../../helpers/lotOccupancyDB/getMaps.js"; + +import { getLotTypes } from "../../helpers/lotOccupancyDB/getLotTypes.js"; + + +export const handler: RequestHandler = (_request, response) => { + + const maps = getMaps(); + const lotTypes = getLotTypes(); + + response.render("lot-search", { + headTitle: configFunctions.getProperty("aliases.lot") + " Search", + maps, + lotTypes + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lots-get/view.js b/handlers/lots-get/view.js index 5841dddd..f5b207e6 100644 --- a/handlers/lots-get/view.js +++ b/handlers/lots-get/view.js @@ -1,7 +1,7 @@ import * as configFunctions from "../../helpers/functions.config.js"; const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix"); export const handler = (_request, response) => { - return response.render("licence-view", { + return response.render("lot-view", { headTitle: "Licence View" }); }; diff --git a/handlers/lots-get/view.ts b/handlers/lots-get/view.ts index 7e32e253..21ab4d30 100644 --- a/handlers/lots-get/view.ts +++ b/handlers/lots-get/view.ts @@ -10,7 +10,7 @@ export const handler: RequestHandler = (_request, response) => { //const licenceID = Number(request.params.licenceID); - return response.render("licence-view", { + return response.render("lot-view", { headTitle: "Licence View" }); }; diff --git a/helpers/databaseInitializer.js b/helpers/databaseInitializer.js index b1a4e601..4020bed2 100644 --- a/helpers/databaseInitializer.js +++ b/helpers/databaseInitializer.js @@ -38,16 +38,14 @@ export const initLotOccupancyDB = () => { ")").run(); lotOccupancyDB.prepare("create index if not exists idx_lottypefields_ordernumber" + " on LotTypeFields (lotTypeId, orderNumber, lotTypeField)").run(); - lotOccupancyDB.prepare("create table if not exists LotTypeStatuses (" + - "lotTypeStatusId integer not null primary key autoincrement," + - " lotTypeId integer not null," + - " lotTypeStatus varchar(100) not null," + + lotOccupancyDB.prepare("create table if not exists LotStatuses (" + + "lotStatusId integer not null primary key autoincrement," + + " lotStatus varchar(100) not null," + " orderNumber smallint not null default 0," + - recordColumns + "," + - " foreign key (lotTypeId) references LotTypes (lotTypeId)" + + recordColumns + ")").run(); - lotOccupancyDB.prepare("create index if not exists idx_lottypestatuses_ordernumber" + - " on LotTypeStatuses (lotTypeId, orderNumber, lotTypeStatus)").run(); + lotOccupancyDB.prepare("create index if not exists idx_lotstatuses_ordernumber" + + " on LotStatuses (orderNumber, lotStatus)").run(); lotOccupancyDB.prepare("create table if not exists Maps (" + "mapId integer not null primary key autoincrement," + " mapName varchar(200) not null," + @@ -71,11 +69,11 @@ export const initLotOccupancyDB = () => { " mapKey varchar(100)," + " lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," + " lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," + - " lotTypeStatusId integer," + + " lotStatusId integer," + recordColumns + "," + " foreign key (lotTypeId) references LotTypes (lotTypeId)," + " foreign key (mapId) references Maps (mapId)," + - " foreign key (lotTypeStatusId) references LotTypeStatuses (lotTypeStatusId)" + + " foreign key (lotStatusId) references LotStatuses (lotStatusId)" + ")").run(); lotOccupancyDB.prepare("create table if not exists LotFields (" + "lotId integer not null," + @@ -131,17 +129,35 @@ export const initLotOccupancyDB = () => { ")").run(); lotOccupancyDB.prepare("create index if not exists idx_occupancytypefields_ordernumber" + " on OccupancyTypeFields (occupancyTypeId, orderNumber, occupancyTypeField)").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupantTypes (" + + "lotOccupantTypeId integer not null primary key autoincrement," + + " lotOccupantType varchar(100) not null," + + " orderNumber smallint not null default 0," + + recordColumns + + ")").run(); + lotOccupancyDB.prepare("create index if not exists idx_lotoccupanttypes_ordernumber" + + " on LotOccupantTypes (orderNumber, lotOccupantType)").run(); lotOccupancyDB.prepare("create table if not exists LotOccupancies (" + "lotOccupancyId integer not null primary key autoincrement," + " occupancyTypeId integer not null," + " lotId integer not null," + - " occupantId integer," + " occupancyStartDate integer not null check (occupancyStartDate > 0)," + " occupancyEndDate integer check (occupancyEndDate > 0)," + recordColumns + "," + " foreign key (lotId) references Lots (lotId)," + - " foreign key (occupantId) references Occupants (occupantId)" + + " foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId)" + ")").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupancyOccupants (" + + "lotOccupancyId integer not null," + + " lotOccupantIndex integer not null," + + " occupantId integer not null," + + " lotOccupantTypeId integer not null," + + recordColumns + "," + + " primary key (lotOccupancyId, lotOccupantIndex)," + + " foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId)," + + " foreign key (occupantId) references Occupants (occupantId)," + + " foreign key (lotOccupantTypeId) references LotOccupantTypes (lotOccupantTypeId)" + + ") without rowid").run(); lotOccupancyDB.prepare("create table if not exists LotOccupancyFields (" + "lotOccupancyId integer not null," + " occupancyTypeFieldId integer not null," + diff --git a/helpers/databaseInitializer.ts b/helpers/databaseInitializer.ts index daea6860..109ad6d8 100644 --- a/helpers/databaseInitializer.ts +++ b/helpers/databaseInitializer.ts @@ -54,17 +54,15 @@ export const initLotOccupancyDB = (): boolean => { lotOccupancyDB.prepare("create index if not exists idx_lottypefields_ordernumber" + " on LotTypeFields (lotTypeId, orderNumber, lotTypeField)").run(); - lotOccupancyDB.prepare("create table if not exists LotTypeStatuses (" + - "lotTypeStatusId integer not null primary key autoincrement," + - " lotTypeId integer not null," + - " lotTypeStatus varchar(100) not null," + + lotOccupancyDB.prepare("create table if not exists LotStatuses (" + + "lotStatusId integer not null primary key autoincrement," + + " lotStatus varchar(100) not null," + " orderNumber smallint not null default 0," + - recordColumns + "," + - " foreign key (lotTypeId) references LotTypes (lotTypeId)" + + recordColumns + ")").run(); - lotOccupancyDB.prepare("create index if not exists idx_lottypestatuses_ordernumber" + - " on LotTypeStatuses (lotTypeId, orderNumber, lotTypeStatus)").run(); + lotOccupancyDB.prepare("create index if not exists idx_lotstatuses_ordernumber" + + " on LotStatuses (orderNumber, lotStatus)").run(); // Maps and Lots @@ -98,12 +96,12 @@ export const initLotOccupancyDB = (): boolean => { " lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," + " lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," + - " lotTypeStatusId integer," + + " lotStatusId integer," + recordColumns + "," + " foreign key (lotTypeId) references LotTypes (lotTypeId)," + " foreign key (mapId) references Maps (mapId)," + - " foreign key (lotTypeStatusId) references LotTypeStatuses (lotTypeStatusId)" + + " foreign key (lotStatusId) references LotStatuses (lotStatusId)" + ")").run(); lotOccupancyDB.prepare("create table if not exists LotFields (" + @@ -166,22 +164,43 @@ export const initLotOccupancyDB = (): boolean => { recordColumns + "," + " foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId)" + ")").run(); - + lotOccupancyDB.prepare("create index if not exists idx_occupancytypefields_ordernumber" + " on OccupancyTypeFields (occupancyTypeId, orderNumber, occupancyTypeField)").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupantTypes (" + + "lotOccupantTypeId integer not null primary key autoincrement," + + " lotOccupantType varchar(100) not null," + + " orderNumber smallint not null default 0," + + recordColumns + + ")").run(); + + lotOccupancyDB.prepare("create index if not exists idx_lotoccupanttypes_ordernumber" + + " on LotOccupantTypes (orderNumber, lotOccupantType)").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupancies (" + "lotOccupancyId integer not null primary key autoincrement," + " occupancyTypeId integer not null," + " lotId integer not null," + - " occupantId integer," + " occupancyStartDate integer not null check (occupancyStartDate > 0)," + " occupancyEndDate integer check (occupancyEndDate > 0)," + recordColumns + "," + " foreign key (lotId) references Lots (lotId)," + - " foreign key (occupantId) references Occupants (occupantId)" + + " foreign key (occupancyTypeId) references OccupancyTypes (occupancyTypeId)" + ")").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupancyOccupants (" + + "lotOccupancyId integer not null," + + " lotOccupantIndex integer not null," + + " occupantId integer not null," + + " lotOccupantTypeId integer not null," + + recordColumns + "," + + " primary key (lotOccupancyId, lotOccupantIndex)," + + " foreign key (lotOccupancyId) references LotOccupancies (lotOccupancyId)," + + " foreign key (occupantId) references Occupants (occupantId)," + + " foreign key (lotOccupantTypeId) references LotOccupantTypes (lotOccupantTypeId)" + + ") without rowid").run(); + lotOccupancyDB.prepare("create table if not exists LotOccupancyFields (" + "lotOccupancyId integer not null," + " occupancyTypeFieldId integer not null," + diff --git a/helpers/functions.cache.d.ts b/helpers/functions.cache.d.ts new file mode 100644 index 00000000..5a879195 --- /dev/null +++ b/helpers/functions.cache.d.ts @@ -0,0 +1,13 @@ +import type * as recordTypes from "../types/recordTypes"; +export declare function getLotOccupantTypes(): recordTypes.LotOccupantType[]; +export declare function getLotOccupantTypeById(lotOccupantTypeId: number): recordTypes.LotOccupantType; +export declare function getLotOccupantTypesByLotOccupantType(lotOccupantType: string): recordTypes.LotOccupantType; +export declare function getLotStatuses(): recordTypes.LotStatus[]; +export declare function getLotStatusById(lotStatusId: number): recordTypes.LotStatus; +export declare function getLotStatusByLotStatus(lotStatus: string): recordTypes.LotStatus; +export declare function getLotTypes(): recordTypes.LotType[]; +export declare function getLotTypeById(lotTypeId: number): recordTypes.LotType; +export declare function getLotTypesByLotType(lotType: string): recordTypes.LotType; +export declare function getOccupancyTypes(): recordTypes.OccupancyType[]; +export declare function getOccupancyTypeById(occupancyTypeId: number): recordTypes.OccupancyType; +export declare function getOccupancyTypeByOccupancyType(occupancyType: string): recordTypes.OccupancyType; diff --git a/helpers/functions.cache.js b/helpers/functions.cache.js new file mode 100644 index 00000000..88287631 --- /dev/null +++ b/helpers/functions.cache.js @@ -0,0 +1,84 @@ +import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from "./lotOccupancyDB/getLotOccupantTypes.js"; +import { getLotStatuses as getLotStatusesFromDatabase } from "./lotOccupancyDB/getLotStatuses.js"; +import { getLotTypes as getLotTypesFromDatabase } from "./lotOccupancyDB/getLotTypes.js"; +import { getOccupancyTypes as getOccupancyTypesFromDatabase } from "./lotOccupancyDB/getOccupancyTypes.js"; +let lotOccupantTypes; +export function getLotOccupantTypes() { + if (!lotOccupantTypes) { + lotOccupantTypes = getLotOccupantTypesFromDatabase(); + } + return lotOccupantTypes; +} +export function getLotOccupantTypeById(lotOccupantTypeId) { + const cachedLotOccupantTypes = getLotOccupantTypes(); + return cachedLotOccupantTypes.find((currentLotOccupantType) => { + return currentLotOccupantType.lotOccupantTypeId === lotOccupantTypeId; + }); +} +export function getLotOccupantTypesByLotOccupantType(lotOccupantType) { + const cachedLotOccupantTypes = getLotOccupantTypes(); + const lotOccupantTypeLowerCase = lotOccupantType.toLowerCase(); + return cachedLotOccupantTypes.find((currentLotOccupantType) => { + return currentLotOccupantType.lotOccupantType.toLowerCase() === lotOccupantTypeLowerCase; + }); +} +let lotStatuses; +export function getLotStatuses() { + if (!lotStatuses) { + lotStatuses = getLotStatusesFromDatabase(); + } + return lotStatuses; +} +export function getLotStatusById(lotStatusId) { + const cachedLotStatuses = getLotStatuses(); + return cachedLotStatuses.find((currentLotStatus) => { + return currentLotStatus.lotStatusId === lotStatusId; + }); +} +export function getLotStatusByLotStatus(lotStatus) { + const cachedLotStatuses = getLotStatuses(); + const lotStatusLowerCase = lotStatus.toLowerCase(); + return cachedLotStatuses.find((currentLotStatus) => { + return currentLotStatus.lotStatus.toLowerCase() === lotStatusLowerCase; + }); +} +let lotTypes; +export function getLotTypes() { + if (!lotTypes) { + lotTypes = getLotTypesFromDatabase(); + } + return lotTypes; +} +export function getLotTypeById(lotTypeId) { + const cachedLotTypes = getLotTypes(); + return cachedLotTypes.find((currentLotType) => { + return currentLotType.lotTypeId === lotTypeId; + }); +} +export function getLotTypesByLotType(lotType) { + const cachedLotTypes = getLotTypes(); + const lotTypeLowerCase = lotType.toLowerCase(); + return cachedLotTypes.find((currentLotType) => { + return currentLotType.lotType.toLowerCase() === lotTypeLowerCase; + }); +} +let occupancyTypes; +export function getOccupancyTypes() { + if (!occupancyTypes) { + occupancyTypes = getOccupancyTypesFromDatabase(); + } + return occupancyTypes; +} +export function getOccupancyTypeById(occupancyTypeId) { + const cachedOccupancyTypes = getOccupancyTypes(); + return cachedOccupancyTypes.find((currentOccupancyType) => { + return currentOccupancyType.occupancyTypeId === occupancyTypeId; + }); +} +export function getOccupancyTypeByOccupancyType(occupancyType) { + const cachedOccupancyTypes = getOccupancyTypes(); + const occupancyTypeLowerCase = occupancyType.toLowerCase(); + return cachedOccupancyTypes.find((currentOccupancyType) => { + return currentOccupancyType.occupancyType.toLowerCase() === occupancyTypeLowerCase; + }); +} diff --git a/helpers/functions.cache.ts b/helpers/functions.cache.ts new file mode 100644 index 00000000..087accfa --- /dev/null +++ b/helpers/functions.cache.ts @@ -0,0 +1,147 @@ +import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from "./lotOccupancyDB/getLotOccupantTypes.js"; +import { getLotStatuses as getLotStatusesFromDatabase } from "./lotOccupancyDB/getLotStatuses.js"; +import { getLotTypes as getLotTypesFromDatabase } from "./lotOccupancyDB/getLotTypes.js"; +import { getOccupancyTypes as getOccupancyTypesFromDatabase } from "./lotOccupancyDB/getOccupancyTypes.js"; + +import type * as recordTypes from "../types/recordTypes"; + +/* + * Lot Occupant Types + */ + +let lotOccupantTypes: recordTypes.LotOccupantType[]; + +export function getLotOccupantTypes() { + + if (!lotOccupantTypes) { + lotOccupantTypes = getLotOccupantTypesFromDatabase(); + } + + return lotOccupantTypes; +} + +export function getLotOccupantTypeById(lotOccupantTypeId: number) { + + const cachedLotOccupantTypes = getLotOccupantTypes(); + + return cachedLotOccupantTypes.find((currentLotOccupantType) => { + return currentLotOccupantType.lotOccupantTypeId === lotOccupantTypeId; + }); +} + +export function getLotOccupantTypesByLotOccupantType(lotOccupantType: string) { + + const cachedLotOccupantTypes = getLotOccupantTypes(); + + const lotOccupantTypeLowerCase = lotOccupantType.toLowerCase(); + + return cachedLotOccupantTypes.find((currentLotOccupantType) => { + return currentLotOccupantType.lotOccupantType.toLowerCase() === lotOccupantTypeLowerCase; + }); +} + + +/* +* Lot Statuses +*/ + +let lotStatuses: recordTypes.LotStatus[]; + +export function getLotStatuses () { + + if (!lotStatuses) { + lotStatuses = getLotStatusesFromDatabase(); + } + + return lotStatuses; +} + +export function getLotStatusById (lotStatusId: number) { + + const cachedLotStatuses = getLotStatuses(); + + return cachedLotStatuses.find((currentLotStatus) => { + return currentLotStatus.lotStatusId === lotStatusId; + }); +} + +export function getLotStatusByLotStatus (lotStatus: string) { + + const cachedLotStatuses = getLotStatuses(); + + const lotStatusLowerCase = lotStatus.toLowerCase(); + + return cachedLotStatuses.find((currentLotStatus) => { + return currentLotStatus.lotStatus.toLowerCase() === lotStatusLowerCase; + }); +} + +/* + * Lot Types + */ + +let lotTypes: recordTypes.LotType[]; + +export function getLotTypes() { + + if (!lotTypes) { + lotTypes = getLotTypesFromDatabase(); + } + + return lotTypes; +} + +export function getLotTypeById(lotTypeId: number) { + + const cachedLotTypes = getLotTypes(); + + return cachedLotTypes.find((currentLotType) => { + return currentLotType.lotTypeId === lotTypeId; + }); +} + +export function getLotTypesByLotType(lotType: string) { + + const cachedLotTypes = getLotTypes(); + + const lotTypeLowerCase = lotType.toLowerCase(); + + return cachedLotTypes.find((currentLotType) => { + return currentLotType.lotType.toLowerCase() === lotTypeLowerCase; + }); +} + +/* +* Occupancy Types +*/ + +let occupancyTypes: recordTypes.OccupancyType[]; + +export function getOccupancyTypes () { + + if (!occupancyTypes) { + occupancyTypes = getOccupancyTypesFromDatabase(); + } + + return occupancyTypes; +} + +export function getOccupancyTypeById (occupancyTypeId: number) { + + const cachedOccupancyTypes = getOccupancyTypes(); + + return cachedOccupancyTypes.find((currentOccupancyType) => { + return currentOccupancyType.occupancyTypeId === occupancyTypeId; + }); +} + +export function getOccupancyTypeByOccupancyType (occupancyType: string) { + + const cachedOccupancyTypes = getOccupancyTypes(); + + const occupancyTypeLowerCase = occupancyType.toLowerCase(); + + return cachedOccupancyTypes.find((currentOccupancyType) => { + return currentOccupancyType.occupancyType.toLowerCase() === occupancyTypeLowerCase; + }); +} \ No newline at end of file diff --git a/helpers/lotOccupancyDB/addLot.d.ts b/helpers/lotOccupancyDB/addLot.d.ts index ddd1fff1..0c9d4cc6 100644 --- a/helpers/lotOccupancyDB/addLot.d.ts +++ b/helpers/lotOccupancyDB/addLot.d.ts @@ -2,7 +2,7 @@ import type * as recordTypes from "../../types/recordTypes"; interface AddLotForm { lotName: string; lotTypeId: string | number; - lotTypeStatusId: string | number; + lotStatusId: string | number; mapId: string | number; mapKey: string; lotLatitude: string; diff --git a/helpers/lotOccupancyDB/addLot.js b/helpers/lotOccupancyDB/addLot.js index a0659e1a..608452c1 100644 --- a/helpers/lotOccupancyDB/addLot.js +++ b/helpers/lotOccupancyDB/addLot.js @@ -5,13 +5,13 @@ export const addLot = (lotForm, requestSession) => { const rightNowMillis = Date.now(); const result = database .prepare("insert into Lots (" + - "lotName, lotTypeId, lotTypeStatusId," + + "lotName, lotTypeId, lotStatusId," + " mapId, mapKey," + " lotLatitude, lotLongitude," + " recordCreate_userName, recordCreate_timeMillis," + " recordUpdate_userName, recordUpdate_timeMillis)" + " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") - .run(lotForm.lotName, lotForm.lotTypeId, (lotForm.lotTypeStatusId === "" ? undefined : lotForm.lotTypeStatusId), (lotForm.mapId === "" ? undefined : lotForm.mapId), lotForm.mapKey, (lotForm.lotLatitude === "" ? undefined : lotForm.lotLatitude), (lotForm.lotLongitude === "" ? undefined : lotForm.lotLongitude), requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); + .run(lotForm.lotName, lotForm.lotTypeId, (lotForm.lotStatusId === "" ? undefined : lotForm.lotStatusId), (lotForm.mapId === "" ? undefined : lotForm.mapId), lotForm.mapKey, (lotForm.lotLatitude === "" ? undefined : lotForm.lotLatitude), (lotForm.lotLongitude === "" ? undefined : lotForm.lotLongitude), requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); database.close(); return result.lastInsertRowid; }; diff --git a/helpers/lotOccupancyDB/addLot.ts b/helpers/lotOccupancyDB/addLot.ts index 009681a5..452f4d05 100644 --- a/helpers/lotOccupancyDB/addLot.ts +++ b/helpers/lotOccupancyDB/addLot.ts @@ -7,7 +7,7 @@ import type * as recordTypes from "../../types/recordTypes"; interface AddLotForm { lotName: string; lotTypeId: string | number; - lotTypeStatusId: string | number; + lotStatusId: string | number; mapId: string | number; mapKey: string; @@ -26,7 +26,7 @@ export const addLot = const result = database .prepare("insert into Lots (" + - "lotName, lotTypeId, lotTypeStatusId," + + "lotName, lotTypeId, lotStatusId," + " mapId, mapKey," + " lotLatitude, lotLongitude," + " recordCreate_userName, recordCreate_timeMillis," + @@ -34,7 +34,7 @@ export const addLot = " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") .run(lotForm.lotName, lotForm.lotTypeId, - (lotForm.lotTypeStatusId === "" ? undefined : lotForm.lotTypeStatusId), + (lotForm.lotStatusId === "" ? undefined : lotForm.lotStatusId), (lotForm.mapId === "" ? undefined : lotForm.mapId), lotForm.mapKey, (lotForm.lotLatitude === "" ? undefined : lotForm.lotLatitude), diff --git a/helpers/lotOccupancyDB/addLotOccupancy.d.ts b/helpers/lotOccupancyDB/addLotOccupancy.d.ts new file mode 100644 index 00000000..8a5625ca --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancy.d.ts @@ -0,0 +1,9 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface AddLotOccupancyForm { + occupancyTypeId: string | number; + lotId: string | number; + occupancyStartDateString: string; + occupancyEndDateString: string; +} +export declare const addLotOccupancy: (lotOccupancyForm: AddLotOccupancyForm, requestSession: recordTypes.PartialSession) => number; +export default addLotOccupancy; diff --git a/helpers/lotOccupancyDB/addLotOccupancy.js b/helpers/lotOccupancyDB/addLotOccupancy.js new file mode 100644 index 00000000..b1f801ef --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancy.js @@ -0,0 +1,20 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; +export const addLotOccupancy = (lotOccupancyForm, requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const result = database + .prepare("insert into LotOccupancies (" + + "occupancyTypeId, lotId," + + " occupancyStartDate, occupancyEndDate," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyForm.occupancyTypeId, lotOccupancyForm.lotId, dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyStartDateString), (lotOccupancyForm.occupancyEndDateString === "" + ? undefined + : dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); + database.close(); + return result.lastInsertRowid; +}; +export default addLotOccupancy; diff --git a/helpers/lotOccupancyDB/addLotOccupancy.ts b/helpers/lotOccupancyDB/addLotOccupancy.ts new file mode 100644 index 00000000..f3b1fdf7 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancy.ts @@ -0,0 +1,49 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface AddLotOccupancyForm { + occupancyTypeId: string | number; + lotId: string | number; + + occupancyStartDateString: string; + occupancyEndDateString: string; +} + + +export const addLotOccupancy = + (lotOccupancyForm: AddLotOccupancyForm, requestSession: recordTypes.PartialSession): number => { + + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("insert into LotOccupancies (" + + "occupancyTypeId, lotId," + + " occupancyStartDate, occupancyEndDate," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyForm.occupancyTypeId, + lotOccupancyForm.lotId, + dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyStartDateString), + (lotOccupancyForm.occupancyEndDateString === "" + ? undefined + : dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), + requestSession.user.userName, + rightNowMillis, + requestSession.user.userName, + rightNowMillis); + + database.close(); + + return result.lastInsertRowid as number; + }; + + +export default addLotOccupancy; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/addLotOccupancyOccupant.d.ts b/helpers/lotOccupancyDB/addLotOccupancyOccupant.d.ts new file mode 100644 index 00000000..60591229 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyOccupant.d.ts @@ -0,0 +1,8 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface AddLotOccupancyOccupantForm { + lotOccupancyId: string | number; + occupantId: string | number; + lotOccupantTypeId: string | number; +} +export declare const addLotOccupancyOccupant: (lotOccupancyOccupantForm: AddLotOccupancyOccupantForm, requestSession: recordTypes.PartialSession) => number; +export default addLotOccupancyOccupant; diff --git a/helpers/lotOccupancyDB/addLotOccupancyOccupant.js b/helpers/lotOccupancyDB/addLotOccupancyOccupant.js new file mode 100644 index 00000000..aeff5d95 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyOccupant.js @@ -0,0 +1,28 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const addLotOccupancyOccupant = (lotOccupancyOccupantForm, requestSession) => { + const database = sqlite(databasePath); + let lotOccupantIndex = 0; + const maxIndexResult = database.prepare("select lotOccupantIndex" + + " from LotOccupancyOccupants" + + " where lotOccupancyId = ?" + + " order by lotOccupantIndex" + + " limit 1") + .get(lotOccupancyOccupantForm.lotOccupancyId); + if (maxIndexResult) { + lotOccupantIndex = maxIndexResult.lotOccupantIndex + 1; + } + const rightNowMillis = Date.now(); + database + .prepare("insert into LotOccupancyOccupants (" + + "lotOccupancyId, lotOccupantIndex," + + " occupantId," + + " lotOccupantTypeId," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyOccupantForm.lotOccupancyId, lotOccupantIndex, lotOccupancyOccupantForm.occupantId, lotOccupancyOccupantForm.lotOccupantTypeId, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); + database.close(); + return lotOccupantIndex; +}; +export default addLotOccupancyOccupant; diff --git a/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts b/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts new file mode 100644 index 00000000..dcd6a0d3 --- /dev/null +++ b/helpers/lotOccupancyDB/addLotOccupancyOccupant.ts @@ -0,0 +1,59 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface AddLotOccupancyOccupantForm { + lotOccupancyId: string | number; + occupantId: string | number; + lotOccupantTypeId: string | number; +} + + +export const addLotOccupancyOccupant = + (lotOccupancyOccupantForm: AddLotOccupancyOccupantForm, requestSession: recordTypes.PartialSession): number => { + + const database = sqlite(databasePath); + + let lotOccupantIndex = 0; + + const maxIndexResult = database.prepare("select lotOccupantIndex" + + " from LotOccupancyOccupants" + + " where lotOccupancyId = ?" + + " order by lotOccupantIndex" + + " limit 1") + .get(lotOccupancyOccupantForm.lotOccupancyId); + + if (maxIndexResult) { + lotOccupantIndex = maxIndexResult.lotOccupantIndex + 1; + } + + const rightNowMillis = Date.now(); + + database + .prepare("insert into LotOccupancyOccupants (" + + "lotOccupancyId, lotOccupantIndex," + + " occupantId," + + " lotOccupantTypeId," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyOccupantForm.lotOccupancyId, + lotOccupantIndex, + lotOccupancyOccupantForm.occupantId, + lotOccupancyOccupantForm.lotOccupantTypeId, + requestSession.user.userName, + rightNowMillis, + requestSession.user.userName, + rightNowMillis); + + database.close(); + + return lotOccupantIndex; + }; + + +export default addLotOccupancyOccupant; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getLotOccupantTypes.d.ts b/helpers/lotOccupancyDB/getLotOccupantTypes.d.ts new file mode 100644 index 00000000..2228bdd9 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotOccupantTypes.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const getLotOccupantTypes: () => recordTypes.LotOccupantType[]; +export default getLotOccupantTypes; diff --git a/helpers/lotOccupancyDB/getLotOccupantTypes.js b/helpers/lotOccupancyDB/getLotOccupantTypes.js new file mode 100644 index 00000000..b506e409 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotOccupantTypes.js @@ -0,0 +1,15 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getLotOccupantTypes = () => { + const database = sqlite(databasePath, { + readonly: true + }); + const lotOccupantTypes = database + .prepare("select * from LotOccupantTypes" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, lotOccupantType") + .all(); + database.close(); + return lotOccupantTypes; +}; +export default getLotOccupantTypes; diff --git a/helpers/lotOccupancyDB/getLotOccupantTypes.ts b/helpers/lotOccupancyDB/getLotOccupantTypes.ts new file mode 100644 index 00000000..acfb66f5 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotOccupantTypes.ts @@ -0,0 +1,25 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const getLotOccupantTypes = (): recordTypes.LotOccupantType[] => { + + const database = sqlite(databasePath, { + readonly: true + }); + + const lotOccupantTypes: recordTypes.LotOccupantType[] = database + .prepare("select * from LotOccupantTypes" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, lotOccupantType") + .all(); + + database.close(); + + return lotOccupantTypes; + }; + + +export default getLotOccupantTypes; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getLotStatuses.d.ts b/helpers/lotOccupancyDB/getLotStatuses.d.ts new file mode 100644 index 00000000..c90775b3 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotStatuses.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const getLotStatuses: () => recordTypes.LotStatus[]; +export default getLotStatuses; diff --git a/helpers/lotOccupancyDB/getLotStatuses.js b/helpers/lotOccupancyDB/getLotStatuses.js new file mode 100644 index 00000000..e508a156 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotStatuses.js @@ -0,0 +1,15 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getLotStatuses = () => { + const database = sqlite(databasePath, { + readonly: true + }); + const lotStatuses = database + .prepare("select * from LotStatuses" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, lotStatus") + .all(); + database.close(); + return lotStatuses; +}; +export default getLotStatuses; diff --git a/helpers/lotOccupancyDB/getLotStatuses.ts b/helpers/lotOccupancyDB/getLotStatuses.ts new file mode 100644 index 00000000..18b61750 --- /dev/null +++ b/helpers/lotOccupancyDB/getLotStatuses.ts @@ -0,0 +1,25 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const getLotStatuses = (): recordTypes.LotStatus[] => { + + const database = sqlite(databasePath, { + readonly: true + }); + + const lotStatuses: recordTypes.LotStatus[] = database + .prepare("select * from LotStatuses" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, lotStatus") + .all(); + + database.close(); + + return lotStatuses; + }; + + +export default getLotStatuses; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getLots.d.ts b/helpers/lotOccupancyDB/getLots.d.ts new file mode 100644 index 00000000..7f737bcb --- /dev/null +++ b/helpers/lotOccupancyDB/getLots.d.ts @@ -0,0 +1,10 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface GetLotsFilters { + mapId?: number | string; +} +interface GetLotsOptions { + limit: number; + offset: number; +} +export declare const getLots: (filters?: GetLotsFilters, options?: GetLotsOptions) => recordTypes.Lot[]; +export default getLots; diff --git a/helpers/lotOccupancyDB/getLots.js b/helpers/lotOccupancyDB/getLots.js new file mode 100644 index 00000000..1f6b9182 --- /dev/null +++ b/helpers/lotOccupancyDB/getLots.js @@ -0,0 +1,32 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getLots = (filters, options) => { + const database = sqlite(databasePath, { + readonly: true + }); + let sqlWhereClause = ""; + const sqlParameters = []; + if (filters.mapId) { + sqlWhereClause += " and l.mapId = ?"; + sqlParameters.push(filters.mapId); + } + const lots = database + .prepare("select l.lotId, l.lotName," + + " t.lotType," + + " m.mapName, l.mapKey," + + " s.lotStatus" + + " from Lots l" + + " left join LotTypes t on l.lotTypeId = t.lotTypeId" + + " left join LotStatuses s on l.lotStatusId = s.lotStatusId" + + " left join Maps m on l.mapId = m.mapId" + + " where l.recordDelete_timeMillis is null" + + sqlWhereClause + + " order by l.lotName" + + (options + ? " limit " + options.limit + " offset " + options.offset + : "")) + .all(sqlParameters); + database.close(); + return lots; +}; +export default getLots; diff --git a/helpers/lotOccupancyDB/getLots.ts b/helpers/lotOccupancyDB/getLots.ts new file mode 100644 index 00000000..1c46d8b1 --- /dev/null +++ b/helpers/lotOccupancyDB/getLots.ts @@ -0,0 +1,56 @@ +import sqlite from "better-sqlite3"; +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface GetLotsFilters { + mapId?: number | string; +} + +interface GetLotsOptions { + limit: number; + offset: number; +} + + +export const getLots = (filters ? : GetLotsFilters, options?: GetLotsOptions): recordTypes.Lot[] => { + + const database = sqlite(databasePath, { + readonly: true + }); + + let sqlWhereClause = ""; + const sqlParameters = []; + + if (filters.mapId) { + sqlWhereClause += " and l.mapId = ?"; + sqlParameters.push(filters.mapId); + } + + const lots: recordTypes.Lot[] = database + .prepare("select l.lotId, l.lotName," + + " t.lotType," + + " m.mapName, l.mapKey," + + " s.lotStatus" + + " from Lots l" + + " left join LotTypes t on l.lotTypeId = t.lotTypeId" + + " left join LotStatuses s on l.lotStatusId = s.lotStatusId" + + " left join Maps m on l.mapId = m.mapId" + + " where l.recordDelete_timeMillis is null" + + sqlWhereClause + + " order by l.lotName" + + (options + ? " limit " + options.limit + " offset " + options.offset + : "")) + .all(sqlParameters); + + database.close(); + + return lots; +}; + + +export default getLots; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getOccupancyTypes.d.ts b/helpers/lotOccupancyDB/getOccupancyTypes.d.ts new file mode 100644 index 00000000..80bec04a --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupancyTypes.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const getOccupancyTypes: () => recordTypes.OccupancyType[]; +export default getOccupancyTypes; diff --git a/helpers/lotOccupancyDB/getOccupancyTypes.js b/helpers/lotOccupancyDB/getOccupancyTypes.js new file mode 100644 index 00000000..3f75c8ae --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupancyTypes.js @@ -0,0 +1,15 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getOccupancyTypes = () => { + const database = sqlite(databasePath, { + readonly: true + }); + const occupancyTypes = database + .prepare("select * from OccupancyTypes" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, occupancyType") + .all(); + database.close(); + return occupancyTypes; +}; +export default getOccupancyTypes; diff --git a/helpers/lotOccupancyDB/getOccupancyTypes.ts b/helpers/lotOccupancyDB/getOccupancyTypes.ts new file mode 100644 index 00000000..963c516f --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupancyTypes.ts @@ -0,0 +1,25 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const getOccupancyTypes = (): recordTypes.OccupancyType[] => { + + const database = sqlite(databasePath, { + readonly: true + }); + + const occupancyTypes: recordTypes.OccupancyType[] = database + .prepare("select * from OccupancyTypes" + + " where recordDelete_timeMillis is null" + + " order by orderNumber, occupancyType") + .all(); + + database.close(); + + return occupancyTypes; + }; + + +export default getOccupancyTypes; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getOccupants.d.ts b/helpers/lotOccupancyDB/getOccupants.d.ts new file mode 100644 index 00000000..2a1075c2 --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupants.d.ts @@ -0,0 +1,10 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface GetOccupantsFilters { + occupantName?: string; + occupantAddress?: string; + occupantCity?: string; + occupantPostalCode?: string; + occupantPhoneNumber?: string; +} +export declare const getOccupants: (filters?: GetOccupantsFilters) => recordTypes.Occupant[]; +export default getOccupants; diff --git a/helpers/lotOccupancyDB/getOccupants.js b/helpers/lotOccupancyDB/getOccupants.js new file mode 100644 index 00000000..c06d60be --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupants.js @@ -0,0 +1,56 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const getOccupants = (filters) => { + const database = sqlite(databasePath, { + readonly: true + }); + let sqlWhereClause = ""; + const sqlParameters = []; + if (filters.occupantName) { + const occupantNamePieces = filters.occupantName.toLowerCase().split(" "); + for (const occupantNamePiece of occupantNamePieces) { + sqlWhereClause += " and instr(lower(occupantName), ?)"; + sqlParameters.push(occupantNamePiece); + } + } + if (filters.occupantAddress) { + const occupantAddressPieces = filters.occupantAddress.toLowerCase().split(" "); + for (const occupantAddressPiece of occupantAddressPieces) { + sqlWhereClause += " and (instr(lower(occupantAddress1), ?) or instr(lower(occupantAddress2), ?))"; + sqlParameters.push(occupantAddressPiece, occupantAddressPiece); + } + } + if (filters.occupantCity) { + const occupantCityPieces = filters.occupantCity.toLowerCase().split(" "); + for (const occupantCityPiece of occupantCityPieces) { + sqlWhereClause += " and instr(lower(occupantCity), ?)"; + sqlParameters.push(occupantCityPiece); + } + } + if (filters.occupantPostalCode) { + const occupantPostalCodePieces = filters.occupantPostalCode.toLowerCase().split(" "); + for (const occupantPostalCodePiece of occupantPostalCodePieces) { + sqlWhereClause += " and instr(lower(occupantPostalCode), ?)"; + sqlParameters.push(occupantPostalCodePiece); + } + } + if (filters.occupantPhoneNumber) { + const occupantPhoneNumberPieces = filters.occupantPhoneNumber.toLowerCase().split(" "); + for (const occupantPhoneNumberPiece of occupantPhoneNumberPieces) { + sqlWhereClause += " and instr(lower(occupantPhoneNumber), ?)"; + sqlParameters.push(occupantPhoneNumberPiece); + } + } + const occupants = database + .prepare("select occupantId, occupantName," + + " occupantAddress1, occupantAddress2, occupantCity, occupantProvince, occupantPostalCode," + + " occupantPhoneNumber" + + " from Occupants" + + " where recordDelete_timeMillis is null" + + sqlWhereClause + + " order by occupantName, occupantAddress1") + .all(sqlParameters); + database.close(); + return occupants; +}; +export default getOccupants; diff --git a/helpers/lotOccupancyDB/getOccupants.ts b/helpers/lotOccupancyDB/getOccupants.ts new file mode 100644 index 00000000..25ac6099 --- /dev/null +++ b/helpers/lotOccupancyDB/getOccupants.ts @@ -0,0 +1,93 @@ +import sqlite from "better-sqlite3"; +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface GetOccupantsFilters { + occupantName?: string; + occupantAddress?: string; + occupantCity?: string; + occupantPostalCode?: string; + occupantPhoneNumber?: string; +} + + +export const getOccupants = (filters ? : GetOccupantsFilters): recordTypes.Occupant[] => { + + const database = sqlite(databasePath, { + readonly: true + }); + + let sqlWhereClause = ""; + const sqlParameters = []; + + if (filters.occupantName) { + + const occupantNamePieces = filters.occupantName.toLowerCase().split(" "); + + for (const occupantNamePiece of occupantNamePieces) { + sqlWhereClause += " and instr(lower(occupantName), ?)"; + sqlParameters.push(occupantNamePiece); + } + } + + if (filters.occupantAddress) { + + const occupantAddressPieces = filters.occupantAddress.toLowerCase().split(" "); + + for (const occupantAddressPiece of occupantAddressPieces) { + sqlWhereClause += " and (instr(lower(occupantAddress1), ?) or instr(lower(occupantAddress2), ?))"; + sqlParameters.push(occupantAddressPiece, occupantAddressPiece); + } + } + + if (filters.occupantCity) { + + const occupantCityPieces = filters.occupantCity.toLowerCase().split(" "); + + for (const occupantCityPiece of occupantCityPieces) { + sqlWhereClause += " and instr(lower(occupantCity), ?)"; + sqlParameters.push(occupantCityPiece); + } + } + + if (filters.occupantPostalCode) { + + const occupantPostalCodePieces = filters.occupantPostalCode.toLowerCase().split(" "); + + for (const occupantPostalCodePiece of occupantPostalCodePieces) { + sqlWhereClause += " and instr(lower(occupantPostalCode), ?)"; + sqlParameters.push(occupantPostalCodePiece); + } + } + + if (filters.occupantPhoneNumber) { + + const occupantPhoneNumberPieces = filters.occupantPhoneNumber.toLowerCase().split(" "); + + for (const occupantPhoneNumberPiece of occupantPhoneNumberPieces) { + sqlWhereClause += " and instr(lower(occupantPhoneNumber), ?)"; + sqlParameters.push(occupantPhoneNumberPiece); + } + } + + const occupants: recordTypes.Occupant[] = database + .prepare("select occupantId, occupantName," + + " occupantAddress1, occupantAddress2, occupantCity, occupantProvince, occupantPostalCode," + + " occupantPhoneNumber" + + " from Occupants" + + " where recordDelete_timeMillis is null" + + sqlWhereClause + + " order by occupantName, occupantAddress1") + .all(sqlParameters); + + database.close(); + + return occupants; +}; + + +export default getOccupants; \ No newline at end of file diff --git a/routes/lots.js b/routes/lots.js index 188ed47b..063e2509 100644 --- a/routes/lots.js +++ b/routes/lots.js @@ -1,16 +1,12 @@ import { Router } from "express"; import * as permissionHandlers from "../handlers/permissions.js"; -import * as configFunctions from "../helpers/functions.config.js"; +import handler_search from "../handlers/lots-get/search.js"; import handler_view from "../handlers/lots-get/view.js"; import handler_new from "../handlers/lots-get/new.js"; import handler_edit from "../handlers/lots-get/edit.js"; import handler_print from "../handlers/lots-get/print.js"; export const router = Router(); -router.get("/", (_request, response) => { - response.render("lot-search", { - headTitle: configFunctions.getProperty("aliases.lots") - }); -}); +router.get("/", handler_search); router.get("/new", permissionHandlers.updateGetHandler, handler_new); router.get("/:licenceID", handler_view); router.get("/:licenceID/edit", permissionHandlers.updateGetHandler, handler_edit); diff --git a/routes/lots.ts b/routes/lots.ts index 8572461e..7486a480 100644 --- a/routes/lots.ts +++ b/routes/lots.ts @@ -1,7 +1,8 @@ import { Router } from "express"; import * as permissionHandlers from "../handlers/permissions.js"; -import * as configFunctions from "../helpers/functions.config.js"; + +import handler_search from "../handlers/lots-get/search.js"; import handler_view from "../handlers/lots-get/view.js"; import handler_new from "../handlers/lots-get/new.js"; @@ -17,13 +18,8 @@ export const router = Router(); */ -router.get("/", (_request, response) => { - - response.render("lot-search", { - headTitle: configFunctions.getProperty("aliases.lots") - }); - -}); +router.get("/", + handler_search); /* diff --git a/temp/legacy.importFromCSV.js b/temp/legacy.importFromCSV.js index d49e4fb5..8544c170 100644 --- a/temp/legacy.importFromCSV.js +++ b/temp/legacy.importFromCSV.js @@ -2,10 +2,15 @@ import fs from "node:fs"; import papa from "papaparse"; import sqlite from "better-sqlite3"; import { lotOccupancyDB as databasePath } from "../data/databasePaths.js"; +import * as cacheFunctions from "../helpers/functions.cache.js"; +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; import { addMap } from "../helpers/lotOccupancyDB/addMap.js"; import { getMap as getMapFromDatabase } from "../helpers/lotOccupancyDB/getMap.js"; -import { getLotTypes } from "../helpers/lotOccupancyDB/getLotTypes.js"; import { addLot } from "../helpers/lotOccupancyDB/addLot.js"; +import { getOccupants } from "../helpers/lotOccupancyDB/getOccupants.js"; +import { addOccupant } from "../helpers/lotOccupancyDB/addOccupant.js"; +import { addLotOccupancy } from "../helpers/lotOccupancyDB/addLotOccupancy.js"; +import { addLotOccupancyOccupant } from "../helpers/lotOccupancyDB/addLotOccupancyOccupant.js"; const user = { user: { userName: "import.unix", @@ -15,11 +20,13 @@ const user = { } } }; -const configTablesInString = "'Maps', 'LotTypes'"; function purgeTables() { const database = sqlite(databasePath); + database.prepare("delete from LotOccupancyOccupants").run(); + database.prepare("delete from LotOccupancies").run(); + database.prepare("delete from Occupants").run(); database.prepare("delete from Lots").run(); - database.prepare("delete from sqlite_sequence where name not in (" + configTablesInString + ")").run(); + database.prepare("delete from sqlite_sequence where name in ('Lots', 'LotOccupancies', 'Occupants')").run(); database.close(); } function purgeConfigTables() { @@ -39,6 +46,11 @@ function getMapByMapDescription(mapDescription) { database.close(); return map; } +function formatDateString(year, month, day) { + return ("0000" + year).slice(-4) + "-" + + ("00" + month).slice(-2) + "-" + + ("00" + day).slice(-2); +} const mapCache = new Map(); function getMap(masterRow) { if (mapCache.has(masterRow.CM_CEMETERY)) { @@ -66,7 +78,10 @@ function getMap(masterRow) { return map; } function importFromCSV() { - const lotTypes = getLotTypes(); + let masterRow; + const lotTypes = cacheFunctions.getLotTypes(); + const preneedOccupancyType = cacheFunctions.getOccupancyTypeByOccupancyType("Preneed"); + const preneedOwnerLotOccupantType = cacheFunctions.getLotOccupantTypesByLotOccupantType("Preneed Owner"); const rawData = fs.readFileSync("./temp/CMMASTER.csv").toString(); const cmmaster = papa.parse(rawData, { delimiter: ",", @@ -76,24 +91,79 @@ function importFromCSV() { for (const parseError of cmmaster.errors) { console.log(parseError); } - for (const masterRow of cmmaster.data) { - const map = getMap(masterRow); - const lotName = masterRow.CM_CEMETERY + "-" + - (masterRow.CM_BLOCK === "" ? "" : masterRow.CM_BLOCK + "-") + - (masterRow.CM_RANGE2 === "" ? masterRow.CM_RANGE1.toString() : masterRow.CM_RANGE2) + "-" + - (masterRow.CM_LOT2 === "" ? masterRow.CM_LOT1.toString() : masterRow.CM_LOT2) + "-" + - (masterRow.CM_GRAVE2 === "" ? masterRow.CM_GRAVE1.toString() : masterRow.CM_GRAVE2) + "-" + - masterRow.CM_INTERMENT; - const lotId = addLot({ - lotName: lotName, - lotTypeId: lotTypes[0].lotTypeId, - lotTypeStatusId: "", - mapId: map.mapId, - mapKey: lotName, - lotLatitude: "", - lotLongitude: "" - }, user); + try { + for (masterRow of cmmaster.data) { + const map = getMap(masterRow); + const lotName = masterRow.CM_CEMETERY + "-" + + (masterRow.CM_BLOCK === "" ? "" : masterRow.CM_BLOCK + "-") + + (masterRow.CM_RANGE2 === "" ? masterRow.CM_RANGE1 : masterRow.CM_RANGE2) + "-" + + (masterRow.CM_LOT2 === "" ? masterRow.CM_LOT1 : masterRow.CM_LOT2) + "-" + + (masterRow.CM_GRAVE2 === "" ? masterRow.CM_GRAVE1 : masterRow.CM_GRAVE2) + "-" + + masterRow.CM_INTERMENT; + const lotId = addLot({ + lotName: lotName, + lotTypeId: lotTypes[0].lotTypeId, + lotStatusId: "", + mapId: map.mapId, + mapKey: lotName, + lotLatitude: "", + lotLongitude: "" + }, user); + if (masterRow.CM_PRENEED_ORDER) { + const occupantPostalCode = ((masterRow.CM_POST1 || "") + " " + (masterRow.CM_POST2 || "")).trim(); + const possibleOccupants = getOccupants({ + occupantName: masterRow.CM_PRENEED_OWNER, + occupantAddress: masterRow.CM_ADDRESS, + occupantCity: masterRow.CM_CITY, + occupantPostalCode + }); + const occupantId = possibleOccupants.length > 0 + ? possibleOccupants[0].occupantId + : addOccupant({ + occupantName: masterRow.CM_PRENEED_ORDER, + occupantAddress1: masterRow.CM_ADDRESS, + occupantAddress2: "", + occupantCity: masterRow.CM_CITY, + occupantProvince: masterRow.CM_PROV, + occupantPostalCode, + occupantPhoneNumber: "" + }, user); + let occupancyStartDateString = formatDateString(masterRow.CM_PURCHASE_YR, masterRow.CM_PURCHASE_MON, masterRow.CM_PURCHASE_DAY); + let occupancyEndDateString = ""; + if (masterRow.CM_DEATH_YR && masterRow.CM_DEATH_YR !== "0") { + occupancyEndDateString = formatDateString(masterRow.CM_DEATH_YR, masterRow.CM_DEATH_MON, masterRow.CM_DEATH_DAY); + } + if (occupancyStartDateString === "0000-00-00" && occupancyEndDateString !== "") { + occupancyStartDateString = occupancyEndDateString; + } + if (occupancyStartDateString === "0000-00-00" && masterRow.CM_INTERMENT_YR !== "") { + occupancyStartDateString = formatDateString(masterRow.CM_INTERMENT_YR, masterRow.CM_INTERMENT_MON, masterRow.CM_INTERMENT_DAY); + } + if (occupancyStartDateString === "0000-00-00" && masterRow.CM_LAST_CHG_DATE !== "") { + occupancyStartDateString = dateTimeFunctions.dateIntegerToString(Number.parseInt(masterRow.CM_LAST_CHG_DATE, 10)); + } + if (occupancyStartDateString === "0000-00-00") { + occupancyStartDateString = "1970-01-01"; + } + const lotOccupancyId = addLotOccupancy({ + occupancyTypeId: preneedOccupancyType.occupancyTypeId, + lotId, + occupancyStartDateString, + occupancyEndDateString + }, user); + addLotOccupancyOccupant({ + lotOccupancyId, + lotOccupantTypeId: preneedOwnerLotOccupantType.lotOccupantTypeId, + occupantId + }, user); + } + } + } + catch (error) { + console.error(error); + console.log(masterRow); } } purgeTables(); +purgeConfigTables(); importFromCSV(); diff --git a/temp/legacy.importFromCSV.ts b/temp/legacy.importFromCSV.ts index 6e38d185..258da24d 100644 --- a/temp/legacy.importFromCSV.ts +++ b/temp/legacy.importFromCSV.ts @@ -6,12 +6,19 @@ import papa from "papaparse"; import sqlite from "better-sqlite3"; import { lotOccupancyDB as databasePath } from "../data/databasePaths.js"; +import * as cacheFunctions from "../helpers/functions.cache.js"; +import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; + import { addMap } from "../helpers/lotOccupancyDB/addMap.js"; import { getMap as getMapFromDatabase } from "../helpers/lotOccupancyDB/getMap.js"; -import { getLotTypes } from "../helpers/lotOccupancyDB/getLotTypes.js"; import { addLot } from "../helpers/lotOccupancyDB/addLot.js"; +import { getOccupants } from "../helpers/lotOccupancyDB/getOccupants.js"; +import { addOccupant } from "../helpers/lotOccupancyDB/addOccupant.js"; +import { addLotOccupancy } from "../helpers/lotOccupancyDB/addLotOccupancy.js"; +import { addLotOccupancyOccupant } from "../helpers/lotOccupancyDB/addLotOccupancyOccupant.js"; + import type * as recordTypes from "../types/recordTypes"; @@ -19,42 +26,42 @@ interface MasterRecord { CM_SYSREC: string; CM_CEMETERY: string; CM_BLOCK: string; - CM_RANGE1: number; + CM_RANGE1: string; CM_RANGE2: string; - CM_LOT1: number; + CM_LOT1: string; CM_LOT2: string; CM_GRAVE1: number; CM_GRAVE2: string; - CM_INTERMENT: number; + CM_INTERMENT: string; CM_PRENEED_OWNER: string; - CM_PRENEED_OWNER_SEQ: number; + CM_PRENEED_OWNER_SEQ: string; CM_DECEASED_NAME: string; - CM_DECEASED_NAME_SEQ: number; + CM_DECEASED_NAME_SEQ: string; CM_ADDRESS: string; CM_CITY: string; CM_PROV: string; CM_POST1: string; CM_POST2: string; CM_PRENEED_ORDER: string; - CM_PURCHASE_YR: number; - CM_PURCHASE_MON: number; - CM_PURCHASE_DAY: number; - CM_NO_GRAVES: number; - CM_DEATH_YR: number; - CM_DEATH_MON: number; - CM_DEATH_DAY: number; + CM_PURCHASE_YR: string; + CM_PURCHASE_MON: string; + CM_PURCHASE_DAY: string; + CM_NO_GRAVES: string; + CM_DEATH_YR: string; + CM_DEATH_MON: string; + CM_DEATH_DAY: string; CM_WORK_ORDER: string; - CM_INTERMENT_YR: number; - CM_INTERMENT_MON: number; - CM_INTERMENT_DAY: number; - CM_AGE: number; + CM_INTERMENT_YR: string; + CM_INTERMENT_MON: string; + CM_INTERMENT_DAY: string; + CM_AGE: string; CM_CONTAINER_TYPE: string; CM_COMMITTAL_TYPE: string; CM_CREMATION: string; CM_FUNERAL_HOME: string; - CM_FUNERAL_YR: number; - CM_FUNERAL_MON: number; - CM_FUNERAL_DAY: number; + CM_FUNERAL_YR: string; + CM_FUNERAL_MON: string; + CM_FUNERAL_DAY: string; CM_RESIDENT_TYPE: string; CM_REMARK1: string; CM_REMARK2: string; @@ -75,13 +82,14 @@ const user: recordTypes.PartialSession = { } }; -const configTablesInString = "'Maps', 'LotTypes'"; - function purgeTables () { const database = sqlite(databasePath); + database.prepare("delete from LotOccupancyOccupants").run(); + database.prepare("delete from LotOccupancies").run(); + database.prepare("delete from Occupants").run(); database.prepare("delete from Lots").run(); - database.prepare("delete from sqlite_sequence where name not in (" + configTablesInString + ")").run(); + database.prepare("delete from sqlite_sequence where name in ('Lots', 'LotOccupancies', 'Occupants')").run(); database.close(); } @@ -110,6 +118,13 @@ function getMapByMapDescription (mapDescription: string) { return map; } +function formatDateString (year: string, month: string, day: string) { + + return ("0000" + year).slice(-4) + "-" + + ("00" + month).slice(-2) + "-" + + ("00" + day).slice(-2); +} + const mapCache: Map = new Map(); @@ -151,7 +166,12 @@ function getMap(masterRow: MasterRecord): recordTypes.Map { function importFromCSV () { - const lotTypes = getLotTypes(); + let masterRow: MasterRecord; + + // Load cached values + const lotTypes = cacheFunctions.getLotTypes(); + const preneedOccupancyType = cacheFunctions.getOccupancyTypeByOccupancyType("Preneed"); + const preneedOwnerLotOccupantType = cacheFunctions.getLotOccupantTypesByLotOccupantType("Preneed Owner"); const rawData = fs.readFileSync("./temp/CMMASTER.csv").toString(); @@ -165,28 +185,103 @@ function importFromCSV () { console.log(parseError); } - for (const masterRow of cmmaster.data) { - const map = getMap(masterRow); + try { + for (masterRow of cmmaster.data) { + const map = getMap(masterRow); - const lotName = masterRow.CM_CEMETERY + "-" + - (masterRow.CM_BLOCK === "" ? "" : masterRow.CM_BLOCK + "-") + - (masterRow.CM_RANGE2 === "" ? masterRow.CM_RANGE1.toString() : masterRow.CM_RANGE2) + "-" + - (masterRow.CM_LOT2 === "" ? masterRow.CM_LOT1.toString() : masterRow.CM_LOT2) + "-" + - (masterRow.CM_GRAVE2 === "" ? masterRow.CM_GRAVE1.toString() : masterRow.CM_GRAVE2) + "-" + - masterRow.CM_INTERMENT; + const lotName = masterRow.CM_CEMETERY + "-" + + (masterRow.CM_BLOCK === "" ? "" : masterRow.CM_BLOCK + "-") + + (masterRow.CM_RANGE2 === "" ? masterRow.CM_RANGE1 : masterRow.CM_RANGE2) + "-" + + (masterRow.CM_LOT2 === "" ? masterRow.CM_LOT1 : masterRow.CM_LOT2) + "-" + + (masterRow.CM_GRAVE2 === "" ? masterRow.CM_GRAVE1 : masterRow.CM_GRAVE2) + "-" + + masterRow.CM_INTERMENT; - const lotId = addLot({ - lotName: lotName, - lotTypeId: lotTypes[0].lotTypeId, - lotTypeStatusId: "", - mapId: map.mapId, - mapKey: lotName, - lotLatitude: "", - lotLongitude: "" - }, user); + const lotId = addLot({ + lotName: lotName, + lotTypeId: lotTypes[0].lotTypeId, + lotStatusId: "", + mapId: map.mapId, + mapKey: lotName, + lotLatitude: "", + lotLongitude: "" + }, user); + + if (masterRow.CM_PRENEED_ORDER) { + + const occupantPostalCode = ((masterRow.CM_POST1 || "") + " " + (masterRow.CM_POST2 || "")).trim(); + + const possibleOccupants = getOccupants({ + occupantName: masterRow.CM_PRENEED_OWNER, + occupantAddress: masterRow.CM_ADDRESS, + occupantCity: masterRow.CM_CITY, + occupantPostalCode + }); + + const occupantId = possibleOccupants.length > 0 + ? possibleOccupants[0].occupantId + : addOccupant({ + occupantName: masterRow.CM_PRENEED_ORDER, + occupantAddress1: masterRow.CM_ADDRESS, + occupantAddress2: "", + occupantCity: masterRow.CM_CITY, + occupantProvince: masterRow.CM_PROV, + occupantPostalCode, + occupantPhoneNumber: "" + }, user); + + let occupancyStartDateString = formatDateString(masterRow.CM_PURCHASE_YR, + masterRow.CM_PURCHASE_MON, + masterRow.CM_PURCHASE_DAY); + + let occupancyEndDateString = ""; + + if (masterRow.CM_DEATH_YR && masterRow.CM_DEATH_YR !== "0") { + occupancyEndDateString = formatDateString(masterRow.CM_DEATH_YR, + masterRow.CM_DEATH_MON, + masterRow.CM_DEATH_DAY); + } + + // if purchase date unavailable + if (occupancyStartDateString === "0000-00-00" && occupancyEndDateString !== "") { + occupancyStartDateString = occupancyEndDateString; + } + + // if end date unavailable + if (occupancyStartDateString === "0000-00-00" && masterRow.CM_INTERMENT_YR !== "") { + occupancyStartDateString = formatDateString(masterRow.CM_INTERMENT_YR, + masterRow.CM_INTERMENT_MON, + masterRow.CM_INTERMENT_DAY); + } + + // if interment date unavailable + if (occupancyStartDateString === "0000-00-00" && masterRow.CM_LAST_CHG_DATE !== "") { + occupancyStartDateString = dateTimeFunctions.dateIntegerToString(Number.parseInt(masterRow.CM_LAST_CHG_DATE, 10)); + } + + if (occupancyStartDateString === "0000-00-00") { + occupancyStartDateString = "1970-01-01"; + } + + const lotOccupancyId = addLotOccupancy({ + occupancyTypeId: preneedOccupancyType.occupancyTypeId, + lotId, + occupancyStartDateString, + occupancyEndDateString + }, user); + + addLotOccupancyOccupant({ + lotOccupancyId, + lotOccupantTypeId: preneedOwnerLotOccupantType.lotOccupantTypeId, + occupantId + }, user); + } + } + } catch (error) { + console.error(error) + console.log(masterRow); } } purgeTables(); -// purgeMaps(); +purgeConfigTables(); importFromCSV(); \ No newline at end of file diff --git a/types/recordTypes.d.ts b/types/recordTypes.d.ts index 68e4cf3f..314b3ef5 100644 --- a/types/recordTypes.d.ts +++ b/types/recordTypes.d.ts @@ -29,7 +29,6 @@ export interface LotType extends Record { lotTypeId?: number; lotType?: string; orderNumber?: number; - lotTypeStatuses?: LotTypeStatus[]; lotTypeFields?: LotTypeField[]; } export interface LotTypeField extends Record { @@ -44,24 +43,34 @@ export interface LotTypeField extends Record { maximumLength?: number; orderNumber?: number; } -export interface LotTypeStatus extends Record { - lotTypeStatusId?: number; - lotTypeId?: number; - lotTypeStatus?: string; +export interface LotStatus extends Record { + lotStatusId?: number; + lotStatus?: string; orderNumber?: number; } export interface Lot extends Record { lotId?: number; lotName?: string; lotTypeId?: number; - lotType?: LotType; + lotType?: LotType | string; mapId?: number; + mapName?: string; map?: Map; mapKey?: string; lotLatitude?: number; lotLongitude?: number; - lotTypeStatusId?: number; - lotTypeStatus?: LotTypeStatus; + lotStatusId?: number; + lotStatus?: LotStatus | string; +} +export interface OccupancyType extends Record { + occupancyTypeId?: number; + occupancyType?: string; + orderNumber?: number; +} +export interface LotOccupantType extends Record { + lotOccupantTypeId?: number; + lotOccupantType?: string; + orderNumber?: number; } export interface Occupant extends Record { occupantId?: number; diff --git a/types/recordTypes.js b/types/recordTypes.js index c8ad2e54..cb0ff5c3 100644 --- a/types/recordTypes.js +++ b/types/recordTypes.js @@ -1,2 +1 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +export {}; diff --git a/types/recordTypes.ts b/types/recordTypes.ts index 95edbc7a..5d7e5199 100644 --- a/types/recordTypes.ts +++ b/types/recordTypes.ts @@ -43,7 +43,6 @@ export interface LotType extends Record { lotTypeId?: number; lotType?: string; orderNumber?: number; - lotTypeStatuses?: LotTypeStatus[]; lotTypeFields?: LotTypeField[]; } @@ -65,10 +64,9 @@ export interface LotTypeField extends Record { } -export interface LotTypeStatus extends Record { - lotTypeStatusId?: number; - lotTypeId?: number; - lotTypeStatus?: string; +export interface LotStatus extends Record { + lotStatusId?: number; + lotStatus?: string; orderNumber?: number; } @@ -78,17 +76,32 @@ export interface Lot extends Record { lotName?: string; lotTypeId?: number; - lotType?: LotType; + lotType?: LotType | string; mapId?: number; + mapName?: string; map?: Map; mapKey?: string; lotLatitude?: number; lotLongitude?: number; - lotTypeStatusId?: number; - lotTypeStatus?: LotTypeStatus; + lotStatusId?: number; + lotStatus?: LotStatus | string; +} + + +export interface OccupancyType extends Record { + occupancyTypeId?: number; + occupancyType?: string; + orderNumber?: number; +} + + +export interface LotOccupantType extends Record { + lotOccupantTypeId?: number; + lotOccupantType?: string; + orderNumber?: number; } diff --git a/views/_header.ejs b/views/_header.ejs index 5e719c65..4b89ffe6 100644 --- a/views/_header.ejs +++ b/views/_header.ejs @@ -35,7 +35,7 @@