development

deepsource-autofix-76c6eb20
Dan Gowans 2022-07-18 16:25:33 -04:00
parent 2f477d87ff
commit 06e60532c0
145 changed files with 1466 additions and 262 deletions

3
.gitignore vendored
View File

@ -13,5 +13,6 @@ data/config.ts
public/images-custom/* public/images-custom/*
temp/wmf/
temp/config.* temp/config.*
temp/wmf/ temp/*.csv

1
app.js
View File

@ -57,6 +57,7 @@ app.use(urlPrefix, express.static(path.join("public")));
app.use(urlPrefix + "/lib/fa", express.static(path.join("node_modules", "@fortawesome", "fontawesome-free"))); app.use(urlPrefix + "/lib/fa", express.static(path.join("node_modules", "@fortawesome", "fontawesome-free")));
app.use(urlPrefix + "/lib/cityssm-bulma-webapp-js", express.static(path.join("node_modules", "@cityssm", "bulma-webapp-js"))); app.use(urlPrefix + "/lib/cityssm-bulma-webapp-js", express.static(path.join("node_modules", "@cityssm", "bulma-webapp-js")));
app.use(urlPrefix + "/lib/cityssm-bulma-js", express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist"))); app.use(urlPrefix + "/lib/cityssm-bulma-js", express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist")));
app.use(urlPrefix + "/lib/leaflet", express.static(path.join("node_modules", "leaflet", "dist")));
const sessionCookieName = configFunctions.getProperty("session.cookieName"); const sessionCookieName = configFunctions.getProperty("session.cookieName");
const FileStoreSession = FileStore(session); const FileStoreSession = FileStore(session);
app.use(session({ app.use(session({

3
app.ts
View File

@ -109,6 +109,9 @@ app.use(urlPrefix + "/lib/cityssm-bulma-webapp-js",
app.use(urlPrefix + "/lib/cityssm-bulma-js", app.use(urlPrefix + "/lib/cityssm-bulma-js",
express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist"))); express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist")));
app.use(urlPrefix + "/lib/leaflet",
express.static(path.join("node_modules", "leaflet", "dist")));
/* /*
* SESSION MANAGEMENT * SESSION MANAGEMENT

3
handlers/maps-get/search.d.ts vendored 100644
View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -0,0 +1,10 @@
import * as configFunctions from "../../helpers/functions.config.js";
import { getMaps } from "../../helpers/lotOccupancyDB/getMaps.js";
export const handler = (_request, response) => {
const maps = getMaps();
response.render("map-search", {
headTitle: configFunctions.getProperty("aliases.map") + " Search",
maps
});
};
export default handler;

View File

@ -0,0 +1,23 @@
import type {
RequestHandler
} from "express";
import * as configFunctions from "../../helpers/functions.config.js";
import {
getMaps
} from "../../helpers/lotOccupancyDB/getMaps.js";
export const handler: RequestHandler = (_request, response) => {
const maps = getMaps();
response.render("map-search", {
headTitle: configFunctions.getProperty("aliases.map") + " Search",
maps
});
};
export default handler;

3
handlers/maps-get/view.d.ts vendored 100644
View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -0,0 +1,13 @@
import * as configFunctions from "../../helpers/functions.config.js";
import { getMap } from "../../helpers/lotOccupancyDB/getMap.js";
export const handler = (request, response) => {
const map = getMap(request.params.mapId);
if (!map) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/maps/?error=mapIdNotFound");
}
response.render("map-view", {
headTitle: map.mapName,
map
});
};
export default handler;

View File

@ -0,0 +1,23 @@
import type { RequestHandler } from "express";
import * as configFunctions from "../../helpers/functions.config.js";
import { getMap } from "../../helpers/lotOccupancyDB/getMap.js";
export const handler: RequestHandler = (request, response) => {
const map = getMap(request.params.mapId);
if (!map) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + "/maps/?error=mapIdNotFound");
}
response.render("map-view", {
headTitle: map.mapName,
map
});
};
export default handler;

View File

@ -68,6 +68,7 @@ export const initLotOccupancyDB = () => {
" lotTypeId integer not null," + " lotTypeId integer not null," +
" lotName varchar(100)," + " lotName varchar(100)," +
" mapId integer," + " mapId integer," +
" mapKey varchar(100)," +
" lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," + " lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," +
" lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," + " lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," +
" lotTypeStatusId integer," + " lotTypeStatusId integer," +

View File

@ -93,6 +93,7 @@ export const initLotOccupancyDB = (): boolean => {
" lotTypeId integer not null," + " lotTypeId integer not null," +
" lotName varchar(100)," + " lotName varchar(100)," +
" mapId integer," + " mapId integer," +
" mapKey varchar(100)," +
" lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," + " lotLatitude decimal(10, 8) check (lotLatitude between -90 and 90)," +
" lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," + " lotLongitude decimal(11, 8) check (lotLongitude between -180 and 180)," +

View File

@ -0,0 +1,12 @@
import type * as recordTypes from "../../types/recordTypes";
interface AddLotForm {
lotName: string;
lotTypeId: string | number;
lotTypeStatusId: string | number;
mapId: string | number;
mapKey: string;
lotLatitude: string;
lotLongitude: string;
}
export declare const addLot: (lotForm: AddLotForm, requestSession: recordTypes.PartialSession) => number;
export default addLot;

View File

@ -0,0 +1,18 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const addLot = (lotForm, requestSession) => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Lots (" +
"lotName, lotTypeId, lotTypeStatusId," +
" 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);
database.close();
return result.lastInsertRowid;
};
export default addLot;

View File

@ -0,0 +1,53 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
interface AddLotForm {
lotName: string;
lotTypeId: string | number;
lotTypeStatusId: string | number;
mapId: string | number;
mapKey: string;
lotLatitude: string;
lotLongitude: string;
}
export const addLot =
(lotForm: AddLotForm, requestSession: recordTypes.PartialSession): number => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Lots (" +
"lotName, lotTypeId, lotTypeStatusId," +
" 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);
database.close();
return result.lastInsertRowid as number;
};
export default addLot;

View File

@ -0,0 +1,16 @@
import type * as recordTypes from "../../types/recordTypes";
interface AddMapForm {
mapName: string;
mapDescription: string;
mapSVG: string;
mapLatitude: string;
mapLongitude: string;
mapAddress1: string;
mapAddress2: string;
mapCity: string;
mapProvince: string;
mapPostalCode: string;
mapPhoneNumber: string;
}
export declare const addMap: (mapForm: AddMapForm, requestSession: recordTypes.PartialSession) => number;
export default addMap;

View File

@ -0,0 +1,18 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const addMap = (mapForm, requestSession) => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Maps (" +
"mapName, mapDescription," +
" mapSVG, mapLatitude, mapLongitude," +
" mapAddress1, mapAddress2, mapCity, mapProvince, mapPostalCode, mapPhoneNumber," +
" recordCreate_userName, recordCreate_timeMillis," +
" recordUpdate_userName, recordUpdate_timeMillis)" +
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
.run(mapForm.mapName, mapForm.mapDescription, mapForm.mapSVG, (mapForm.mapLatitude === "" ? undefined : mapForm.mapLatitude), (mapForm.mapLongitude === "" ? undefined : mapForm.mapLongitude), mapForm.mapAddress1, mapForm.mapAddress2, mapForm.mapCity, mapForm.mapProvince, mapForm.mapPostalCode, mapForm.mapPhoneNumber, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis);
database.close();
return result.lastInsertRowid;
};
export default addMap;

View File

@ -0,0 +1,59 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
interface AddMapForm {
mapName: string;
mapDescription: string;
mapSVG: string;
mapLatitude: string;
mapLongitude: string;
mapAddress1: string;
mapAddress2: string;
mapCity: string;
mapProvince: string;
mapPostalCode: string;
mapPhoneNumber: string;
}
export const addMap =
(mapForm: AddMapForm, requestSession: recordTypes.PartialSession): number => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Maps (" +
"mapName, mapDescription," +
" mapSVG, mapLatitude, mapLongitude," +
" mapAddress1, mapAddress2, mapCity, mapProvince, mapPostalCode, mapPhoneNumber," +
" recordCreate_userName, recordCreate_timeMillis," +
" recordUpdate_userName, recordUpdate_timeMillis)" +
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
.run(mapForm.mapName,
mapForm.mapDescription,
mapForm.mapSVG,
(mapForm.mapLatitude === "" ? undefined : mapForm.mapLatitude),
(mapForm.mapLongitude === "" ? undefined : mapForm.mapLongitude),
mapForm.mapAddress1,
mapForm.mapAddress2,
mapForm.mapCity,
mapForm.mapProvince,
mapForm.mapPostalCode,
mapForm.mapPhoneNumber,
requestSession.user.userName,
rightNowMillis,
requestSession.user.userName,
rightNowMillis);
database.close();
return result.lastInsertRowid as number;
};
export default addMap;

View File

@ -0,0 +1,13 @@
import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
interface AddOccupantForm {
occupantName: string;
occupantAddress1: string;
occupantAddress2: string;
occupantCity: string;
occupantProvince: string;
occupantPostalCode: string;
occupantPhoneNumber: string;
}
export declare const addOccupant: (occupantForm: AddOccupantForm, requestSession: recordTypes.PartialSession, connectedDatabase?: sqlite.Database) => number;
export default addOccupant;

View File

@ -0,0 +1,19 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const addOccupant = (occupantForm, requestSession, connectedDatabase) => {
const database = connectedDatabase || sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Occupants (" +
"occupantName," +
" occupantAddress1, occupantAddress2, occupantCity, occupantProvince, occupantPostalCode, occupantPhoneNumber," +
" recordCreate_userName, recordCreate_timeMillis," +
" recordUpdate_userName, recordUpdate_timeMillis)" +
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
.run(occupantForm.occupantName, occupantForm.occupantAddress1, occupantForm.occupantAddress2, occupantForm.occupantCity, occupantForm.occupantProvince, occupantForm.occupantPostalCode, occupantForm.occupantPhoneNumber, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis);
if (!connectedDatabase) {
database.close();
}
return result.lastInsertRowid;
};
export default addOccupant;

View File

@ -0,0 +1,54 @@
import sqlite from "better-sqlite3";
import {
lotOccupancyDB as databasePath
} from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
interface AddOccupantForm {
occupantName: string;
occupantAddress1: string;
occupantAddress2: string;
occupantCity: string;
occupantProvince: string;
occupantPostalCode: string;
occupantPhoneNumber: string;
}
export const addOccupant =
(occupantForm: AddOccupantForm, requestSession: recordTypes.PartialSession, connectedDatabase ? : sqlite.Database): number => {
const database = connectedDatabase || sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("insert into Occupants (" +
"occupantName," +
" occupantAddress1, occupantAddress2, occupantCity, occupantProvince, occupantPostalCode, occupantPhoneNumber," +
" recordCreate_userName, recordCreate_timeMillis," +
" recordUpdate_userName, recordUpdate_timeMillis)" +
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
.run(occupantForm.occupantName,
occupantForm.occupantAddress1,
occupantForm.occupantAddress2,
occupantForm.occupantCity,
occupantForm.occupantProvince,
occupantForm.occupantPostalCode,
occupantForm.occupantPhoneNumber,
requestSession.user.userName,
rightNowMillis,
requestSession.user.userName,
rightNowMillis);
if (!connectedDatabase) {
database.close();
}
return result.lastInsertRowid as number;
};
export default addOccupant;

View File

@ -0,0 +1,3 @@
import type * as recordTypes from "../../types/recordTypes";
export declare const getLotTypes: () => recordTypes.LotType[];
export default getLotTypes;

View File

@ -0,0 +1,15 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getLotTypes = () => {
const database = sqlite(databasePath, {
readonly: true
});
const lotTypes = database
.prepare("select * from LotTypes" +
" where recordDelete_timeMillis is null" +
" order by orderNumber, lotType")
.all();
database.close();
return lotTypes;
};
export default getLotTypes;

View File

@ -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 getLotTypes = (): recordTypes.LotType[] => {
const database = sqlite(databasePath, {
readonly: true
});
const lotTypes: recordTypes.LotType[] = database
.prepare("select * from LotTypes" +
" where recordDelete_timeMillis is null" +
" order by orderNumber, lotType")
.all();
database.close();
return lotTypes;
};
export default getLotTypes;

View File

@ -0,0 +1,3 @@
import type * as recordTypes from "../../types/recordTypes";
export declare const getMap: (mapId: number | string) => recordTypes.Map;
export default getMap;

View File

@ -0,0 +1,28 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getMap = (mapId) => {
const database = sqlite(databasePath, {
readonly: true
});
const map = database
.prepare("select m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" m.recordCreate_userName, m.recordCreate_timeMillis," +
" m.recordUpdate_userName, m.recordUpdate_timeMillis," +
" m.recordDelete_userName, m.recordDelete_timeMillis," +
" count(l.lotId) as lotCount" +
" from Maps m" +
" left join Lots l on m.mapId = l.mapId and l.recordDelete_timeMillis is null" +
" where m.mapId = ?" +
" group by m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" m.recordCreate_userName, m.recordCreate_timeMillis," +
" m.recordUpdate_userName, m.recordUpdate_timeMillis," +
" m.recordDelete_userName, m.recordDelete_timeMillis")
.get(mapId);
database.close();
return map;
};
export default getMap;

View File

@ -0,0 +1,38 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
export const getMap = (mapId: number | string): recordTypes.Map => {
const database = sqlite(databasePath, {
readonly: true
});
const map: recordTypes.Map = database
.prepare("select m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" m.recordCreate_userName, m.recordCreate_timeMillis," +
" m.recordUpdate_userName, m.recordUpdate_timeMillis," +
" m.recordDelete_userName, m.recordDelete_timeMillis," +
" count(l.lotId) as lotCount" +
" from Maps m" +
" left join Lots l on m.mapId = l.mapId and l.recordDelete_timeMillis is null" +
" where m.mapId = ?" +
" group by m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" m.recordCreate_userName, m.recordCreate_timeMillis," +
" m.recordUpdate_userName, m.recordUpdate_timeMillis," +
" m.recordDelete_userName, m.recordDelete_timeMillis")
.get(mapId);
database.close();
return map;
};
export default getMap;

View File

@ -0,0 +1,5 @@
import type * as recordTypes from "../../types/recordTypes";
interface GetMapsFilters {
}
export declare const getMaps: (filters?: GetMapsFilters) => recordTypes.Map[];
export default getMaps;

View File

@ -0,0 +1,24 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const getMaps = (filters) => {
const database = sqlite(databasePath, {
readonly: true
});
const maps = database
.prepare("select m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" l.lotCount" +
" from Maps m" +
(" left join (" +
"select mapId, count(lotId) as lotCount" +
" from Lots" +
" where recordDelete_timeMillis is null" +
" group by mapId) l on m.mapId = l.mapId") +
" where m.recordDelete_timeMillis is null" +
" order by m.mapName, m.mapId")
.all();
database.close();
return maps;
};
export default getMaps;

View File

@ -0,0 +1,41 @@
import sqlite from "better-sqlite3";
import {
lotOccupancyDB as databasePath
} from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
interface GetMapsFilters {
}
export const getMaps = (filters ? : GetMapsFilters): recordTypes.Map[] => {
const database = sqlite(databasePath, {
readonly: true
});
const maps: recordTypes.Map[] = database
.prepare("select m.mapId, m.mapName, m.mapDescription," +
" m.mapLatitude, m.mapLongitude, m.mapSVG," +
" m.mapAddress1, m.mapAddress2, m.mapCity, m.mapProvince, m.mapPostalCode, m.mapPhoneNumber," +
" l.lotCount" +
" from Maps m" +
(" left join (" +
"select mapId, count(lotId) as lotCount" +
" from Lots" +
" where recordDelete_timeMillis is null" +
" group by mapId) l on m.mapId = l.mapId") +
" where m.recordDelete_timeMillis is null" +
" order by m.mapName, m.mapId")
.all();
database.close();
return maps;
};
export default getMaps;

74
package-lock.json generated
View File

@ -27,6 +27,7 @@
"express-rate-limit": "^6.4.0", "express-rate-limit": "^6.4.0",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"session-file-store": "^1.5.0" "session-file-store": "^1.5.0"
}, },
"devDependencies": { "devDependencies": {
@ -46,9 +47,11 @@
"@types/gulp-minify": "^3.1.1", "@types/gulp-minify": "^3.1.1",
"@types/gulp-sass": "^5.0.0", "@types/gulp-sass": "^5.0.0",
"@types/http-errors": "^1.8.2", "@types/http-errors": "^1.8.2",
"@types/leaflet": "^1.7.11",
"@types/mocha": "^9.1.1", "@types/mocha": "^9.1.1",
"@types/mssql": "^8.0.3", "@types/mssql": "^8.0.3",
"@types/node-windows": "^0.1.2", "@types/node-windows": "^0.1.2",
"@types/papaparse": "^5.3.2",
"@types/session-file-store": "^1.2.2", "@types/session-file-store": "^1.2.2",
"@typescript-eslint/eslint-plugin": "^5.30.6", "@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6", "@typescript-eslint/parser": "^5.30.6",
@ -62,6 +65,7 @@
"gulp-minify": "^3.1.0", "gulp-minify": "^3.1.0",
"gulp-sass": "^5.1.0", "gulp-sass": "^5.1.0",
"nodemon": "^2.0.19", "nodemon": "^2.0.19",
"papaparse": "^5.3.2",
"sass": "^1.53.0" "sass": "^1.53.0"
}, },
"engines": { "engines": {
@ -843,6 +847,12 @@
"@types/express": "*" "@types/express": "*"
} }
}, },
"node_modules/@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"node_modules/@types/glob": { "node_modules/@types/glob": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@ -930,6 +940,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/leaflet": {
"version": "1.7.11",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.11.tgz",
"integrity": "sha512-VwAYom2pfIAf/pLj1VR5aLltd4tOtHyvfaJlNYCoejzP2nu52PrMi1ehsLRMUS+bgafmIIKBV1cMfKeS+uJ0Vg==",
"dev": true,
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@ -1019,6 +1038,15 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true "dev": true
}, },
"node_modules/@types/papaparse": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz",
"integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.7", "version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -6329,6 +6357,11 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/leaflet": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz",
"integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA=="
},
"node_modules/levn": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -7506,6 +7539,12 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/papaparse": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz",
"integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==",
"dev": true
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -11187,6 +11226,12 @@
"@types/express": "*" "@types/express": "*"
} }
}, },
"@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"@types/glob": { "@types/glob": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@ -11274,6 +11319,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/leaflet": {
"version": "1.7.11",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.11.tgz",
"integrity": "sha512-VwAYom2pfIAf/pLj1VR5aLltd4tOtHyvfaJlNYCoejzP2nu52PrMi1ehsLRMUS+bgafmIIKBV1cMfKeS+uJ0Vg==",
"dev": true,
"requires": {
"@types/geojson": "*"
}
},
"@types/mime": { "@types/mime": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@ -11362,6 +11416,15 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true "dev": true
}, },
"@types/papaparse": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz",
"integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/qs": { "@types/qs": {
"version": "6.9.7", "version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -15459,6 +15522,11 @@
"flush-write-stream": "^1.0.2" "flush-write-stream": "^1.0.2"
} }
}, },
"leaflet": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz",
"integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA=="
},
"levn": { "levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -16381,6 +16449,12 @@
"integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
"dev": true "dev": true
}, },
"papaparse": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz",
"integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==",
"dev": true
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",

View File

@ -9,15 +9,12 @@
}, },
"scripts": { "scripts": {
"build:version": "npx genversion --es6 --semi version.js", "build:version": "npx genversion --es6 --semi version.js",
"start": "cross-env NODE_ENV=production node ./bin/www", "start": "cross-env NODE_ENV=production node ./bin/www",
"dev:test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true nodemon ./bin/www", "dev:test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true nodemon ./bin/www",
"dev:live": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* nodemon ./bin/www", "dev:live": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* nodemon ./bin/www",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"temp:legacy:importFromCSV": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true node ./temp/legacy.importFromCSV.js",
"temp:so:importMaps": "node ./temp/so.importMaps.js" "temp:so:exportMaps": "node ./temp/so.exportMaps.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -49,6 +46,7 @@
"express-rate-limit": "^6.4.0", "express-rate-limit": "^6.4.0",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"session-file-store": "^1.5.0" "session-file-store": "^1.5.0"
}, },
"devDependencies": { "devDependencies": {
@ -68,9 +66,11 @@
"@types/gulp-minify": "^3.1.1", "@types/gulp-minify": "^3.1.1",
"@types/gulp-sass": "^5.0.0", "@types/gulp-sass": "^5.0.0",
"@types/http-errors": "^1.8.2", "@types/http-errors": "^1.8.2",
"@types/leaflet": "^1.7.11",
"@types/mocha": "^9.1.1", "@types/mocha": "^9.1.1",
"@types/mssql": "^8.0.3", "@types/mssql": "^8.0.3",
"@types/node-windows": "^0.1.2", "@types/node-windows": "^0.1.2",
"@types/papaparse": "^5.3.2",
"@types/session-file-store": "^1.2.2", "@types/session-file-store": "^1.2.2",
"@typescript-eslint/eslint-plugin": "^5.30.6", "@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6", "@typescript-eslint/parser": "^5.30.6",
@ -84,6 +84,7 @@
"gulp-minify": "^3.1.0", "gulp-minify": "^3.1.0",
"gulp-sass": "^5.1.0", "gulp-sass": "^5.1.0",
"nodemon": "^2.0.19", "nodemon": "^2.0.19",
"papaparse": "^5.3.2",
"sass": "^1.53.0" "sass": "^1.53.0"
} }
} }

View File

@ -1,93 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const formElement = document.querySelector("#form--filters");
const limitElement = document.querySelector("#filter--limit");
const offsetElement = document.querySelector("#filter--offset");
const searchResultsElement = document.querySelector("#container--searchResults");
const doLicenceSearchFunction = () => {
const currentLimit = Number.parseInt(limitElement.value, 10);
const currentOffset = Number.parseInt(offsetElement.value, 10);
searchResultsElement.innerHTML = "<p class=\"has-text-centered has-text-grey-lighter\">" +
"<i class=\"fas fa-3x fa-circle-notch fa-spin\" aria-hidden=\"true\"></i><br />" +
"<em>Loading licences...</em>" +
"</p>";
cityssm.postJSON(urlPrefix + "/licences/doSearch", formElement, (licenceResults) => {
const licenceList = licenceResults.licences;
if (licenceList.length === 0) {
searchResultsElement.innerHTML = "<div class=\"message is-info\">" +
"<div class=\"message-body\">" +
"<strong>Your search returned no results.</strong><br />" +
"Please try expanding your search criteria." +
"</div>" +
"</div>";
return;
}
searchResultsElement.innerHTML = "<table class=\"table is-fullwidth is-striped is-hoverable has-sticky-header\">" +
"<thead><tr>" +
"</tr></thead>" +
"<tbody></tbody>" +
"</table>";
const tbodyElement = searchResultsElement.querySelector("tbody");
for (const licenceObject of licenceList) {
const trElement = document.createElement("tr");
trElement.innerHTML = "";
tbodyElement.append(trElement);
}
searchResultsElement.insertAdjacentHTML("beforeend", "<div class=\"level is-block-print\">" +
"<div class=\"level-left has-text-weight-bold\">" +
"Displaying licences " +
(currentOffset + 1).toString() +
" to " +
Math.min(currentLimit + currentOffset, licenceResults.count).toString() +
" of " +
licenceResults.count.toString() +
"</div>" +
"</div>");
if (currentLimit < licenceResults.count) {
const paginationElement = document.createElement("nav");
paginationElement.className = "level-right is-hidden-print";
paginationElement.setAttribute("role", "pagination");
paginationElement.setAttribute("aria-label", "pagination");
if (currentOffset > 0) {
const previousElement = document.createElement("a");
previousElement.className = "button";
previousElement.textContent = "Previous";
previousElement.addEventListener("click", (clickEvent) => {
clickEvent.preventDefault();
offsetElement.value = Math.max(0, currentOffset - currentLimit).toString();
doLicenceSearchFunction();
});
paginationElement.append(previousElement);
}
if (currentLimit + currentOffset < licenceResults.count) {
const nextElement = document.createElement("a");
nextElement.className = "button ml-3";
nextElement.innerHTML =
"<span>Next Licences</span>" +
"<span class=\"icon\"><i class=\"fas fa-chevron-right\" aria-hidden=\"true\"></i></span>";
nextElement.addEventListener("click", (clickEvent) => {
clickEvent.preventDefault();
offsetElement.value = (currentOffset + currentLimit).toString();
doLicenceSearchFunction();
});
paginationElement.append(nextElement);
}
searchResultsElement.querySelector(".level").append(paginationElement);
}
});
};
const resetOffsetAndDoLicenceSearchFunction = () => {
offsetElement.value = "0";
doLicenceSearchFunction();
};
formElement.addEventListener("submit", (formEvent) => {
formEvent.preventDefault();
});
const inputElements = formElement.querySelectorAll(".input, .select select");
for (const inputElement of inputElements) {
inputElement.addEventListener("change", resetOffsetAndDoLicenceSearchFunction);
}
resetOffsetAndDoLicenceSearchFunction();
})();

View File

@ -1,142 +0,0 @@
/* eslint-disable unicorn/filename-case */
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
import type * as recordTypes from "../types/recordTypes";
declare const cityssm: cityssmGlobal;
(() => {
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const formElement = document.querySelector("#form--filters") as HTMLFormElement;
const limitElement = document.querySelector("#filter--limit") as HTMLInputElement;
const offsetElement = document.querySelector("#filter--offset") as HTMLInputElement;
const searchResultsElement = document.querySelector("#container--searchResults") as HTMLElement;
const doLicenceSearchFunction = () => {
const currentLimit = Number.parseInt(limitElement.value, 10);
const currentOffset = Number.parseInt(offsetElement.value, 10);
searchResultsElement.innerHTML = "<p class=\"has-text-centered has-text-grey-lighter\">" +
"<i class=\"fas fa-3x fa-circle-notch fa-spin\" aria-hidden=\"true\"></i><br />" +
"<em>Loading licences...</em>" +
"</p>";
cityssm.postJSON(urlPrefix + "/licences/doSearch",
formElement,
(licenceResults: { count: number; licences: recordTypes.Licence[] }) => {
const licenceList = licenceResults.licences;
if (licenceList.length === 0) {
searchResultsElement.innerHTML = "<div class=\"message is-info\">" +
"<div class=\"message-body\">" +
"<strong>Your search returned no results.</strong><br />" +
"Please try expanding your search criteria." +
"</div>" +
"</div>";
return;
}
searchResultsElement.innerHTML = "<table class=\"table is-fullwidth is-striped is-hoverable has-sticky-header\">" +
"<thead><tr>" +
"</tr></thead>" +
"<tbody></tbody>" +
"</table>";
const tbodyElement = searchResultsElement.querySelector("tbody");
for (const licenceObject of licenceList) {
const trElement = document.createElement("tr");
trElement.innerHTML = "";
tbodyElement.append(trElement);
}
searchResultsElement.insertAdjacentHTML("beforeend", "<div class=\"level is-block-print\">" +
"<div class=\"level-left has-text-weight-bold\">" +
"Displaying licences " +
(currentOffset + 1).toString() +
" to " +
Math.min(currentLimit + currentOffset, licenceResults.count).toString() +
" of " +
licenceResults.count.toString() +
"</div>" +
"</div>");
if (currentLimit < licenceResults.count) {
const paginationElement = document.createElement("nav");
paginationElement.className = "level-right is-hidden-print";
paginationElement.setAttribute("role", "pagination");
paginationElement.setAttribute("aria-label", "pagination");
if (currentOffset > 0) {
const previousElement = document.createElement("a");
previousElement.className = "button";
previousElement.textContent = "Previous";
previousElement.addEventListener("click", (clickEvent) => {
clickEvent.preventDefault();
offsetElement.value = Math.max(0, currentOffset - currentLimit).toString();
doLicenceSearchFunction();
});
paginationElement.append(previousElement);
}
if (currentLimit + currentOffset < licenceResults.count) {
const nextElement = document.createElement("a");
nextElement.className = "button ml-3";
nextElement.innerHTML =
"<span>Next Licences</span>" +
"<span class=\"icon\"><i class=\"fas fa-chevron-right\" aria-hidden=\"true\"></i></span>";
nextElement.addEventListener("click", (clickEvent) => {
clickEvent.preventDefault();
offsetElement.value = (currentOffset + currentLimit).toString();
doLicenceSearchFunction();
});
paginationElement.append(nextElement);
}
searchResultsElement.querySelector(".level").append(paginationElement);
}
}
);
};
const resetOffsetAndDoLicenceSearchFunction = () => {
offsetElement.value = "0";
doLicenceSearchFunction();
};
formElement.addEventListener("submit", (formEvent) => {
formEvent.preventDefault();
});
const inputElements = formElement.querySelectorAll(".input, .select select");
for (const inputElement of inputElements) {
inputElement.addEventListener("change", resetOffsetAndDoLicenceSearchFunction);
}
resetOffsetAndDoLicenceSearchFunction();
})();

View File

@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const maps = exports.maps;
const searchFilterElement = document.querySelector("#searchFilter--map");
const searchResultsContainerElement = document.querySelector("#container--searchResults");
const renderResults = () => {
searchResultsContainerElement.innerHTML = "<div class=\"has-text-grey has-text-centered\">" +
"<i class=\"fas fa-5x fa-circle-notch fa-spin\" aria-hidden=\"true\"></i><br />" +
"Loading " + exports.aliases.maps + "..." +
"</div>";
let searchResultCount = 0;
const searchResultsTbodyElement = document.createElement("tbody");
const filterStringSplit = searchFilterElement.value.trim().toLowerCase().split(" ");
for (const map of maps) {
const mapSearchString = (map.mapName + " " +
map.mapDescription + " " +
map.mapAddress1 + " " +
map.mapAddress2).toLowerCase();
let showMap = true;
for (const filterStringPiece of filterStringSplit) {
if (!mapSearchString.includes(filterStringPiece)) {
showMap = false;
break;
}
}
if (!showMap) {
continue;
}
searchResultCount += 1;
const mapName = map.mapName === ""
? "(No Name)"
: map.mapName;
searchResultsTbodyElement.insertAdjacentHTML("beforeend", "<tr>" +
("<td>" +
"<a class=\"has-text-weight-bold\" href=\"" + urlPrefix + "/maps/" + map.mapId + "\">" +
cityssm.escapeHTML(mapName) +
"</a><br />" +
cityssm.escapeHTML(map.mapAddress1) +
"</td>") +
"<td class=\"has-text-centered\">" +
(map.mapLatitude && map.mapLongitude
? "<i class=\"fas fa-map-marker-alt\" title=\"Has Geographic Coordinates\"></i>"
: "") +
"</td>" +
"<td class=\"has-text-centered\">" +
(map.mapSVG
? "<i class=\"fas fa-image\" title=\"Has Image\"></i>"
: "") +
"</td>" +
"<td class=\"has-text-right\">" + map.lotCount + "</td>" +
"</tr>");
}
searchResultsContainerElement.innerHTML = "";
if (searchResultCount === 0) {
searchResultsContainerElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">There are no " + exports.aliases.maps.toLowerCase() + " that meet the search criteria.</p>" +
"</div>";
}
else {
const searchResultsTableElement = document.createElement("table");
searchResultsTableElement.className = "table is-fullwidth is-striped is-hoverable";
searchResultsTableElement.innerHTML = "<thead><tr>" +
"<th>" + exports.aliases.map + "</th>" +
"<th class=\"has-text-centered\">Coordinates</th>" +
"<th class=\"has-text-centered\">Image</th>" +
"<th class=\"has-text-right\">" + exports.aliases.lot + " Count</th>" +
"</tr></thead>";
searchResultsTableElement.append(searchResultsTbodyElement);
searchResultsContainerElement.append(searchResultsTableElement);
}
};
searchFilterElement.addEventListener("keyup", renderResults);
document.querySelector("#form--searchFilters").addEventListener("submit", renderResults);
renderResults();
})();

View File

@ -0,0 +1,107 @@
/* eslint-disable unicorn/prefer-module */
import type * as recordTypes from "../types/recordTypes";
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
import type { BulmaJS } from "@cityssm/bulma-js/types";
declare const cityssm: cityssmGlobal;
declare const bulmaJS: BulmaJS;
(() => {
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const maps: recordTypes.Map[] = exports.maps;
const searchFilterElement = document.querySelector("#searchFilter--map") as HTMLInputElement;
const searchResultsContainerElement = document.querySelector("#container--searchResults") as HTMLElement;
const renderResults = () => {
searchResultsContainerElement.innerHTML = "<div class=\"has-text-grey has-text-centered\">" +
"<i class=\"fas fa-5x fa-circle-notch fa-spin\" aria-hidden=\"true\"></i><br />" +
"Loading " + exports.aliases.maps + "..." +
"</div>";
let searchResultCount = 0;
const searchResultsTbodyElement = document.createElement("tbody");
const filterStringSplit = searchFilterElement.value.trim().toLowerCase().split(" ");
for (const map of maps) {
const mapSearchString = (map.mapName + " " +
map.mapDescription + " " +
map.mapAddress1 + " " +
map.mapAddress2).toLowerCase();
let showMap = true;
for (const filterStringPiece of filterStringSplit) {
if (!mapSearchString.includes(filterStringPiece)) {
showMap = false;
break;
}
}
if (!showMap) {
continue;
}
searchResultCount += 1;
const mapName = map.mapName === ""
? "(No Name)"
: map.mapName;
searchResultsTbodyElement.insertAdjacentHTML("beforeend", "<tr>" +
("<td>" +
"<a class=\"has-text-weight-bold\" href=\"" + urlPrefix + "/maps/" + map.mapId + "\">" +
cityssm.escapeHTML(mapName) +
"</a><br />" +
cityssm.escapeHTML(map.mapAddress1) +
"</td>") +
"<td class=\"has-text-centered\">" +
(map.mapLatitude && map.mapLongitude
? "<i class=\"fas fa-map-marker-alt\" title=\"Has Geographic Coordinates\"></i>"
: "") +
"</td>" +
"<td class=\"has-text-centered\">" +
(map.mapSVG
? "<i class=\"fas fa-image\" title=\"Has Image\"></i>"
: "") +
"</td>" +
"<td class=\"has-text-right\">" + map.lotCount + "</td>" +
"</tr>");
}
searchResultsContainerElement.innerHTML = "";
if (searchResultCount === 0) {
searchResultsContainerElement.innerHTML = "<div class=\"message is-info\">" +
"<p class=\"message-body\">There are no " + exports.aliases.maps.toLowerCase() + " that meet the search criteria.</p>" +
"</div>";
} else {
const searchResultsTableElement = document.createElement("table");
searchResultsTableElement.className = "table is-fullwidth is-striped is-hoverable";
searchResultsTableElement.innerHTML = "<thead><tr>" +
"<th>" + exports.aliases.map + "</th>" +
"<th class=\"has-text-centered\">Coordinates</th>" +
"<th class=\"has-text-centered\">Image</th>" +
"<th class=\"has-text-right\">" + exports.aliases.lot + " Count</th>" +
"</tr></thead>";
searchResultsTableElement.append(searchResultsTbodyElement);
searchResultsContainerElement.append(searchResultsTableElement);
}
};
searchFilterElement.addEventListener("keyup", renderResults);
document.querySelector("#form--searchFilters").addEventListener("submit", renderResults);
renderResults();
})();

1
public-typescript/mapView.d.ts vendored 100644
View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
const mapContainerElement = document.querySelector("#map--leaflet");
if (mapContainerElement) {
const mapLatitude = Number.parseFloat(mapContainerElement.dataset.mapLatitude);
const mapLongitude = Number.parseFloat(mapContainerElement.dataset.mapLongitude);
const mapCoordinates = [mapLatitude, mapLongitude];
const map = L.map(mapContainerElement);
map.setView(mapCoordinates, 15);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
L.marker(mapCoordinates).addTo(map);
}
})();

View File

@ -0,0 +1,30 @@
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
import type { BulmaJS } from "@cityssm/bulma-js/types";
import type * as Leaflet from "leaflet";
declare const cityssm: cityssmGlobal;
declare const bulmaJS: BulmaJS;
declare const L;
(() => {
const mapContainerElement = document.querySelector("#map--leaflet") as HTMLElement;
if (mapContainerElement) {
const mapLatitude = Number.parseFloat(mapContainerElement.dataset.mapLatitude);
const mapLongitude = Number.parseFloat(mapContainerElement.dataset.mapLongitude);
const mapCoordinates: Leaflet.LatLngTuple = [mapLatitude, mapLongitude];
const map: Leaflet.Map = L.map(mapContainerElement);
map.setView(mapCoordinates, 15);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
L.marker(mapCoordinates).addTo(map);
}
})();

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