From 75e13330a7641d92500c78a6998a303d3b86c371 Mon Sep 17 00:00:00 2001
From: Luis Felipe Risch <lfr20@inf.ufpr.br>
Date: Thu, 1 Apr 2021 11:38:38 -0300
Subject: [PATCH] Finish pages of requirements

---
 .../Components/DataCards/RequirementCard.js   | 288 +++++++++
 .../Components/Inputs/CreateRequirement.js    | 382 ++++++++++++
 .../Components/Inputs/EditRequirements.js     | 413 +++++++++++++
 .../Pages/SubPages/GameficationRequires.js    | 545 ++++++++++++++++++
 4 files changed, 1628 insertions(+)
 create mode 100644 src/Admin/Components/Components/DataCards/RequirementCard.js
 create mode 100644 src/Admin/Components/Components/Inputs/CreateRequirement.js
 create mode 100644 src/Admin/Components/Components/Inputs/EditRequirements.js
 create mode 100644 src/Admin/Pages/Pages/SubPages/GameficationRequires.js

diff --git a/src/Admin/Components/Components/DataCards/RequirementCard.js b/src/Admin/Components/Components/DataCards/RequirementCard.js
new file mode 100644
index 00000000..68b264c6
--- /dev/null
+++ b/src/Admin/Components/Components/DataCards/RequirementCard.js
@@ -0,0 +1,288 @@
+/*Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana
+
+This file is part of Plataforma Integrada MEC.
+
+Plataforma Integrada MEC is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Plataforma Integrada MEC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Plataforma Integrada MEC.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import React, { useState, useEffect } from "react";
+import moment from "moment";
+// Maerial ui components
+import Card from "@material-ui/core/Card";
+import CardContent from "@material-ui/core/CardContent";
+import Typography from "@material-ui/core/Typography";
+import Button from "@material-ui/core/Button";
+import ListRoundedIcon from "@material-ui/icons/ListRounded";
+import { useStyles } from "../../Styles/DataCard";
+import Grid from "@material-ui/core/Grid";
+import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
+// Icons
+import EditRoundedIcon from "@material-ui/icons/EditRounded";
+//imports from local files
+import { GetAData, DeleteFilter } from "../../../Filters";
+import { Link, useHistory } from "react-router-dom";
+import LoadingSpinner from "../../../../Components/LoadingSpinner";
+import SnackBar from "../../../../Components/SnackbarComponent";
+import {
+  getRequest,
+  deleteRequest,
+} from "../../../../Components/HelperFunctions/getAxiosConfig";
+import styled from 'styled-components'
+import { DiRequirejs } from 'react-icons/di'
+
+const AchievementCard = ({ match }) => {
+  let history = useHistory();
+  const classes = useStyles();
+  const WINDOW_WIDTH = window.innerWidth
+  const [error, setError] = useState(false) //Necessary to consult the API, catch errors
+  const [isLoaded, setIsLoaded] = useState(false) //Necessary to consult the API, wait until complete
+  const [item, setItem] = useState()
+  const [reloadPage, setReloadPage] = useState(false);
+  const [snackInfo, setSnackInfo] = useState({
+    message: "",
+    icon: "",
+    open: false,
+    color: "",
+  });
+
+  const DisplayDate = (date) => {
+    const convertedData = moment.utc(date);
+    return moment(convertedData)
+      .format("LLL")
+      .toString();
+  };
+
+  const HandleSnack = (message, state, icon, color) => {
+    setSnackInfo({
+      message: message,
+      icon: icon,
+      open: state,
+      color: color,
+    });
+  };
+
+  const deleteHandler = () => {
+    deleteRequest(
+      DeleteFilter("requirements", item.id),
+      (data) => {
+        if (data.errors)
+          HandleSnack("Ocorreu algum erro", true, "warning", "#FA8072");
+        else {
+          HandleSnack(
+            "O item foi deletada com sucesso",
+            true,
+            "success",
+            "#228B22"
+          );
+        }
+        history.goBack()
+      },
+      (error) => {
+        HandleSnack("Ocorreu algum erro", true, "warning", "#FA8072");
+      }
+    )
+  }
+
+  useEffect(() => {
+    setIsLoaded(false)
+    getRequest(
+      GetAData("requirements", match.params.id),
+      (data, header) => {
+        setItem(data);
+        setIsLoaded(true);
+        setError(false);
+      },
+      (error) => {
+        setIsLoaded(true);
+        setError(true);
+      }
+    );
+  }, [reloadPage]);
+
+  if (error) {
+    return <div>Houve um erro</div>;
+  } else if (!isLoaded) {
+    return <LoadingSpinner text="Carregando..." />;
+  } else {
+    return (
+      <>
+        <SnackBar
+          severity={snackInfo.icon}
+          text={snackInfo.message}
+          snackbarOpen={snackInfo.open}
+          color={snackInfo.color}
+          handleClose={() =>
+            setSnackInfo({
+              message: "",
+              icon: "",
+              open: false,
+              color: "",
+            })
+          }
+        />
+        <Grid container direction="row" spacing={1}>
+          <Grid item xs={12}>
+            <Card>
+              <CardContent>
+                <Grid
+                  xs={12}
+                  justify="space-between"
+                  alignItems="center"
+                  container
+                >
+                  <Grid item>
+                    <Typography
+                      className={classes.title}
+                      color="inherit"
+                      gutterBottom
+                    >
+                      Informações do requisito
+                    </Typography>
+                  </Grid>
+                  <Grid item>
+                    <Link
+                      style={{ textDecoration: "none" }}
+                      to={`/admin/requirements`}
+                    >
+                      <Button
+                        startIcon={<ListRoundedIcon />}
+                        color="primary"
+                        variant="outlined"
+                      >
+                        Listar
+                      </Button>
+                    </Link>
+                    <Link
+                      to={`/admin/EditRequirement/${match.params.id}`}
+                      style={{ textDecoration: "none" }}
+                    >
+                      <Button
+                        startIcon={<EditRoundedIcon />}
+                        color="primary"
+                        variant="outlined"
+                      >
+                        Editar
+                      </Button>
+                    </Link>
+                    <Button
+                      startIcon={<DeleteRoundedIcon />}
+                      color="secondary"
+                      variant="outlined"
+                      onClick={deleteHandler}
+                    >
+                      Deletar
+                    </Button>
+                  </Grid>
+                </Grid>
+                <div style={{ height: "1em" }} />
+                <Grid container direction="row" spacing={3}>
+                  <Grid item sm={5} xs={12} style={WINDOW_WIDTH >= 600 ? {
+                    borderRight: "solid #d4d4d4 1px"
+                  } : {
+                      borderBottom: "solid #d4d4d4 1px"
+                    }}>
+                    <ImgDiv>
+                      <DiRequirejs size={200} color="#673ab7" />
+                    </ImgDiv>
+                  </Grid>
+                  <Grid item sm={7} xs={12}>
+                    <Grid container direction="column" spacing={1}>
+                      <Grid item>
+                        <Typography
+                          color="initial"
+                          className={classes.subTitle}
+                        >
+                          Nome
+                          </Typography>
+                        <Link to={`/admin/action/${item.action.id}`}>
+                          <a style={{ textDecoration: 'none', color: "#673ab7" }}>
+                            {item.action.name}
+                          </a>
+                        </Link>
+                      </Grid>
+                      <Grid item>
+                        <Typography
+                          color="initial"
+                          className={classes.subTitle}
+                        >
+                          Criado em
+                                                </Typography>
+                        <Typography color="textSecondary">
+                          {DisplayDate(item.created_at)}
+                        </Typography>
+                      </Grid>
+                      <Grid item>
+                        <Typography
+                          color="initial"
+                          className={classes.subTitle}
+                        >
+                          Atualizado em
+                                                </Typography>
+                        <Typography color="textSecondary">
+                          {DisplayDate(item.updated_at)}
+                        </Typography>
+                      </Grid>
+                      <Grid item>
+                        <Typography color="initial" className={classes.subTitle}>
+                          Descrição
+                        </Typography>
+                        <Typography color="textSecondary">
+                          {item.description}
+                        </Typography>
+                      </Grid>
+                    </Grid>
+                  </Grid>
+                </Grid>
+                <Grid container direction="column" spacing={1}>
+                  <Grid item>
+                    <Typography color="initial" className={classes.subTitle}>
+                      Achievements que dependem desse requisito
+                    </Typography>
+                    <Typography color="textSecondary">
+                      <ul>
+                        {
+                          item.achievements.map((achieve) => {
+                            return (
+                              <li key={achieve.created_at}>
+                                <Link to={`/admin/achievement/${achieve.id}`}>
+                                  <a style={{ textDecoration: 'none', color: "#673ab7" }}>
+                                    {achieve.name}
+                                  </a>
+                                </Link>
+                              </li>
+                            )
+                          })
+                        }
+                      </ul>
+                    </Typography>
+                  </Grid>
+                </Grid>
+              </CardContent>
+            </Card>
+          </Grid>
+        </Grid>
+      </>
+    );
+  }
+};
+
+export default AchievementCard;
+
+const ImgDiv = styled.div`
+  display: flex; 
+  justify-content: center; 
+  align-items: center;
+  height: 100%; 
+  width: 100%;
+`
diff --git a/src/Admin/Components/Components/Inputs/CreateRequirement.js b/src/Admin/Components/Components/Inputs/CreateRequirement.js
new file mode 100644
index 00000000..e7d51d25
--- /dev/null
+++ b/src/Admin/Components/Components/Inputs/CreateRequirement.js
@@ -0,0 +1,382 @@
+/*Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana
+
+This file is part of Plataforma Integrada MEC.
+
+Plataforma Integrada MEC is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Plataforma Integrada MEC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Plataforma Integrada MEC.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import React, { useState, useContext, useEffect } from 'react'
+//imports material ui componets
+import Card from "@material-ui/core/Card"
+import CardContent from "@material-ui/core/CardContent"
+import CardAction from '@material-ui/core/CardActions'
+import { Typography, TextField, Button, Grid } from '@material-ui/core'
+import CircularProgress from '@material-ui/core/CircularProgress'
+import AddRoundedIcon from '@material-ui/icons/AddRounded'
+import ListRoundedIcon from '@material-ui/icons/ListRounded'
+import Chip from '@material-ui/core/Chip';
+import MenuItem from "@material-ui/core/MenuItem"
+//imports local files
+import SnackBar from '../../../../Components/SnackbarComponent'
+import { Store } from '../../../../Store'
+import Unauthorized from '../Unauthorized'
+import { postRequest } from "../../../../Components/HelperFunctions/getAxiosConfig"
+//router
+import { Link } from 'react-router-dom'
+
+
+const CreateRequirement = () => {
+  const { state } = useContext(Store)
+
+  const [description, setDescription] = useState('')
+  const [goal, setGoal] = useState('')
+  const [resettable, setResettable] = useState('')
+  const [actionId, setActionId] = useState('')
+  const [achievements, setAchievements] = useState([])
+  const [stringReq, setStringReq] = useState("")
+
+  const [isLoading, setIsLoading] = useState(false)
+  const [isLoaded, setIsLoaded] = useState(false)
+  const [error, setError] = useState(false)
+
+  const [errorInGoal, setErrorInGoal] = useState({
+    error: false,
+    message: '',
+  })
+  const [errorInDescription, setErrorInDescription] = useState({
+    error: false,
+    message: '',
+  })
+  const [errorInActionId, setErrorInActionId] = useState({
+    error: false,
+    message: '',
+  })
+
+  const [snackInfo, setSnackInfo] = useState({
+    message: '',
+    icon: '',
+    open: false,
+    color: '',
+  })
+
+  const resettableOptions = [
+    { name: 1, value: "Sim" },
+    { name: 0, value: "Não" },
+
+  ]
+
+  const DescriptionHandler = (e) => {
+    if (errorInDescription.error)
+      setErrorInDescription({
+        error: false,
+        message: ''
+      })
+    setDescription(e.target.value)
+  }
+  const goalHandler = (e) => {
+    if (errorInGoal.error)
+      setErrorInGoal({
+        error: false,
+        message: ''
+      })
+    setGoal(e.target.value)
+  }
+  const actionIdHandler = (e) => {
+    if (errorInActionId.error)
+      setErrorInActionId({
+        error: false,
+        message: ''
+      })
+    setActionId(e.target.value)
+  };
+
+  const resettableHandler = (e) => {
+    setResettable(e.target.value)
+  }
+  const stringReqHandler = (e) => {
+    setStringReq(e.target.value)
+  };
+
+  // Handle snack infos
+  const HandleSnack = (message, state, icon, color) => {
+    setSnackInfo({
+      message: message,
+      icon: icon,
+      open: state,
+      color: color
+    })
+  }
+
+  const CheckUserPermission = () => {
+    let canUserEdit = false
+
+    if (state.userIsLoggedIn) {
+      const roles = [...state.currentUser.roles]
+      for (let i = 0; i < roles.length; i++)
+        if (roles[i].name === 'admin' || roles[i].name === 'editor')
+          canUserEdit = true
+    }
+    else {
+      canUserEdit = false
+    }
+
+    return canUserEdit
+  }
+
+  //Handle submit 
+  async function onSubmit() {
+    setIsLoading(true)
+    const api = `/requirements`
+    const body = {
+      "requirement": {
+        "description": description,
+        "goal": goal,
+        "repeatable": resettable,
+        "action_id": parseInt(actionId),
+        "achievements": achievements
+      }
+    }
+    postRequest(
+      api,
+      body,
+      (data) => {
+        if (data.id)
+          HandleSnack('O requisito foi criado com sucesso!', true, 'success', '#228B22')
+        else {
+          if (data.errors) {
+            HandleSnack(`${data.errors[0]}`, true, 'warning', '#FA8072')
+          }
+          if (data.goal) {
+            let goalError = ""
+            data.goal.map((msg) => (
+              goalError = goalError + msg + " e "
+            ))
+            setErrorInGoal({
+              error: true,
+              message: goalError
+            })
+          }
+          if (data.description) {
+            let descriptionError = ""
+            data.description.map((msg) => (
+              descriptionError = descriptionError + msg + " e "
+            ))
+            setErrorInDescription({
+              error: true,
+              message: descriptionError
+            })
+          }
+          if (data.action) {
+            let actionError = ""
+            data.action.map((msg) => (
+              actionError = actionError + msg + " e "
+            ))
+            setErrorInActionId({
+              error: true,
+              message: actionError
+            })
+          }
+        }
+        setIsLoading(false)
+      },
+      (error) => {
+        HandleSnack('Ocorreu algum erro', true, 'warning', '#FA8072')
+        setIsLoading(false)
+      }
+    )
+  }
+
+  const HandleDelete = (i) => {
+    const copyReq = [...achievements];
+    copyReq.splice(i, 1);
+    setAchievements(copyReq);
+  };
+
+  const OnKeyPressHandler = (key) => {
+    if (key === 13) {
+      const copyReq = [...achievements];
+      copyReq.push(parseInt(stringReq));
+      setAchievements(copyReq);
+      setStringReq("");
+    }
+  };
+
+  // Fields
+  const fields = [
+    {
+      select: false,
+      label: 'Descrição',
+      value: description,
+      required: false,
+      error: errorInDescription.error,
+      errorMessage: errorInDescription.message,
+      onChange: (event) => DescriptionHandler(event)
+    },
+    {
+      select: false,
+      label: 'Meta',
+      value: goal,
+      error: errorInGoal.error,
+      errorMessage: errorInGoal.message,
+      required: false,
+      onChange: (event) => goalHandler(event)
+    },
+    {
+      select: false,
+      label: 'ID da ação',
+      value: actionId,
+      error: errorInActionId.error,
+      errorMessage: errorInActionId.message,
+      required: false,
+      onChange: (event) => actionIdHandler(event)
+    },
+    {
+      select: true,
+      label: 'Repetível',
+      value: resettable,
+      required: false,
+      options: [...resettableOptions],
+      onChange: (event) => resettableHandler(event)
+    },
+  ]
+
+  if (CheckUserPermission()) {
+    return (
+      <Card>
+        <SnackBar
+          severity={snackInfo.icon}
+          text={snackInfo.message}
+          snackbarOpen={snackInfo.open}
+          color={snackInfo.color}
+          handleClose={() => setSnackInfo({
+            message: '',
+            icon: '',
+            open: false,
+            color: ''
+          })}
+        />
+        <CardContent>
+          <Grid container direction='row' justify='space-between' alignContent="center" alignItems="center" xs={12}>
+            <Grid item>
+              <Typography variant='h4'>
+                Criar requisito
+              </Typography>
+            </Grid>
+            <Grid item>
+              <Link style={{ textDecoration: 'none' }} to={'/admin/requirements'}>
+                <Button
+                  // onClick={props.BackToList}
+                  startIcon={<ListRoundedIcon />}
+                  variant='outlined'
+                  color='primary'
+                >
+                  Listar
+                </Button>
+              </Link>
+            </Grid>
+          </Grid>
+
+          <div style={{ height: '1em' }}></div>
+          <form style={{ display: 'flex', flexDirection: 'column' }}>
+            {fields.map((field, index) => (
+              field.select ?
+                <TextField
+                  select
+                  key={index}
+                  required={field.required}
+                  error={field.error}
+                  helperText={field.error ? field.errorMessage : ''}
+                  style={{ width: '250px', marginBottom: '1em' }}
+                  label={field.label}
+                  value={field.value}
+                  onChange={field.onChange}
+                  type="search"
+                  multiline={true}
+                >
+                  {field.options.map((option, index) => (
+                    <MenuItem
+                      key={option.value}
+                      value={option.name}
+                      name={option.value}
+                    >
+                      {option.value}
+                    </MenuItem>
+                  ))}
+                </TextField>
+                :
+                <TextField
+                  key={index}
+                  required={field.required}
+                  error={field.error}
+                  helperText={field.error ? field.errorMessage : ''}
+                  style={{ width: '250px', marginBottom: '1em' }}
+                  label={field.label}
+                  value={field.value}
+                  onChange={field.onChange}
+                  type="search"
+                  multiline={true}
+                />
+            ))}
+            <>
+              <div
+                style={{
+                  display: "flex",
+                  flexDirection: "row",
+                  flexWrap: "wrap",
+                  marginBottom: "1em"
+                }}
+              >
+                {achievements.map((req, index) => (
+                  <li key={req.id} style={{ listStyleType: "none", marginBottom: "0.5em", marginRight: "0.5em" }}>
+                    <Chip
+                      label={req}
+                      onDelete={() => HandleDelete(index)}
+                    />
+                  </li>
+                ))}
+              </div>
+
+              <TextField
+                id="outlined-input"
+                label="Requisitos"
+                rows={1}
+                value={stringReq}
+                onKeyPress={(key) => OnKeyPressHandler(key.which)}
+                onChange={stringReqHandler}
+                // onBlur={ShowEmails}
+                helperText="Digite o ID do requisito, um por vez, e pressione Enter"
+                style={{ marginBottom: "1em", width: '250px' }}
+              />
+            </>
+          </form>
+        </CardContent>
+        <CardAction>
+          <Button
+            onClick={onSubmit}
+            variant="contained"
+            color="primary"
+            disabled={isLoading}
+            startIcon={isLoading ? null : <AddRoundedIcon />}
+          >
+            {
+              isLoading ? <CircularProgress size={24} /> : 'Criar'
+            }
+          </Button>
+        </CardAction>
+      </Card>
+    )
+  } else return <Unauthorized />
+}
+
+export default CreateRequirement
\ No newline at end of file
diff --git a/src/Admin/Components/Components/Inputs/EditRequirements.js b/src/Admin/Components/Components/Inputs/EditRequirements.js
new file mode 100644
index 00000000..db4b459a
--- /dev/null
+++ b/src/Admin/Components/Components/Inputs/EditRequirements.js
@@ -0,0 +1,413 @@
+/*Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana
+
+This file is part of Plataforma Integrada MEC.
+
+Plataforma Integrada MEC is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Plataforma Integrada MEC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Plataforma Integrada MEC.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import React, { useState, useContext, useEffect } from 'react'
+//imports material ui componets
+import Card from "@material-ui/core/Card"
+import CardContent from "@material-ui/core/CardContent"
+import CardAction from '@material-ui/core/CardActions'
+import { Typography, TextField, Button, Grid } from '@material-ui/core'
+import CircularProgress from '@material-ui/core/CircularProgress'
+import AddRoundedIcon from '@material-ui/icons/AddRounded'
+import ListRoundedIcon from '@material-ui/icons/ListRounded'
+import Chip from '@material-ui/core/Chip';
+import MenuItem from "@material-ui/core/MenuItem"
+//imports local files
+import SnackBar from '../../../../Components/SnackbarComponent'
+import { Store } from '../../../../Store'
+import Unauthorized from '../Unauthorized'
+import { getRequest, putRequest } from "../../../../Components/HelperFunctions/getAxiosConfig"
+import LoadingSpinner from '../../../../Components/LoadingSpinner'
+import { GetAData } from '../../../Filters'
+//router
+import { Link } from 'react-router-dom'
+
+
+const EditRequirements = ({ match }) => {
+  const { state } = useContext(Store)
+
+  const [description, setDescription] = useState('')
+  const [goal, setGoal] = useState('')
+  const [resettable, setResettable] = useState('')
+  const [actionId, setActionId] = useState('')
+  const [achievements, setAchievements] = useState([])
+  const [stringReq, setStringReq] = useState("")
+
+  const [isLoading, setIsLoading] = useState(false)
+  const [isLoaded, setIsLoaded] = useState(false)
+  const [error, setError] = useState(false)
+
+  const [errorInGoal, setErrorInGoal] = useState({
+    error: false,
+    message: '',
+  })
+  const [errorInDescription, setErrorInDescription] = useState({
+    error: false,
+    message: '',
+  })
+  const [errorInActionId, setErrorInActionId] = useState({
+    error: false,
+    message: '',
+  })
+
+  const [snackInfo, setSnackInfo] = useState({
+    message: '',
+    icon: '',
+    open: false,
+    color: '',
+  })
+
+  const resettableOptions = [
+    { name: 1, value: "Sim" },
+    { name: 0, value: "Não" },
+
+  ]
+
+  const DescriptionHandler = (e) => {
+    if (errorInDescription.error)
+      setErrorInDescription({
+        error: false,
+        message: ''
+      })
+    setDescription(e.target.value)
+  }
+  const goalHandler = (e) => {
+    if (errorInGoal.error)
+      setErrorInGoal({
+        error: false,
+        message: ''
+      })
+    setGoal(e.target.value)
+  }
+  const actionIdHandler = (e) => {
+    if (errorInActionId.error)
+      setErrorInActionId({
+        error: false,
+        message: ''
+      })
+    setActionId(e.target.value)
+  };
+
+  const resettableHandler = (e) => {
+    setResettable(e.target.value)
+  }
+  const stringReqHandler = (e) => {
+    setStringReq(e.target.value)
+  };
+  
+  // Handle snack infos
+  const HandleSnack = (message, state, icon, color) => {
+    setSnackInfo({
+      message: message,
+      icon: icon,
+      open: state,
+      color: color
+    })
+  }
+
+  const CheckUserPermission = () => {
+    let canUserEdit = false
+
+    if (state.userIsLoggedIn) {
+      const roles = [...state.currentUser.roles]
+      for (let i = 0; i < roles.length; i++)
+        if (roles[i].name === 'admin' || roles[i].name === 'editor')
+          canUserEdit = true
+    }
+    else {
+      canUserEdit = false
+    }
+
+    return canUserEdit
+  }
+
+  //Handle submit 
+  async function onSubmit() {
+    setIsLoading(true)
+    const api = `/requirements/${match.params.id}`
+    const body = {
+      "requirement": {
+        "description": description,
+        "goal": goal,
+        "repeatable": resettable,
+        "action_id" : parseInt(actionId),
+        "achievements": achievements
+      }
+    }
+    putRequest(
+      api,
+      body,
+      (data) => {
+        if (data.id)
+          HandleSnack('O item foi alterado com sucesso!', true, 'success', '#228B22')
+        else {
+          if (data.errors) {
+            HandleSnack(`${data.errors[0]}`, true, 'warning', '#FA8072')
+          }
+          if (data.goal) {
+            let goalError = ""
+            data.goal.map((msg) => (
+              goalError = goalError + msg + " e "
+            ))
+            setErrorInGoal({
+              error: true,
+              message: goalError
+            })
+          }
+          if (data.description) {
+            let descriptionError = ""
+            data.description.map((msg) => (
+              descriptionError = descriptionError + msg + " e "
+            ))
+            setErrorInDescription({
+              error: true,
+              message: descriptionError
+            })
+          }
+          if (data.action) {
+            let actionError = ""
+            data.action.map((msg) => (
+              actionError = actionError + msg + " e "
+            ))
+            setErrorInActionId({
+              error: true,
+              message: actionError
+            })
+          }
+        }
+        setIsLoading(false)
+      },
+      (error) => {
+        HandleSnack('Ocorreu algum erro', true, 'warning', '#FA8072')
+        setIsLoading(false)
+      }
+    )
+  }
+
+  const HandleDelete = (i) => {
+    const copyReq = [...achievements];
+    copyReq.splice(i, 1);
+    setAchievements(copyReq);
+  };
+
+  const OnKeyPressHandler = (key) => {
+    if (key === 13) {
+      const copyReq = [...achievements];
+      copyReq.push(parseInt(stringReq));
+      setAchievements(copyReq);
+      setStringReq("");
+    }
+  };
+
+  // Fields
+  const fields = [
+    {
+      select: false,
+      label: 'Descrição',
+      value: description,
+      required: false,
+      error: errorInDescription.error,
+      errorMessage: errorInDescription.message,
+      onChange: (event) => DescriptionHandler(event)
+    },
+    {
+      select: false,
+      label: 'Meta',
+      value: goal,
+      error: errorInGoal.error,
+      errorMessage: errorInGoal.message,
+      required: false,
+      onChange: (event) => goalHandler(event)
+    },
+    {
+      select: false,
+      label: 'ID da ação',
+      value: actionId,
+      error: errorInActionId.error,
+      errorMessage: errorInActionId.message,
+      required: false,
+      onChange: (event) => actionIdHandler(event)
+    },
+    {
+      select: true,
+      label: 'Repetível',
+      value: resettable,
+      required: false,
+      options: [...resettableOptions],
+      onChange: (event) => resettableHandler(event)
+    },
+  ]
+
+  useEffect(() => {
+    getRequest(
+      GetAData("requirements", match.params.id),
+      (data, header) => {
+        const achievementsId = []
+        for (let index = 0; index < data.achievements.length; index++) {
+          const element = data.achievements[index];
+          achievementsId.push(element.id)
+        }
+        setAchievements(achievementsId)
+        setDescription(data.description)
+        setGoal(data.goal)
+        setResettable(data.repeatable)
+        setActionId(data.action.id)
+        
+        setError(false)
+        setIsLoaded(true)
+      },
+      (error) => {
+        setIsLoaded(true)
+        setError(true)
+      }
+    )
+  }, [])
+
+  if (error) {
+    return <div> Error... </div>
+  } else if (!isLoaded) {
+    return <LoadingSpinner text="Carregando..." />
+  } else if (CheckUserPermission()) {
+    return (
+      <Card>
+        <SnackBar
+          severity={snackInfo.icon}
+          text={snackInfo.message}
+          snackbarOpen={snackInfo.open}
+          color={snackInfo.color}
+          handleClose={() => setSnackInfo({
+            message: '',
+            icon: '',
+            open: false,
+            color: ''
+          })}
+        />
+        <CardContent>
+          <Grid container direction='row' justify='space-between' alignContent="center" alignItems="center" xs={12}>
+            <Grid item>
+              <Typography variant='h4'>
+                Editar requisito
+              </Typography>
+            </Grid>
+            <Grid item>
+              <Link style={{ textDecoration: 'none' }} to={'/admin/requirements'}>
+                <Button
+                  // onClick={props.BackToList}
+                  startIcon={<ListRoundedIcon />}
+                  variant='outlined'
+                  color='primary'
+                >
+                  Listar
+                </Button>
+              </Link>
+            </Grid>
+          </Grid>
+
+          <div style={{ height: '1em' }}></div>
+          <form style={{ display: 'flex', flexDirection: 'column' }}>
+            {fields.map((field, index) => (
+              field.select ?
+                <TextField
+                  select
+                  key={index}
+                  required={field.required}
+                  error={field.error}
+                  helperText={field.error ? field.errorMessage : ''}
+                  style={{ width: '250px', marginBottom: '1em' }}
+                  label={field.label}
+                  value={field.value}
+                  onChange={field.onChange}
+                  type="search"
+                  multiline={true}
+                >
+                  {field.options.map((option, index) => (
+                    <MenuItem
+                      key={option.value}
+                      value={option.name}
+                      name={option.value}
+                    >
+                      {option.value}
+                    </MenuItem>
+                  ))}
+                </TextField>
+                :
+                <TextField
+                  key={index}
+                  required={field.required}
+                  error={field.error}
+                  helperText={field.error ? field.errorMessage : ''}
+                  style={{ width: '250px', marginBottom: '1em' }}
+                  label={field.label}
+                  value={field.value}
+                  onChange={field.onChange}
+                  type="search"
+                  multiline={true}
+                />
+            ))}
+            <>
+              <div
+                style={{
+                  display: "flex",
+                  flexDirection: "row",
+                  flexWrap: "wrap",
+                  marginBottom: "1em"
+                }}
+              >
+                {achievements.map((req, index) => (
+                  <li key={req.id} style={{ listStyleType: "none", marginBottom: "0.5em", marginRight: "0.5em" }}>
+                    <Chip
+                      label={req}
+                      onDelete={() => HandleDelete(index)}
+                    />
+                  </li>
+                ))}
+              </div>
+
+              <TextField
+                id="outlined-input"
+                label="Requisitos"
+                rows={1}
+                value={stringReq}
+                onKeyPress={(key) => OnKeyPressHandler(key.which)}
+                onChange={stringReqHandler}
+                // onBlur={ShowEmails}
+                helperText="Digite o ID do requisito, um por vez, e pressione Enter"
+                style={{ marginBottom: "1em", width: '250px' }}
+              />
+            </>
+          </form>
+        </CardContent>
+        <CardAction>
+          <Button
+            onClick={onSubmit}
+            variant="contained"
+            color="primary"
+            disabled={isLoading}
+            startIcon={isLoading ? null : <AddRoundedIcon />}
+          >
+            {
+              isLoading ? <CircularProgress size={24} /> : 'Editar'
+            }
+          </Button>
+        </CardAction>
+      </Card>
+    )
+  } else return <Unauthorized />
+}
+
+export default EditRequirements
\ No newline at end of file
diff --git a/src/Admin/Pages/Pages/SubPages/GameficationRequires.js b/src/Admin/Pages/Pages/SubPages/GameficationRequires.js
new file mode 100644
index 00000000..64099c70
--- /dev/null
+++ b/src/Admin/Pages/Pages/SubPages/GameficationRequires.js
@@ -0,0 +1,545 @@
+/*Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana
+
+This file is part of Plataforma Integrada MEC.
+
+Plataforma Integrada MEC is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Plataforma Integrada MEC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Plataforma Integrada MEC.  If not, see <http://www.gnu.org/licenses/>.*/
+
+import React, { useContext, useEffect, useState } from "react";
+import moment from "moment";
+import Unauthorized from "../../../Components/Components/Unauthorized";
+import { Store } from "../../../../Store";
+import { Link, useHistory } from "react-router-dom";
+import { getRequest, deleteRequest, putRequest } from "../../../../Components/HelperFunctions/getAxiosConfig";
+import { Url, DeleteFilter, EditFilter } from "../../../Filters";
+import UpdateRoundedIcon from "@material-ui/icons/UpdateRounded";
+import { withStyles } from "@material-ui/core/styles";
+import AddRoundedIcon from "@material-ui/icons/AddRounded";
+import FilterListRoundedIcon from "@material-ui/icons/FilterListRounded";
+import VisibilityIcon from "@material-ui/icons/Visibility";
+import LoadingSpinner from "../../../../Components/LoadingSpinner";
+import PageHeader from "../../../Components/Components/PageHeader";
+import SnackBar from "../../../../Components/SnackbarComponent";
+import TableData from "../../../Components/Components/Table";
+import TextField from "@material-ui/core/TextField";
+import MenuItem from "@material-ui/core/MenuItem";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
+import TableRow from "@material-ui/core/TableRow";
+import Button from "@material-ui/core/Button";
+import CircularProgress from "@material-ui/core/CircularProgress";
+import Paper from "@material-ui/core/Paper";
+import MobilePageHeader from "../../../Components/Components/MobileComponents/MobilePageHeader"
+import styled from 'styled-components'
+import MobileList from "../../../Components/Components/MobileComponents/SimpleList"
+import { Grid } from "@material-ui/core";
+import AlertDialog from "../../../Components/Components/AlertDialog"
+import { DiRequirejs } from 'react-icons/di'
+import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
+
+const StyledTableCell = withStyles((theme) => ({
+  head: {
+    backgroundColor: theme.palette.common.black,
+    color: theme.palette.common.white,
+  },
+  body: {
+    fontSize: 14,
+  },
+}))(TableCell);
+
+const StyledTableRow = withStyles((theme) => ({
+  root: {
+    "&:nth-of-type(odd)": {
+      backgroundColor: theme.palette.action.hover,
+    },
+  },
+}))(TableRow);
+
+const GameficationRequires = () => {
+  const history = useHistory()
+  const WINDOW_WIDTH = window.innerWidth
+  const ADD_ONE_LENGHT = [""];
+  const TOP_LABELS = [
+    "ID",
+    "NAME",
+    "DESCRIÇÃO",
+    "META",
+    "REPETIVEL",
+    "CRIADO EM",
+    "ATUALIZADO EM",
+    "AÇÕES"
+  ]; //Labels from Table
+  const { state } = useContext(Store);
+  const [currPage, setcurrPage] = useState(0);
+  const [error, setError] = useState(false);
+  const [loaded, setLoaded] = useState(true);
+  const [isLoadingMoreItems, setIsLoadingMoreItems] = useState(false);
+  const [showFilter, setShowFilter] = useState(false);
+  const [items, setItems] = useState([]);
+  const [stateOpt, setStateOpt] = useState(1)
+  const [descricao, setDescricao] = useState("")
+  const [valueDescricaoField, setValueDescricaoField] = useState("")
+  const [open, setOpen] = useState(false)
+  const [deleteItem, setDeleteItem] = useState({})
+  const [snackInfo, setSnackInfo] = useState({
+    message: "",
+    icon: "",
+    open: false,
+    color: "",
+  });
+
+  //handle snack info
+  const HandleSnack = (message, state, icon, color) => {
+    setSnackInfo({
+      message: message,
+      icon: icon,
+      open: state,
+      color: color,
+    });
+  };
+
+  const CheckUserPermission = () => {
+    let canUserEdit = false;
+
+    if (state.userIsLoggedIn) {
+      const roles = [...state.currentUser.roles];
+      for (let i = 0; i < roles.length; i++)
+        if (roles[i].name === "admin" || roles[i].name === "editor")
+          canUserEdit = true;
+    } else {
+      canUserEdit = false;
+    }
+
+    return canUserEdit;
+  };
+
+  const DisplayDate = (date) => {
+    const convertedData = moment.utc(date);
+    return moment(convertedData)
+      .format("LLL")
+      .toString();
+  };
+
+  const handleAlertDialog = (item) => {
+    setOpen(true)
+    setDeleteItem(item)
+  }
+
+  const findIndexOfWantedItem = (item) => {
+    const index = items.findIndex((item) => item.id === deleteItem.id)
+    return index;
+  }
+
+  const deleteHandler = () => {
+    deleteRequest(
+      DeleteFilter("requirements", deleteItem.id),
+      (data) => {
+        if (data.errors)
+          HandleSnack("Ocorreu algum erro", true, "warning", "#FA8072");
+        else {
+          HandleSnack(
+            "O achievement foi deletada com sucesso",
+            true,
+            "success",
+            "#228B22"
+          );
+          handleChangeStateItem(findIndexOfWantedItem(deleteItem), "deleted")
+        }
+        setcurrPage(0)
+      },
+      (error) => {
+        HandleSnack("Ocorreu algum erro", true, "warning", "#FA8072");
+      }
+    )
+  }
+
+  const handleChangeStateItem = (index, state) => {
+    const currItems = [...items]
+    currItems.splice(index, 1)
+    setItems(currItems)
+  }
+
+  const buildUrl = (description) => {
+    if (description)
+      return Url("/requirements", `"description" : "${description}"`, currPage, "DESC")
+    else
+      return Url("/requirements", "", currPage, "DESC")
+  }
+
+  useEffect(() => {
+    if (currPage === 0)
+      setLoaded(false)
+    else
+      setIsLoadingMoreItems(true)
+    getRequest(
+      buildUrl(descricao),
+      (data, header) => {
+        const arrData = [...data]
+        if (arrData.length === 0) {
+          HandleSnack('Não há mais dados para serem carregados', true, 'warning', '#FFC125')
+        } else {
+          const arrItems = [...items]
+          if (currPage === 0) {
+            setItems(arrData.concat(ADD_ONE_LENGHT))
+          }
+          else {
+            arrItems.pop(); //Deleting the last position, that was used to display the button of load more items 
+            const arrResult = arrItems.concat(arrData)
+            setItems(arrResult.concat(ADD_ONE_LENGHT))
+          }
+        }
+        setLoaded(true)
+        setIsLoadingMoreItems(false)
+      },
+      (error) => {
+        HandleSnack('Erro ao carregar os dados', true, 'warning', '#FA8072')
+        setIsLoadingMoreItems(false)
+      }
+    )
+  }, [currPage, descricao, stateOpt])
+
+  useEffect(() => {
+    setDescricao("")
+    setStateOpt(1)
+  }, [showFilter])
+
+  if (error) {
+    return <div>Error</div>;
+  } else if (!loaded) {
+    return <LoadingSpinner text="Carregando..." />;
+  } else {
+    if (CheckUserPermission()) {
+      if (WINDOW_WIDTH <= 950) {
+        return (
+          <>
+            <AlertDialog
+              open={open}
+              OnDelete={deleteHandler}
+              deleteItem={deleteItem}
+              HandleClose={() => {
+                setOpen(false)
+              }}
+            />
+            <SnackBar
+              severity={snackInfo.icon}
+              text={snackInfo.message}
+              snackbarOpen={snackInfo.open}
+              color={snackInfo.color}
+              handleClose={() =>
+                setSnackInfo({
+                  message: "",
+                  icon: "",
+                  open: false,
+                  color: "",
+                })
+              }
+            />
+            <MobilePageHeader
+              title="Requisitos"
+              actions={[
+                {
+                  name: "Atualizar",
+                  isLoading: false,
+                  func: () => {
+                    setcurrPage(0);
+                  },
+                  icon: <UpdateRoundedIcon />,
+                },
+                {
+                  name: "Filtrar",
+                  isLoading: false,
+                  func: () => {
+                    setShowFilter(!showFilter);
+                  },
+                  icon: <FilterListRoundedIcon />,
+                },
+                {
+                  name: "Novo",
+                  isLoading: false,
+                  func: () => {
+                    history.push("/admin/CreateRequirement")
+                  },
+                  icon: <AddRoundedIcon />,
+                },
+              ]}
+            >
+              {showFilter ? (
+                <Grid
+                  container
+                  direction="row"
+                  justify="space-between"
+                  alignItems="center"
+                  alignContent="flex-end"
+                  spacing={3}
+                  xs={12}
+                >
+                  <Grid item>
+                    <TextField
+                      label="Descrição"
+                      value={valueDescricaoField}
+                      onChange={(e) => { setValueDescricaoField(e.target.value) }}
+                      onBlur={(e) => { setDescricao(e.target.value) }}
+                      helperText="Por favor, ao digitar o descrição que você quer filtar, retire o foco do campo de texto"
+                    />
+                  </Grid>
+                </Grid>
+              ) : null}
+            </MobilePageHeader>
+            <div style={{ height: '2em' }}></div>
+
+            {items.map((row, index) =>
+              index === items.length - 1 ? (
+                <StyledDivButton>
+                  <Button
+                    key={index}
+                    color="primary"
+                    variant="text"
+                    // disabled={isLoadingMoreItems}
+                    startIcon={<AddRoundedIcon />}
+                    disabled={isLoadingMoreItems}
+                    onClick={() => {
+                      setcurrPage(currPage + 1)
+                    }}
+                  >
+                    {isLoadingMoreItems ? (
+                      <CircularProgress size={24} />
+                    ) : (
+                        "Carregar mais itens"
+                      )}
+                  </Button>
+                </StyledDivButton>
+              ) : (
+                  <>
+                    <MobileList
+                      key={index}
+                      title={row.action.name}
+                      subtitle={row.id}
+                      backColor={"#673ab7"}
+                      avatar={
+                        <DiRequirejs size={20} color="white" />
+                      }
+                      href={`/admin/requirement/${row.id}`}
+                      reset={() => { }}
+                      data={[
+                        {
+                          title: "Criado em",
+                          subtitle: DisplayDate(row.created_at)
+                        },
+                        {
+                          title: "Atualizado em",
+                          subtitle: DisplayDate(row.updated_at)
+                        },
+                        {
+                          title: "Descrição",
+                          subtitle: row.description
+                        },
+                        {
+                          title: "Achivements que depende desse requisito",
+                          subtitle: <ul>
+                            {
+                              row.achievements.map((achieve) => {
+                                return (
+                                  <li key={achieve.created_at}>
+                                    <Link to={`/admin/achievement/${achieve.id}`}>
+                                      <a style={{textDecoration: "none", color: '#673ab7'}}>
+                                        {achieve.name}
+                                      </a>
+                                    </Link>
+                                  </li>
+                                )
+                              })
+                            }
+                          </ul>
+                        },
+                        {
+                          title: "Ações",
+                          subtitle: <Button
+                            variant="contained"
+                            color="secondary"
+                            startIcon={<DeleteRoundedIcon />}
+                            onClick={() => handleAlertDialog(row)}
+                          >
+                            Deletar
+                          </Button>
+                        },
+                      ]}
+                    />
+                    <div style={{ height: "0.5em" }} />
+                  </>
+                )
+            )}
+          </>
+        )
+      } else {
+        return (
+          <>
+            <SnackBar
+              severity={snackInfo.icon}
+              text={snackInfo.message}
+              snackbarOpen={snackInfo.open}
+              color={snackInfo.color}
+              handleClose={() =>
+                setSnackInfo({
+                  message: "",
+                  icon: "",
+                  open: false,
+                  color: "",
+                })
+              }
+            />
+            <PageHeader
+              title="Requisitos"
+              actions={[
+                {
+                  name: "Atualizar",
+                  isLoading: false,
+                  func: () => {
+                    setcurrPage(0);
+                  },
+                  icon: <UpdateRoundedIcon />,
+                },
+                {
+                  name: "Filtrar",
+                  isLoading: false,
+                  func: () => {
+                    setShowFilter(!showFilter);
+                  },
+                  icon: <FilterListRoundedIcon />,
+                },
+                {
+                  name: "Novo",
+                  isLoading: false,
+                  func: () => {
+                    history.push("/admin/CreateRequirement")
+                  },
+                  icon: <AddRoundedIcon />,
+                },
+              ]}
+            >
+              {showFilter ? (
+                <Grid
+                  container
+                  direction="row"
+                  justify="space-between"
+                  alignItems="center"
+                  alignContent="flex-end"
+                  spacing={3}
+                  xs={12}
+                >
+                  <Grid item>
+                    <TextField
+                      label="Descrição"
+                      value={valueDescricaoField}
+                      onChange={(e) => { setValueDescricaoField(e.target.value) }}
+                      onBlur={(e) => { setDescricao(e.target.value) }}
+                      helperText="Por favor, ao digitar o descrição que você quer filtar, retire o foco do campo de texto"
+                    />
+                  </Grid>
+                </Grid>
+              ) : null}
+            </PageHeader>
+
+            <div style={{ height: "2em" }}></div>
+
+            <TableData top={TOP_LABELS}>
+              <TableBody>
+                {items.map((row, index) =>
+                  index === items.length - 1 ? (
+                    <StyledTableRow key={index} style={{ padding: "1em" }}>
+                      {/* Button to load more data */}
+                      <StyledTableCell>
+                        <Button
+                          color="primary"
+                          variant="text"
+                          // disabled={isLoadingMoreItems}
+                          startIcon={<AddRoundedIcon />}
+                          disabled={isLoadingMoreItems}
+                          onClick={() => {
+                            setcurrPage(currPage + 1);
+                          }}
+                        >
+                          {isLoadingMoreItems ? (
+                            <CircularProgress size={24} />
+                          ) : (
+                              "Carregar mais itens"
+                            )}
+                        </Button>
+                      </StyledTableCell>
+                    </StyledTableRow>
+                  ) : (
+                      <StyledTableRow key={index}>
+                        <StyledTableCell component="th" scope="row">
+                          {row.id}
+                        </StyledTableCell>
+                        <StyledTableCell align="right">{row.action.name}</StyledTableCell>
+                        <StyledTableCell align="right">{row.description}</StyledTableCell>
+                        <StyledTableCell align="right">{row.goal}</StyledTableCell>
+                        <StyledTableCell align="right">{row.repeatable}</StyledTableCell>
+                        <StyledTableCell align="right">
+                          {DisplayDate(row.created_at)}
+                        </StyledTableCell>
+                        <StyledTableCell align="right">
+                          {DisplayDate(row.updated_at)}
+                        </StyledTableCell>
+                        <StyledTableCell align="right">
+                          <Link to={`/admin/requirement/${row.id}`}>
+                            <Button
+                              style={{ width: "100%", marginBottom: "0.5em" }}
+                              variant="contained"
+                              color="primary"
+                              startIcon={<VisibilityIcon />}
+                            >
+                              Visualizar
+                            </Button>
+                          </Link>
+
+                          <Button
+                            style={{ width: "100%" }}
+                            variant="contained"
+                            color="secondary"
+                            startIcon={<DeleteRoundedIcon />}
+                            onClick={() => handleAlertDialog(row)}
+                          >
+                            Deletar
+                          </Button>
+                        </StyledTableCell>
+                      </StyledTableRow>
+                    )
+                )}
+              </TableBody>
+            </TableData>
+            <AlertDialog
+              open={open}
+              OnDelete={deleteHandler}
+              deleteItem={deleteItem}
+              HandleClose={() => {
+                setOpen(false)
+              }}
+            />
+          </>
+        );
+      }
+    } else return <Unauthorized />;
+  }
+};
+export default GameficationRequires;
+
+const StyledDivButton = styled(Paper)`
+    width : 100%;
+    display : flex; 
+    justify-content : center; 
+    align-items : center; 
+`
+
+
-- 
GitLab