From 922603a1f2df30a76957ba21321b6e4d14f094b8 Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Fri, 28 Mar 2025 12:36:56 -0400 Subject: [PATCH] development and linting --- CONTRIBUTING.md | 2 +- README.md | 27 +++++++++++++++++++++----- data/config.baseSsm.js | 26 ++++++++++++------------- data/config.baseSsm.ts | 27 +++++++++++++------------- data/config.defaultValues.d.ts | 2 +- data/config.defaultValues.js | 10 +++++----- data/config.defaultValues.ts | 12 ++++++------ database/getBurialSiteComments.ts | 3 ++- database/getBurialSiteTypeSummary.d.ts | 6 +++--- database/getBurialSiteTypeSummary.ts | 8 ++++---- database/getCemeteries.js | 13 ++++++------- database/getCemeteries.ts | 13 ++++++------- routes/login.js | 2 +- routes/login.ts | 2 +- views/burialSite-edit.ejs | 4 ++-- 15 files changed, 87 insertions(+), 70 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd39be6f..8cba48f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Thank you for your interest in making the Lot Occupancy System better +# Thank you for your interest in making Sunrise CMS better Together, we can build high quality software that meets the needs of municipalities, while remaining open and budget conscious. diff --git a/README.md b/README.md index 6de76bd8..07a63b91 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,32 @@ This is a major refactoring of the now archived [Lot Occupancy System](https://github.com/cityssm/lot-occupancy-system), -originally built with multiple focuses. This fork completely reworks the project +originally built with multiple focuses in mind. This fork completely reworks the project to focus exculsively on cemetery management. +## Why Sunrise CMS? + +### ✔️ Cemetery Maps are NOT Required + +Many cemetery applications rely on maps to drill down into burial sites. +In Sunrise CMS, maps are completely optional, greatly reducing the effort needed to get started. + +### ✔️ The System Requirements are Very Low + +Sunrise CMS does not need an expensive server to run. +No separate database server is required either. +The whole application could run on a current, modest workstation. + +### ✔️ Sunrise CMS is Free and Open Source + +There's no cost to use it. + ## About this Project -- [Code of Conduct](CODE_OF_CONDUCT.md) -- [Contributing Guidelines](CONTRIBUTING.md) -- [Security Policy](SECURITY.md) -- [MIT Licence](LICENSE.md) +- 🤗 [Code of Conduct](CODE_OF_CONDUCT.md) +- 🥰 [Contributing Guidelines](CONTRIBUTING.md) +- 🛡️ [Security Policy](SECURITY.md) +- 📃 [MIT Licence](LICENSE.md) Although the system is quite niche, it's being released in an open source environment in hopes to pool developer resources from other municipalities looking to move away from older, legacy systems. diff --git a/data/config.baseSsm.js b/data/config.baseSsm.js index b3521bfa..f82f45ee 100644 --- a/data/config.baseSsm.js +++ b/data/config.baseSsm.js @@ -2,36 +2,36 @@ import { config as cemeteryConfig } from './config.baseOntario.js'; export const config = { ...cemeteryConfig }; config.aliases.externalReceiptNumber = 'GP Receipt Number'; config.settings.burialSites.burialSiteNameSegments = { - separator: '-', includeCemeteryKey: true, + separator: '-', segments: { 1: { - isRequired: false, isAvailable: true, + isRequired: false, label: 'Block', - minLength: 1, - maxLength: 1 + maxLength: 1, + minLength: 1 }, 2: { - isRequired: false, isAvailable: true, + isRequired: false, label: 'Range', - minLength: 1, - maxLength: 3 + maxLength: 3, + minLength: 1 }, 3: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Lot', - minLength: 1, - maxLength: 4 + maxLength: 4, + minLength: 1 }, 4: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Grave', - minLength: 1, - maxLength: 2 + maxLength: 2, + minLength: 1 } } }; diff --git a/data/config.baseSsm.ts b/data/config.baseSsm.ts index 7fc6dc4c..d70ac314 100644 --- a/data/config.baseSsm.ts +++ b/data/config.baseSsm.ts @@ -7,36 +7,37 @@ export const config: Config = { ...cemeteryConfig } config.aliases.externalReceiptNumber = 'GP Receipt Number' config.settings.burialSites.burialSiteNameSegments = { - separator: '-', includeCemeteryKey: true, + separator: '-', + segments: { 1: { - isRequired: false, isAvailable: true, + isRequired: false, label: 'Block', - minLength: 1, - maxLength: 1 + maxLength: 1, + minLength: 1 }, 2: { - isRequired: false, isAvailable: true, + isRequired: false, label: 'Range', - minLength: 1, - maxLength: 3 + maxLength: 3, + minLength: 1 }, 3: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Lot', - minLength: 1, - maxLength: 4 + maxLength: 4, + minLength: 1 }, 4: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Grave', - minLength: 1, - maxLength: 2 + maxLength: 2, + minLength: 1 } } } diff --git a/data/config.defaultValues.d.ts b/data/config.defaultValues.d.ts index 8c28a8d7..99be0898 100644 --- a/data/config.defaultValues.d.ts +++ b/data/config.defaultValues.d.ts @@ -7,9 +7,9 @@ export declare const configDefaultValues: { 'application.httpPort': number; 'application.logoURL': string; 'application.maximumProcesses': number; + 'application.ntfyStartup': ConfigNtfyStartup | undefined; 'application.userDomain': string; 'application.useTestDatabases': boolean; - 'application.ntfyStartup': ConfigNtfyStartup | undefined; 'reverseProxy.disableCompression': boolean; 'reverseProxy.disableEtag': boolean; 'reverseProxy.urlPrefix': string; diff --git a/data/config.defaultValues.js b/data/config.defaultValues.js index 14301403..a6f1dfc1 100644 --- a/data/config.defaultValues.js +++ b/data/config.defaultValues.js @@ -6,9 +6,9 @@ export const configDefaultValues = { 'application.httpPort': 9000, 'application.logoURL': '/images/sunrise-cms.svg', 'application.maximumProcesses': 4, + 'application.ntfyStartup': undefined, 'application.userDomain': '', 'application.useTestDatabases': false, - 'application.ntfyStartup': undefined, 'reverseProxy.disableCompression': false, 'reverseProxy.disableEtag': false, 'reverseProxy.urlPrefix': '', @@ -30,15 +30,15 @@ export const configDefaultValues = { 'settings.longitudeMax': 180, 'settings.longitudeMin': -180, 'settings.burialSites.burialSiteNameSegments': { - separator: '-', includeCemeteryKey: false, + separator: '-', segments: { 1: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Plot Number', - minLength: 1, - maxLength: 20 + maxLength: 20, + minLength: 1 } } }, diff --git a/data/config.defaultValues.ts b/data/config.defaultValues.ts index 23666d09..7dcece34 100644 --- a/data/config.defaultValues.ts +++ b/data/config.defaultValues.ts @@ -17,11 +17,10 @@ export const configDefaultValues = { 'application.httpPort': 9000, 'application.logoURL': '/images/sunrise-cms.svg', 'application.maximumProcesses': 4, + 'application.ntfyStartup': undefined as ConfigNtfyStartup | undefined, 'application.userDomain': '', 'application.useTestDatabases': false, - 'application.ntfyStartup': undefined as ConfigNtfyStartup | undefined, - 'reverseProxy.disableCompression': false, 'reverseProxy.disableEtag': false, 'reverseProxy.urlPrefix': '', @@ -49,15 +48,16 @@ export const configDefaultValues = { 'settings.longitudeMin': -180, 'settings.burialSites.burialSiteNameSegments': { - separator: '-', includeCemeteryKey: false, + separator: '-', + segments: { 1: { - isRequired: true, isAvailable: true, + isRequired: true, label: 'Plot Number', - minLength: 1, - maxLength: 20 + maxLength: 20, + minLength: 1 } } } as unknown as ConfigBurialSiteNameSegments, diff --git a/database/getBurialSiteComments.ts b/database/getBurialSiteComments.ts index 4bfcd14d..5326a1cf 100644 --- a/database/getBurialSiteComments.ts +++ b/database/getBurialSiteComments.ts @@ -1,9 +1,10 @@ +import type { PoolConnection } from 'better-sqlite-pool' + import { dateIntegerToString, timeIntegerToPeriodString, timeIntegerToString } from '@cityssm/utils-datetime' -import type { PoolConnection } from 'better-sqlite-pool' import type { BurialSiteComment } from '../types/recordTypes.js' diff --git a/database/getBurialSiteTypeSummary.d.ts b/database/getBurialSiteTypeSummary.d.ts index c7bc1a32..e0808779 100644 --- a/database/getBurialSiteTypeSummary.d.ts +++ b/database/getBurialSiteTypeSummary.d.ts @@ -1,9 +1,9 @@ import type { BurialSiteType } from '../types/recordTypes.js'; -interface GetFilters { - cemeteryId?: number | string; -} interface BurialSiteTypeSummary extends BurialSiteType { lotCount: number; } +interface GetFilters { + cemeteryId?: number | string; +} export default function getBurialSiteTypeSummary(filters: GetFilters): Promise; export {}; diff --git a/database/getBurialSiteTypeSummary.ts b/database/getBurialSiteTypeSummary.ts index 047fa50e..fb78e4fc 100644 --- a/database/getBurialSiteTypeSummary.ts +++ b/database/getBurialSiteTypeSummary.ts @@ -2,14 +2,14 @@ import type { BurialSiteType } from '../types/recordTypes.js' import { acquireConnection } from './pool.js' -interface GetFilters { - cemeteryId?: number | string -} - interface BurialSiteTypeSummary extends BurialSiteType { lotCount: number } +interface GetFilters { + cemeteryId?: number | string +} + export default async function getBurialSiteTypeSummary( filters: GetFilters ): Promise { diff --git a/database/getCemeteries.js b/database/getCemeteries.js index 0ea947f1..f0dcc960 100644 --- a/database/getCemeteries.js +++ b/database/getCemeteries.js @@ -6,15 +6,14 @@ export default async function getCemeteries() { m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, m.cemeteryPhoneNumber, - ifnull(l.burialSiteCount, 0) as burialSiteCount + count(b.burialSiteId) as burialSiteCount from Cemeteries m - left join ( - select cemeteryId, count(burialSiteId) as burialSiteCount - from BurialSites - where recordDelete_timeMillis is null - group by cemeteryId - ) l on m.cemeteryId = l.cemeteryId + left join BurialSites b on m.cemeteryId = b.cemeteryId and b.recordDelete_timeMillis is null where m.recordDelete_timeMillis is null + group by m.cemeteryId, m.cemeteryName, m.cemeteryDescription, + m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, + m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, + m.cemeteryPhoneNumber order by m.cemeteryName, m.cemeteryId`) .all(); database.release(); diff --git a/database/getCemeteries.ts b/database/getCemeteries.ts index cb4615eb..352cb718 100644 --- a/database/getCemeteries.ts +++ b/database/getCemeteries.ts @@ -11,15 +11,14 @@ export default async function getCemeteries(): Promise { m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, m.cemeteryPhoneNumber, - ifnull(l.burialSiteCount, 0) as burialSiteCount + count(b.burialSiteId) as burialSiteCount from Cemeteries m - left join ( - select cemeteryId, count(burialSiteId) as burialSiteCount - from BurialSites - where recordDelete_timeMillis is null - group by cemeteryId - ) l on m.cemeteryId = l.cemeteryId + left join BurialSites b on m.cemeteryId = b.cemeteryId and b.recordDelete_timeMillis is null where m.recordDelete_timeMillis is null + group by m.cemeteryId, m.cemeteryName, m.cemeteryDescription, + m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, + m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, + m.cemeteryPhoneNumber order by m.cemeteryName, m.cemeteryId` ) .all() as Cemetery[] diff --git a/routes/login.js b/routes/login.js index 02c81163..59a7583f 100644 --- a/routes/login.js +++ b/routes/login.js @@ -40,7 +40,7 @@ async function postHandler(request, response) { else if (userName !== '' && passwordPlain !== '') { isAuthenticated = await authenticate(userName, passwordPlain); } - let userObject = undefined; + let userObject; if (isAuthenticated) { const userNameLowerCase = userName.toLowerCase(); const canLogin = getConfigProperty('users.canLogin').some((currentUserName) => userNameLowerCase === currentUserName.toLowerCase()); diff --git a/routes/login.ts b/routes/login.ts index 6197d7dd..c21a9e5c 100644 --- a/routes/login.ts +++ b/routes/login.ts @@ -72,7 +72,7 @@ async function postHandler( isAuthenticated = await authenticate(userName, passwordPlain) } - let userObject: User | undefined = undefined + let userObject: User | undefined if (isAuthenticated) { const userNameLowerCase = userName.toLowerCase() diff --git a/views/burialSite-edit.ejs b/views/burialSite-edit.ejs index 80474aa3..1646575c 100644 --- a/views/burialSite-edit.ejs +++ b/views/burialSite-edit.ejs @@ -127,8 +127,8 @@ name="burialSiteNameSegment<%= segmentIndexString %>" value="<%= burialSite[`burialSiteNameSegment${segmentIndex}`] %>" type="text" - minlength="<%= Math.min(segment.minLength ?? 1, 20) %>" - maxlength="<%= Math.min(segment.maxLength ?? 20, 20) %>" + minlength="<%= Math.max(Math.min(segment.minLength ?? 1, 20), 1) %>" + maxlength="<%= Math.max(Math.min(segment.maxLength ?? 20, 20), 1) %>" placeholder="<%= segment.label ?? '' %>" aria-label="<%= segment.label ?? '' %>" <%= (segment.isRequired ?? false) ? ' required' : '' %>