- Add Fields to your Form
+ const options = [
+ { label: "checkbox", value: "checkbox" },
+ { label: "color", value: "color" },
+ { label: "date", value: "date" },
+ { label: "datetime-local", value: "datetime-local" },
+ { label: "email", value: "email" },
+ { label: "file", value: "file" },
+ { label: "hidden", value: "hidden" },
+ { label: "image", value: "image" },
+ { label: "month", value: "month" },
+ { label: "number", value: "number" },
+ { label: "password", value: "password" },
+ { label: "radio", value: "radio" },
+ { label: "range", value: "range" },
+ { label: "reset", value: "reset" },
+ { label: "search", value: "search" },
+ { label: "tel", value: "tel" },
+ { label: "text", value: "text" },
+ { label: "time", value: "time" },
+ { label: "url", value: "url" },
+ { label: "week", value: "week" },
+ ];
+ const menuProps = {
+ options,
+ onClick: handleMenuClick,
+ };
+ const handleMenuClick = (e) => {};
+ const addField = () => {
+ if (fields.length < 10) {
+ setFields([...fields, { name: "", isRequired: false }]);
+ } else {
+ message.error("You cannot add more than 10 fields");
+ }
+ };
+ const handleRemove = (i) => {
+ if (fields.length > 1) {
+ setFields((prev) => {
+ let nFields = [...prev];
+ nFields.splice(i, 1);
+ return nFields;
+ });
+ } else {
+ message.error("At least 1 field is required");
+ }
+ };
+ return (
+ <>
+
+
Add Fields to your Form
+ {fields.map((field, i) => (
+
+
+
+
+ option.label !== "file" && option.label !== "image")
+ : options
+ }
+ showArrow={true}
+ placeholder="Select Field Type"
+ size="large"
+ maxTagCount={1}
+ value={field.type}
+ onChange={(e) =>
+ setFields((prev) => {
+ let nFields = [...prev];
+ nFields[i].type = e;
+ return nFields;
+ })
+ }
+ />
- {fields.map((field, i) => (
-
-
-
-
-
- option.label !==
- "file" &&
- option.label !==
- "image"
- )
- : options
- }
- showArrow={true}
- placeholder="Select Field Type"
- size="large"
- maxTagCount={1}
- value={field.type}
- onChange={(e) =>
- setFields((prev) => {
- let nFields = [...prev];
- nFields[i].type = e;
- return nFields;
- })
- }
- />
-
-
-
- setFields((prev) => {
- let nFields = [...prev];
- nFields[i].name =
- e.target.value;
- return nFields;
- })
- }
- />
-
-
-
- setFields((prev) => {
- let nFields = [...prev];
- nFields[i].isRequired =
- e.target.checked;
- return nFields;
- })
- }
- >
- Required
-
-
-
-
- {/*
+
+
+ setFields((prev) => {
+ let nFields = [...prev];
+ nFields[i].name = e.target.value;
+ return nFields;
+ })
+ }
+ />
+
+
+
+ setFields((prev) => {
+ let nFields = [...prev];
+ nFields[i].isRequired = e.target.checked;
+ return nFields;
+ })
+ }
+ >
+ Required
+
+
+
+
+ {/*
@@ -146,20 +123,20 @@ export default function FormInput({ fields, setFields, hasFileField }) {
*/}
-
-
- ))}
-
-
-
- New Field
-
-
- >
- );
+
+ ))}
+
+
+
+ New Field
+
+
+
+ >
+ );
}
diff --git a/components/sections/Feature/index.js b/components/sections/Feature/index.js
index 60788d2..e8ce555 100644
--- a/components/sections/Feature/index.js
+++ b/components/sections/Feature/index.js
@@ -3,145 +3,127 @@ import templateCode from "../../../assets/images/illustrations/templateCode.png"
import Image from "next/image";
import { useInView } from "react-intersection-observer";
const feature = () => {
- const { ref: text, inView: textVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: code, inView: codeVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: circle1, inView: circle1Visible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: circle2, inView: circle2Visible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: circle3, inView: circle3Visible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
- JSON {" "}
- Schema
-
- Validation
-
-
- JSON {" "}
- Schema Validation
-
-
- Our JSON Schema Validation is like a
- vigilant guardian that meticulously checks
- every piece of data against predefined
- rules, ensuring that it perfectly aligns
- with your specified schema.
- {/* Gone are the
- days of frustrating data mismatches and
- compatibility issues. With our cutting-edge
- validation system, you can rest assured that
- your data is always in its optimal form,
- meeting all the necessary requirements
- effortlessly. */}
-
-
-
-
JSON Schema
+
+ Validation
+
+
+ JSON Schema Validation
+
+
+ Our JSON Schema Validation is like a vigilant guardian that meticulously checks every piece of data
+ against predefined rules, ensuring that it perfectly aligns with your specified schema.
+
+
+
+
-
+
-
-
-
-
- >
- );
+ >
+
+
+
+
+ >
+ );
};
export default feature;
diff --git a/components/sections/Responses/index.js b/components/sections/Responses/index.js
index 3ef4de8..bada3f9 100644
--- a/components/sections/Responses/index.js
+++ b/components/sections/Responses/index.js
@@ -1,207 +1,182 @@
import React from "react";
import { useInView } from "react-intersection-observer";
const responses = () => {
- const { ref: circle1, inView: circle1Visible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: circle2, inView: circle2Visible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: docImg, inView: docImgVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: text, inView: textVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- return (
- <>
-
-
-
-
-
-
-
-
-
-
+ const { ref: circle1, inView: circle1Visible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ const { ref: circle2, inView: circle2Visible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ const { ref: docImg, inView: docImgVisible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ const { ref: text, inView: textVisible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
-
-
+ >
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- Save{" "}
-
- 1000
- {" "}
- form responses for{" "}
-
- 30
- {" "}
- days !
-
+
+
+
+ Save 1000 form responses for{" "}
+ 30 days !
+
-
- Our exceptional capability to save 1000 form
- responses ensures that you can capture and retain
- valuable user data like never before. Whether you're
- running a high-demand survey, managing event
- registrations, or receiving a surge of feedback, our
- feature empowers you to effortlessly handle large
- volumes of responses without skipping a beat.
-
-
-
-
-
+
+ Our exceptional capability to save 1000 form responses ensures that you can capture and retain valuable
+ user data like never before. Whether you're running a high-demand survey, managing event registrations, or
+ receiving a surge of feedback, our feature empowers you to effortlessly handle large volumes of responses
+ without skipping a beat.
- >
- );
+
+
+
+
+
+ >
+ );
};
export default responses;
diff --git a/components/sections/SingleFileUpload/index.js b/components/sections/SingleFileUpload/index.js
index 4db8884..1ebfdf5 100644
--- a/components/sections/SingleFileUpload/index.js
+++ b/components/sections/SingleFileUpload/index.js
@@ -1,213 +1,188 @@
import React from "react";
import { useInView } from "react-intersection-observer";
const SingleFileUpload = () => {
- const { ref: docImg, inView: docImgVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- const { ref: text, inView: textVisible } = useInView({
- triggerOnce: true,
- rootMargin: "300px",
- });
- return (
- <>
-
-
-
-
+ const { ref: docImg, inView: docImgVisible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ const { ref: text, inView: textVisible } = useInView({
+ triggerOnce: true,
+ rootMargin: "300px",
+ });
+ return (
+ <>
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- Single file upload allowed!
-
+
+
+
+ Single file upload allowed!
+
-
- Our feature empowers you to effortlessly upload a
- variety of file formats, whether it's documents,
- images, videos, or more. No more hassle of separate
- upload forms or convoluted sharing methods. With a
- few clicks, you can securely transfer your files to
- the intended recipients in a swift and efficient
- manner.
-
-
-
+
+ Our feature empowers you to effortlessly upload a variety of file formats, whether it's documents, images,
+ videos, or more. No more hassle of separate upload forms or convoluted sharing methods. With a few clicks,
+ you can securely transfer your files to the intended recipients in a swift and efficient manner.
- >
- );
+
+
+
+ >
+ );
};
export default SingleFileUpload;
diff --git a/components/utils/API/index.js b/components/utils/API/index.js
index 375b569..1c3329f 100644
--- a/components/utils/API/index.js
+++ b/components/utils/API/index.js
@@ -2,100 +2,98 @@ import axios from "axios";
import { getLS, removeLS } from "../LocalStorage/index";
const API_URL =
- process.env.NEXT_PUBLIC_ENVIORNMENT === "prod"
- ? "https://api.savemyform.tk"
- : "https://dev-api.savemyform.tk";
+ process.env.NEXT_PUBLIC_ENVIORNMENT === "prod"
+ ? "https://api.savemyform.tk"
+ : process.env.NEXT_PUBLIC_ENVIORNMENT === "pro-dev"
+ ? "http://localhost:8080"
+ : "https://dev-api.savemyform.tk";
const getAccessToken = () => {
- return getLS("secret");
+ return getLS("secret");
};
const getHeaders = (token) => {
- if (!token) token = getAccessToken();
- if (token) {
- return {
- headers: {
- Accept: "application/json",
- Authorization: `Bearer ${token}`,
- },
- };
- }
+ if (!token) token = getAccessToken();
+ if (token) {
return {
- headers: {
- Accept: "application/json",
- },
+ headers: {
+ Accept: "application/json",
+ Authorization: `Bearer ${token}`,
+ },
};
+ }
+ return {
+ headers: {
+ Accept: "application/json",
+ },
+ };
};
const post = async (endpoint, body, token = null, form = false) => {
- let options = getHeaders(token);
- if (form) {
- options.headers["Content-Type"] = "multipart/form-data";
- }
- try {
- const response = await axios.post(API_URL + endpoint, body, options);
- return response;
- } catch (err) {
- console.error(err?.response?.data || err);
- if (err?.response?.status === 401) {
- console.log("Wrong password");
- removeLS("secret");
- } else if (err?.response?.status === 404) {
- console.log("404 Error");
- removeLS("secret");
- }
- return err?.response?.data || err;
+ let options = getHeaders(token);
+ if (form) {
+ options.headers["Content-Type"] = "multipart/form-data";
+ }
+ try {
+ const response = await axios.post(API_URL + endpoint, body, options);
+ return response;
+ } catch (err) {
+ console.error(err?.response?.data || err);
+ if (err?.response?.status === 401) {
+ console.log("Wrong password");
+ removeLS("secret");
+ } else if (err?.response?.status === 404) {
+ console.log("404 Error");
+ removeLS("secret");
}
+ return err?.response?.data || err;
+ }
};
const get = async (endpoint, token = null) => {
- try {
- const response = await axios.get(API_URL + endpoint, getHeaders(token));
- return response;
- } catch (err) {
- console.error(err?.response?.data || err);
- if (err?.response?.status === 401) {
- console.log("Wrong password");
- removeLS("secret");
- }
- return err?.response?.data || err;
+ try {
+ const response = await axios.get(API_URL + endpoint, getHeaders(token));
+ return response;
+ } catch (err) {
+ console.error(err?.response?.data || err);
+ if (err?.response?.status === 401) {
+ console.log("Wrong password");
+ removeLS("secret");
}
+ return err?.response?.data || err;
+ }
};
const patch = async (endpoint, body, token = null) => {
- try {
- const response = await axios.patch(
- API_URL + endpoint,
- body,
- getHeaders(token)
- );
- return response.data;
- } catch (err) {
- console.error(err?.response?.data || err);
- if (err?.response?.status === 401) {
- console.log("Wrong password");
- removeLS("secret");
- }
- return err?.response?.data || err;
+ try {
+ const response = await axios.patch(API_URL + endpoint, body, getHeaders(token));
+ return response.data;
+ } catch (err) {
+ console.error(err?.response?.data || err);
+ if (err?.response?.status === 401) {
+ console.log("Wrong password");
+ removeLS("secret");
}
+ return err?.response?.data || err;
+ }
};
const remove = async (endpoint, body, token = null) => {
- try {
- let conf = getHeaders(token);
- conf["data"] = body;
- const response = await axios.delete(API_URL + endpoint, conf);
- return response.data;
- } catch (err) {
- console.log(err);
+ try {
+ let conf = getHeaders(token);
+ conf["data"] = body;
+ const response = await axios.delete(API_URL + endpoint, conf);
+ return response.data;
+ } catch (err) {
+ console.log(err);
- console.error(err?.response?.data || err);
- if (err?.response?.status === 401) {
- console.log("Wrong password");
- removeLS("secret");
- }
- return err?.response?.data || err;
+ console.error(err?.response?.data || err);
+ if (err?.response?.status === 401) {
+ console.log("Wrong password");
+ removeLS("secret");
}
+ return err?.response?.data || err;
+ }
};
export { getAccessToken, post, get, patch, remove, API_URL };
diff --git a/package-lock.json b/package-lock.json
index 75c0af5..fec433f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
"flowbite": "^1.5.5",
"moment": "^2.29.4",
"next": "13.0.6",
+ "nextjs-progressbar": "^0.0.16",
"nextra": "^2.0.3",
"nextra-theme-docs": "^2.0.3",
"react": "18.2.0",
@@ -4355,6 +4356,11 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "node_modules/@types/nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A=="
+ },
"node_modules/@types/parse5": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
@@ -8968,6 +8974,20 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/nextjs-progressbar": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/nextjs-progressbar/-/nextjs-progressbar-0.0.16.tgz",
+ "integrity": "sha512-GV0fD38EMD3vSDCmkq+tObmoup6QA91a6a9MxGuhJZuRk/9TNsrHGnIQQQ/sggkMkXuT4fBgF6jRjFwScDT3zA==",
+ "dependencies": {
+ "@types/nprogress": "^0.2.0",
+ "nprogress": "^0.2.0",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "next": ">= 6.0.0",
+ "react": ">= 16.0.0"
+ }
+ },
"node_modules/nextra": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/nextra/-/nextra-2.0.3.tgz",
@@ -9064,6 +9084,11 @@
"node": ">=4"
}
},
+ "node_modules/nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -15037,6 +15062,11 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "@types/nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A=="
+ },
"@types/parse5": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
@@ -18277,6 +18307,16 @@
"integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==",
"requires": {}
},
+ "nextjs-progressbar": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/nextjs-progressbar/-/nextjs-progressbar-0.0.16.tgz",
+ "integrity": "sha512-GV0fD38EMD3vSDCmkq+tObmoup6QA91a6a9MxGuhJZuRk/9TNsrHGnIQQQ/sggkMkXuT4fBgF6jRjFwScDT3zA==",
+ "requires": {
+ "@types/nprogress": "^0.2.0",
+ "nprogress": "^0.2.0",
+ "prop-types": "^15.8.1"
+ }
+ },
"nextra": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/nextra/-/nextra-2.0.3.tgz",
@@ -18353,6 +18393,11 @@
}
}
},
+ "nprogress": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
+ "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
+ },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
diff --git a/package.json b/package.json
index 4450cae..0224c56 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"flowbite": "^1.5.5",
"moment": "^2.29.4",
"next": "13.0.6",
+ "nextjs-progressbar": "^0.0.16",
"nextra": "^2.0.3",
"nextra-theme-docs": "^2.0.3",
"react": "18.2.0",
diff --git a/pages/_app.js b/pages/_app.js
index 115a306..b42fe34 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -1,90 +1,85 @@
import "../styles/globals.css";
+import React, { useState, useEffect } from "react";
import { NextUIProvider } from "@nextui-org/react";
+import NextNProgress from "nextjs-progressbar";
+import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { QueryClient, QueryClientProvider, Hydrate } from "@tanstack/react-query";
+
import Appbar from "../components/elements/Appbar/index";
import Alert from "../components/elements/Alert";
-import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3";
import { UserContext, AppbarContext } from "../components/context";
-import React, { useState, useEffect } from "react";
import { existsLS, getLS, removeLS } from "../components/utils/LocalStorage";
-import {
- QueryClient,
- QueryClientProvider,
- Hydrate,
-} from "@tanstack/react-query";
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { get } from "../components/utils/API";
const queryClient = new QueryClient();
function MyApp({ Component, pageProps }) {
- const [isLoggedIn, setIsLoggedIn] = useState(false),
- [user, setUser] = useState(null),
- [active, setActive] = useState({
- home: false,
- dashboard: false,
- faq: false,
- documentation: false,
- }),
- [loading, setLoading] = useState(true);
+ const [isLoggedIn, setIsLoggedIn] = useState(false),
+ [user, setUser] = useState(null),
+ [active, setActive] = useState({
+ home: false,
+ dashboard: false,
+ faq: false,
+ documentation: false,
+ }),
+ [loading, setLoading] = useState(true);
- useEffect(() => {
- setIsLoggedIn(existsLS("secret"));
- if (getLS("secret")) {
- get("/user/self")
- .then((res) => {
- setUser(res.data.data);
- })
- .catch((err) => {
- if (err?.response?.status === 401) {
- setIsLoggedIn(false);
- removeLS("secret");
- } else {
- console.log(err?.response?.message);
- }
- });
- }
- setLoading(false);
- }, []);
- if (loading) {
- return (
-
- );
+ useEffect(() => {
+ setIsLoggedIn(existsLS("secret"));
+ if (getLS("secret")) {
+ get("/user/self")
+ .then((res) => {
+ setUser(res.data.data);
+ })
+ .catch((err) => {
+ if (err?.response?.status === 401) {
+ setIsLoggedIn(false);
+ removeLS("secret");
+ } else {
+ console.log(err?.response?.message);
+ }
+ });
}
+ setLoading(false);
+ }, []);
+ if (loading) {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
);
+ }
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
}
export default MyApp;
diff --git a/pages/dashboard/index.js b/pages/dashboard/index.js
index 4e11233..57250d8 100644
--- a/pages/dashboard/index.js
+++ b/pages/dashboard/index.js
@@ -1,174 +1,157 @@
import React, { useEffect, useContext } from "react";
-import { get } from "../../components/utils/API/index.js";
+import { useQuery, QueryClient, dehydrate } from "@tanstack/react-query";
import { useRouter } from "next/router";
+import Image from "next/image";
+import Link from "next/link";
+
+import { get } from "../../components/utils/API/index.js";
import { AppbarContext, UserContext } from "../../components/context";
-import { useQuery, QueryClient, dehydrate } from "@tanstack/react-query";
import Loader from "../../components/elements/Loader";
import ProjectCard from "../../components/elements/ProjectCard";
import Icon from "../../assets/svgs/iconDash.svg";
-import Image from "next/image";
import Footer from "../../components/elements/Footer";
-import Link from "next/link";
import { PlusOutlined } from "@ant-design/icons";
import SEO from "../../components/utils/SEO";
+import DashboardVector from "../../assets/svgs/dashboardsVector.svg";
+
async function getUserDashboard() {
- return await get("/user/dashboard").then((data) => {
- data?.data?.data?.projects.forEach((date) => {
- let iostr = date.date_created;
- let tempDate = new Date(iostr).toDateString().slice(4);
- date.date_created = tempDate.slice(0, 6) + "," + tempDate.slice(6);
- });
- return data?.data?.data;
+ return await get("/user/dashboard").then((data) => {
+ data?.data?.data?.projects.forEach((date) => {
+ let iostr = date.date_created;
+ let tempDate = new Date(iostr).toDateString().slice(4);
+ date.date_created = tempDate.slice(0, 6) + "," + tempDate.slice(6);
});
+ return data?.data?.data;
+ });
}
export default function Dashboard() {
- const router = useRouter();
- const { setActive } = useContext(AppbarContext);
- let { isLoggedIn, user } = useContext(UserContext);
- const userQuery = useQuery({
- queryKey: ["userData"],
- queryFn: getUserDashboard,
- staleTime: 10 * 60 * 1000,
- });
+ const router = useRouter();
+ const { setActive } = useContext(AppbarContext);
+ let { isLoggedIn, user } = useContext(UserContext);
+ const userQuery = useQuery({
+ queryKey: ["userData"],
+ queryFn: getUserDashboard,
+ staleTime: 10 * 60 * 1000,
+ });
- useEffect(() => {
- if (!isLoggedIn) {
- setActive({
- home: false,
- dashboard: false,
- documentation: false,
- faq: false,
- });
- router.push("/signin");
- }
- setActive({
- home: false,
- dashboard: true,
- documentation: false,
- faq: false,
- });
- if (user && user.verified === false) {
- setActive({
- home: false,
- dashboard: false,
- documentation: false,
- faq: false,
- });
- router.push("/verify");
- }
- }, [user]);
- if (userQuery?.isLoading) return
;
- if (userQuery?.isSuccess) {
- return (
- <>
-
{
+ if (!isLoggedIn) {
+ setActive({
+ home: false,
+ dashboard: false,
+ documentation: false,
+ faq: false,
+ });
+ router.push("/signin");
+ }
+ setActive({
+ home: false,
+ dashboard: true,
+ documentation: false,
+ faq: false,
+ });
+ if (user && user.verified === false) {
+ setActive({
+ home: false,
+ dashboard: false,
+ documentation: false,
+ faq: false,
+ });
+ router.push("/verify");
+ }
+ }, [user]);
+ if (userQuery?.isLoading) return ;
+ if (userQuery?.isSuccess) {
+ return (
+ <>
+
-
-
-
-
-
+
+
+
+
-
- Your Projects
-
-
-
-
+ Your Projects
+
+
+
+
-
-
- Create a Project
-
-
-
- {userQuery.isRefetching ? (
-
-
-
- ) : (
- userQuery.data.projects &&
- userQuery.data.projects.length > 0 &&
- userQuery.data.projects.map((project, i) => (
-
- ))
- )}
-
-
-
-
-
-
- Projects represent your website
-
-
- A project may contain multiple forms and
- those forms share the same domain name and
- reCaptcha details.
-
-
Learn More
-
-
-
-
-
-
-
- You can create upto 6 Projects.
-
-
- We are working to provide you more
-
-
-
-
+ >
+
+
Create a Project
- >
- );
- }
+
+ {userQuery.isRefetching ? (
+
+
+
+ ) : (
+ userQuery.data.projects &&
+ userQuery.data.projects.length > 0 &&
+ userQuery.data.projects.map((project, i) => (
+
+ ))
+ )}
+
+
+
+
+
+
Projects represent your website
+
+ A project may contain multiple forms and those forms share the same domain name and reCaptcha details.
+
+
Learn More
+
+
+
+
+
+
+
You can create upto 6 Projects.
+
We are working to provide you more
+
+
+
+
+ >
+ );
+ }
}
export async function getServerSideProps() {
- const queryClient = new QueryClient();
- await queryClient.fetchQuery(["userData"], getUserDashboard);
- return {
- props: {
- dehydratedState: dehydrate(queryClient),
- },
- };
+ const queryClient = new QueryClient();
+ await queryClient.fetchQuery(["userData"], getUserDashboard);
+ return {
+ props: {
+ dehydratedState: dehydrate(queryClient),
+ },
+ };
}
diff --git a/pages/dashboard/project/[id]/[formid]/index.js b/pages/dashboard/project/[id]/[formid]/index.js
index 3d3aeeb..36c14ae 100644
--- a/pages/dashboard/project/[id]/[formid]/index.js
+++ b/pages/dashboard/project/[id]/[formid]/index.js
@@ -1,124 +1,139 @@
import React, { useState, useContext, useEffect } from "react";
-import { Typography, Col, Row, Button, Input, Modal, message } from "antd";
-import { PlusOutlined } from "@ant-design/icons";
-import { get, remove } from "../../../../../components/utils/API";
-import { useGoogleReCaptcha } from "react-google-recaptcha-v3";
+import Image from "next/image";
import { useRouter } from "next/router";
-import { useWindowSize } from "../../../../../components/utils/hooks/useWindowSize";
-import {
- useQuery,
- dehydrate,
- QueryClient,
- useQueryClient,
-} from "@tanstack/react-query";
+import { useQuery, dehydrate, QueryClient, useQueryClient } from "@tanstack/react-query";
+import { BiSortAlt2 } from "react-icons/bi";
+import { Modal, Button, Input, Space } from "antd";
+
+import { get, remove } from "../../../../../components/utils/API";
import { AppbarContext, UserContext } from "../../../../../components/context";
-import Loader from "../../../../../components/elements/Loader";
-import Form from "../../../../../components/elements/Form";
import Footer from "../../../../../components/elements/Footer";
-import Link from "next/link";
-import { BiSortAlt2 } from "react-icons/bi";
import FormResponse from "../../../../../components/elements/FormResponse";
import DashboardVector from "../../../../../assets/svgs/dashboardsVector.svg";
-import Image from "next/image";
import SEO from "../../../../../components/utils/SEO";
+import { MdOutlineContentCopy } from "react-icons/md";
+
export default function FormDashboard() {
- const router = useRouter();
- const { formid } = router.query;
- const { setActive } = useContext(AppbarContext);
- let { isLoggedIn, user } = useContext(UserContext);
- const [form, setForm] = useState(null);
- const getFormDashboard = async () => {
- const res = await get(`/form/dashboard/${formid}`);
- const data = res?.data?.data[0];
- setForm(data);
- };
- useEffect(() => {
- if (!isLoggedIn) {
- setActive({
- home: false,
- dashboard: false,
- documentation: false,
- faq: false,
- });
- router.push("/signin");
- }
- setActive({
- home: false,
- dashboard: true,
- documentation: false,
- faq: false,
- });
- }, [user]);
- useEffect(() => {
- if (formid) getFormDashboard();
- }, [formid]);
- return (
- <>
-
{
+ const res = await get(`/form/dashboard/${formid}`);
+ const data = res?.data?.data;
+ setForm(data);
+ };
+
+ const copySubmissionLink = () => {
+ navigator.clipboard.writeText(submissionLink);
+ };
+ useEffect(() => {
+ if (!isLoggedIn) {
+ setActive({
+ home: false,
+ dashboard: false,
+ documentation: false,
+ faq: false,
+ });
+ router.push("/signin");
+ }
+ setActive({
+ home: false,
+ dashboard: true,
+ documentation: false,
+ faq: false,
+ });
+ }, [user]);
+ useEffect(() => {
+ if (formid) getFormDashboard();
+ }, [formid]);
+ useEffect(() => {
+ if (isNew && submissionLink) setNewFormSubmissionLinkModalOpen(true);
+ }, [isNew, submissionLink]);
+ return (
+ <>
+
-
-
-
-
-
-
-
-
- {form?.name}
-
-
- Manage
-
-
-
-
- {form?.project?.name}
-
-
-
-
-
-
- {"45"}{" "}
-
- Responses
-
-
-
-
-
- Latest
-
-
-
-
-
-
-
-
-
-
-
- Prev
-
-
- Next
-
-
-
-
+ />
+
+
+
+
+
+
+
+
{form?.name}
+ {form?.is_owner && (
+ Manage
+ )}
+
+
+
{form?.project?.name}
+
+
+
+
+
+ {"45"} Responses
+
+
+
+ Latest
+
+
+
+
+
+
+
+
+
+ Prev
+ Next
-
- >
- );
+
+
+
+
{
+ setNewFormSubmissionLinkModalOpen(false);
+ router.replace(router.asPath.split("?")[0], undefined, { shallow: true });
+ }}
+ >
+ Ok
+ ,
+ ]}
+ >
+ Submission Link
+
+ Here is your form submission link, send POST request to this link to store your form submission
+
+
+
+
+
+
+
+
+
+ >
+ );
}
diff --git a/pages/dashboard/project/[id]/index.js b/pages/dashboard/project/[id]/index.js
index e33a5e2..1b07a81 100644
--- a/pages/dashboard/project/[id]/index.js
+++ b/pages/dashboard/project/[id]/index.js
@@ -10,124 +10,129 @@ import Link from "next/link";
import SEO from "../../../../components/utils/SEO";
import DashboardVector from "../../../../assets/svgs/dashboardsVector.svg";
import Image from "next/image";
+
async function getProjectData(id) {
- return await get(`/project/dashboard/${id}`).then((data) => {
- data?.data?.data?.forms?.forEach((form) => {
- let iostr = form.date_created;
- let iostr2 = form.last_updated;
- let tempDate = new Date(iostr).toDateString().slice(4);
- let tempDate2 = new Date(iostr2).toDateString().slice(4);
- form.date_created = tempDate.slice(0, 6) + "," + tempDate.slice(6);
- form.last_updated =
- tempDate2.slice(0, 6) + "," + tempDate2.slice(6);
- });
- return data?.data?.data;
+ return await get(`/project/dashboard/${id}`).then((data) => {
+ data?.data?.data?.forms?.forEach((form) => {
+ let iostr = form.date_created;
+ let iostr2 = form.last_updated;
+ let tempDate = new Date(iostr).toDateString().slice(4);
+ let tempDate2 = new Date(iostr2).toDateString().slice(4);
+ form.date_created = tempDate.slice(0, 6) + "," + tempDate.slice(6);
+ form.last_updated = tempDate2.slice(0, 6) + "," + tempDate2.slice(6);
});
+ return data?.data?.data;
+ });
}
export default function Project({ id }) {
- const { setActive } = useContext(AppbarContext);
- let { isLoggedIn, user } = useContext(UserContext);
- useEffect(() => {
- if (!isLoggedIn) {
- setActive({
- home: false,
- dashboard: false,
- documentation: false,
- faq: false,
- });
- router.push("/signin");
- }
- setActive({
- home: false,
- dashboard: true,
- documentation: false,
- faq: false,
- });
- }, [user]);
- const projectQuery = useQuery({
- queryKey: ["projectData", id],
- queryFn: () => {
- return getProjectData(id);
- },
- staleTime: 10 * 60 * 1000,
+ const { setActive } = useContext(AppbarContext);
+ let { isLoggedIn, user } = useContext(UserContext);
+ useEffect(() => {
+ if (!isLoggedIn) {
+ setActive({
+ home: false,
+ dashboard: false,
+ documentation: false,
+ faq: false,
+ });
+ router.push("/signin");
+ }
+ setActive({
+ home: false,
+ dashboard: true,
+ documentation: false,
+ faq: false,
});
- if (projectQuery?.isLoading) return
;
- if (projectQuery?.isSuccess) {
- return (
- <>
-
{
+ return getProjectData(id);
+ },
+ staleTime: 10 * 60 * 1000,
+ });
+ if (projectQuery?.isLoading) return ;
+ if (projectQuery?.isSuccess) {
+ return (
+ <>
+
-
-
-
-
-
-
-
-
- {projectQuery?.data?.name}
-
- {projectQuery?.data?.is_owner && (
-
-
- Manage
-
-
- )}
-
-
-
-
-
- {projectQuery.data.forms.map((form) => (
-
- ))}
-
-
-
+ />
+
+
+
+
+
+
+
+
{projectQuery?.data?.name}
+ {projectQuery?.data?.is_owner && (
+
+
+ Manage
+
+
+ )}
+
+ {projectQuery.data.forms.length !== 0 && (
+
-
- >
- );
- }
+ )}
+
+
+
+ {projectQuery.data.forms.length === 0 && (
+
+
Create your first Form
+
+ )}
+ {projectQuery.data.forms.map((form, i) => (
+
+ ))}
+
+
+
+
+
+ >
+ );
+ }
}
export async function getServerSideProps({ params: { id } }) {
- const queryClient = new QueryClient();
- await queryClient.fetchQuery(["projectData", id], getProjectData);
- return {
- props: {
- dehydratedState: dehydrate(queryClient),
- id,
- },
- };
+ const queryClient = new QueryClient();
+ await queryClient.fetchQuery(["projectData", id], getProjectData);
+ return {
+ props: {
+ dehydratedState: dehydrate(queryClient),
+ id,
+ },
+ };
}
diff --git a/pages/dashboard/project/[id]/newform/index.js b/pages/dashboard/project/[id]/newform/index.js
index 9f1b0bb..7dff681 100644
--- a/pages/dashboard/project/[id]/newform/index.js
+++ b/pages/dashboard/project/[id]/newform/index.js
@@ -1,137 +1,164 @@
import React, { useState, useRef, useContext, useEffect, useMemo } from "react";
import Image from "next/image";
+import { Input, Checkbox, message } from "antd";
+import { useQuery, dehydrate, QueryClient } from "@tanstack/react-query";
+
import DashboardVector from "../../../../../assets/svgs/dashboardsVector.svg";
-import { Input, Space, Checkbox, message } from "antd";
import FormInput from "../../../../../components/elements/FormInput";
import Footer from "../../../../../components/elements/Footer";
import createJSONSchema from "../../../../../components/utils/JSONSchema";
import { useRouter } from "next/router";
import { AppbarContext, UserContext } from "../../../../../components/context";
-import { post, API_URL } from "../../../../../components/utils/API";
+import { get, post, API_URL } from "../../../../../components/utils/API";
+import Loader from "../../../../../components/elements/Loader";
import SEO from "../../../../../components/utils/SEO";
-export default function NewForm() {
- const { setActive } = useContext(AppbarContext);
- let { isLoggedIn, user } = useContext(UserContext);
- let [fields, setFields] = useState([{ name: "", isRequired: false }]);
+async function getProjectData(id) {
+ if (id)
+ return await get(`/project/dashboard/${id}`).then((data) => {
+ data?.data?.data?.forms?.forEach((form) => {
+ let iostr = form.date_created;
+ let iostr2 = form.last_updated;
+ let tempDate = new Date(iostr).toDateString().slice(4);
+ let tempDate2 = new Date(iostr2).toDateString().slice(4);
+ form.date_created = tempDate.slice(0, 6) + "," + tempDate.slice(6);
+ form.last_updated = tempDate2.slice(0, 6) + "," + tempDate2.slice(6);
+ });
+ return data?.data?.data;
+ });
+ return null;
+}
+
+export default function NewForm() {
+ const { setActive } = useContext(AppbarContext);
+ let { isLoggedIn, user } = useContext(UserContext);
- const router = useRouter();
- const { id } = router.query;
- const formName = useRef(null),
- reCAPTCHA = useRef(null);
+ let [fields, setFields] = useState([{ name: "", isRequired: false }]);
- const hasFileField = useMemo(
- () =>
- fields.some(
- (field) => field.type === "image" || field.type === "file"
- ),
- [fields]
- );
- const handleSubmit = async (e) => {
- e.preventDefault();
- try {
- const name = formName.current.input.value;
- const hasRecaptcha = reCAPTCHA.current.input.checked;
- const schema = createJSONSchema(fields);
- const body = {
- name,
- schema,
- hasRecaptcha,
- hasFileField,
- hostUrl: API_URL,
- };
- const response = await post(`/form/new/${id}`, body);
- if (response.status === "Inserted") {
- message.success("Form Created Successfully");
- router.replace(`/dashboard/project/${id}/${response.data.id}`);
- }
- } catch (err) {
- message.error("Error in creating form");
- }
- };
+ const router = useRouter();
+ const { id } = router.query;
+ const formName = useRef(null),
+ reCAPTCHA = useRef(null);
- useEffect(() => {
- if (!isLoggedIn) {
- setActive({
- home: false,
- dashboard: false,
- documentation: false,
- faq: false,
- });
- router.push("/signin");
- }
- setActive({
- home: false,
- dashboard: true,
- documentation: false,
- faq: false,
+ const hasFileField = useMemo(() => fields.some((field) => field.type === "image" || field.type === "file"), [fields]);
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ const name = formName.current.input.value;
+ const hasRecaptcha = reCAPTCHA.current.input.checked;
+ const schema = createJSONSchema(fields);
+ const body = {
+ name,
+ schema,
+ hasRecaptcha,
+ hasFileField,
+ hostUrl: API_URL,
+ };
+ const response = await post(`/form/new/${id}`, body);
+ if (response.status === 201) {
+ message.success("Form created successfully");
+ router.push({
+ pathname: `/dashboard/project/${id}/${response.data.data.id}`,
+ query: { isNew: true, submissionLink: response.data.data.submisssionUrl },
});
- }, [user]);
+ } else message.error(response.data.error);
+ } catch (err) {
+ message.error("Error in creating form");
+ }
+ };
+ const handleCancel = async () => {
+ router.back();
+ };
+
+ useEffect(() => {
+ if (!isLoggedIn) {
+ setActive({
+ home: false,
+ dashboard: false,
+ documentation: false,
+ faq: false,
+ });
+ router.push("/signin");
+ }
+ setActive({
+ home: false,
+ dashboard: true,
+ documentation: false,
+ faq: false,
+ });
+ }, [user]);
+
+ const projectQuery = useQuery({
+ queryKey: ["projectData", id],
+ queryFn: () => {
+ return getProjectData(id);
+ },
+ staleTime: 10 * 60 * 1000,
+ });
+ if (projectQuery?.isLoading) return
;
+ if (projectQuery?.isSuccess)
return (
- <>
-
+
+
+
+
-
-
-
+
+
+
+
+
{"New Form"}
+
+
+
{projectQuery.data?.name}
+
+
+
+
+
+
+ >
);
}
diff --git a/pages/dashboard/project/[id]/settings/index.js b/pages/dashboard/project/[id]/settings/index.js
index a84b962..6f9cbee 100644
--- a/pages/dashboard/project/[id]/settings/index.js
+++ b/pages/dashboard/project/[id]/settings/index.js
@@ -227,7 +227,7 @@ export default function editProject({ id }) {
Allowed Domains
handleAddDomain()}
>
@@ -266,7 +266,7 @@ export default function editProject({ id }) {
Collaborators
{
handleAddCollab();
}}
@@ -306,7 +306,7 @@ export default function editProject({ id }) {
Save
diff --git a/pages/dashboard/project/newproject/index.js b/pages/dashboard/project/newproject/index.js
index 5597e3e..542e2d5 100644
--- a/pages/dashboard/project/newproject/index.js
+++ b/pages/dashboard/project/newproject/index.js
@@ -3,6 +3,7 @@ import Footer from "../../../../components/elements/Footer";
import { Input } from "antd";
import { Switch, Space } from "antd";
import Image from "next/image";
+import { Loading } from "@nextui-org/react";
import { AiOutlinePlus, AiOutlineClose } from "react-icons/ai";
import { message } from "antd";
import { post } from "../../../../components/utils/API";
@@ -10,13 +11,17 @@ import { useRouter } from "next/router";
import { useGoogleReCaptcha } from "react-google-recaptcha-v3";
import SEO from "../../../../components/utils/SEO";
import DashboardVector from "../../../../assets/svgs/dashboardsVector.svg";
+import { useQueryClient } from "@tanstack/react-query";
+
export default function NewProject() {
+ let queryClient = useQueryClient();
const { executeRecaptcha } = useGoogleReCaptcha();
const router = useRouter();
const [state, setState] = useState(false);
const [projectName, setProjectName] = useState(null);
const [recaptchaKey, setReCaptchaKey] = useState(null);
const [recaptchaSecret, setReCaptchaSecret] = useState(null);
+ const [loading, setLoading] = useState(false);
const [domain, setDomain] = useState([""]);
const onChange = (checked) => {
setState(checked);
@@ -24,6 +29,7 @@ export default function NewProject() {
const success = () => {
message.success("Project Successfully saved");
router.push("/dashboard");
+ queryClient.invalidateQueries(["userData"]);
};
const addDomain = () => {
if (domain.length < 3) {
@@ -53,6 +59,7 @@ export default function NewProject() {
message.error("Recaptcha Failed");
return;
}
+ setLoading(true);
const res = await post("/project/new", {
name: projectName,
hasRecaptcha: state,
@@ -61,14 +68,11 @@ export default function NewProject() {
allowedOrigins: domain,
recaptcha_token: token,
});
+ setLoading(false);
res.status === "error" ? message.error(res.error) : success();
};
const handleCancel = async () => {
- setProjectName(null);
- setReCaptchaKey(null);
- setReCaptchaSecret(null);
- setState(false);
- setDomain([""]);
+ router.back();
};
return (
<>
@@ -173,10 +177,11 @@ export default function NewProject() {
- Submit
+ {loading ? : "Save"}