From eec32aa38a8762eccc8575a37a628bd5ae2cc1d0 Mon Sep 17 00:00:00 2001
From: "Elizabeth (Lizzy) Hunt" <elizabeth.hunt@simponic.xyz>
Date: Mon, 29 May 2023 16:28:27 -0700
Subject: Bots (#23)

* squash all the things for bots

* fix warnings

* change colors a bit and README updates

* fix frontend warnings
---
 front/src/routes/bots.jsx | 194 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 194 insertions(+)
 create mode 100644 front/src/routes/bots.jsx

(limited to 'front/src/routes/bots.jsx')

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>
+    </>
+  );
+};
-- 
cgit v1.2.3-70-g09d2

