vorteil/direktiv

View on GitHub
ui/src/pages/namespace/JqPlayground/index.tsx

Summary

Maintainability
D
1 day
Test Coverage
import {
  BookOpen,
  FileInput,
  FileOutput,
  Play,
  PlaySquare,
} from "lucide-react";
import { FC, useState } from "react";
import {
  useJqPlaygroundActions,
  useJqPlaygroundInput,
  useJqPlaygroundQuery,
} from "~/util/store/jqPlaygrpund";

import Button from "~/design/Button";
import { Card } from "~/design/Card";
import CopyButton from "~/design/CopyButton";
import Editor from "~/design/Editor";
import Examples from "./Examples";
import FormErrors from "~/components/FormErrors";
import Input from "~/design/Input";
import { prettifyJsonString } from "~/util/helpers";
import { useExecuteJQuery } from "~/api/jq/mutate/executeQuery";
import { useTheme } from "~/util/store/theme";
import { useTranslation } from "react-i18next";

const JqPlaygroundPage: FC = () => {
  const { t } = useTranslation();
  const theme = useTheme();
  const {
    setInput: storeInputInLocalstorage,
    setQuery: storeQueryInLocalstorage,
  } = useJqPlaygroundActions();

  const [query, setQuery] = useState(useJqPlaygroundQuery() ?? ".");
  const [input, setInput] = useState(useJqPlaygroundInput() ?? "{}");
  const [output, setOutput] = useState("");
  const [error, setError] = useState("");

  const { mutate: executeQuery, isLoading } = useExecuteJQuery({
    onSuccess: (data) => {
      setOutput(prettifyJsonString(data.results?.[0] ?? "{}"));
    },
    onError: (error) => {
      setOutput("");
      if (error) {
        setError(error);
      }
    },
  });

  const submitQuery = ({ query, input }: { query: string; input: string }) => {
    /**
     * Always clear the output before submiting a new query to the backend.
     * Otherwise when the request takes longer or produces an error the input
     * and output displayed to the user would not match.
     */
    setOutput("");
    executeQuery({ query, inputJsonString: input });
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    submitQuery({ query, input });
  };

  const changeQuery = (newQuery: string) => {
    setQuery(newQuery);
    storeQueryInLocalstorage(newQuery);
    setError("");
  };

  const updateInput = (newData: string | undefined) => {
    if (newData === undefined) return;
    setInput(newData);
    storeInputInLocalstorage(newData);
    setError("");
  };

  const onRunSnippet = ({ query, input }: { query: string; input: string }) => {
    window.scrollTo({ top: 0, behavior: "smooth" });
    updateInput(prettifyJsonString(input));
    changeQuery(query);
    submitQuery({ query, input });
  };

  const formId = "jq-playground-form";
  return (
    <div className="flex grow flex-col gap-y-4 p-5">
      <div className="flex">
        <h3 className="flex grow items-center gap-x-2 font-bold">
          <PlaySquare className="h-5" />
          {t("pages.jqPlayground.title")}
        </h3>
        <Button variant="outline" asChild isAnchor>
          <a
            href="https://stedolan.github.io/jq/manual/"
            target="_blank"
            rel="noopener noreferrer"
          >
            <BookOpen />
            {t("pages.jqPlayground.openManualBtn")}
          </a>
        </Button>
      </div>
      <Card className="p-5 text-sm ">
        {t("pages.jqPlayground.description")}
      </Card>
      <Card className="p-5">
        <form
          id={formId}
          onSubmit={handleSubmit}
          className="flex flex-col gap-5"
        >
          <div className="flex flex-col gap-5 sm:flex-row">
            <Input
              data-testid="jq-query-input"
              placeholder={t("pages.jqPlayground.queryPlaceholder")}
              value={query}
              onChange={(e) => changeQuery(e.target.value)}
            />
            <Button
              data-testid="jq-run-btn"
              className="grow sm:w-44"
              type="submit"
              variant="primary"
              disabled={isLoading}
              loading={isLoading}
            >
              {!isLoading && <Play />}
              {t("pages.jqPlayground.submitBtn")}
            </Button>
          </div>
          {error && <FormErrors errors={{ error: { message: error } }} />}
          <div className="flex flex-col gap-5 md:flex-row">
            <Card className="flex h-96 w-full flex-col p-4" noShadow>
              <div className="mb-5 flex">
                <h3 className="flex grow items-center gap-x-2 font-medium">
                  <FileInput className="h-5" />
                  {t("pages.jqPlayground.input")}
                </h3>
                <CopyButton
                  value={input}
                  buttonProps={{
                    variant: "outline",
                    size: "sm",
                    type: "button",
                    disabled: !input,
                    "data-testid": "copy-input-btn",
                  }}
                />
              </div>
              <div data-testid="jq-input-editor" className="flex grow">
                <Editor
                  value={input}
                  language="json"
                  onChange={updateInput}
                  theme={theme ?? undefined}
                />
              </div>
            </Card>
            <Card className="flex h-96 w-full flex-col p-4" noShadow>
              <div className="mb-5 flex">
                <h3 className="flex grow items-center gap-x-2 font-medium">
                  <FileOutput className="h-5" />
                  {t("pages.jqPlayground.output")}
                </h3>
                <CopyButton
                  value={output}
                  buttonProps={{
                    variant: "outline",
                    size: "sm",
                    type: "button",
                    disabled: !output,
                    "data-testid": "copy-output-btn",
                  }}
                />
              </div>
              <div data-testid="jq-output-editor" className="flex grow">
                <Editor
                  language="json"
                  value={output}
                  options={{
                    readOnly: true,
                  }}
                  theme={theme ?? undefined}
                />
              </div>
            </Card>
          </div>
        </form>
      </Card>
      <Examples onRunSnippet={onRunSnippet} />
    </div>
  );
};

export default JqPlaygroundPage;