Bonjour,
Je suis entrain de faire une application en React avec un back en FastAPI, dans cette application j'ai des outils qui font appels à mon API, les outils sont défini par des cards sur ces cards il y a un bouton qui me permet de lancer l'action, à ce niveau tout fonctionne correctement, mais pour le moment mes paramètres sont definis dans le code, j'aimerais ajouter une modal qui me permet de paramétrer mes outils, donc sur chaque card je rajoute un bouton qui me permet d'ouvrir une modal pour le moment j'ai un composant modal générique, dans ce composant je rajoute mes champs avec le props children (je ne sais pas si c'est la meilleur manière), j'aimerais faire un sorte d'avoir une modal pour chaque outil avec des champs différent et ensuite sauvegarder les paramètres pour les utiliser par la suite lorsque je fais un appel à mon API
Voici un schema de ce que je voudrais (cela sera peut être plus compréhensible)
Ma question étant comment m'y prendre ? Je ne sais pas si il est mieux d'utiliser un context ? un portail ? ou autres ?
Je suis un peu bloqué sur ca :/
Si quelqu'un aurait une solution ou une Idée je suis preneur !
Merci à vous,
je suis disponible sur discord si des personnes veulent consulter mon code actuel :)
Si tu veux créer qu'un seul modal, je passerai probablement le contenu de la modal en props du coup. A l'interieur j'aurai un état inital que je passerai aussi en props et une fonction handleChange un truc dans ce style, t'ajustera le nom des props (je suis pas inspiré la).
<ToolCard
name="NMAP"
description="Permet de scanner les hôtes, services, et ports ouverts sur un réseau."
settingsClick={() => {
setSelectedToolCard({
open: true,
toolName: "NMAP",
callback : handleSave,
modalContent : <> // idéalement créer des composants ça évite les trucs moche comme ça
<Input label="Target" placeholder="127.0.0.1/24" />
<Input label="Arguments" placeholder="-sn" />
<Input label="Toto" placeholder="-sn" />
<Input label="Tata" placeholder="-sn" />
<Input label="Titi" placeholder="-sn" />
<Input label="Tutu" placeholder="-sn" />
</>
})
}}
progress={nmapScanProgress}
onAction={handleNmap.bind(setNmapScanProgress)}
/>
<ToolModal
initialData={selectedToolCard.datas}
modalContent={selectedToolCard.modalContent}
open={isOpen}
onClose={() => {
open: false,
toolName: null,
callback : null,
modalContent :null
}}
toolName={selectedToolCard.toolName}
submitCallback={selectedToolCard.callback}
/>
// ToolModal.tsx
type ToolModalProps = {
...les bon types
}
const ToolModal = ({...lespropsQuiVontBien}: ToolModalProps) => {
const [datas, setDatas] = useState(initialData)
const handleChange = (event: ChangEvent<HTMLElement>) => {
// A AJUSTER EN FONCTION DES DATAS OU ALORS TU PASSES LE SETTER EN PROPS AUSSI
setDatas(datas => ({...datas, [event.target.name]: event.target.value}))
}
return (
<Modal isOpen={open} onClose={onClose} toolName={toolName}>
<div className="flex flex-col space-y-4">
{modalContent}
<div className="flex justify-end">
<Button
onClick={submitCallback}
size="md"
icon={Save}
text="Valider"
className="bg-whiteColor"
/>
</div>
</div>
</Modal>
)
}
Sinon tu créer un composant et une Modal pour chaque card et chacun se gere lui même
const Nmap = () => {
... les différents etats et fonctions
return (
<>
le composant card de NMAP
la modal dédié à NMAP
</>
)
}
Salut,
En faite je dirais que ta surtout plusieurs maniere de le faire et que ca va dépendre de la complexité de ton app.
Tu peux soit "préparer" à l'avance tes différents contenu et à l'ouverture de ta modal passé un parametre pour spécifier quel type de modal/contenu tu veux et dans ton composant Modal avoir un switch avec tes contenus.
Soit tu passes directement le contenu dans tes cards au clic et ton composant Modal à juste children en props.
Tu peux aussi mais c'est un peu plus avancé, utiliser le pattern render props + l'API context pour utiliser un contenu différent.
Salut,
Merci beaucoup pour ta réponse ! :)
Est ce que tu penses que je peux faire de cette manière : j'ai mon composant générique Modal, pour chaque outil je crée un fichier ToolModal (par ex) qui reprends ma modal générique, dans ma page Tools je mets un appel à cette modal en fonction de l'outil, dans mon ToolModal je fais une fonction qui me permet de remplir un tableau de paramètre, et ensuite au niveau de mon appel à l'API je transmets ce tableau ? Qu'en penses tu ? Le code ne va pas devenir un peu trop verbeux, si j'ai 10 outils par ex, je me retrouverais avec 10 tableaux de params etc ?
Merci !
Ben ta pas 40 choix je penses. C'est ce que tu as dit soit faire 10 modal différentes.
As-tu un exemple de code avec 2 card + modal différentes pour être sur d'avoir bien compris ta demande stp ?
Actuellement j'ai qu'un seul outil mais le code donne ceci, mon composant Card :
import {
LucideBolt,
LucideLoader2,
LucidePlayCircle,
LucideCheckCircle,
} from "lucide-react";
import Button from "./Button";
import { useState } from "react";
type ToolCardProps = {
name: string;
description: string;
progress: number;
settingsClick?: () => void;
onAction: (
setIsLoading: (isLoading: boolean) => void,
setCurrentProgress: React.Dispatch<React.SetStateAction<number>>,
setButtonText: (text: string) => void
) => void;
};
export default function ToolCard({
name,
description,
progress = 0,
settingsClick,
onAction,
}: ToolCardProps) {
const [isLoading, setIsLoading] = useState(false);
const [buttonText, setButtonText] = useState("Lancer");
const [currentProgress, setCurrentProgress] = useState(progress);
const handleClick = () => {
onAction(setIsLoading, setCurrentProgress, setButtonText);
};
return (
<div className="relative flex flex-col w-56 p-2 bg-white rounded-lg">
<div className="flex items-center justify-between">
<h2 className="text-sm font-semibold">{name}</h2>
<button
className="text-textColor hover:text-activeColor"
onClick={settingsClick}
>
<LucideBolt strokeWidth={1.0} size={16} />
</button>
</div>
<p className="my-2 text-[10px] font-extralight">{description}</p>
<div className="flex justify-end">
{isLoading ? (
<Button icon={LucideLoader2} className="bg-bgColor" animate />
) : (
<Button
icon={
buttonText === "Succès" ? LucideCheckCircle : LucidePlayCircle
}
text={buttonText}
className="bg-bgColor"
onClick={handleClick}
/>
)}
</div>
<div className="absolute bottom-0 left-0 w-full h-1 overflow-hidden rounded-b-lg">
<div
className="h-full transition-all duration-300 ease-in-out bg-activeColor"
style={{ width: `${currentProgress}%` }}
></div>
</div>
</div>
);
}
,ma page Tool :
import Header from "../shared/Header";
import ToolCard from "../components/ToolCard";
import Modal from "../components/Modal";
import { useState } from "react";
import Input from "../components/Input";
import Button from "../components/Button";
import { Save } from "lucide-react";
import { handleNmap } from "../utils/nmapActions";
export default function Tools() {
const [isOpen, setIsOpen] = useState(false);
const [nmapScanProgress, setNmapScanProgress] = useState<number>(0);
return (
<>
<Header title="Outils" />
<div className="flex flex-wrap gap-2 mx-6 my-4">
<ToolCard
name="NMAP"
description="Permet de scanner les hôtes, services, et ports ouverts sur un réseau."
settingsClick={() => setIsOpen(true)}
progress={nmapScanProgress}
onAction={handleNmap.bind(setNmapScanProgress)}
/>
<ToolCard
name="NMAP"
description="Permet de scanner les hôtes, services, et ports ouverts sur un réseau."
settingsClick={() => setIsOpen(true)}
progress={nmapScanProgress}
onAction={handleNmap.bind(setNmapScanProgress)}
/>
</div>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} toolName="NMAP">
<div className="flex flex-col space-y-4">
<Input label="Target" placeholder="127.0.0.1/24" />
<Input label="Arguments" placeholder="-sn" />
<div className="flex justify-end">
<Button
size="md"
icon={Save}
text="Valider"
className="bg-whiteColor"
/>
</div>
</div>
</Modal>
</>
);
}
qui fait appel à mon composant Modal :
import { CircleX } from "lucide-react";
export default function Modal({
isOpen,
onClose,
children,
toolName,
}: {
isOpen: boolean;
onClose?: () => void;
children: React.ReactNode;
toolName: string;
}) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 flex items-center justify-center transition-opacity duration-300 ease-out bg-opacity-50 bg-textColor">
<div className="w-full max-w-[250px] p-4 rounded-lg bg-bgColor transform transition-transform duration-300 ease-out scale-95 opacity-0 animate-modal-enter">
<div className="flex items-center justify-between mb-4">
<h2 className="text-sm font-semibold">
Paramètrage -<span className="text-xs"> {toolName}</span>
</h2>
<CircleX size={16} className="text-red-600" onClick={onClose} />
</div>
<div>{children}</div>
</div>
</div>
);
}
, pour le moment je n'ai pas encore associé la récupération des inputs mais l’idée est là
Merci pour ton aide :)
Pour être sur d'avoir bien compris.
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} toolName="NMAP">
<div className="flex flex-col space-y-4">
<Input label="Target" placeholder="127.0.0.1/24" />
<Input label="Arguments" placeholder="-sn" />
<div className="flex justify-end">
<Button
size="md"
icon={Save}
text="Valider"
className="bg-whiteColor"
/>
</div>
</div>
</Modal>
Peut devenir ceci et donc la data que tu envoies a ton API change c'est bien ça ?
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} toolName="NMAP">
<div className="flex flex-col space-y-4">
<Input label="Target" placeholder="127.0.0.1/24" />
<Input label="Arguments" placeholder="-sn" />
<Input label="Toto" placeholder="-sn" />
<Input label="Tata" placeholder="-sn" />
<Input label="Titi" placeholder="-sn" />
<Input label="Tutu" placeholder="-sn" />
<div className="flex justify-end">
<Button
size="md"
icon={Save}
text="Valider"
className="bg-whiteColor"
/>
</div>
</div>
</Modal>
Oui voila c'est ça, en gros les éléments dans ma Modal change en fonction de l'outil, pour le moment je penses avoir seulement des inputs
Je pense que je vais partir sur ça, merci beaucoup pour ton aide ! :)
Pour le moment je n'ai pas prevu beaucoup d'outil donc ca devrait le faire au niveau des données
En tout cas, merci beaucoup !