diff --git a/database/addAssetGroup.d.ts b/database/addAssetGroup.d.ts index 3069b70..343f292 100644 --- a/database/addAssetGroup.d.ts +++ b/database/addAssetGroup.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { AssetGroup } from '../types/recordTypes.js'; -export declare function addAssetGroup(group: AssetGroup, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addAssetGroup(group: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addAssetGroup.ts b/database/addAssetGroup.ts index f32f7b2..9198709 100644 --- a/database/addAssetGroup.ts +++ b/database/addAssetGroup.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { AssetGroup } from '../types/recordTypes.js' export function addAssetGroup( - group: AssetGroup, + group: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addAssetGroupMember.d.ts b/database/addAssetGroupMember.d.ts new file mode 100644 index 0000000..97bd433 --- /dev/null +++ b/database/addAssetGroupMember.d.ts @@ -0,0 +1 @@ +export declare function addAssetGroupMember(groupId: number | string, assetId: number | string, sessionUser: EmileUser): boolean; diff --git a/database/addAssetGroupMember.js b/database/addAssetGroupMember.js new file mode 100644 index 0000000..4a93738 --- /dev/null +++ b/database/addAssetGroupMember.js @@ -0,0 +1,27 @@ +import sqlite from 'better-sqlite3'; +import { databasePath } from '../helpers/functions.database.js'; +export function addAssetGroupMember(groupId, assetId, sessionUser) { + const emileDB = sqlite(databasePath); + const rightNowMillis = Date.now(); + let result = emileDB + .prepare(`update AssetGroupMembers + set recordDelete_userName = null, + recordDelete_timeMillis = null, + recordUpdate_userName = ?, + recordUpdate_timeMillis = ? + where recordDelete_timeMillis is not null + and groupId = ? + and assetId = ?`) + .run(sessionUser.userName, rightNowMillis, groupId, assetId); + if (result.changes === 0) { + result = emileDB + .prepare(`insert into AssetGroupMembers ( + groupId, assetId, + recordCreate_userName, recordCreate_timeMillis, + recordUpdate_userName, recordUpdate_timeMillis) + values (?, ?, ?, ?, ?, ?)`) + .run(groupId, assetId, sessionUser.userName, rightNowMillis, sessionUser.userName, rightNowMillis); + } + emileDB.close(); + return result.changes > 0; +} diff --git a/database/addAssetGroupMember.ts b/database/addAssetGroupMember.ts new file mode 100644 index 0000000..9540677 --- /dev/null +++ b/database/addAssetGroupMember.ts @@ -0,0 +1,49 @@ +import sqlite from 'better-sqlite3' + +import { databasePath } from '../helpers/functions.database.js' + +export function addAssetGroupMember( + groupId: number | string, + assetId: number | string, + sessionUser: EmileUser +): boolean { + const emileDB = sqlite(databasePath) + + const rightNowMillis = Date.now() + + let result = emileDB + .prepare( + `update AssetGroupMembers + set recordDelete_userName = null, + recordDelete_timeMillis = null, + recordUpdate_userName = ?, + recordUpdate_timeMillis = ? + where recordDelete_timeMillis is not null + and groupId = ? + and assetId = ?` + ) + .run(sessionUser.userName, rightNowMillis, groupId, assetId) + + if (result.changes === 0) { + result = emileDB + .prepare( + `insert into AssetGroupMembers ( + groupId, assetId, + recordCreate_userName, recordCreate_timeMillis, + recordUpdate_userName, recordUpdate_timeMillis) + values (?, ?, ?, ?, ?, ?)` + ) + .run( + groupId, + assetId, + sessionUser.userName, + rightNowMillis, + sessionUser.userName, + rightNowMillis + ) + } + + emileDB.close() + + return result.changes > 0 +} diff --git a/database/addEnergyAccumulationBehaviour.d.ts b/database/addEnergyAccumulationBehaviour.d.ts index 2babd53..697bf23 100644 --- a/database/addEnergyAccumulationBehaviour.d.ts +++ b/database/addEnergyAccumulationBehaviour.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyAccumulationBehaviour } from '../types/recordTypes.js'; -export declare function addEnergyAccumulationBehaviour(accumulationBehaviour: EnergyAccumulationBehaviour, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyAccumulationBehaviour(accumulationBehaviour: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyAccumulationBehaviour.ts b/database/addEnergyAccumulationBehaviour.ts index 1a0d14f..98b52c7 100644 --- a/database/addEnergyAccumulationBehaviour.ts +++ b/database/addEnergyAccumulationBehaviour.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyAccumulationBehaviour } from '../types/recordTypes.js' export function addEnergyAccumulationBehaviour( - accumulationBehaviour: EnergyAccumulationBehaviour, + accumulationBehaviour: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addEnergyCommodity.d.ts b/database/addEnergyCommodity.d.ts index 2092ff6..0b3bd4d 100644 --- a/database/addEnergyCommodity.d.ts +++ b/database/addEnergyCommodity.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyCommodity } from '../types/recordTypes.js'; -export declare function addEnergyCommodity(commodity: EnergyCommodity, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyCommodity(commodity: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyCommodity.ts b/database/addEnergyCommodity.ts index 6992737..d8ac8f8 100644 --- a/database/addEnergyCommodity.ts +++ b/database/addEnergyCommodity.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyCommodity } from '../types/recordTypes.js' export function addEnergyCommodity( - commodity: EnergyCommodity, + commodity: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addEnergyDataType.d.ts b/database/addEnergyDataType.d.ts index 1d4609a..6e6ea81 100644 --- a/database/addEnergyDataType.d.ts +++ b/database/addEnergyDataType.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyDataType } from '../types/recordTypes.js'; -export declare function addEnergyDataType(energyDataType: EnergyDataType, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyDataType(energyDataType: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyDataType.ts b/database/addEnergyDataType.ts index 6512798..b732ea6 100644 --- a/database/addEnergyDataType.ts +++ b/database/addEnergyDataType.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyDataType } from '../types/recordTypes.js' export function addEnergyDataType( - energyDataType: EnergyDataType, + energyDataType: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addEnergyReadingType.d.ts b/database/addEnergyReadingType.d.ts index acbfb7f..5630eb9 100644 --- a/database/addEnergyReadingType.d.ts +++ b/database/addEnergyReadingType.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyReadingType } from '../types/recordTypes.js'; -export declare function addEnergyReadingType(readingType: EnergyReadingType, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyReadingType(readingType: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyReadingType.ts b/database/addEnergyReadingType.ts index 7e5b8d6..b5d1497 100644 --- a/database/addEnergyReadingType.ts +++ b/database/addEnergyReadingType.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyReadingType } from '../types/recordTypes.js' export function addEnergyReadingType( - readingType: EnergyReadingType, + readingType: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addEnergyServiceCategory.d.ts b/database/addEnergyServiceCategory.d.ts index 28b44ae..9beb79c 100644 --- a/database/addEnergyServiceCategory.d.ts +++ b/database/addEnergyServiceCategory.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyServiceCategory } from '../types/recordTypes.js'; -export declare function addEnergyServiceCategory(serviceCategory: EnergyServiceCategory, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyServiceCategory(serviceCategory: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyServiceCategory.ts b/database/addEnergyServiceCategory.ts index 6d48d0d..2aacf24 100644 --- a/database/addEnergyServiceCategory.ts +++ b/database/addEnergyServiceCategory.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyServiceCategory } from '../types/recordTypes.js' export function addEnergyServiceCategory( - serviceCategory: EnergyServiceCategory, + serviceCategory: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/addEnergyUnit.d.ts b/database/addEnergyUnit.d.ts index 33edf0f..d108738 100644 --- a/database/addEnergyUnit.d.ts +++ b/database/addEnergyUnit.d.ts @@ -1,3 +1,3 @@ import sqlite from 'better-sqlite3'; import type { EnergyUnit } from '../types/recordTypes.js'; -export declare function addEnergyUnit(unit: EnergyUnit, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; +export declare function addEnergyUnit(unit: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database): number; diff --git a/database/addEnergyUnit.ts b/database/addEnergyUnit.ts index 027b571..a72e399 100644 --- a/database/addEnergyUnit.ts +++ b/database/addEnergyUnit.ts @@ -4,7 +4,7 @@ import { databasePath } from '../helpers/functions.database.js' import type { EnergyUnit } from '../types/recordTypes.js' export function addEnergyUnit( - unit: EnergyUnit, + unit: Partial, sessionUser: EmileUser, connectedEmileDB?: sqlite.Database ): number { diff --git a/database/deleteAssetGroupMember.d.ts b/database/deleteAssetGroupMember.d.ts new file mode 100644 index 0000000..67d5de5 --- /dev/null +++ b/database/deleteAssetGroupMember.d.ts @@ -0,0 +1 @@ +export declare function deleteAssetGroupMember(groupId: number | string, assetId: number | string, sessionUser: EmileUser): boolean; diff --git a/database/deleteAssetGroupMember.js b/database/deleteAssetGroupMember.js new file mode 100644 index 0000000..ded2418 --- /dev/null +++ b/database/deleteAssetGroupMember.js @@ -0,0 +1,15 @@ +import sqlite from 'better-sqlite3'; +import { databasePath } from '../helpers/functions.database.js'; +export function deleteAssetGroupMember(groupId, assetId, sessionUser) { + const emileDB = sqlite(databasePath); + const result = emileDB + .prepare(`update AssetGroupMembers + set recordDelete_userName = ?, + recordDelete_timeMillis = ? + where recordDelete_timeMillis is null + and groupId = ? + and assetId = ?`) + .run(sessionUser.userName, Date.now(), groupId, assetId); + emileDB.close(); + return result.changes > 0; +} diff --git a/database/deleteAssetGroupMember.ts b/database/deleteAssetGroupMember.ts new file mode 100644 index 0000000..c18e016 --- /dev/null +++ b/database/deleteAssetGroupMember.ts @@ -0,0 +1,26 @@ +import sqlite from 'better-sqlite3' + +import { databasePath } from '../helpers/functions.database.js' + +export function deleteAssetGroupMember( + groupId: number | string, + assetId: number | string, + sessionUser: EmileUser +): boolean { + const emileDB = sqlite(databasePath) + + const result = emileDB + .prepare( + `update AssetGroupMembers + set recordDelete_userName = ?, + recordDelete_timeMillis = ? + where recordDelete_timeMillis is null + and groupId = ? + and assetId = ?` + ) + .run(sessionUser.userName, Date.now(), groupId, assetId) + + emileDB.close() + + return result.changes > 0 +} diff --git a/handlers/assets-post/doAddAssetGroupMember.d.ts b/handlers/assets-post/doAddAssetGroupMember.d.ts new file mode 100644 index 0000000..d34b4be --- /dev/null +++ b/handlers/assets-post/doAddAssetGroupMember.d.ts @@ -0,0 +1,3 @@ +import type { Request, Response } from 'express'; +export declare function handler(request: Request, response: Response): void; +export default handler; diff --git a/handlers/assets-post/doAddAssetGroupMember.js b/handlers/assets-post/doAddAssetGroupMember.js new file mode 100644 index 0000000..67694ba --- /dev/null +++ b/handlers/assets-post/doAddAssetGroupMember.js @@ -0,0 +1,13 @@ +import { addAssetGroupMember } from '../../database/addAssetGroupMember.js'; +import { getAssets } from '../../database/getAssets.js'; +export function handler(request, response) { + const success = addAssetGroupMember(request.body.groupId, request.body.assetId, request.session.user); + const groupMembers = getAssets({ + groupId: request.body.groupId + }); + response.json({ + success, + groupMembers + }); +} +export default handler; diff --git a/handlers/assets-post/doAddAssetGroupMember.ts b/handlers/assets-post/doAddAssetGroupMember.ts new file mode 100644 index 0000000..7abf3df --- /dev/null +++ b/handlers/assets-post/doAddAssetGroupMember.ts @@ -0,0 +1,23 @@ +import type { Request, Response } from 'express' + +import { addAssetGroupMember } from '../../database/addAssetGroupMember.js' +import { getAssets } from '../../database/getAssets.js' + +export function handler(request: Request, response: Response): void { + const success = addAssetGroupMember( + request.body.groupId, + request.body.assetId, + request.session.user as EmileUser + ) + + const groupMembers = getAssets({ + groupId: request.body.groupId + }) + + response.json({ + success, + groupMembers + }) +} + +export default handler diff --git a/handlers/assets-post/doDeleteAssetGroupMember.d.ts b/handlers/assets-post/doDeleteAssetGroupMember.d.ts new file mode 100644 index 0000000..d34b4be --- /dev/null +++ b/handlers/assets-post/doDeleteAssetGroupMember.d.ts @@ -0,0 +1,3 @@ +import type { Request, Response } from 'express'; +export declare function handler(request: Request, response: Response): void; +export default handler; diff --git a/handlers/assets-post/doDeleteAssetGroupMember.js b/handlers/assets-post/doDeleteAssetGroupMember.js new file mode 100644 index 0000000..0348ff5 --- /dev/null +++ b/handlers/assets-post/doDeleteAssetGroupMember.js @@ -0,0 +1,13 @@ +import { deleteAssetGroupMember } from '../../database/deleteAssetGroupMember.js'; +import { getAssets } from '../../database/getAssets.js'; +export function handler(request, response) { + const success = deleteAssetGroupMember(request.body.groupId, request.body.assetId, request.session.user); + const groupMembers = getAssets({ + groupId: request.body.groupId + }); + response.json({ + success, + groupMembers + }); +} +export default handler; diff --git a/handlers/assets-post/doDeleteAssetGroupMember.ts b/handlers/assets-post/doDeleteAssetGroupMember.ts new file mode 100644 index 0000000..143ff3e --- /dev/null +++ b/handlers/assets-post/doDeleteAssetGroupMember.ts @@ -0,0 +1,23 @@ +import type { Request, Response } from 'express' + +import { deleteAssetGroupMember } from '../../database/deleteAssetGroupMember.js' +import { getAssets } from '../../database/getAssets.js' + +export function handler(request: Request, response: Response): void { + const success = deleteAssetGroupMember( + request.body.groupId, + request.body.assetId, + request.session.user as EmileUser + ) + + const groupMembers = getAssets({ + groupId: request.body.groupId + }) + + response.json({ + success, + groupMembers + }) +} + +export default handler diff --git a/public-typescript/assetGroups.d.ts b/public-typescript/assetGroups.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/public-typescript/assetGroups.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/public-typescript/assetGroups.js b/public-typescript/assetGroups.js new file mode 100644 index 0000000..fe4aa41 --- /dev/null +++ b/public-typescript/assetGroups.js @@ -0,0 +1,334 @@ +"use strict"; +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable @typescript-eslint/indent */ +Object.defineProperty(exports, "__esModule", { value: true }); +; +(() => { + var _a; + const Emile = exports.Emile; + const assetGroupFilterElement = document.querySelector('#filter--assetGroups'); + function deleteGroupMember(clickEvent) { + var _a, _b; + const rowElement = clickEvent.currentTarget.closest('tr'); + const groupId = (_a = rowElement.dataset.groupId) !== null && _a !== void 0 ? _a : ''; + const assetId = (_b = rowElement.dataset.assetId) !== null && _b !== void 0 ? _b : ''; + function doDelete() { + cityssm.postJSON(Emile.urlPrefix + '/assets/doDeleteAssetGroupMember', { + groupId, + assetId + }, (rawResponseJSON) => { + var _a; + const responseJSON = rawResponseJSON; + if (responseJSON.success) { + renderAssetGroupMembers(groupId, responseJSON.groupMembers); + } + else { + bulmaJS.alert({ + title: 'Error Deleting Group Member', + message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : 'Please try again.', + contextualColorName: 'danger' + }); + } + }); + } + bulmaJS.confirm({ + title: 'Delete Group Member', + message: 'Are you sure you want to remove this asset from this group?', + contextualColorName: 'warning', + okButton: { + text: 'Yes, Remove Member from Group', + callbackFunction: doDelete + } + }); + } + function renderAssetGroupMembers(groupId, groupMembers) { + var _a, _b, _c, _d, _e, _f; + const tbodyElement = document.querySelector('.modal #tbody--groupMembers'); + tbodyElement.innerHTML = ''; + for (const groupMember of groupMembers) { + const rowElement = document.createElement('tr'); + rowElement.dataset.groupId = groupId; + rowElement.dataset.assetId = groupMember.assetId.toString(); + rowElement.innerHTML = ` + + + ${Emile.canUpdate + ? `` + : ''} + `; + rowElement.querySelector('[data-field="category"]').textContent = (_a = groupMember.category) !== null && _a !== void 0 ? _a : ''; + rowElement.querySelector('[data-field="assetName"]').textContent = (_b = groupMember.assetName) !== null && _b !== void 0 ? _b : ''; + (_c = rowElement + .querySelector('.is-delete-button')) === null || _c === void 0 ? void 0 : _c.addEventListener('click', deleteGroupMember); + tbodyElement.append(rowElement); + } + if (Emile.canUpdate) { + const groupMemberSelectElement = document.querySelector('.modal #groupMemberAdd--assetId'); + groupMemberSelectElement.innerHTML = + ''; + for (const asset of Emile.assets) { + const assetInGroup = groupMembers.some((possibleAsset) => { + return possibleAsset.assetId === asset.assetId; + }); + if (assetInGroup) { + continue; + } + const optionElement = document.createElement('option'); + optionElement.value = asset.assetId.toString(); + optionElement.textContent = asset.assetName; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + let optgroupElement = groupMemberSelectElement.querySelector(`optgroup[data-category-id="${(_d = asset.categoryId) !== null && _d !== void 0 ? _d : ''}"]`); + if (optgroupElement === null) { + optgroupElement = document.createElement('optgroup'); + optgroupElement.dataset.categoryId = (_e = asset.categoryId) === null || _e === void 0 ? void 0 : _e.toString(); + optgroupElement.label = (_f = asset.category) !== null && _f !== void 0 ? _f : ''; + groupMemberSelectElement.append(optgroupElement); + } + optgroupElement.append(optionElement); + } + } + } + function populateAssetGroupModal(modalElement, groupId) { + cityssm.postJSON(Emile.urlPrefix + '/assets/doGetAssetGroup', { + groupId + }, (rawResponseJSON) => { + var _a, _b, _c; + const responseJSON = rawResponseJSON; + if (!responseJSON.success) { + bulmaJS.alert({ + title: 'Error Loading Asset Group Details', + message: 'Please refresh the page and try again.', + contextualColorName: 'danger' + }); + return; + } + ; + modalElement.querySelector('#assetGroupView--groupId').value = (_b = (_a = responseJSON.assetGroup.groupId) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''; + modalElement.querySelector('.modal-card-head [data-field="groupName"]').textContent = responseJSON.assetGroup.groupName; + modalElement.querySelector('#assetGroupView--groupName').value = responseJSON.assetGroup.groupName; + modalElement.querySelector('#assetGroupView--isShared').value = responseJSON.assetGroup.isShared ? '1' : '0'; + modalElement.querySelector('#assetGroupView--groupDescription').value = responseJSON.assetGroup.groupDescription; + renderAssetGroupMembers(groupId, (_c = responseJSON.assetGroup.groupMembers) !== null && _c !== void 0 ? _c : []); + }); + } + function updateAssetGroup(formEvent) { + formEvent.preventDefault(); + cityssm.postJSON(Emile.urlPrefix + '/assets/doUpdateAssetGroup', formEvent.currentTarget, (rawResponseJSON) => { + var _a; + const responseJSON = rawResponseJSON; + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups; + bulmaJS.alert({ + message: 'Asset group updated successfully.', + contextualColorName: 'success' + }); + renderAssetGroups(); + } + else { + bulmaJS.alert({ + title: 'Error Updating Asset Group', + message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : 'Please try again.', + contextualColorName: 'danger' + }); + } + }); + } + function addAssetGroupMember(formEvent) { + formEvent.preventDefault(); + const formElement = formEvent.currentTarget; + cityssm.postJSON(Emile.urlPrefix + '/assets/doAddAssetGroupMember', formElement, (rawResponseJSON) => { + var _a; + const responseJSON = rawResponseJSON; + if (responseJSON.success) { + renderAssetGroupMembers(formElement.querySelector('[name="groupId"]') + .value, responseJSON.groupMembers); + formElement.reset(); + } + else { + bulmaJS.alert({ + title: 'Error Adding Group Member', + message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : 'Please try again.', + contextualColorName: 'danger' + }); + } + }); + } + function openGroupByGroupId(groupId) { + let assetGroupCloseModalFunction; + function deleteAssetGroup(clickEvent) { + clickEvent.preventDefault(); + function doDelete() { + cityssm.postJSON(Emile.urlPrefix + '/assets/doDeleteAssetGroup', { + groupId + }, (rawResponseJSON) => { + var _a; + const responseJSON = rawResponseJSON; + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups; + renderAssetGroups(); + assetGroupCloseModalFunction(); + } + else { + bulmaJS.alert({ + title: 'Error Deleting Asset Group', + message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : '', + contextualColorName: 'danger' + }); + } + }); + } + bulmaJS.confirm({ + title: 'Delete Asset Group', + message: 'Are you sure you want to delete this group?', + contextualColorName: 'warning', + okButton: { + text: 'Yes, Delete Asset Group', + callbackFunction: doDelete + } + }); + } + cityssm.openHtmlModal('assetGroup-view', { + onshow(modalElement) { + var _a; + populateAssetGroupModal(modalElement, groupId); + if (Emile.canUpdate) { + ; + modalElement.querySelector('#form--assetGroupView fieldset').disabled = false; + modalElement.querySelector('#groupMemberAdd--groupId').value = groupId; + } + else { + (_a = modalElement.querySelector('#tbody--groupMemberAdd')) === null || _a === void 0 ? void 0 : _a.remove(); + } + }, + onshown(modalElement, closeModalFunction) { + var _a, _b, _c; + bulmaJS.toggleHtmlClipped(); + bulmaJS.init(modalElement); + if (Emile.canUpdate) { + assetGroupCloseModalFunction = closeModalFunction; + (_a = modalElement + .querySelector('#form--assetGroupView')) === null || _a === void 0 ? void 0 : _a.addEventListener('submit', updateAssetGroup); + (_b = modalElement + .querySelector('.is-delete-button')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', deleteAssetGroup); + (_c = modalElement + .querySelector('#form--groupMemberAdd')) === null || _c === void 0 ? void 0 : _c.addEventListener('submit', addAssetGroupMember); + } + }, + onremoved() { + bulmaJS.toggleHtmlClipped(); + } + }); + } + function openGroupByClick(clickEvent) { + var _a, _b; + clickEvent.preventDefault(); + const groupId = (_b = (_a = clickEvent.currentTarget.closest('tr')) === null || _a === void 0 ? void 0 : _a.dataset.groupId) !== null && _b !== void 0 ? _b : ''; + openGroupByGroupId(groupId); + } + function renderAssetGroups() { + var _a, _b, _c; + ; + document.querySelector('#count--assetGroups').textContent = Emile.assetGroups.length.toString(); + const containerElement = document.querySelector('#container--assetGroups'); + if (Emile.assetGroups.length === 0) { + containerElement.innerHTML = `
+

+ No Asset Groups Found
+ Grouping together assets can assist with reporting on multiple assets. +

+
`; + return; + } + const searchPieces = assetGroupFilterElement.value + .trim() + .toLowerCase() + .split(' '); + const tableElement = document.createElement('table'); + tableElement.className = 'table is-fullwidth is-striped has-sticky-header'; + tableElement.innerHTML = ` + Group + Members + Shared + + `; + // eslint-disable-next-line no-labels + assetGroupLoop: for (const assetGroup of Emile.assetGroups) { + const searchText = assetGroup.groupName.toLowerCase() + + ' ' + + ((_a = assetGroup.groupDescription) !== null && _a !== void 0 ? _a : '').toLowerCase(); + for (const searchPiece of searchPieces) { + if (!searchText.includes(searchPiece)) { + // eslint-disable-next-line no-labels + continue assetGroupLoop; + } + } + const rowElement = document.createElement('tr'); + rowElement.dataset.groupId = assetGroup.groupId.toString(); + rowElement.innerHTML = ` +
+ + + + ${(_b = assetGroup.groupMemberCount) !== null && _b !== void 0 ? _b : 0} + + + ${assetGroup.isShared ? 'Shared Group' : 'Private'} + `; + const groupNameElement = rowElement.querySelector('[data-field="groupName"]'); + groupNameElement.textContent = assetGroup.groupName; + groupNameElement.addEventListener('click', openGroupByClick); + tableElement.querySelector('tbody').append(rowElement); + rowElement.querySelector('[data-field="groupDescription"]').textContent = (_c = assetGroup.groupDescription) !== null && _c !== void 0 ? _c : ''; + } + if (tableElement.querySelectorAll('tbody tr').length === 0) { + containerElement.innerHTML = `
+

+ There are no groups that meet your search criteria.
+ Try to be less specific in your search. +

+
`; + } + else { + containerElement.innerHTML = ''; + containerElement.append(tableElement); + } + } + (_a = document + .querySelector('#button--addAssetGroup')) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => { + let addAssetGroupCloseModalFunction; + function doAddAssetGroup(formEvent) { + formEvent.preventDefault(); + cityssm.postJSON(Emile.urlPrefix + '/assets/doAddAssetGroup', formEvent.currentTarget, (rawResponseJSON) => { + const responseJSON = rawResponseJSON; + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups; + renderAssetGroups(); + addAssetGroupCloseModalFunction(); + openGroupByGroupId(responseJSON.groupId.toString()); + } + }); + } + cityssm.openHtmlModal('assetGroup-add', { + onshown(modalElement, closeModalFunction) { + var _a; + addAssetGroupCloseModalFunction = closeModalFunction; + bulmaJS.toggleHtmlClipped(); + modalElement.querySelector('#assetGroupAdd--groupName').focus(); + (_a = modalElement + .querySelector('form')) === null || _a === void 0 ? void 0 : _a.addEventListener('submit', doAddAssetGroup); + }, + onremoved() { + bulmaJS.toggleHtmlClipped(); + } + }); + }); + /* + * Initialize + */ + // Asset Groups + assetGroupFilterElement.addEventListener('keyup', renderAssetGroups); + renderAssetGroups(); +})(); diff --git a/public-typescript/assetGroups.ts b/public-typescript/assetGroups.ts new file mode 100644 index 0000000..92108f8 --- /dev/null +++ b/public-typescript/assetGroups.ts @@ -0,0 +1,540 @@ +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable @typescript-eslint/indent */ + +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable unicorn/prefer-module */ + +// eslint-disable-next-line n/no-missing-import +import type { BulmaJS } from '@cityssm/bulma-js/types.js' +import type { cityssmGlobal } from '@cityssm/bulma-webapp-js/src/types.js' + +import type { Asset, AssetGroup } from '../types/recordTypes.js' + +import type { Emile as EmileGlobal } from './globalTypes.js' + +declare const bulmaJS: BulmaJS +declare const cityssm: cityssmGlobal + +interface ErrorResponse { + success: false + errorMessage?: string +} + +;(() => { + const Emile = exports.Emile as EmileGlobal + + /* + * Asset Groups + */ + + type GroupMembersResponseJSON = + | { + success: true + groupMembers: Asset[] + } + | { + success: false + errorMessage?: string + } + + const assetGroupFilterElement = document.querySelector( + '#filter--assetGroups' + ) as HTMLInputElement + + function deleteGroupMember(clickEvent: Event): void { + const rowElement = (clickEvent.currentTarget as HTMLElement).closest( + 'tr' + ) as HTMLTableRowElement + + const groupId = rowElement.dataset.groupId ?? '' + const assetId = rowElement.dataset.assetId ?? '' + + function doDelete(): void { + cityssm.postJSON( + Emile.urlPrefix + '/assets/doDeleteAssetGroupMember', + { + groupId, + assetId + }, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as GroupMembersResponseJSON + + if (responseJSON.success) { + renderAssetGroupMembers(groupId, responseJSON.groupMembers) + } else { + bulmaJS.alert({ + title: 'Error Deleting Group Member', + message: responseJSON.errorMessage ?? 'Please try again.', + contextualColorName: 'danger' + }) + } + } + ) + } + + bulmaJS.confirm({ + title: 'Delete Group Member', + message: 'Are you sure you want to remove this asset from this group?', + contextualColorName: 'warning', + okButton: { + text: 'Yes, Remove Member from Group', + callbackFunction: doDelete + } + }) + } + + function renderAssetGroupMembers( + groupId: string, + groupMembers: Asset[] + ): void { + const tbodyElement = document.querySelector( + '.modal #tbody--groupMembers' + ) as HTMLTableSectionElement + + tbodyElement.innerHTML = '' + + for (const groupMember of groupMembers) { + const rowElement = document.createElement('tr') + rowElement.dataset.groupId = groupId + rowElement.dataset.assetId = groupMember.assetId.toString() + + rowElement.innerHTML = ` + + + ${ + Emile.canUpdate + ? `` + : '' + } + ` + ;( + rowElement.querySelector('[data-field="category"]') as HTMLElement + ).textContent = groupMember.category ?? '' + ;( + rowElement.querySelector('[data-field="assetName"]') as HTMLElement + ).textContent = groupMember.assetName ?? '' + + rowElement + .querySelector('.is-delete-button') + ?.addEventListener('click', deleteGroupMember) + + tbodyElement.append(rowElement) + } + + if (Emile.canUpdate) { + const groupMemberSelectElement = document.querySelector( + '.modal #groupMemberAdd--assetId' + ) as HTMLSelectElement + + groupMemberSelectElement.innerHTML = + '' + + for (const asset of Emile.assets) { + const assetInGroup = groupMembers.some((possibleAsset) => { + return possibleAsset.assetId === asset.assetId + }) + + if (assetInGroup) { + continue + } + + const optionElement = document.createElement('option') + optionElement.value = asset.assetId.toString() + optionElement.textContent = asset.assetName + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + let optgroupElement = groupMemberSelectElement.querySelector( + `optgroup[data-category-id="${asset.categoryId ?? ''}"]` + ) as HTMLOptGroupElement | null + + if (optgroupElement === null) { + optgroupElement = document.createElement('optgroup') + optgroupElement.dataset.categoryId = asset.categoryId?.toString() + optgroupElement.label = asset.category ?? '' + groupMemberSelectElement.append(optgroupElement) + } + + optgroupElement.append(optionElement) + } + } + } + + function populateAssetGroupModal( + modalElement: HTMLElement, + groupId: string + ): void { + cityssm.postJSON( + Emile.urlPrefix + '/assets/doGetAssetGroup', + { + groupId + }, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as + | { + success: true + assetGroup: AssetGroup + } + | ErrorResponse + + if (!responseJSON.success) { + bulmaJS.alert({ + title: 'Error Loading Asset Group Details', + message: 'Please refresh the page and try again.', + contextualColorName: 'danger' + }) + return + } + + ;( + modalElement.querySelector( + '#assetGroupView--groupId' + ) as HTMLInputElement + ).value = responseJSON.assetGroup.groupId?.toString() ?? '' + ;( + modalElement.querySelector( + '.modal-card-head [data-field="groupName"]' + ) as HTMLElement + ).textContent = responseJSON.assetGroup.groupName + ;( + modalElement.querySelector( + '#assetGroupView--groupName' + ) as HTMLInputElement + ).value = responseJSON.assetGroup.groupName + ;( + modalElement.querySelector( + '#assetGroupView--isShared' + ) as HTMLSelectElement + ).value = responseJSON.assetGroup.isShared ? '1' : '0' + ;( + modalElement.querySelector( + '#assetGroupView--groupDescription' + ) as HTMLTextAreaElement + ).value = responseJSON.assetGroup.groupDescription + + renderAssetGroupMembers( + groupId, + responseJSON.assetGroup.groupMembers ?? [] + ) + } + ) + } + + function updateAssetGroup(formEvent: Event): void { + formEvent.preventDefault() + + cityssm.postJSON( + Emile.urlPrefix + '/assets/doUpdateAssetGroup', + formEvent.currentTarget, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as + | { + success: true + assetGroups: AssetGroup[] + } + | ErrorResponse + + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups + + bulmaJS.alert({ + message: 'Asset group updated successfully.', + contextualColorName: 'success' + }) + + renderAssetGroups() + } else { + bulmaJS.alert({ + title: 'Error Updating Asset Group', + message: responseJSON.errorMessage ?? 'Please try again.', + contextualColorName: 'danger' + }) + } + } + ) + } + + function addAssetGroupMember(formEvent: Event): void { + formEvent.preventDefault() + + const formElement = formEvent.currentTarget as HTMLFormElement + + cityssm.postJSON( + Emile.urlPrefix + '/assets/doAddAssetGroupMember', + formElement, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as GroupMembersResponseJSON + + if (responseJSON.success) { + renderAssetGroupMembers( + (formElement.querySelector('[name="groupId"]') as HTMLInputElement) + .value, + responseJSON.groupMembers + ) + formElement.reset() + } else { + bulmaJS.alert({ + title: 'Error Adding Group Member', + message: responseJSON.errorMessage ?? 'Please try again.', + contextualColorName: 'danger' + }) + } + } + ) + } + + function openGroupByGroupId(groupId: string): void { + let assetGroupCloseModalFunction: () => void + + function deleteAssetGroup(clickEvent: Event): void { + clickEvent.preventDefault() + + function doDelete(): void { + cityssm.postJSON( + Emile.urlPrefix + '/assets/doDeleteAssetGroup', + { + groupId + }, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as + | { + success: true + assetGroups: AssetGroup[] + } + | ErrorResponse + + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups + renderAssetGroups() + assetGroupCloseModalFunction() + } else { + bulmaJS.alert({ + title: 'Error Deleting Asset Group', + message: responseJSON.errorMessage ?? '', + contextualColorName: 'danger' + }) + } + } + ) + } + + bulmaJS.confirm({ + title: 'Delete Asset Group', + message: 'Are you sure you want to delete this group?', + contextualColorName: 'warning', + okButton: { + text: 'Yes, Delete Asset Group', + callbackFunction: doDelete + } + }) + } + + cityssm.openHtmlModal('assetGroup-view', { + onshow(modalElement) { + populateAssetGroupModal(modalElement, groupId) + + if (Emile.canUpdate) { + ;( + modalElement.querySelector( + '#form--assetGroupView fieldset' + ) as HTMLFieldSetElement + ).disabled = false + ;( + modalElement.querySelector( + '#groupMemberAdd--groupId' + ) as HTMLInputElement + ).value = groupId + } else { + modalElement.querySelector('#tbody--groupMemberAdd')?.remove() + } + }, + onshown(modalElement, closeModalFunction) { + bulmaJS.toggleHtmlClipped() + bulmaJS.init(modalElement) + + if (Emile.canUpdate) { + assetGroupCloseModalFunction = closeModalFunction + + modalElement + .querySelector('#form--assetGroupView') + ?.addEventListener('submit', updateAssetGroup) + + modalElement + .querySelector('.is-delete-button') + ?.addEventListener('click', deleteAssetGroup) + + modalElement + .querySelector('#form--groupMemberAdd') + ?.addEventListener('submit', addAssetGroupMember) + } + }, + onremoved() { + bulmaJS.toggleHtmlClipped() + } + }) + } + + function openGroupByClick(clickEvent: MouseEvent): void { + clickEvent.preventDefault() + + const groupId = + (clickEvent.currentTarget as HTMLElement).closest('tr')?.dataset + .groupId ?? '' + + openGroupByGroupId(groupId) + } + + function renderAssetGroups(): void { + ;( + document.querySelector('#count--assetGroups') as HTMLElement + ).textContent = Emile.assetGroups.length.toString() + + const containerElement = document.querySelector( + '#container--assetGroups' + ) as HTMLElement + + if (Emile.assetGroups.length === 0) { + containerElement.innerHTML = `
+

+ No Asset Groups Found
+ Grouping together assets can assist with reporting on multiple assets. +

+
` + + return + } + + const searchPieces = assetGroupFilterElement.value + .trim() + .toLowerCase() + .split(' ') + + const tableElement = document.createElement('table') + tableElement.className = 'table is-fullwidth is-striped has-sticky-header' + tableElement.innerHTML = ` + Group + Members + Shared + + ` + + // eslint-disable-next-line no-labels + assetGroupLoop: for (const assetGroup of Emile.assetGroups) { + const searchText = + assetGroup.groupName.toLowerCase() + + ' ' + + (assetGroup.groupDescription ?? '').toLowerCase() + + for (const searchPiece of searchPieces) { + if (!searchText.includes(searchPiece)) { + // eslint-disable-next-line no-labels + continue assetGroupLoop + } + } + + const rowElement = document.createElement('tr') + rowElement.dataset.groupId = assetGroup.groupId.toString() + + rowElement.innerHTML = ` +
+ + + + ${assetGroup.groupMemberCount ?? 0} + + + ${assetGroup.isShared ? 'Shared Group' : 'Private'} + ` + + const groupNameElement = rowElement.querySelector( + '[data-field="groupName"]' + ) as HTMLAnchorElement + + groupNameElement.textContent = assetGroup.groupName + + groupNameElement.addEventListener('click', openGroupByClick) + ;(tableElement.querySelector('tbody') as HTMLTableSectionElement).append( + rowElement + ) + ;( + rowElement.querySelector( + '[data-field="groupDescription"]' + ) as HTMLElement + ).textContent = assetGroup.groupDescription ?? '' + } + + if (tableElement.querySelectorAll('tbody tr').length === 0) { + containerElement.innerHTML = `
+

+ There are no groups that meet your search criteria.
+ Try to be less specific in your search. +

+
` + } else { + containerElement.innerHTML = '' + containerElement.append(tableElement) + } + } + + document + .querySelector('#button--addAssetGroup') + ?.addEventListener('click', () => { + let addAssetGroupCloseModalFunction: () => void + + function doAddAssetGroup(formEvent: SubmitEvent): void { + formEvent.preventDefault() + + cityssm.postJSON( + Emile.urlPrefix + '/assets/doAddAssetGroup', + formEvent.currentTarget, + (rawResponseJSON) => { + const responseJSON = rawResponseJSON as + | { + success: true + groupId: number + assetGroups: AssetGroup[] + } + | ErrorResponse + + if (responseJSON.success) { + Emile.assetGroups = responseJSON.assetGroups + renderAssetGroups() + addAssetGroupCloseModalFunction() + + openGroupByGroupId(responseJSON.groupId.toString()) + } + } + ) + } + + cityssm.openHtmlModal('assetGroup-add', { + onshown(modalElement, closeModalFunction) { + addAssetGroupCloseModalFunction = closeModalFunction + + bulmaJS.toggleHtmlClipped() + ;( + modalElement.querySelector( + '#assetGroupAdd--groupName' + ) as HTMLInputElement + ).focus() + + modalElement + .querySelector('form') + ?.addEventListener('submit', doAddAssetGroup) + }, + onremoved() { + bulmaJS.toggleHtmlClipped() + } + }) + }) + + /* + * Initialize + */ + + // Asset Groups + assetGroupFilterElement.addEventListener('keyup', renderAssetGroups) + renderAssetGroups() +})() diff --git a/public-typescript/assets.js b/public-typescript/assets.js index 1d9f757..4387fed 100644 --- a/public-typescript/assets.js +++ b/public-typescript/assets.js @@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); ; (() => { - var _a, _b; + var _a; const Emile = exports.Emile; const assetAliasTypes = exports.assetAliasTypes; const assetCategoryFilterElement = document.querySelector('#filter--categoryId'); @@ -354,225 +354,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); } }); }); - /* - * Asset Groups - */ - const assetGroupFilterElement = document.querySelector('#filter--assetGroups'); - function populateAssetGroupModal(modalElement, groupId) { - cityssm.postJSON(Emile.urlPrefix + '/assets/doGetAssetGroup', { - groupId - }, (rawResponseJSON) => { - var _a, _b; - const responseJSON = rawResponseJSON; - if (!responseJSON.success) { - bulmaJS.alert({ - title: 'Error Loading Asset Group Details', - message: 'Please refresh the page and try again.', - contextualColorName: 'danger' - }); - return; - } - ; - modalElement.querySelector('#assetGroupView--groupId').value = (_b = (_a = responseJSON.assetGroup.groupId) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''; - modalElement.querySelector('.modal-card-head [data-field="groupName"]').textContent = responseJSON.assetGroup.groupName; - modalElement.querySelector('#assetGroupView--groupName').value = responseJSON.assetGroup.groupName; - modalElement.querySelector('#assetGroupView--isShared').value = responseJSON.assetGroup.isShared ? '1' : '0'; - modalElement.querySelector('#assetGroupView--groupDescription').value = responseJSON.assetGroup.groupDescription; - }); - } - function updateAssetGroup(formEvent) { - formEvent.preventDefault(); - cityssm.postJSON(Emile.urlPrefix + '/assets/doUpdateAssetGroup', formEvent.currentTarget, (rawResponseJSON) => { - var _a; - const responseJSON = rawResponseJSON; - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups; - bulmaJS.alert({ - message: 'Asset group updated successfully.', - contextualColorName: 'success' - }); - renderAssets(); - } - else { - bulmaJS.alert({ - title: 'Error Updating Asset Group', - message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : 'Please try again.', - contextualColorName: 'danger' - }); - } - }); - } - function openGroupByGroupId(groupId) { - let assetGroupCloseModalFunction; - function deleteAssetGroup(clickEvent) { - clickEvent.preventDefault(); - function doDelete() { - cityssm.postJSON(Emile.urlPrefix + '/assets/doDeleteAssetGroup', { - groupId - }, (rawResponseJSON) => { - var _a; - const responseJSON = rawResponseJSON; - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups; - renderAssetGroups(); - assetGroupCloseModalFunction(); - } - else { - bulmaJS.alert({ - title: 'Error Deleting Asset Group', - message: (_a = responseJSON.errorMessage) !== null && _a !== void 0 ? _a : '', - contextualColorName: 'danger' - }); - } - }); - } - bulmaJS.confirm({ - title: 'Delete Asset Group', - message: 'Are you sure you want to delete this group?', - contextualColorName: 'warning', - okButton: { - text: 'Yes, Delete Asset Group', - callbackFunction: doDelete - } - }); - } - cityssm.openHtmlModal('assetGroup-view', { - onshow(modalElement) { - populateAssetGroupModal(modalElement, groupId); - if (Emile.canUpdate) { - ; - modalElement.querySelector('#form--assetGroupView fieldset').disabled = false; - } - }, - onshown(modalElement, closeModalFunction) { - var _a, _b; - bulmaJS.toggleHtmlClipped(); - bulmaJS.init(modalElement); - if (Emile.canUpdate) { - assetGroupCloseModalFunction = closeModalFunction; - (_a = modalElement - .querySelector('#form--assetGroupView')) === null || _a === void 0 ? void 0 : _a.addEventListener('submit', updateAssetGroup); - (_b = modalElement - .querySelector('.is-delete-button')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', deleteAssetGroup); - } - }, - onremoved() { - bulmaJS.toggleHtmlClipped(); - } - }); - } - function openGroupByClick(clickEvent) { - var _a, _b; - clickEvent.preventDefault(); - const groupId = (_b = (_a = clickEvent.currentTarget.closest('tr')) === null || _a === void 0 ? void 0 : _a.dataset.groupId) !== null && _b !== void 0 ? _b : ''; - openGroupByGroupId(groupId); - } - function renderAssetGroups() { - var _a, _b, _c, _d, _e; - ; - document.querySelector('#count--assetGroups').textContent = Emile.assetGroups.length.toString(); - const containerElement = document.querySelector('#container--assetGroups'); - if (Emile.assetGroups.length === 0) { - containerElement.innerHTML = `
-

- No Asset Groups Found
- Grouping together assets can assist with reporting on multiple assets. -

-
`; - return; - } - const searchPieces = assetGroupFilterElement.value - .trim() - .toLowerCase() - .split(' '); - const tableElement = document.createElement('table'); - tableElement.className = 'table is-fullwidth is-striped has-sticky-header'; - tableElement.innerHTML = ` - Group - - - Number of Assets - - - `; - // eslint-disable-next-line no-labels - assetGroupLoop: for (const assetGroup of Emile.assetGroups) { - const searchText = assetGroup.groupName.toLowerCase() + - ' ' + - ((_a = assetGroup.groupDescription) !== null && _a !== void 0 ? _a : '').toLowerCase(); - for (const searchPiece of searchPieces) { - if (!searchText.includes(searchPiece)) { - // eslint-disable-next-line no-labels - continue assetGroupLoop; - } - } - const rowElement = document.createElement('tr'); - rowElement.dataset.groupId = (_c = (_b = assetGroup.groupId) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : ''; - rowElement.innerHTML = ` -
- - - - ${(_d = assetGroup.groupMemberCount) !== null && _d !== void 0 ? _d : 0} - `; - const groupNameElement = rowElement.querySelector('[data-field="groupName"]'); - groupNameElement.textContent = assetGroup.groupName; - groupNameElement.addEventListener('click', openGroupByClick); - tableElement.querySelector('tbody').append(rowElement); - rowElement.querySelector('[data-field="groupDescription"]').textContent = (_e = assetGroup.groupDescription) !== null && _e !== void 0 ? _e : ''; - } - if (tableElement.querySelectorAll('tbody tr').length === 0) { - containerElement.innerHTML = `
-

- There are no groups that meet your search criteria.
- Try to be less specific in your search. -

-
`; - } - else { - containerElement.innerHTML = ''; - containerElement.append(tableElement); - } - } - (_b = document - .querySelector('#button--addAssetGroup')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => { - let addAssetGroupCloseModalFunction; - function doAddAssetGroup(formEvent) { - formEvent.preventDefault(); - cityssm.postJSON(Emile.urlPrefix + '/assets/doAddAssetGroup', formEvent.currentTarget, (rawResponseJSON) => { - const responseJSON = rawResponseJSON; - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups; - renderAssetGroups(); - addAssetGroupCloseModalFunction(); - openGroupByGroupId(responseJSON.groupId.toString()); - } - }); - } - cityssm.openHtmlModal('assetGroup-add', { - onshown(modalElement, closeModalFunction) { - var _a; - addAssetGroupCloseModalFunction = closeModalFunction; - bulmaJS.toggleHtmlClipped(); - modalElement.querySelector('#assetGroupAdd--groupName').focus(); - (_a = modalElement - .querySelector('form')) === null || _a === void 0 ? void 0 : _a.addEventListener('submit', doAddAssetGroup); - }, - onremoved() { - bulmaJS.toggleHtmlClipped(); - } - }); - }); /* * Initialize */ - // Initialize tabs - bulmaJS.init(); - // Assets assetFilterElement.addEventListener('keyup', renderAssets); assetCategoryFilterElement.addEventListener('change', renderAssets); renderAssets(); - // Asset Groups - assetGroupFilterElement.addEventListener('keyup', renderAssetGroups); - renderAssetGroups(); })(); diff --git a/public-typescript/assets.ts b/public-typescript/assets.ts index 6dda7a2..1848b02 100644 --- a/public-typescript/assets.ts +++ b/public-typescript/assets.ts @@ -8,12 +8,7 @@ import type { BulmaJS } from '@cityssm/bulma-js/types.js' import type { cityssmGlobal } from '@cityssm/bulma-webapp-js/src/types.js' -import type { - Asset, - AssetAlias, - AssetAliasType, - AssetGroup -} from '../types/recordTypes.js' +import type { Asset, AssetAlias, AssetAliasType } from '../types/recordTypes.js' import type { Emile as EmileGlobal } from './globalTypes.js' @@ -569,351 +564,11 @@ interface ErrorResponse { }) }) - /* - * Asset Groups - */ - - const assetGroupFilterElement = document.querySelector( - '#filter--assetGroups' - ) as HTMLInputElement - - function populateAssetGroupModal( - modalElement: HTMLElement, - groupId: string - ): void { - cityssm.postJSON( - Emile.urlPrefix + '/assets/doGetAssetGroup', - { - groupId - }, - (rawResponseJSON) => { - const responseJSON = rawResponseJSON as - | { - success: true - assetGroup: AssetGroup - } - | ErrorResponse - - if (!responseJSON.success) { - bulmaJS.alert({ - title: 'Error Loading Asset Group Details', - message: 'Please refresh the page and try again.', - contextualColorName: 'danger' - }) - return - } - - ;( - modalElement.querySelector( - '#assetGroupView--groupId' - ) as HTMLInputElement - ).value = responseJSON.assetGroup.groupId?.toString() ?? '' - ;( - modalElement.querySelector( - '.modal-card-head [data-field="groupName"]' - ) as HTMLElement - ).textContent = responseJSON.assetGroup.groupName - ;( - modalElement.querySelector( - '#assetGroupView--groupName' - ) as HTMLInputElement - ).value = responseJSON.assetGroup.groupName - ;( - modalElement.querySelector( - '#assetGroupView--isShared' - ) as HTMLSelectElement - ).value = responseJSON.assetGroup.isShared ? '1' : '0' - ;( - modalElement.querySelector( - '#assetGroupView--groupDescription' - ) as HTMLTextAreaElement - ).value = responseJSON.assetGroup.groupDescription - } - ) - } - - function updateAssetGroup(formEvent: Event): void { - formEvent.preventDefault() - - cityssm.postJSON( - Emile.urlPrefix + '/assets/doUpdateAssetGroup', - formEvent.currentTarget, - (rawResponseJSON) => { - const responseJSON = rawResponseJSON as - | { - success: true - assetGroups: AssetGroup[] - } - | ErrorResponse - - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups - - bulmaJS.alert({ - message: 'Asset group updated successfully.', - contextualColorName: 'success' - }) - - renderAssets() - } else { - bulmaJS.alert({ - title: 'Error Updating Asset Group', - message: responseJSON.errorMessage ?? 'Please try again.', - contextualColorName: 'danger' - }) - } - } - ) - } - - function openGroupByGroupId(groupId: string): void { - let assetGroupCloseModalFunction: () => void - - function deleteAssetGroup(clickEvent: Event): void { - clickEvent.preventDefault() - - function doDelete(): void { - cityssm.postJSON( - Emile.urlPrefix + '/assets/doDeleteAssetGroup', - { - groupId - }, - (rawResponseJSON) => { - const responseJSON = rawResponseJSON as - | { - success: true - assetGroups: AssetGroup[] - } - | ErrorResponse - - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups - renderAssetGroups() - assetGroupCloseModalFunction() - } else { - bulmaJS.alert({ - title: 'Error Deleting Asset Group', - message: responseJSON.errorMessage ?? '', - contextualColorName: 'danger' - }) - } - } - ) - } - - bulmaJS.confirm({ - title: 'Delete Asset Group', - message: 'Are you sure you want to delete this group?', - contextualColorName: 'warning', - okButton: { - text: 'Yes, Delete Asset Group', - callbackFunction: doDelete - } - }) - } - - cityssm.openHtmlModal('assetGroup-view', { - onshow(modalElement) { - populateAssetGroupModal(modalElement, groupId) - - if (Emile.canUpdate) { - ;( - modalElement.querySelector( - '#form--assetGroupView fieldset' - ) as HTMLFieldSetElement - ).disabled = false - } - }, - onshown(modalElement, closeModalFunction) { - bulmaJS.toggleHtmlClipped() - bulmaJS.init(modalElement) - - if (Emile.canUpdate) { - assetGroupCloseModalFunction = closeModalFunction - - modalElement - .querySelector('#form--assetGroupView') - ?.addEventListener('submit', updateAssetGroup) - - modalElement - .querySelector('.is-delete-button') - ?.addEventListener('click', deleteAssetGroup) - } - }, - onremoved() { - bulmaJS.toggleHtmlClipped() - } - }) - } - - function openGroupByClick(clickEvent: MouseEvent): void { - clickEvent.preventDefault() - - const groupId = - (clickEvent.currentTarget as HTMLElement).closest('tr')?.dataset - .groupId ?? '' - - openGroupByGroupId(groupId) - } - - function renderAssetGroups(): void { - ;( - document.querySelector('#count--assetGroups') as HTMLElement - ).textContent = Emile.assetGroups.length.toString() - - const containerElement = document.querySelector( - '#container--assetGroups' - ) as HTMLElement - - if (Emile.assetGroups.length === 0) { - containerElement.innerHTML = `
-

- No Asset Groups Found
- Grouping together assets can assist with reporting on multiple assets. -

-
` - - return - } - - const searchPieces = assetGroupFilterElement.value - .trim() - .toLowerCase() - .split(' ') - - const tableElement = document.createElement('table') - tableElement.className = 'table is-fullwidth is-striped has-sticky-header' - tableElement.innerHTML = ` - Group - - - Number of Assets - - - ` - - // eslint-disable-next-line no-labels - assetGroupLoop: for (const assetGroup of Emile.assetGroups) { - const searchText = - assetGroup.groupName.toLowerCase() + - ' ' + - (assetGroup.groupDescription ?? '').toLowerCase() - - for (const searchPiece of searchPieces) { - if (!searchText.includes(searchPiece)) { - // eslint-disable-next-line no-labels - continue assetGroupLoop - } - } - - const rowElement = document.createElement('tr') - rowElement.dataset.groupId = assetGroup.groupId?.toString() ?? '' - - rowElement.innerHTML = ` -
- - - - ${ - assetGroup.groupMemberCount ?? 0 - } - ` - - const groupNameElement = rowElement.querySelector( - '[data-field="groupName"]' - ) as HTMLAnchorElement - - groupNameElement.textContent = assetGroup.groupName - - groupNameElement.addEventListener('click', openGroupByClick) - ;(tableElement.querySelector('tbody') as HTMLTableSectionElement).append( - rowElement - ) - ;( - rowElement.querySelector( - '[data-field="groupDescription"]' - ) as HTMLElement - ).textContent = assetGroup.groupDescription ?? '' - } - - if (tableElement.querySelectorAll('tbody tr').length === 0) { - containerElement.innerHTML = `
-

- There are no groups that meet your search criteria.
- Try to be less specific in your search. -

-
` - } else { - containerElement.innerHTML = '' - containerElement.append(tableElement) - } - } - - document - .querySelector('#button--addAssetGroup') - ?.addEventListener('click', () => { - let addAssetGroupCloseModalFunction: () => void - - function doAddAssetGroup(formEvent: SubmitEvent): void { - formEvent.preventDefault() - - cityssm.postJSON( - Emile.urlPrefix + '/assets/doAddAssetGroup', - formEvent.currentTarget, - (rawResponseJSON) => { - const responseJSON = rawResponseJSON as - | { - success: true - groupId: number - assetGroups: AssetGroup[] - } - | ErrorResponse - - if (responseJSON.success) { - Emile.assetGroups = responseJSON.assetGroups - renderAssetGroups() - addAssetGroupCloseModalFunction() - - openGroupByGroupId(responseJSON.groupId.toString()) - } - } - ) - } - - cityssm.openHtmlModal('assetGroup-add', { - onshown(modalElement, closeModalFunction) { - addAssetGroupCloseModalFunction = closeModalFunction - - bulmaJS.toggleHtmlClipped() - ;( - modalElement.querySelector( - '#assetGroupAdd--groupName' - ) as HTMLInputElement - ).focus() - - modalElement - .querySelector('form') - ?.addEventListener('submit', doAddAssetGroup) - }, - onremoved() { - bulmaJS.toggleHtmlClipped() - } - }) - }) - /* * Initialize */ - // Initialize tabs - bulmaJS.init() - - // Assets assetFilterElement.addEventListener('keyup', renderAssets) assetCategoryFilterElement.addEventListener('change', renderAssets) renderAssets() - - // Asset Groups - assetGroupFilterElement.addEventListener('keyup', renderAssetGroups) - renderAssetGroups() })() diff --git a/public/html/assetGroup-view.html b/public/html/assetGroup-view.html index dd5e17d..8df9e0e 100644 --- a/public/html/assetGroup-view.html +++ b/public/html/assetGroup-view.html @@ -128,7 +128,50 @@