cityssm/attendance-tracking

View on GitHub
public-typescript/selfService.ts

Summary

Maintainability
A
35 mins
Test Coverage
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/indent */

// 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 { CallOutList } from '../types/recordTypes.js'

declare const bulmaJS: BulmaJS
declare const cityssm: cityssmGlobal
;(() => {
  // eslint-disable-next-line unicorn/prefer-module
  const urlPrefix = exports.urlPrefix as string

  /*
   * Employee Tab
   */

  const employeeMessageContainerElement = document.querySelector(
    '#employee--message'
  ) as HTMLElement

  const employeeNumberElement = document.querySelector(
    '#employee--employeeNumber'
  ) as HTMLInputElement

  function employeeKeyUp(event: KeyboardEvent): void {
    if (event.key === 'Enter') {
      validateEmployee()
    } else if (event.key !== 'Shift' && event.key !== 'Tab') {
      employeeMessageContainerElement.innerHTML = ''
    }
  }

  employeeNumberElement.addEventListener('keyup', employeeKeyUp)

  const employeeHomeContactLastFourDigitsElement = document.querySelector(
    '#employee--homeContact_lastFourDigits'
  ) as HTMLInputElement

  employeeHomeContactLastFourDigitsElement.addEventListener(
    'keyup',
    employeeKeyUp
  )

  function validateEmployee(): void {
    employeeMessageContainerElement.innerHTML = `<p class="has-text-centered">
      <i class="fas fa-4x fa-cog fa-spin" aria-hidden="true"></i><br />
      <em>Loading employee...</em>
      </p>`

    if (!employeeNumberElement.checkValidity()) {
      employeeMessageContainerElement.innerHTML = `<div class="message is-danger">
        <p class="message-body">
        Please enter a valid <strong>Employee Number</strong>.
        </p>
        </div>`

      employeeNumberElement.focus()

      return
    }

    if (!employeeHomeContactLastFourDigitsElement.checkValidity()) {
      employeeMessageContainerElement.innerHTML = `<div class="message is-danger">
        <p class="message-body">
        Please enter a valid <strong>Last Four Digits</strong>.
        </p>
        </div>`

      employeeHomeContactLastFourDigitsElement.focus()

      return
    }

    cityssm.postJSON(
      `${urlPrefix}/doValidateEmployee`,
      {
        employeeNumber: employeeNumberElement.value,
        employeeHomeContactLastFourDigits:
          employeeHomeContactLastFourDigitsElement.value
      },
      (rawResponseJSON) => {
        const responseJSON = rawResponseJSON as {
          success: boolean
          isAbuser?: boolean
          errorMessage?: string
          employeeName?: string
        }

        if (responseJSON.success) {
          employeeMessageContainerElement.innerHTML = ''
          document.querySelector('#tab--employee')?.classList.add('is-hidden')
          ;(
            document.querySelector(
              '#employeeOptions--employeeName'
            ) as HTMLElement
          ).textContent = responseJSON.employeeName ?? ''

          document
            .querySelector('#tab--employeeOptions')
            ?.classList.remove('is-hidden')

          document
            .querySelector('#employeeOptionsTab--menu')
            ?.classList.remove('is-hidden')
        } else {
          if (responseJSON.isAbuser ?? false) {
            window.location.reload()
          }

          employeeMessageContainerElement.innerHTML = `<div class="message is-danger">
            <p class="message-body">
            <strong>${responseJSON.errorMessage ?? ''}</strong><br />
            If errors persist, please contact a manager for assistance.</p>
            </div>`
        }
      }
    )
  }

  document
    .querySelector('#employee--nextButton')
    ?.addEventListener('click', validateEmployee)

  /*
   * Sign Out
   */

  function signOut(clickEvent?: Event): void {
    if (clickEvent !== undefined) {
      clickEvent.preventDefault()
    }

    // Hide all tabs

    const panelTabElements = document.querySelectorAll(
      '.panel-tab, .employeeOptionsTab'
    )

    for (const panelTabElement of panelTabElements) {
      panelTabElement.classList.add('is-hidden')
    }

    employeeNumberElement.value = ''
    employeeHomeContactLastFourDigitsElement.value = ''
    employeeMessageContainerElement.innerHTML = ''

    document.querySelector('#tab--employee')?.classList.remove('is-hidden')
  }

  const signOutButtonElements = document.querySelectorAll('.is-sign-out-button')

  for (const signOutButtonElement of signOutButtonElements) {
    signOutButtonElement.addEventListener('click', signOut)
  }

  function monitorInactivity(): void {
    let time: number
    window.addEventListener('load', resetTimer)
    document.addEventListener('mousemove', resetTimer)
    document.addEventListener('keydown', resetTimer)

    function resetTimer(): void {
      window.clearTimeout(time)
      time = window.setTimeout(signOut, 2 * 60 * 1000) // 2 minutes
    }
  }

  window.addEventListener('load', monitorInactivity)

  /*
   * Select Employee Option
   */

  function selectEmployeeOptionTab(clickEvent: Event): void {
    clickEvent.preventDefault()

    const tabHash = (clickEvent.currentTarget as HTMLAnchorElement).hash

    document
      .querySelector('#employeeOptionsTab--menu')
      ?.classList.add('is-hidden')

    // Chage to switch if more options are added
    if (tabHash === '#employeeOptionsTab--callOutListAdd') {
      loadCallOutLists()
    }

    document.querySelector(tabHash)?.classList.remove('is-hidden')
  }

  const employeeOptionTabElements = document.querySelectorAll(
    '#employeeOptionsTab--menu a.panel-block'
  )

  for (const employeeOptionTabElement of employeeOptionTabElements) {
    employeeOptionTabElement.addEventListener('click', selectEmployeeOptionTab)
  }

  function backToOptions(clickEvent?: Event): void {
    if (clickEvent !== undefined) {
      clickEvent.preventDefault()
    }

    // Hide all tabs

    const panelTabElements = document.querySelectorAll('.employeeOptionsTab')

    for (const panelTabElement of panelTabElements) {
      panelTabElement.classList.add('is-hidden')
    }

    document
      .querySelector('#employeeOptionsTab--menu')
      ?.classList.remove('is-hidden')
  }

  const backToOptionsElements = document.querySelectorAll(
    '.is-back-to-options-button'
  )

  for (const backToOptionsElement of backToOptionsElements) {
    backToOptionsElement.addEventListener('click', backToOptions)
  }

  /*
   * Call Out List Add
   */

  function addEmployeeToCallOutList(clickEvent: Event): void {
    clickEvent.preventDefault()

    const buttonElement = clickEvent.currentTarget as HTMLButtonElement
    buttonElement.disabled = true
    buttonElement.classList.add('is-loading')

    const panelBlockElement = buttonElement.closest(
      '.panel-block'
    ) as HTMLElement

    const listId = panelBlockElement.dataset.listId ?? ''

    let selfSignUpKey = ''

    const selfSignUpKeyElement = panelBlockElement.querySelector(
      `#callOutListAdd--selfSignUpKey_${listId}`
    )

    if (selfSignUpKeyElement !== null) {
      selfSignUpKey = (selfSignUpKeyElement as HTMLInputElement).value
      if (selfSignUpKey === '') {
        // eslint-disable-next-line no-extra-semi
        ;(selfSignUpKeyElement as HTMLInputElement).focus()

        buttonElement.disabled = false
        buttonElement.classList.remove('is-loading')
        return
      }
    }

    cityssm.postJSON(
      `${urlPrefix}/doAddEmployeeToCallOutList`,
      {
        employeeNumber: employeeNumberElement.value,
        employeeHomeContactLastFourDigits:
          employeeHomeContactLastFourDigitsElement.value,
        listId,
        selfSignUpKey
      },
      (rawResponseJSON) => {
        const responseJSON = rawResponseJSON as {
          success: boolean
          errorMessage?: string
        }

        if (responseJSON.success) {
          // eslint-disable-next-line no-extra-semi
          ;(
            panelBlockElement.querySelector(
              '.is-add-button-container'
            ) as HTMLElement
          ).innerHTML =
            '<i class="fas fa-check" aria-hidden="true"></i> Added to List'
        } else {
          bulmaJS.alert({
            title: 'Error Adding Employee to List',
            message:
              responseJSON.errorMessage ?? 'Unknown error.  Please try again',
            contextualColorName: 'danger'
          })

          buttonElement.disabled = false
          buttonElement.classList.remove('is-loading')
        }
      }
    )
  }

  function loadCallOutLists(): void {
    const containerElement = document.querySelector(
      '#container--callOutListAdd'
    ) as HTMLElement

    containerElement.innerHTML = `<p class="has-text-centered">
      <i class="fas fa-4x fa-cog fa-spin" aria-hidden="true"></i><br />
      <em>Loading call out lists...</em>
      </p>`

    cityssm.postJSON(
      `${urlPrefix}/doGetAvailableCallOutLists`,
      {
        employeeNumber: employeeNumberElement.value,
        employeeHomeContactLastFourDigits:
          employeeHomeContactLastFourDigitsElement.value
      },
      (rawResponseJSON) => {
        const responseJSON = rawResponseJSON as {
          callOutLists: CallOutList[]
        }

        if (responseJSON.callOutLists.length === 0) {
          containerElement.innerHTML = `<div class="message is-info">
          <p class="message-body">
            There are no call out lists that you are eligible to sign up for.
          </p>
          </div>`

          return
        }

        const panelElement = document.createElement('div')
        panelElement.className = 'panel'

        for (const callOutList of responseJSON.callOutLists) {
          const panelBlockElement = document.createElement('div')
          panelBlockElement.className =
            'panel-block is-justify-content-space-between'
          panelBlockElement.dataset.listId = callOutList.listId

          panelBlockElement.innerHTML = `<div>
            <strong>${callOutList.listName}</strong><br />
            ${callOutList.listDescription ?? ''}
            </div>
            <div class="is-add-button-container"></div>`

          if (callOutList.hasSelfSignUpKey ?? true) {
            // eslint-disable-next-line no-extra-semi
            ;(
              panelBlockElement.querySelector(
                '.is-add-button-container'
              ) as HTMLElement
            ).innerHTML = `<div class="field has-addons">
                <div class="control has-icons-left">
                  <input class="input"
                    id="callOutListAdd--selfSignUpKey_${callOutList.listId}"
                    name="selfSignUpKey_${callOutList.listId}"
                    type="text"
                    placeholder="Sign Up Key"
                    maxlength="10"
                    aria-label="Sign Up Key"
                    required />
                  <span class="icon is-small is-left">
                    <i class="fas fa-key" aria-hidden="true"></i>
                  </span>
                </div>
                <div class="control">
                  <button class="button is-success is-add-button" type="button">
                    <span class="icon"><i class="fas fa-plus" aria-hidden="true"></i></span>
                    <span>Add to List</span>
                  </button>
                </div>
              </div>`
          } else {
            // eslint-disable-next-line no-extra-semi
            ;(
              panelBlockElement.querySelector(
                '.is-add-button-container'
              ) as HTMLElement
            ).innerHTML = `<button class="button is-success is-add-button" type="button">
                <span class="icon"><i class="fas fa-plus" aria-hidden="true"></i></span>
                <span>Add to List</span>
              </button>`
          }

          panelBlockElement
            .querySelector('.is-add-button')
            ?.addEventListener('click', addEmployeeToCallOutList)

          panelElement.append(panelBlockElement)
        }

        containerElement.innerHTML = ''
        containerElement.append(panelElement)
      }
    )
  }
})()