Skip to content

Commit

Permalink
Merge pull request #301 from dataforgoodfr/feat/D4G-294-remove-game
Browse files Browse the repository at this point in the history
Feat/d4g 294 remove game
  • Loading branch information
Baboo7 authored Jul 7, 2023
2 parents 6d12fb8 + 11665e9 commit cd4c6a9
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { useMutation, useQueryClient } from "react-query";
import { http } from "../../../../../utils/request";

export const useRemovePlayerMutation = (gameId: number) => {
export { useRemoveGameMutation, useRemovePlayerMutation };

const useRemoveGameMutation = () => {
const queryClient = useQueryClient();

const removeGameMutation = useMutation<
Response,
{ message: string },
{ gameId: number }
>(({ gameId }) => http.delete(`/api/games/${gameId}`), {
onSuccess: () => {
queryClient.invalidateQueries(`games`);
},
});

return { removeGameMutation };
};

const useRemovePlayerMutation = (gameId: number) => {
const queryClient = useQueryClient();

const removePlayerMutation = useMutation<
Response,
{ message: string },
{ userId: number }
>(
({ userId }) => {
return http.post("/api/games/remove-player", { gameId, userId });
>(({ userId }) => http.post("/api/games/remove-player", { gameId, userId }), {
onSuccess: () => {
queryClient.invalidateQueries(`/api/games/${gameId}/players`);
},
{
onSuccess: () => {
queryClient.invalidateQueries(`/api/games/${gameId}/players`);
},
}
);
});

return { removePlayerMutation };
};
145 changes: 109 additions & 36 deletions packages/client/src/modules/administration/Games/Games.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Box, CircularProgress, Paper } from "@mui/material";
import {
Box,
Button,
CircularProgress,
Paper,
Typography,
} from "@mui/material";
import AddBoxIcon from "@mui/icons-material/AddBox";
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
DataGrid,
GridActionsCellItem,
GridColumns,
GridRenderCellParams,
GridRowParams,
} from "@mui/x-data-grid";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useNavigate } from "react-router-dom";
import { ErrorAlert, SuccessAlert } from "../../alert";
Expand All @@ -15,6 +14,13 @@ import { IGameWithTeacher } from "../../../utils/types";
import { I18nTranslateFunction } from "../../translations";
import { useTranslation } from "../../translations/useTranslation";
import { http } from "../../../utils/request";
import { Icon } from "../../common/components/Icon";
import { useRemoveGameMutation } from "./Game/services/mutations";
import { useCallback, useState } from "react";
import { Dialog } from "../../common/components/Dialog";
import { Button } from "../../common/components/Button";
import { Typography } from "../../common/components/Typography";
import { useDialog } from "../../common/hooks/useDialog";

export { Games };

Expand All @@ -23,7 +29,7 @@ function Games(): JSX.Element {

return (
<>
<Box alignItems="center" display="flex">
<Box alignItems="center" display="flex" justifyContent="space-between">
<Typography variant="h3">{t("page.admin.games.title")}</Typography>
<NewGame />
</Box>
Expand All @@ -34,9 +40,15 @@ function Games(): JSX.Element {
);
}

const buildColumns: (args: {
const buildColumns = ({
idOfGameBeingRemoved,
removeGame,
t,
}: {
idOfGameBeingRemoved?: number;
removeGame: (gameId: number) => void;
t: I18nTranslateFunction;
}) => GridColDef<IGameWithTeacher>[] = ({ t }) => [
}): GridColumns<IGameWithTeacher> => [
{ field: "id", headerName: t("table.column.game-id.label"), width: 80 },
{
field: "code",
Expand Down Expand Up @@ -82,15 +94,50 @@ const buildColumns: (args: {
flex: 1,
minWidth: 150,
},
{
field: "actions",
type: "actions",
headerName: t("table.column.actions.label"),
width: 80,
getActions: (params: GridRowParams<IGameWithTeacher>) => [
<GridActionsCellItem
icon={<Icon name="delete" />}
label={`Delete game ${params.row.name}`}
onClick={() => removeGame(params.row.id)}
disabled={idOfGameBeingRemoved === params.row.id}
/>,
],
},
];

function GamesDataGrid() {
const navigate = useNavigate();
const { t } = useTranslation();
const [idOfGameToRemove, setIdOfGameToRemove] = useState<number | null>(null);
const {
isOpen: isRemoveGameDialogOpen,
openDialog: openRemoveGameDialog,
closeDialog: closeRemoveGameDialog,
} = useDialog();

const query = useQuery("games", () => {
return http.get<undefined, { data: { documents: any[] } }>("/api/games");
});
const { removeGameMutation } = useRemoveGameMutation();

const onRemoveGame = useCallback(
(gameId: number) => {
setIdOfGameToRemove(gameId);
openRemoveGameDialog();
},
[openRemoveGameDialog, setIdOfGameToRemove]
);

const removeGame = useCallback(() => {
if (idOfGameToRemove != null) {
removeGameMutation.mutate({ gameId: idOfGameToRemove });
}
}, [idOfGameToRemove, removeGameMutation]);

if (query.isLoading) {
return <CircularProgress />;
Expand All @@ -99,26 +146,56 @@ function GamesDataGrid() {
const rows: IGameWithTeacher[] = query?.data?.data?.documents ?? [];

return (
<Box style={{ height: 600, width: "100%", cursor: "pointer" }}>
<DataGrid
rows={rows}
columns={buildColumns({ t })}
disableSelectionOnClick
initialState={{
sorting: {
sortModel: [{ field: "date", sort: "desc" }],
},
}}
pageSize={20}
rowsPerPageOptions={[20]}
onRowClick={(rowData) => {
const path = rowData.id
? `/administration/games/${rowData.id}`
: "/administration/games/";
return navigate(path);
}}
<>
<Box style={{ height: 600, width: "100%", cursor: "pointer" }}>
<DataGrid
rows={rows}
columns={buildColumns({
idOfGameBeingRemoved: removeGameMutation.variables?.gameId,
removeGame: onRemoveGame,
t,
})}
disableSelectionOnClick
initialState={{
sorting: {
sortModel: [{ field: "date", sort: "desc" }],
},
}}
pageSize={20}
rowsPerPageOptions={[20]}
onRowClick={(rowData) => {
const path = rowData.id
? `/administration/games/${rowData.id}`
: "/administration/games/";
return navigate(path);
}}
/>
</Box>
<Dialog
open={isRemoveGameDialogOpen}
content={
<Typography>
{t("modal.remove-game.title", { game: idOfGameToRemove })}
</Typography>
}
actions={
<>
<Button type="secondary" onClick={closeRemoveGameDialog}>
{t("cta.cancel")}
</Button>
<Button
onClick={() => {
removeGame();
closeRemoveGameDialog();
}}
>
{t("cta.delete")}
</Button>
</>
}
handleClose={closeRemoveGameDialog}
/>
</Box>
</>
);
}

Expand All @@ -139,12 +216,8 @@ function NewGame() {
<>
{mutation.isError && <ErrorAlert message={mutation.error.message} />}
{mutation.isSuccess && <SuccessAlert />}
<Button
onClick={() => mutation.mutate()}
variant="contained"
sx={{ marginLeft: "auto" }}
>
<AddBoxIcon sx={{ mr: 1 }} /> {t("cta.create-game")}
<Button iconName="create" onClick={() => mutation.mutate()}>
{t("cta.create-game")}
</Button>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const CustomDialogActions = styled(DialogActions)(({ theme }) => ({
padding: "12px 24px 16px",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
justifyContent: "space-evenly",
},

"> *": {
Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/modules/common/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import AutoGraphRoundedIcon from "@mui/icons-material/AutoGraphRounded";
import { SvgIconProps } from "@mui/material";
import PersonPinRounded from "@mui/icons-material/PersonPinRounded";
import {
Add,
Badge,
Bolt,
Close,
Computer,
ConnectingAirports,
ContentCopy,
Delete,
DirectionsCar,
DoNotDisturb,
DryCleaning,
Expand Down Expand Up @@ -75,7 +77,9 @@ const ICONS = {
computer: Computer,
consumption: ShoppingCart,
copy: ContentCopy,
create: Add,
draft: HistoryEdu,
delete: Delete,
energy: Bolt,
"form-draft": HistoryEdu,
"form-pending-validation": SettingsSuggestIcon,
Expand Down
21 changes: 21 additions & 0 deletions packages/client/src/modules/common/hooks/useDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useCallback, useState } from "react";

export { useDialog };

function useDialog() {
const [isOpen, setIsOpen] = useState<boolean>(false);

const openDialog = useCallback(() => {
setIsOpen(true);
}, [setIsOpen]);

const closeDialog = useCallback(() => {
setIsOpen(false);
}, [setIsOpen]);

return {
isOpen,
closeDialog,
openDialog,
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"cta.assign-team-to-players": "Répartir les joueurs",
"cta.cancel": "Annuler",
"cta.check-forms": "Vérifier les formulaires",
"cta.delete": "Supprimer",
"cta.retrieve-emails": "Récupérer les emails",
"cta.create-game": "Nouveau jeu",
"cta.end-turn": "Terminer le tour",
Expand Down Expand Up @@ -58,6 +60,7 @@
"message.success.game-joined": "Vous avez rejoint l'atelier avec succès",
"message.success.sign-in.user-authenticated": "Connexion réussie",

"modal.remove-game.title": "Voulez-vous supprimer le jeu {{game}} ?",
"modal.remove-player.title": "Voulez-vous supprimer le joueur ?",
"modal.remove-team.title": "Voulez-vous supprimer l'équipe ?",
"modal.validate-consumption-choices.title": "Les choix ne seront plus modifiables, souhaites-tu valider tes choix ?",
Expand All @@ -72,6 +75,7 @@
"form.validation.yellow": "Les cases colorées en jaune contiennent les valeurs à vérifier.",
"form.validation.heating": "Concernant le chauffage, si le joueur a donné deux valeurs (facture en € et consommation en kWh), il est nécessaire de vérifier que les deux valeurs correspondent. Si ce n’est pas le cas, merci de ne laisser que la valeur la plus probable et de supprimer l’autre.",

"table.column.actions.label": "Actions",
"table.column.form-status.label": "Statut du formulaire",
"table.column.form-status.label.short": "Statut",
"table.column.game-code.label": "Code",
Expand Down
26 changes: 25 additions & 1 deletion packages/server/src/modules/games/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const crudController = {
getPlayersController,
putPlayersInTeamsController,
registerController,
removeGame,
removePlayerController,
removeTeamController,
updateGame,
Expand Down Expand Up @@ -79,7 +80,18 @@ async function getGame(request: Request, response: Response) {
id: z.string().regex(/^\d+$/).transform(Number),
});
const { id } = paramsSchema.parse(request.params);
const document = await services.getDocument(id);
const document = await services.findOne(
{ id },
{
include: {
teams: {
include: {
players: true,
},
},
},
}
);
response.status(200).json({ document });
}

Expand Down Expand Up @@ -134,3 +146,15 @@ async function prepareGameForLaunch(gameId: number) {
await Promise.all(processingTeamActions);
});
}

async function removeGame(request: Request, response: Response) {
const paramsSchema = z.object({
id: z.string().regex(/^\d+$/).transform(Number),
});

const { id } = paramsSchema.parse(request.params);

await services.remove({ id });

response.status(200).json({});
}
Loading

0 comments on commit cd4c6a9

Please sign in to comment.