);
}
diff --git a/components/builder/UsageIntro.tsx b/components/builder/UsageIntro.tsx
new file mode 100644
index 0000000000..885e4c8446
--- /dev/null
+++ b/components/builder/UsageIntro.tsx
@@ -0,0 +1,36 @@
+/* This example requires Tailwind CSS v2.0+ */
+import { InformationCircleIcon } from "@heroicons/react/solid";
+import { useState } from "react";
+
+export default function UsageIntro() {
+ const [dismissed, setDismissed] = useState(false);
+ return (
+ !dismissed && (
+
+
+
+
+
+
+
+ Welcome to the snoopForms No-Code Editor. Use 'tab' to
+ add new blocks or change their options. You can also drag 'n
+ drop blocks to reorder them.
+
diff --git a/components/layout/LayoutFormBuilder.tsx b/components/layout/LayoutFormBuilder.tsx
index 71475544cb..ed61661fe2 100644
--- a/components/layout/LayoutFormBuilder.tsx
+++ b/components/layout/LayoutFormBuilder.tsx
@@ -40,18 +40,6 @@ export default function LayoutFormResults({
-
-
-
-
-
{/* Main content */}
diff --git a/lib/noCodeForm.ts b/lib/noCodeForm.ts
index ab1cb6e919..47b85a8ce6 100644
--- a/lib/noCodeForm.ts
+++ b/lib/noCodeForm.ts
@@ -30,3 +30,15 @@ export const createNoCodeForm = async (formId) => {
);
}
};
+
+export const persistNoCodeForm = async (noCodeForm) => {
+ try {
+ await fetch(`/api/forms/${noCodeForm.formId}/nocodeform`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(noCodeForm),
+ });
+ } catch (error) {
+ console.error(error);
+ }
+};
diff --git a/lib/pipelines.ts b/lib/pipelines.ts
new file mode 100644
index 0000000000..e0f0149731
--- /dev/null
+++ b/lib/pipelines.ts
@@ -0,0 +1,16 @@
+import useSWR from "swr";
+import { fetcher } from "./utils";
+
+export const usePipelines = (formId: string) => {
+ const { data, error, mutate } = useSWR(
+ () => `/api/forms/${formId}/pipelines`,
+ fetcher
+ );
+
+ return {
+ pipelines: data,
+ isLoadingPipelines: !error && !data,
+ isErrorPipelines: error,
+ mutatePipeliness: mutate,
+ };
+};
diff --git a/package.json b/package.json
index 825af2970f..9dcbc08214 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
},
"dependencies": {
"@editorjs/editorjs": "^2.24.3",
+ "@editorjs/header": "^2.6.2",
"@editorjs/paragraph": "^2.8.0",
"@headlessui/react": "^1.6.1",
"@heroicons/react": "^1.0.6",
@@ -17,15 +18,17 @@
"babel-plugin-superjson-next": "^0.4.3",
"bcryptjs": "^2.4.3",
"date-fns": "^2.28.0",
+ "editorjs-drag-drop": "^1.1.2",
+ "editorjs-undo": "^2.0.3",
"json2csv": "^5.0.7",
"next": "12.1.6",
"next-auth": "^4.3.4",
"nextjs-cors": "^2.1.1",
"nodemailer": "^6.7.5",
- "react": "18.1.0",
- "react-dom": "18.1.0",
- "react-editor-js": "^2.0.6",
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
"react-icons": "^4.4.0",
+ "react-loader-spinner": "^5.1.5",
"superjson": "^1.9.1",
"swr": "^1.3.0"
},
diff --git a/pages/api/forms/[id]/pipelines/index.ts b/pages/api/forms/[id]/pipelines/index.ts
new file mode 100644
index 0000000000..fde6730d89
--- /dev/null
+++ b/pages/api/forms/[id]/pipelines/index.ts
@@ -0,0 +1,69 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import { getSession } from "next-auth/react";
+import NextCors from "nextjs-cors";
+import { formHasOwnership } from "../../../../../lib/api";
+import { prisma } from "../../../../../lib/prisma";
+
+export default async function handle(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const formId = req.query.id.toString();
+
+ await NextCors(req, res, {
+ // Options
+ methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
+ origin: "*",
+ optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
+ });
+
+ // check if session exist
+ const session = await getSession({ req: req });
+ if (!session) {
+ return res.status(401).json({ message: "Not authenticated" });
+ }
+ // check if user is form owner
+ const ownership = await formHasOwnership(session, formId);
+ if (!ownership) {
+ return res
+ .status(401)
+ .json({ message: "You are not authorized to change this noCodeForm" });
+ }
+
+ // GET /api/forms/[formId]/pipelines
+ // Get all pipelines for a specific form
+ if (req.method === "GET") {
+ const pipelinesData = await prisma.pipeline.findMany({
+ where: {
+ form: { id: formId },
+ },
+ orderBy: [
+ {
+ createdAt: "desc",
+ },
+ ],
+ });
+ return res.json(pipelinesData);
+ }
+
+ // POST /api/forms/:id/pipelines
+ // Creates a new submission session
+ // Required fields in body: -
+ // Optional fields in body: -
+ if (req.method === "POST") {
+ const { type, data } = req.body;
+ if (!["WEBHOOK"].includes(type)) {
+ return res.status(400).json({ message: "Unknown pipeline type" });
+ }
+ const prismaRes = await prisma.pipeline.create({
+ data: { type, data, form: { connect: { id: formId } } },
+ });
+ return res.json(prismaRes);
+ }
+ // Unknown HTTP Method
+ else {
+ throw new Error(
+ `The HTTP ${req.method} method is not supported by this route.`
+ );
+ }
+}
diff --git a/pages/api/forms/[id]/submissionSessions/index.ts b/pages/api/forms/[id]/submissionSessions/index.ts
index f52ed1b383..bf0ecc9b5a 100644
--- a/pages/api/forms/[id]/submissionSessions/index.ts
+++ b/pages/api/forms/[id]/submissionSessions/index.ts
@@ -1,5 +1,7 @@
import type { NextApiRequest, NextApiResponse } from "next";
+import { getSession } from "next-auth/react";
import NextCors from "nextjs-cors";
+import { formHasOwnership } from "../../../../../lib/api";
import { prisma } from "../../../../../lib/prisma";
export default async function handle(
@@ -17,6 +19,19 @@ export default async function handle(
// GET /api/forms
// Gets all forms of a user
if (req.method === "GET") {
+ // check if session exist
+ const session = await getSession({ req: req });
+ if (!session) {
+ return res.status(401).json({ message: "Not authenticated" });
+ }
+ // check if user is form owner
+ const ownership = await formHasOwnership(session, formId);
+ if (!ownership) {
+ return res
+ .status(401)
+ .json({ message: "You are not authorized to change this noCodeForm" });
+ }
+
const submissionSessionsData = await prisma.submissionSession.findMany({
where: {
form: { id: formId },
@@ -33,6 +48,7 @@ export default async function handle(
return res.json(submissionSessionsData);
}
+ // PUBLIC
// POST /api/forms/:id/submissionSessions
// Creates a new submission session
// Required fields in body: -
diff --git a/pages/forms/[id]/form.tsx b/pages/forms/[id]/form.tsx
index b966ed9071..dc8dcdd632 100644
--- a/pages/forms/[id]/form.tsx
+++ b/pages/forms/[id]/form.tsx
@@ -38,7 +38,7 @@ export default function FormPage() {
return (
<>
-
+
>
);
diff --git a/pages/forms/[id]/pipelines.tsx b/pages/forms/[id]/pipelines.tsx
index f22f88ff80..a6a79f5d47 100644
--- a/pages/forms/[id]/pipelines.tsx
+++ b/pages/forms/[id]/pipelines.tsx
@@ -76,11 +76,11 @@ export default function PipelinesPage() {
- snoopHub automatically stores your data and gives you and overview
- of your submissions and form analytics. If you want to use your
+ snoopHub automatically stores your data and gives you an overview of
+ your submissions and form analytics. If you want to use your
submissions or form events in other systems you can set up pipelines
to let snoopHub sent the data to these applications as soon as it
- arrives.
+ arrives and keep everything in sync.