Generateurv2/frontend/components/exos/ModalCard.jsx

336 lines
10 KiB
JavaScript

import { useEffect, useRef, useState } from "react";
import styles from "../../styles/exos/ModalCard.module.scss";
import { isBrowser, isEmpty, parseClassName } from "../../utils/utils.js";
import ExoPrinterForm from "./ExoPrinterForm.jsx";
import Tag from "../Tag.jsx";
import { HiOutlineTrash } from "react-icons/hi";
import { TiEdit } from "react-icons/ti";
import { AiOutlineClose } from "react-icons/ai";
import { notificationService } from "../../services/notification.service.js";
import { AlertType, useAlert } from "../../context/alert.context.js";
import { ExoEditForm } from "./ExoEditForm.jsx";
import {
delExo,
delTag,
favExo,
getExos,
getModelFile,
setTags,
} from "../../requests/requests.exos.js";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { TagCreatable } from "./TagCreate.jsx";
import Link from "next/link";
import { HiOutlineDuplicate } from "react-icons/hi";
import { useSessionContext } from "../../context/session.context.js";
export default function ModalCard({ step, onClose, onDelete, tags, queryKey }) {
const [tagMode, setTagMode] = useState(false);
const alert = useAlert();
const [edit, setEdit] = useState(false);
const queryClient = useQueryClient();
const {
isLoading: isUpdating,
isFetching,
data: step_data,
} = useQuery(
["exo-data", step.id_code],
async () => await getExos(step.id_code).catch(() => {
notificationService.error('Erreur', "Exercice introuvable")
}),
{ initialData: step, refetchOnWindowFocus: !edit }
);
const [selected, setSelected] = useState([]);
const [tagOptions, setTagOptions] = useState(
tags.map((tag) => {
return {
...tag,
isDisabled: step_data.tags.map((t) => t.id_code).includes(tag.id_code),
};
})
);
const {authData} = useSessionContext()
useEffect(() => {
setTagOptions(
tags.map((tag) => {
return {
...tag,
isDisabled: step_data.tags
.map((t) => t.id_code)
.includes(tag.id_code),
};
})
);
return () => {
setTagOptions([]);
};
}, [step_data.tags, tags]);
const { mutate: setTagMutate } = useMutation(
async () => await setTags({ step: step.id_code, tags: [...selected] }),
{
onSuccess: (data) => {
//queryClient.invalidateQueries(step.id_code);
queryClient.setQueryData(['exo-data', step.id_code],(old) => {
return {...old, tags: data.data.tags}
})
queryClient.setQueryData(queryKey, (old) => {
console.log('TEST OLD', old)
return {
...old,
results: [
...old.results.map((r) => {
if (r.id_code === data.data.id_code) {
return { ...r, tags: data.data.tags };
}
return r;
}),
],
};
});
queryClient.invalidateQueries("tags");
queryClient.invalidateQueries(queryKey[0], { inactive: true });
setSelected([]);
notificationService.success("Tags", `Tags de ${step.name} modifiés !`, {
autoClose: true,
});
setTagMode(false);
},
onError: (e) => {
console.log('error', e)
notificationService.error(
"Erreur",
`Les tags de ${step.name} n'ont pu être modifiés !`
);
},
}
);
const { mutate: delTagMutate } = useMutation(
async (tag) => {
return await delTag({ tag: tag, step: step.id_code });
},
{
onSuccess: (data) => {
var tagsSelected = queryKey[2].tags || null;
if (tagsSelected && tagsSelected.includes(data.data.tagId)) {
queryClient.invalidateQueries(queryKey);
}
queryClient.setQueryData(["exo-data", step.id_code], (old) => {
return { ...old, tags: data.data.tags };
});
queryClient.setQueryData(queryKey, (old) => {
return {
...old,
results: [
...old.results.map((r) => {
if (r.id_code === data.data.id_code) {
return { ...r, tags: data.data.tags };
}
return r;
}),
],
};
});
queryClient.invalidateQueries(queryKey[0], { inactive: true });
queryClient.invalidateQueries("tags");
notificationService.success(
"Tags",
`Tag ${data.data.name} retiré à ${step.name} !`,
{
autoClose: true,
}
);
setTagMode(false);
},
onError: () => {
notificationService.error(
"Erreur",
`Le tag n'a pu être retiré de ${step.name} !`
);
},
}
);
const [full, setFull] = useState({ state: false, fade: false });
const fadeFull = (state) => {
setFull({ state: !state, fade: state });
setTimeout(() => {
setFull({ state: state, fade: false });
}, 300);
};
return (
<div
className={styles["ex_card--full"]}
onKeyDown={(e) => {
if (e.code == "Escape") {
onClose();
}
}}
//id={"card-" + step_data.id_code}
>
<div
className={parseClassName([
styles["ex_card--body"],
edit ? "marginb-p0" : undefined,
full.fade ? styles["ex_card-fade"] : undefined,
full.fade || full.state ? styles["ex_card--big"] : undefined,
full.state ? "vh-100" : undefined,
edit ? styles["edit-card"] : undefined,
])}
>
{isFetching && (
<div className={styles["updating"]}>
<span className="loader"></span>
</div>
)}
{!(isFetching || isUpdating) && (
<>
{!edit && full.state == false && (
<div className={styles["ex_card--title"]}>
{step_data.name}
{step_data.original != null && (
<Link href={`/exercices/${step_data.original}`}>
<a title="Voir l'exercice original">Exercice original</a>
</Link>
)}
{!step_data.isUser && (
<p className={styles.author}>{step_data.author.username}</p>
)}
</div>
)}
{!edit && full.state == false && (
<ExoPrinterForm step={step_data} />
)}
{edit && (
<ExoEditForm
step={step_data}
cancel={() => {
setEdit(false);
}}
setFull={fadeFull}
full={full.state}
/>
)}
</>
)}
</div>
{!(isFetching || isUpdating) && full.state == false && !isEmpty(authData)&& (
<div
className={parseClassName([
styles["ex_card--footer"],
tagMode == true
? styles["tag_card"]
: styles["ex_card--footer-not-tag"],
])}
>
{tagMode == false && !edit && full.state == false && (
<>
<div className={styles["tag--container"]}>
{step_data.tags.map((t, i) => {
return <Tag key={i} tag={t} onDelete={delTagMutate} />;
})}
</div>
<div
onClick={() => {
setTagMode(true);
}}
className={styles["ex_card--add-tag"]}
>
{" "}
<hr /> <p> +</p>
</div>
</>
)}
{tagMode && !edit && full.state == false && (
<>
<TagCreatable
options={tagOptions}
selectOption={setSelected}
select={selected}
onCreate={setTagOptions}
/>
<div className="margint-p3 flex-column">
<button onClick={setTagMutate} className="exo-btn">
Valider
</button>
<button
onClick={() => {
setTagMode(false);
setSelected([]);
}}
className="cancel-btn"
>
Annuler
</button>
</div>
</>
)}
</div>
)}
{!edit && !(isFetching || isUpdating) && full.state == false && (
<div className={styles["icon-container"]}>
{/* <Delete className={styles['icon']}/> */}
<AiOutlineClose
className={parseClassName([styles["icon"]])}
onClick={onClose}
/>
{isBrowser &&
localStorage.getItem("token") != null &&
!step_data.isUser && (
<HiOutlineDuplicate
onClick={() => {
favExo(step.id_code).then((res) => {
setRegistered(res.isRegistered);
});
}}
/>
)}
{step_data.isUser == true && (
<div>
<HiOutlineTrash
className={parseClassName([styles["icon"], styles["delete"]])}
onClick={() => {
alert.alert({
title: "Êtes-vous sûr ?",
active: true,
message: `Vous allez supprimer l'exercice ${step_data.name} ! Cette action est irréversible !`,
type: AlertType.Warning,
validate: () => {
delExo(step.id_code).then(() => {
notificationService.success(
"Suppression",
`Exercice ${step_data.name} supprimé !`,
{ autoClose: true, keepAfterRouteChange: true }
);
onDelete(step.id_code);
onClose();
});
},
});
}}
/>
<TiEdit
className={parseClassName([styles["icon"], styles["edit"]])}
onClick={() => {
setEdit(true);
}}
/>
</div>
)}
</div>
)}
</div>
);
}