diff options
Diffstat (limited to 'front/src/routes')
-rw-r--r-- | front/src/routes/bots.jsx | 194 | ||||
-rw-r--r-- | front/src/routes/keys.jsx | 10 | ||||
-rw-r--r-- | front/src/routes/man_pages.jsx | 6 |
3 files changed, 203 insertions, 7 deletions
diff --git a/front/src/routes/bots.jsx b/front/src/routes/bots.jsx new file mode 100644 index 0000000..c2f1565 --- /dev/null +++ b/front/src/routes/bots.jsx @@ -0,0 +1,194 @@ +import Modal from "react-modal"; +import { useAuthContext } from "../context/auth_context"; +import { useEffect, useState } from "react"; + +Modal.setAppElement("#root"); + +const BotButton = ({ onSave, givenBot }) => { + const [open, setOpen] = useState(false); + const [name, setName] = useState(givenBot?.name || ""); + const [webhook, setWebhook] = useState(givenBot?.webhook || ""); + const [errors, setErrors] = useState(null); + const [isPublic, setIsPublic] = useState(givenBot?.public || false); + + const setDefaults = () => { + setName(""); + setWebhook(""); + setErrors(null); + }; + + const close = () => { + if (!givenBot) { + setDefaults(); + } + setOpen(false); + }; + + const updateBot = () => { + fetch(givenBot ? `/api/player/bots/${givenBot.id}` : "/api/player/bots", { + credentials: "same-origin", + method: givenBot ? "PUT" : "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + webhook: webhook.trim(), + name: name.trim(), + public: isPublic, + }), + }) + .then((r) => r.json()) + .then((d) => { + if (d.success) { + if (onSave) { + onSave(); + } + close(); + } else if (d.errors) { + if (typeof d.errors === "object") { + setErrors( + Object.keys(d.errors).map( + (field) => `${field}: ${d.errors[field].join(",")}` + ) + ); + } else { + setErrors([d.errors]); + } + } + }); + }; + + return ( + <div> + <button className="button" onClick={() => setOpen(true)}> + {givenBot ? "Update" : "+ Add"} Bot + </button> + {givenBot && ( + <> + <button + style={{ marginLeft: "1rem" }} + className="button gold" + onClick={() => { + navigator.clipboard.writeText(givenBot?.token); + alert("Bot's token was copied to the clipboard."); + }} + > + Copy Token + </button> + <button + style={{ marginLeft: "1rem" }} + className="button red" + onClick={() => + fetch(`/api/player/bots/${givenBot.id}/redrive`) + .then((r) => r.json()) + .then(({ message }) => alert(message)) + } + > + Schedule Redrive + </button> + </> + )} + <Modal + isOpen={open} + onRequestClose={close} + className="modal" + contentLabel="Add Bot" + > + <div style={{ minWidth: "20vw" }}> + <h3>Add Bot</h3> + <hr /> + <p>Bot Name *</p> + <input + style={{ width: "100%" }} + value={name} + onChange={(e) => setName(e.target.value)} + required + /> + </div> + <div> + <p>Webhook *</p> + <input + style={{ width: "100%" }} + value={webhook} + onChange={(e) => setWebhook(e.target.value)} + required + /> + </div> + <p> + Public *{" "} + <input + type="checkbox" + value={name} + checked={isPublic} + onChange={(e) => setIsPublic(!isPublic)} + required + /> + </p> + <div> + {errors && ( + <div style={{ color: "red" }}> + {errors.map((error, i) => ( + <p key={i}>{error}</p> + ))} + </div> + )} + </div> + <div className="flex-end-row"> + <button className="button" onClick={updateBot}> + {givenBot ? "Update" : "+ Add"} + </button> + <button className="button red" onClick={close}> + Cancel + </button> + </div> + </Modal> + </div> + ); +}; + +export const BotCard = ({ botStruct, onSave }) => { + const { name } = botStruct; + return ( + <div className="key-card"> + <h4>{name}</h4> + <BotButton onSave={onSave} givenBot={botStruct} /> + </div> + ); +}; + +export const Bots = () => { + const { + player: { id: userId }, + } = useAuthContext(); + const [bots, setBots] = useState(null); + + const refreshBots = () => + fetch("/api/player/bots") + .then((r) => r.json()) + .then((bots) => setBots(bots)); + + useEffect(() => { + if (userId) { + refreshBots(); + } + }, [userId]); + + if (bots === null) return <p>Loading...</p>; + + return ( + <> + <h1>Bots</h1> + <BotButton onSave={refreshBots} /> + + <div className="key-card-collection"> + {bots.length ? ( + bots.map((bot) => ( + <BotCard key={bot.id} onSave={refreshBots} botStruct={bot} /> + )) + ) : ( + <p>Looks like you've got no bots, try adding one!</p> + )} + </div> + </> + ); +}; diff --git a/front/src/routes/keys.jsx b/front/src/routes/keys.jsx index 5b50fa9..b9d4542 100644 --- a/front/src/routes/keys.jsx +++ b/front/src/routes/keys.jsx @@ -14,16 +14,14 @@ const minimizeKey = (key) => { return key; }; -const KeyCard = ({ onDelete, props }) => { - const { id, name, key } = props; - +const KeyCard = ({ onDelete, keyStruct: { id, name, key } }) => { const deleteThisKey = () => { if ( window.confirm( "Are you sure? This will close all your currently opened ssh sessions." ) ) { - fetch(`/api/keys/${id}`, { + fetch(`/api/player/keys/${id}`, { credentials: "same-origin", method: "DELETE", }) @@ -182,7 +180,7 @@ export const Keys = () => { } }, [userId, refreshKeys]); - if (!keys) return <p>Loading...</p>; + if (keys === null) return <p>Loading...</p>; if (Array.isArray(keys)) { return ( @@ -192,7 +190,7 @@ export const Keys = () => { <div className="key-card-collection"> {keys.length ? ( keys.map((key) => ( - <KeyCard key={key.id} onDelete={refreshKeys} props={key} /> + <KeyCard key={key.id} onDelete={refreshKeys} keyStruct={key} /> )) ) : ( <p>Looks like you've got no keys, try adding some!</p> diff --git a/front/src/routes/man_pages.jsx b/front/src/routes/man_pages.jsx index ea03c2a..f904394 100644 --- a/front/src/routes/man_pages.jsx +++ b/front/src/routes/man_pages.jsx @@ -74,7 +74,11 @@ export const ManPages = () => { <li> In the "Previous Games" viewer, use "m" to show the game's move history in UCI notation (which you may convert to PGN{" "} - <a href="https://www.dcode.fr/uci-chess-notation" target="_blank"> + <a + href="https://www.dcode.fr/uci-chess-notation" + target="_blank" + rel="noreferrer" + > here </a> ). |