Add survey page

This commit is contained in:
1ilit 2023-10-12 23:17:23 +03:00
parent eaf4812dbe
commit e2a37b68e4
5 changed files with 323 additions and 12 deletions

View File

@ -978,8 +978,8 @@ export default function ControlPanel(props) {
"Report a bug": {
function: () => window.open("/bug_report", "_blank"),
},
"Suggest a feature": {
function: () => {},
"Give feedback": {
function: () => window.open("/survey", "_blank"),
},
},
};

View File

@ -6,7 +6,7 @@ import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { ClearEditorPlugin } from '@lexical/react/LexicalClearEditorPlugin';
import { ClearEditorPlugin } from "@lexical/react/LexicalClearEditorPlugin";
import { TRANSFORMERS } from "@lexical/markdown";
import ToolbarPlugin from "../plugins/ToolbarPlugin";
@ -15,18 +15,18 @@ import CodeHighlightPlugin from "../plugins/CodeHighlightPlugin";
import AutoLinkPlugin from "../plugins/AutoLinkPlugin";
import "../styles/richeditor.css";
function Placeholder() {
return <div className="editor-placeholder">Describe the bug</div>;
function Placeholder({ text }) {
return <div className="editor-placeholder">{text || ""}</div>;
}
export default function RichEditor({ theme }) {
export default function RichEditor({ theme, placeholder }) {
return (
<div className="editor-container">
<ToolbarPlugin theme={theme} />
<div className="editor-inner">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
placeholder={<Placeholder text={placeholder} />}
ErrorBoundary={LexicalErrorBoundary}
/>
<HistoryPlugin />
@ -37,7 +37,7 @@ export default function RichEditor({ theme }) {
<AutoLinkPlugin />
<ListMaxIndentLevelPlugin maxDepth={7} />
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
<ClearEditorPlugin/>
<ClearEditorPlugin />
</div>
</div>
);

View File

@ -93,7 +93,7 @@ function Form({ theme }) {
value={data.title}
onChange={(v) => setData((prev) => ({ ...prev, title: v }))}
/>
<RichEditor theme={theme} />
<RichEditor theme={theme} placeholder={"Describe the bug"} />
<Upload
action="#"
ref={uploadRef}

View File

@ -42,7 +42,7 @@ export default function SignUp() {
return (
<div className="grid grid-cols-5 h-screen select-none">
<div className="bg-indigo-300 col-span-3 sm:hidden md:hidden lg:col-span-2 overflow-y-hidden"></div>
<div className="col-span-2 lg:col-span-3 sm:col-span-full md:col-span-full flex flex-col justify-center items-center my-6 mx-2">
<div className="col-span-2 lg:col-span-3 sm:col-span-full md:col-span-full flex flex-col justify-center items-center my-6">
<Link to="/">
<img src={logo} alt="logo" className="mx-auto h-[38px]" />
</Link>

View File

@ -1,5 +1,316 @@
import React from "react";
import React, { useEffect, useState, useCallback, useMemo } from "react";
import logo_light from "../assets/logo_light_46.png";
import logo_dark from "../assets/logo_dark_46.png";
import {
Banner,
Button,
Toast,
Spin,
Slider,
Radio,
RadioGroup,
Select,
TextArea,
} from "@douyinfe/semi-ui";
import { IconSun, IconMoon } from "@douyinfe/semi-icons";
import RichEditor from "../components/rich_editor";
import { LexicalComposer } from "@lexical/react/LexicalComposer";
import { editorConfig } from "../data/editor_config";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $generateHtmlFromNodes } from "@lexical/html";
import { CLEAR_EDITOR_COMMAND } from "lexical";
import axios from "axios";
function SurveyForm({ theme }) {
const [editor] = useLexicalComposerContext();
const questions = useMemo(
() => ({
satisfaction: "How satisfied are you with drawDB?",
ease: "How easy was it to get started with drawDB?",
wouldRecommend: "How likely are you to recommend drawDB?",
hadDifficulty:
"Did you encounter any difficulties when navigating drawDB?",
difficulty: "What were the difficulties you faced?",
triedOtherApps: "Have you tried apps like drawDB?",
comparison: "How did you find drawDB as compared to other apps?",
occupation: "What is your occupation?",
}),
[]
);
const [form, setForm] = useState({
satisfaction: 5,
ease: 5,
wouldRecommend: 5,
hadDifficulty: "",
difficulty: "",
triedOtherApps: "",
comparison: "",
occupation: "",
});
const [loading, setLoading] = useState(false);
const resetForm = () => {
setForm({});
setLoading(false);
};
const onSubmit = useCallback(
(e) => {
setLoading(true);
editor.update(() => {
const sendMail = async () => {
await axios
.post(`${process.env.REACT_APP_BACKEND_URL}/report_bug`, {
subject: `[SURVEY]: ${new Date().toDateString()}`,
message: `${Object.keys(questions).map(
(k) => `<div>${questions[k]}</div><div>${form[k]}</div>`
)}<div>Anything else?</div>${$generateHtmlFromNodes(editor)}`,
})
.then((res) => {
Toast.success("Thanks for the feedback!");
editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
resetForm();
})
.catch((err) => {
Toast.error("Oops! Something went wrong.");
setLoading(false);
});
};
sendMail();
});
},
[editor, form, questions]
);
return (
<div className="py-5 px-8 mt-6 card-theme rounded-md">
<div className="my-3">
<div className="font-semibold ms-1 mb-2">{questions.satisfaction}</div>
<Slider
field="satisfaction"
min={0}
max={10}
value={form.satisfaction}
onChange={(v) => {
setForm((prev) => ({ ...prev, satisfaction: v }));
}}
/>
<div className="text-sm flex justify-between opacity-80">
<div>Not at all</div>
<div>Extremely</div>
</div>
</div>
<div className="my-3">
<div className="font-semibold ms-1 mb-2">{questions.ease}</div>
<Slider
field="ease"
min={0}
max={10}
value={form.ease}
onChange={(v) => {
setForm((prev) => ({ ...prev, ease: v }));
}}
/>
<div className="text-sm flex justify-between opacity-80">
<div>Not at all</div>
<div>Extremely</div>
</div>
</div>
<div className="my-3">
<div className="font-semibold ms-1 mb-2">
{questions.wouldRecommend}
</div>
<Slider
field="ease"
min={0}
max={10}
value={form.wouldRecommend}
onChange={(v) => {
setForm((prev) => ({ ...prev, wouldRecommend: v }));
}}
/>
<div className="text-sm flex justify-between opacity-80">
<div>Not at all</div>
<div>Extremely</div>
</div>
</div>
<div className="my-3">
<div className="font-semibold ms-1 mb-3">{questions.hadDifficulty}</div>
<RadioGroup
direction="vertical"
onChange={(e) =>
setForm((prev) => ({ ...prev, hadDifficulty: e.target.value }))
}
>
<Radio value={"yes"}>Yes</Radio>
<Radio value={"no"}>No</Radio>
</RadioGroup>
</div>
{form.hadDifficulty === "yes" && (
<div className="my-3">
<div className="font-semibold ms-1 mb-3">{questions.difficulty}</div>
<TextArea
rows={2}
onChange={(v) => setForm((prev) => ({ ...prev, difficulty: v }))}
/>
</div>
)}
<div className="my-3">
<div className="font-semibold ms-1 mb-3">
{questions.triedOtherApps}
</div>
<RadioGroup
direction="vertical"
onChange={(e) =>
setForm((prev) => ({ ...prev, triedOtherApps: e.target.value }))
}
>
<Radio value={"yes"}>Yes</Radio>
<Radio value={"no"}>No</Radio>
</RadioGroup>
</div>
{form.triedOtherApps === "yes" && (
<div className="my-3">
<div className="font-semibold ms-1 mb-3">{questions.comparison}</div>
<TextArea
rows={2}
onChange={(v) => setForm((prev) => ({ ...prev, comparison: v }))}
/>
</div>
)}
<div className="my-3">
<div className="font-semibold ms-1 mb-3">{questions.occupation}</div>
<Select
optionList={[
{ value: "Student", label: "Student" },
{ value: "Teacher", label: "Teacher" },
{ value: "Developer", label: "Developer" },
]}
className="w-full"
placeholder="Occupation"
onSelect={(v) => setForm((prev) => ({ ...prev, occupation: v }))}
/>
</div>
<div className="ms-1 font-semibold">
How can we make drawDB a better experience for you?
</div>
<RichEditor theme={theme} />
<div className="flex justify-between items-center">
<div className="text-sm opacity-80">
<i className="fa-brands fa-markdown me-1"></i>Styling with markdown is
supported
</div>
<div className="flex items-center">
<Button
onClick={onSubmit}
style={{ padding: "16px 32px" }}
disabled={loading}
>
Submit
</Button>
<div className={loading ? "ms-2" : "hidden"}>
<Spin />
</div>
</div>
</div>
</div>
);
}
export default function Survey() {
return <div>Survey</div>;
const [theme, setTheme] = useState("");
useEffect(() => {
const t = localStorage.getItem("theme");
setTheme(t);
if (t === "dark") {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
}
} else {
const body = document.body;
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
}
}
document.title = "Share you feedback | drawDB";
document.body.setAttribute("class", "theme");
}, [setTheme]);
const changeTheme = () => {
const body = document.body;
const t = body.getAttribute("theme-mode");
if (t === "dark") {
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "light");
setTheme("light");
}
} else {
if (body.hasAttribute("theme-mode")) {
body.setAttribute("theme-mode", "dark");
setTheme("dark");
}
}
};
return (
<>
<div className="sm:py-3 py-5 md:px-8 px-8 flex justify-between items-center">
<div className="flex items-center justify-start">
<img
src={theme === "dark" ? logo_dark : logo_light}
alt="logo"
className="me-2 sm:h-[28px] md:h-[46px]"
/>
<div className="ms-4 sm:text-sm xl:text-lg font-semibold">
Share your feedback
</div>
</div>
<div className="flex items-center">
<Button
icon={
theme === "dark" ? (
<IconSun size="extra-large" />
) : (
<IconMoon size="extra-large" />
)
}
theme="borderless"
onClick={changeTheme}
></Button>
</div>
</div>
<hr
className={`${
theme === "dark" ? "border-zinc-700" : "border-zinc-300"
} my-1`}
/>
<div className="md:w-[90%] w-[74%] mx-auto my-8">
<Banner
fullMode={false}
type="info"
icon={null}
closeIcon={null}
description={
<div>
Thanks for taking this survey! We highly value your feedback and
strive to make drawDB fit your needs better.
</div>
}
/>
<LexicalComposer initialConfig={editorConfig}>
<SurveyForm theme={theme} />
</LexicalComposer>
</div>
<hr
className={`${
theme === "dark" ? "border-zinc-700" : "border-zinc-300"
} my-1`}
/>
<div className="text-center text-sm py-3">
&copy; 2024 <strong>drawDB</strong> - All right reserved.
</div>
</>
);
}