Skip to content
Snippets Groups Projects
CompetitionManager.tsx 14.5 KiB
Newer Older
  • Learn to ignore specific revisions
  •   Button,
      Dialog,
      DialogActions,
    
      DialogContentText,
    
      DialogTitle,
      ListItem,
    
      Menu,
      TablePagination,
      TextField,
    
      useMediaQuery,
    
    } from '@material-ui/core'
    
    import FormControl from '@material-ui/core/FormControl'
    import InputLabel from '@material-ui/core/InputLabel'
    import MenuItem from '@material-ui/core/MenuItem'
    import Paper from '@material-ui/core/Paper'
    import Select from '@material-ui/core/Select'
    
    import { createStyles, makeStyles, Theme, useTheme } from '@material-ui/core/styles'
    
    import Table from '@material-ui/core/Table'
    import TableBody from '@material-ui/core/TableBody'
    import TableCell from '@material-ui/core/TableCell'
    import TableContainer from '@material-ui/core/TableContainer'
    import TableHead from '@material-ui/core/TableHead'
    import TableRow from '@material-ui/core/TableRow'
    
    import FileCopyIcon from '@material-ui/icons/FileCopy'
    import LinkIcon from '@material-ui/icons/Link'
    
    import MoreHorizIcon from '@material-ui/icons/MoreHoriz'
    
    import RefreshIcon from '@material-ui/icons/Refresh'
    
    import axios from 'axios'
    import React, { useEffect } from 'react'
    
    import { Link, useHistory } from 'react-router-dom'
    
    import { getCompetitions, setFilterParams } from '../../../actions/competitions'
    import { useAppDispatch, useAppSelector } from '../../../hooks'
    
    import { Team } from '../../../interfaces/ApiModels'
    
    import { CompetitionFilterParams } from '../../../interfaces/FilterParams'
    
    Carl Schönfelder's avatar
    Carl Schönfelder committed
    import { FilterContainer, RemoveMenuItem, TopBar, YearFilterTextField } from '../styledComp'
    
    import AddCompetition from './AddCompetition'
    
    /**
     * Component description:
     * This component shows a list of all the competitions which a user can search through
     * We can also start, duplicate or delete a competition
     */
    
    // Use defined styling
    
    const useStyles = makeStyles((theme: Theme) =>
      createStyles({
        table: {
    
          width: '100%',
    
        },
        margin: {
          margin: theme.spacing(1),
        },
    
        paper: {
          backgroundColor: theme.palette.background.paper,
          boxShadow: theme.shadows[5],
          padding: 4,
          outline: 'none',
        },
    
    interface Code {
      id: number
      code: string
      view_type_id: number
      competition_id: number
      team_id: number
    }
    
    
    const CompetitionManager: React.FC = (props: any) => {
    
      // for dialog alert
      const [openAlert, setOpen] = React.useState(false)
      const theme = useTheme()
      const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))
    
    
      const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
    
      const [activeId, setActiveId] = React.useState<number | undefined>(undefined)
      const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
    
      const [dialogIsOpen, setDialogIsOpen] = React.useState(false)
      const [codes, setCodes] = React.useState<Code[]>([])
      const [teams, setTeams] = React.useState<Team[]>([])
      const [competitionName, setCompetitionName] = React.useState<string | undefined>(undefined)
    
    robban64's avatar
    robban64 committed
      const loading = useAppSelector((state) => state.user.userInfo === null)
    
      const competitions = useAppSelector((state) => state.competitions.competitions)
      const filterParams = useAppSelector((state) => state.competitions.filterParams)
      const competitionTotal = useAppSelector((state) => state.competitions.total)
    
      const cities = useAppSelector((state) => state.cities.cities)
    
      const classes = useStyles()
      const noFilterText = 'Alla'
    
      const dispatch = useAppDispatch()
    
      const history = useHistory()
    
      const handleClick = (event: React.MouseEvent<HTMLButtonElement>, id: number) => {
        setActiveId(id)
    
        getCodes(id)
        getTeams(id)
        setAnchorEl(event.currentTarget)
    
      }
    
      const handleClose = () => {
        setAnchorEl(null)
    
        setActiveId(undefined)
    
      const handleCloseVerify = () => {
        setOpen(false)
      }
    
    
      useEffect(() => {
        dispatch(getCompetitions())
      }, [])
    
    
      // Searchfuntion to search for a specific string
    
      const onSearchChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
    
        if (timerHandle) {
          clearTimeout(timerHandle)
          setTimerHandle(undefined)
        }
        //Only updates filter and api 100ms after last input was made
        setTimerHandle(window.setTimeout(() => dispatch(getCompetitions()), 100))
        dispatch(setFilterParams({ ...filterParams, name: event.target.value }))
      }
    
    
      const handleVerifyDelete = () => {
        setOpen(true)
      }
    
    
      // Function to remove a competition from the systems database
    
      const handleDeleteCompetition = async () => {
    
        setOpen(false)
    
        if (activeId) {
          await axios
    
            .delete(`/api/competitions/${activeId}`)
    
            .then(() => {
              setAnchorEl(null)
    
              dispatch(getCompetitions()) // refresh the competition list
    
            })
            .catch(({ response }) => {
              console.warn(response.data)
            })
        }
      }
    
    
    Björn Modée's avatar
    Björn Modée committed
      /** Start the competition by redirecting with URL with Code */
    
      const handleStartCompetition = () => {
    
        const operatorCode = codes.find((code) => code.view_type_id === 4)?.code
        if (operatorCode) {
          history.push(`/${operatorCode}`)
        }
    
    Björn Modée's avatar
    Björn Modée committed
      /** Fetch all the connection codes from the server */
    
      const getCodes = async (id: number) => {
    
          .get(`/api/competitions/${id}/codes`)
    
          .then((response) => {
            setCodes(response.data.items)
          })
          .catch(console.log)
      }
    
    
    Björn Modée's avatar
    Björn Modée committed
      /** Fetch all the teams from the server that is connected to a specific competition*/
    
      const getTeams = async (id: number) => {
    
          .get(`/api/competitions/${id}/teams`)
    
            // console.log(response.data.items)
    
            setTeams(response.data.items)
          })
          .catch((err) => {
            console.log(err)
          })
      }
    
    
    Björn Modée's avatar
    Björn Modée committed
      /** Fetch the copetition name from the server */
    
      const getCompetitionName = async () => {
        await axios
          .get(`/api/competitions/${activeId}`)
          .then((response) => {
    
            // console.log(response.data.name)
    
            setCompetitionName(response.data.name)
          })
          .catch((err) => {
            console.log(err)
          })
      }
    
      const getTypeName = (code: Code) => {
        let typeName = ''
        switch (code.view_type_id) {
          case 1:
            const team = teams.find((team) => team.id === code.team_id)
            if (team) {
              typeName = team.name
            } else {
              typeName = 'Lagnamn hittades ej'
            }
            break
          case 2:
            typeName = 'Domare'
            break
          case 3:
            typeName = 'Publik'
            break
          case 4:
    
            typeName = 'Tävlingsoperatör'
    
            break
          default:
            typeName = 'Typ hittades ej'
            break
        }
        return typeName
      }
    
    
    Björn Modée's avatar
    Björn Modée committed
      /** Handles the opening of the code dialog box */
    
      const handleOpenDialog = async () => {
        await getCompetitionName()
        setDialogIsOpen(true)
      }
    
    Björn Modée's avatar
    Björn Modée committed
      /** Handles the closing of the code dialog box */
    
      const handleCloseDialog = () => {
        setDialogIsOpen(false)
        setAnchorEl(null)
    
    Björn Modée's avatar
    Björn Modée committed
      /** Function that copies an existing competition */
    
      const handleDuplicateCompetition = async () => {
        if (activeId) {
          await axios
    
            .post(`/api/competitions/${activeId}/copy`)
    
            .then(() => {
              setAnchorEl(null)
              dispatch(getCompetitions())
            })
            .catch(({ response }) => {
              console.warn(response.data)
            })
        }
      }
    
    
      const handleFilterChange = (newParams: CompetitionFilterParams) => {
        dispatch(setFilterParams(newParams))
        dispatch(getCompetitions())
    
      const refreshCode = async (code: Code) => {
    
        if (activeId) {
          await axios
            .put(`/api/competitions/${activeId}/codes/${code.id}`)
            .then(() => {
              getCodes(activeId)
              dispatch(getCompetitions())
            })
            .catch(({ response }) => {
              console.warn(response.data)
            })
        }
    
          <TopBar>
    
            <FilterContainer>
    
              <TextField className={classes.margin} value={filterParams.name || ''} onChange={onSearchChange} label="Sök" />
    
              <FormControl className={classes.margin}>
                <InputLabel shrink id="demo-customized-select-native">
                  Region
                </InputLabel>
                <Select
                  labelId="demo-customized-select-label"
                  id="demo-customized-select"
    
                  value={filterParams.cityId ? cities.find((city) => filterParams.cityId === city.id)?.name : noFilterText}
    
                  <MenuItem value={noFilterText} onClick={() => handleFilterChange({ ...filterParams, cityId: undefined })}>
    
                    {noFilterText}
                  </MenuItem>
    
                  {cities &&
                    cities.map((city) => (
                      <MenuItem
                        key={city.name}
                        value={city.name}
                        onClick={() => handleFilterChange({ ...filterParams, cityId: city.id })}
                      >
                        {city.name}
                      </MenuItem>
                    ))}
    
                </Select>
              </FormControl>
    
              <YearFilterTextField
                label="År"
                name="model.year"
                type="number"
    
    robban64's avatar
    robban64 committed
                value={filterParams.year || ''}
    
                onChange={(event) => handleFilterChange({ ...filterParams, year: +event.target.value })}
                margin="normal"
              />
            </FilterContainer>
    
    robban64's avatar
    robban64 committed
            {!loading && <AddCompetition />}
    
          </TopBar>
    
          <TableContainer component={Paper}>
            <Table className={classes.table} aria-label="simple table">
              <TableHead>
                <TableRow>
                  <TableCell>Namn</TableCell>
                  <TableCell align="right">Region</TableCell>
                  <TableCell align="right">År</TableCell>
                  <TableCell align="right"></TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
    
                {competitions &&
                  competitions.map((row) => (
    
                    <TableRow key={row.name}>
                      <TableCell scope="row">
    
                        <Button color="primary" component={Link} to={`/editor/competition-id=${row.id}`}>
    
                          {row.name}
                        </Button>
                      </TableCell>
    
                      <TableCell align="right">{cities.find((city) => city.id === row.city_id)?.name || ''}</TableCell>
    
                      <TableCell align="right">{row.year}</TableCell>
                      <TableCell align="right">
    
                        <Button onClick={(event) => handleClick(event, row.id)} data-testid={row.name}>
    
                          <MoreHorizIcon />
                        </Button>
                      </TableCell>
                    </TableRow>
                  ))}
              </TableBody>
            </Table>
    
            {/** We can't find any competitions at all or with a specific filter */}
    
            {(!competitions || competitions.length === 0) && (
              <Typography>Inga tävlingar hittades med nuvarande filter</Typography>
            )}
    
          </TableContainer>
    
          <TablePagination
            component="div"
            rowsPerPageOptions={[]}
            rowsPerPage={filterParams.pageSize}
            count={competitionTotal}
            page={filterParams.page}
            onChangePage={(event, newPage) => handleFilterChange({ ...filterParams, page: newPage })}
          />
    
          <Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
    
            <MenuItem onClick={handleStartCompetition}>Starta</MenuItem>
    
            <MenuItem onClick={handleOpenDialog}>Visa koder</MenuItem>
    
            <MenuItem onClick={handleDuplicateCompetition}>Duplicera</MenuItem>
    
            <RemoveMenuItem onClick={handleVerifyDelete} data-testid="removeCompetitionButton">
    
              Ta bort
            </RemoveMenuItem>
    
          <Dialog
            fullScreen={fullScreen}
            open={openAlert}
            onClose={handleCloseVerify}
            aria-labelledby="responsive-dialog-title"
          >
            <DialogTitle id="responsive-dialog-title">{'Ta bort tävlingen?'}</DialogTitle>
            <DialogContent>
              <DialogContentText>
                Är du säker på att du vill ta bort tävlingen och all dess information från systemet?
              </DialogContentText>
            </DialogContent>
            <DialogActions>
              <Button autoFocus onClick={handleCloseVerify} color="primary">
                Avbryt
              </Button>
    
    Björn Modée's avatar
    Björn Modée committed
              <Button data-testid="acceptRemoveCompetition" onClick={handleDeleteCompetition} color="primary" autoFocus>
    
                Ta bort
              </Button>
            </DialogActions>
          </Dialog>
    
    
          <Dialog
            open={dialogIsOpen}
            onClose={handleCloseDialog}
            aria-labelledby="max-width-dialog-title"
            maxWidth="xl"
            fullWidth={false}
            fullScreen={false}
          >
            <DialogTitle id="max-width-dialog-title" className={classes.paper}>
              Koder för {competitionName}
            </DialogTitle>
            <DialogContent>
              {/* <DialogContentText>Här visas tävlingskoderna till den valda tävlingen.</DialogContentText> */}
              {codes.map((code) => (
                <ListItem key={code.id} style={{ display: 'flex' }}>
                  <ListItemText primary={`${getTypeName(code)}: `} />
                  <Typography component="div">
                    <ListItemText style={{ textAlign: 'right', marginLeft: '10px' }}>
                      <Box fontFamily="Monospace" fontWeight="fontWeightBold">
                        {code.code}
                      </Box>
                    </ListItemText>
                  </Typography>
                  <Tooltip title="Generera ny kod" arrow>
                    <Button
                      margin-right="0px"
                      onClick={() => {
                        refreshCode(code)
                      }}
                    >
                      <RefreshIcon fontSize="small" />
                    </Button>
                  </Tooltip>
                  <Tooltip title="Kopiera kod" arrow>
                    <Button
                      margin-right="0px"
                      onClick={() => {
                        navigator.clipboard.writeText(code.code)
                      }}
                    >
                      <FileCopyIcon fontSize="small" />
                    </Button>
                  </Tooltip>
    
                  <Tooltip title="Kopiera länk" arrow>
                    <Button
                      margin-right="0px"
                      onClick={() => {
                        navigator.clipboard.writeText(`${window.location.host}/${code.code}`)
                      }}
                    >
                      <LinkIcon fontSize="small" />
                    </Button>
                  </Tooltip>
    
                </ListItem>
              ))}
            </DialogContent>
            <DialogActions>
              <Button onClick={handleCloseDialog} color="primary">
                Stäng
              </Button>
            </DialogActions>
          </Dialog>
    
        </div>
      )
    }
    
    export default CompetitionManager