public/javascripts/main.ts
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/indent */
import type { cityssmGlobal } from '@cityssm/bulma-webapp-js/src/types.js'
import type {
ConfigLicencePlateCountry,
ConfigLocationClass,
ConfigParkingTicketStatus
} from '../../types/configTypes.js'
import type { ptsGlobal } from '../../types/publicTypes.js'
declare const cityssm: cityssmGlobal
const pts: Partial<ptsGlobal> = {
urlPrefix: document.querySelector('main')?.dataset.urlPrefix
}
// CONFIG DEFAULTS
interface DefaultConfigProperties {
locationClasses?: ConfigLocationClass[]
ticketNumber_fieldLabel?: string
parkingTicketStatuses?: ConfigParkingTicketStatus[]
licencePlateCountryAliases?: Record<string, string>
licencePlateProvinceAliases?: Record<string, Record<string, string>>
licencePlateProvinces?: Record<string, ConfigLicencePlateCountry>
}
;(() => {
let defaultConfigProperties: DefaultConfigProperties = {}
let defaultConfigPropertiesIsLoaded = false
function loadConfigPropertiesFromStorage(): boolean {
try {
const defaultConfigPropertiesString = window.localStorage.getItem(
'defaultConfigProperties'
)
if (defaultConfigPropertiesString !== null) {
defaultConfigProperties = JSON.parse(defaultConfigPropertiesString)
defaultConfigPropertiesIsLoaded = true
return true
}
} catch {
defaultConfigProperties = {}
defaultConfigPropertiesIsLoaded = true
}
return false
}
pts.loadDefaultConfigProperties = (callbackFunction: () => void) => {
if (defaultConfigPropertiesIsLoaded) {
callbackFunction()
return
}
if (loadConfigPropertiesFromStorage()) {
callbackFunction()
return
}
cityssm.postJSON(
`${pts.urlPrefix}/dashboard/doGetDefaultConfigProperties`,
{},
(rawResponseJSON) => {
const defaultConfigPropertiesResult =
rawResponseJSON as DefaultConfigProperties
defaultConfigProperties = defaultConfigPropertiesResult
defaultConfigPropertiesIsLoaded = true
try {
window.localStorage.setItem(
'defaultConfigProperties',
JSON.stringify(defaultConfigProperties)
)
} catch {
// Ignore
}
callbackFunction()
}
)
}
pts.getDefaultConfigProperty = (
propertyName,
propertyValueCallbackFunction: (propertyValue: unknown) => void
) => {
// Check memory
if (defaultConfigPropertiesIsLoaded) {
propertyValueCallbackFunction(defaultConfigProperties[propertyName])
return
}
// Check local storage
if (loadConfigPropertiesFromStorage()) {
propertyValueCallbackFunction(defaultConfigProperties[propertyName])
return
}
// Populate local storage
pts.loadDefaultConfigProperties(() => {
propertyValueCallbackFunction(defaultConfigProperties[propertyName])
})
}
pts.getLicencePlateCountryProperties = (originalLicencePlateCountry) => {
if (!defaultConfigPropertiesIsLoaded) {
return {}
}
const licencePlateCountryAlias =
defaultConfigProperties.licencePlateCountryAliases?.[
originalLicencePlateCountry.toUpperCase()
] ?? originalLicencePlateCountry
if (
Object.prototype.hasOwnProperty.call(
defaultConfigProperties.licencePlateProvinces,
licencePlateCountryAlias
)
) {
return defaultConfigProperties.licencePlateProvinces?.[
licencePlateCountryAlias
]
}
return {}
}
pts.getLicencePlateLocationProperties = (
originalLicencePlateCountry,
originalLicencePlateProvince
) => {
const licencePlateProvinceDefault = {
provinceShortName: originalLicencePlateProvince,
color: '#000',
backgroundColor: '#fff'
}
if (!defaultConfigPropertiesIsLoaded) {
return {
licencePlateCountryAlias: originalLicencePlateCountry,
licencePlateProvinceAlias: originalLicencePlateProvince,
licencePlateProvince: licencePlateProvinceDefault
}
}
// Get the country alias
const licencePlateCountryAlias =
defaultConfigProperties.licencePlateCountryAliases?.[
originalLicencePlateCountry.toUpperCase()
] ?? originalLicencePlateCountry
// Get the province alias
let licencePlateProvinceAlias = originalLicencePlateProvince
if (
Object.prototype.hasOwnProperty.call(
defaultConfigProperties.licencePlateProvinceAliases,
licencePlateCountryAlias
)
) {
const provinceAliases =
defaultConfigProperties.licencePlateProvinceAliases?.[licencePlateCountryAlias] ?? {}
licencePlateProvinceAlias =
provinceAliases[originalLicencePlateProvince.toUpperCase()] ||
originalLicencePlateProvince
}
// Get the province object
let licencePlateProvince = licencePlateProvinceDefault
if (
Object.prototype.hasOwnProperty.call(
defaultConfigProperties.licencePlateProvinces,
licencePlateCountryAlias
)
) {
licencePlateProvince =
defaultConfigProperties.licencePlateProvinces?.[licencePlateCountryAlias]
.provinces[licencePlateProvinceAlias] || licencePlateProvinceDefault
}
// Return
return {
licencePlateCountryAlias,
licencePlateProvinceAlias,
licencePlateProvince
}
}
const ticketStatusKeyToObject = new Map<string, ConfigParkingTicketStatus>()
let ticketStatusKeyToObjectIsLoaded = false
pts.getTicketStatus = (statusKey) => {
const noResult = {
statusKey,
status: statusKey,
isUserSettable: false,
isFinalStatus: false
}
if (!defaultConfigPropertiesIsLoaded) {
return noResult
}
if (!ticketStatusKeyToObjectIsLoaded) {
for (const ticketStatusObject of defaultConfigProperties.parkingTicketStatuses ??
[]) {
ticketStatusKeyToObject.set(
ticketStatusObject.statusKey,
ticketStatusObject
)
}
ticketStatusKeyToObjectIsLoaded = true
}
return ticketStatusKeyToObject.has(statusKey)
? ticketStatusKeyToObject.get(statusKey)
: noResult
}
const locationClassKeyToObject = new Map<string, ConfigLocationClass>()
let locationClassKeyToObjectIsLoaded = false
pts.getLocationClass = (locationClassKey) => {
const noResult: ConfigLocationClass = {
locationClassKey,
locationClass: locationClassKey
}
if (!defaultConfigPropertiesIsLoaded) {
return noResult
}
if (!locationClassKeyToObjectIsLoaded) {
for (const locationClassObject of defaultConfigProperties.locationClasses ??
[]) {
locationClassKeyToObject.set(
locationClassObject.locationClassKey,
locationClassObject
)
}
locationClassKeyToObjectIsLoaded = true
}
return locationClassKeyToObject.has(locationClassKey)
? locationClassKeyToObject.get(locationClassKey)
: noResult
}
})()
// TABS
pts.initializeTabs = (tabsListElement, callbackFunctions) => {
if (!tabsListElement) {
return
}
const isPanelOrMenuListTabs =
tabsListElement.classList.contains('panel-tabs') ||
tabsListElement.classList.contains('menu-list')
const listItemElements = tabsListElement.querySelectorAll(
isPanelOrMenuListTabs ? 'a' : 'li'
)
const tabLinkElements = isPanelOrMenuListTabs
? listItemElements
: tabsListElement.querySelectorAll('a')
function tabClickFunction(clickEvent: Event): void {
clickEvent.preventDefault()
const selectedTabLinkElement = clickEvent.currentTarget as HTMLAnchorElement
const selectedTabContentElement = document.querySelector(
selectedTabLinkElement.getAttribute('href') ?? ''
) as HTMLElement
for (const [index, listItemElement] of listItemElements.entries()) {
listItemElement.classList.remove('is-active')
tabLinkElements[index].setAttribute('aria-selected', 'false')
}
// Add .is-active to the selected tab
;(isPanelOrMenuListTabs
? selectedTabLinkElement
: selectedTabLinkElement.parentElement
)?.classList.add('is-active')
selectedTabLinkElement.setAttribute('aria-selected', 'true')
const tabContentElements =
selectedTabContentElement.parentElement?.querySelectorAll('.tab-content')
for (const tabContentElement_ of tabContentElements ?? []) {
tabContentElement_.classList.remove('is-active')
}
selectedTabContentElement.classList.add('is-active')
if (callbackFunctions?.onshown) {
callbackFunctions.onshown(selectedTabContentElement)
}
}
for (const listItemElement of listItemElements) {
;(isPanelOrMenuListTabs
? listItemElement
: listItemElement.querySelector('a')
)?.addEventListener('click', tabClickFunction)
}
}
// TOGGLE CONTAINERS
;(() => {
function toggleHiddenFunction(clickEvent: Event): void {
clickEvent.preventDefault()
const href = (clickEvent.currentTarget as HTMLAnchorElement).href
const divId = href.slice(Math.max(0, href.indexOf('#') + 1))
document.querySelector(`#${divId}`)?.classList.toggle('is-hidden')
}
pts.initializeToggleHiddenLinks = (searchContainerElement) => {
const linkElements = searchContainerElement.querySelectorAll(
'.is-toggle-hidden-link'
)
for (const linkElement of linkElements) {
linkElement.addEventListener('click', toggleHiddenFunction)
}
}
})()