serge-web/serge-web

View on GitHub
client/src/Views/GameSetupNew.tsx

Summary

Maintainability
F
4 days
Test Coverage
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React, { useEffect } from 'react'
import _ from 'lodash'
import uniqid from 'uniqid'
import { useSelector, useDispatch } from 'react-redux'
import GameSetup from '../Components/local/templates/game-setup'
import { checkUnique, getUniquePasscode, findDuplicatePasscodes, findEmptyRolenames } from 'src/Helpers'
import {
  addNewForce,
  setCurrentTab,
  saveSettings,
  saveForce,
  saveChannel,
  setTabSaved,
  setGameData,
  setSelectedForce,
  setSelectedChannel,
  duplicateChannel,
  saveWargameTitle,
  initiateWargame,
  duplicateForce
} from '../ActionsAndReducers/dbWargames/wargames_ActionCreators'
import { addNotification } from '../ActionsAndReducers/Notification/Notification_ActionCreators'
import { modalAction } from '../ActionsAndReducers/Modal/Modal_ActionCreators'
import { setCurrentViewFromURI } from '../ActionsAndReducers/setCurrentViewFromURI/setCurrentViewURI_ActionCreators'
import { ADMIN_ROUTE, iconUploaderPath, AdminTabs, forceTemplate } from 'src/config'
import { ChannelTypes, ForceData, MessageTypes, Role, RootState, WargamesState, WargameOverview, Wargame } from 'src/custom-types'

/**
 * TODOS:
 updateWargameTitle
 game setup: saveWargame
 checkWargameNameSaveable
 notSavedNotification
 channels: createChannel
 back button
 */

 type UniqueNameInterface = {
  newName: string
  list: any[]
  label: string
  oldName: string
 }

const AdminGameSetup: React.FC = () => {
  const dispatch = useDispatch()
  const { wargame, messageTypes }: { wargame: WargamesState, messageTypes: MessageTypes } = useSelector(({ wargame, messageTypes }: RootState) => ({ wargame, messageTypes }))

  const {
    data,
    currentWargame,
    currentTab,
    wargameList,
    wargameTitle
  } = wargame
  const {
    overview,
    forces,
    channels,
    templates
  } = data
  const tabs = Object.keys(data)

  const isWargameChanged = () => {
    return Object.values(data).some((item) => item.dirty)
  }
  const onTabChange = (tab: string) => {
    if (!isWargameChanged()) {
      dispatch(setCurrentTab(tab))
    } else {
      dispatch(addNotification('Unsaved changes', 'warning'))
    }
  }

  const onPressBack = (e: MouseEvent) => {
    e.preventDefault()
    dispatch(setCurrentViewFromURI(ADMIN_ROUTE))
  }
  const isUniqueName = ({ newName, list, label }: UniqueNameInterface) => {
    let listNames = list.map((item: ForceData) => item.name)
    listNames = _.pull(listNames, newName)

    if (!checkUnique(newName, listNames)) {
      dispatch(addNotification(`${label} name is not unique.`, 'warning'))
      return false
    }
    return true
  }

  const isUniqueForceName = (force: ForceData) => {
    const selectedForce = forces.selectedForce
    return isUniqueName({
      oldName: selectedForce as string,
      newName: force.name,
      list: forces.forces,
      label: 'Force'
    })
  }

  const isUniqueChannelName = (channel: ChannelTypes) => {
    const selectedChannel = channels.selectedChannel
    return isUniqueName({
      oldName: selectedChannel as string,
      newName: channel.name,
      list: channels.channels,
      label: 'Channel'
    })
  }

  const handleFormChange = (changes: WargameOverview) => {
    dispatch(setGameData(changes))
  }

  const handleDeleteGameControl = (roles: Role[], key: number, handleChange: () => void) => {
    const role = roles[key]
    if (role.isGameControl) {
      dispatch(addNotification(`Role ${role.name} with Game Control permissions cannot be deleted. Please remove Game Control permission.`, 'warning'))
    } else {
      dispatch(modalAction.open('confirmDelete', { type: 'role', data: { roles, key, handleChange } }))
    }
  }

  const handleSaveOverview = (overview: WargameOverview) => {
    console.log('currentWargame', currentWargame, overview)
    if (currentWargame) dispatch(saveSettings(currentWargame, overview))
  }

  const handleSaveForce = (newForces: ForceData[]) => {
    const { selectedForce } = forces
    const { uniqid } = selectedForce as ForceData
    const newForceData = newForces.find(force => force.uniqid === uniqid)
    if (newForceData) {
      const forceOverview = newForceData.overview
      const forceName = newForceData.name
      // @ts-ignore
      newForceData.overview = forceOverview === 'string' ? forceOverview : forces.forces.find((force) => force.uniqid === uniqid).overview
  
      const empForceRoleNames = findEmptyRolenames(newForceData, forces.forces)
      if (empForceRoleNames.length > 0) {
        dispatch(addNotification(`A Role Name must be provided for: ${_.join(_.map(empForceRoleNames, empForceRoleName => empForceRoleName.forceName + '-' + empForceRoleName.roleName), ',')}`, 'warning'))
        return
      }
  
      const dupForceRoleNames = findDuplicatePasscodes(newForceData, forces.forces)
      if (dupForceRoleNames.length > 0) {
        dispatch(addNotification(`Duplicate passcodes for: ${_.join(_.map(dupForceRoleNames, dupForceRoleName => dupForceRoleName.forceName + '-' + dupForceRoleName.roleName), ',')}`, 'warning'))
        return
      }
  
      if (typeof forceName === 'string' && forceName.length > 0) {
        if (!isUniqueForceName(newForceData)) return
        if (currentWargame) dispatch(saveForce(currentWargame, newForceData))
      }
  
      if (forceName === null) {
        if (currentWargame) dispatch(saveForce(currentWargame, newForceData))
      } else if (forceName.length === 0) {
        dispatch(addNotification('No Force Name', 'warning'))
      }
    }
  }

  const handleSaveChannel = (channel: ChannelTypes) => {
    const channelName = channel.name
    const selectedChannelId = channel.uniqid
    const newChannelData = channels.channels.find((c) => c.uniqid === selectedChannelId)

    if (currentWargame && newChannelData) {
      if (typeof channelName === 'string' && channelName.length > 0) {
        if (!isUniqueChannelName(channel)) return
  
        dispatch(setTabSaved())
        dispatch(saveChannel(currentWargame, newChannelData))
      }
  
      if (channelName === null) {
        dispatch(saveChannel(currentWargame, newChannelData))
      } else if (channelName.length === 0) {
        window.alert('no channel name')
      }
    }
  }

  const onSave = (updates: WargameOverview | ForceData | ChannelTypes) => {
    let saveAction
    switch (currentTab) {
      case AdminTabs.Overview:
        saveAction = handleSaveOverview
        break
      case AdminTabs.Forces:
        saveAction = handleSaveForce
        break
      case AdminTabs.Channels:
        saveAction = handleSaveChannel
        break
      default:
        saveAction = console.error
        break
    }
    saveAction(updates)
  }

  const onCreateForce = async () => {
    if (forces.dirty) {
      dispatch(modalAction.open('unsavedForce', 'create-new'))
    } else {
      const id = 'force-' + uniqid.time()
      const newForce = { name: id, uniqid: id }
      dispatch(addNewForce(newForce))
      dispatch(setSelectedForce(newForce))

      const template = forceTemplate
      template.name = id
      template.uniqid = id
      template.roles.forEach(role => {
        role.roleId = getUniquePasscode(forces.forces, 'p')
      })
      if (currentWargame) dispatch(saveForce(currentWargame, template))
    }
  }

  const onDeleteForce = ({ uniqid: data }: { uniqid: string }) => {
    dispatch(modalAction.open('confirmDelete', {
      type: 'force',
      data
    }))
  }

  const onDuplicateForce = (data: ForceData) => {
    if (currentWargame) dispatch(duplicateForce(currentWargame, data))
  }

  const onCreateChannel = (_id: string, createdChannel: ChannelTypes) => {
    if (channels.dirty) {
      dispatch(modalAction.open('unsavedChannel', 'create-new'))
    } else {
      if (currentWargame) dispatch(saveChannel(currentWargame, createdChannel))
    }
  }

  const onDeleteChannel = ({ uniqid }: { uniqid: string }) => {
    dispatch(modalAction.open('confirmDelete', { type: 'channel', data: uniqid }))
  }

  const onDuplicateChannel = ({ uniqid }: { uniqid: string }) => {
    if (currentWargame) dispatch(duplicateChannel(currentWargame, uniqid))
  }

  const onWargameInitiate = () => {
    if (currentWargame) dispatch(initiateWargame(currentWargame))
  }

  const handleSidebarForcesClick = (force: { name: string, uniqid: string, iconURL: string }) => {
    if (forces.dirty) {
      dispatch(modalAction.open('unsavedForce', force))
    } else {
      dispatch(setSelectedForce(force))
    }
  }

  const handleSidebarChannelsClick = (channel: ChannelTypes) => {
    if (channels.dirty) {
      dispatch(modalAction.open('unsavedChannel', channel))
    } else {
      dispatch(setTabSaved())
      dispatch(setSelectedChannel(channel))
    }
  }

  const handleSaveWargameTitle = (newGameTitle: string) => {
    let wargameNames = wargameList.map((game) => game.title)
    wargameNames = _.pull(wargameNames, wargameTitle)

    if (!checkUnique(newGameTitle, wargameNames)) {
      dispatch(addNotification('Wargame name is not unique.', 'warning'))
      return
    }

    if (typeof newGameTitle === 'string' && newGameTitle.length > 0) {
      if (currentWargame) dispatch(saveWargameTitle(currentWargame, newGameTitle))
    }

    if (newGameTitle === null || newGameTitle.length === 0) {
      window.alert('no channel name')
    }
  }

  useEffect(() => {
    if (currentTab === 'forces' && forces.selectedForce === '') {
      dispatch(setSelectedForce(forces.forces[0]))
    } else if (currentTab === 'channels' && channels.selectedChannel === '') {
      dispatch(setSelectedChannel(channels.channels[0]))
    }
  }, [currentTab])

  const getSelectedChannel = (): any => {
    if (channels.selectedChannel) {
      const { uniqid } = channels.selectedChannel as { uniqid: string }
      return uniqid && channels.channels.find((channel: ChannelTypes) => channel.uniqid === uniqid)
    }
  }
  return (
    <GameSetup
      activeTab={currentTab || tabs[0]}
      tabs={tabs}
      wargame={wargame as Wargame}
      wargameChanged={isWargameChanged()}
      // @ts-ignore
      onTabChange={onTabChange}
      onPressBack={onPressBack}
      overview={overview}
      forces={forces.forces}
      // @ts-ignore
      selectedForce={forces.selectedForce}
      channels={channels.channels}
      onOverviewChange={handleFormChange}
      // @ts-ignore
      onForcesChange={handleFormChange}
      onCreateForce={onCreateForce}
      // @ts-ignore
      onDeleteForce={onDeleteForce}
      // @ts-ignore
      onDuplicateForce={onDuplicateForce}
      onSidebarForcesClick={handleSidebarForcesClick}
      onSidebarChannelsClick={handleSidebarChannelsClick}
      // @ts-ignore
      onChannelsChange={handleFormChange}
      // @ts-ignore
      onCreateChannel={onCreateChannel}
      // @ts-ignore
      onDeleteChannel={onDeleteChannel}
      // @ts-ignore
      onDuplicateChannel={onDuplicateChannel}
      selectedChannel={getSelectedChannel()}
      onSave={onSave}
      messageTemplates={templates?.templates || messageTypes.messages}
      onSaveGameTitle={handleSaveWargameTitle}
      onWargameInitiate={onWargameInitiate}
      iconUploadUrl={iconUploaderPath}
      // @ts-ignore
      customDeleteHandler={handleDeleteGameControl}
    />
  )
}

export default AdminGameSetup