development and linting
parent
66a697e097
commit
922603a1f2
|
|
@ -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.
|
||||
|
|
|
|||
27
README.md
27
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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<BurialSiteTypeSummary[]>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -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<BurialSiteTypeSummary[]> {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -11,15 +11,14 @@ export default async function getCemeteries(): Promise<Cemetery[]> {
|
|||
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[]
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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' : '' %>
|
||||
|
|
|
|||
Loading…
Reference in New Issue