mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-05 21:32:02 -06:00
update hq db schema, add form overview page
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"next": "^13.0.5",
|
||||
"next-auth": "^4.17.0",
|
||||
"nodemailer": "^6.8.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.6.0",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import AvatarPlaceholder from "@/images/avatar-placeholder.png";
|
||||
import { Disclosure, Menu, Transition } from "@headlessui/react";
|
||||
import { Bars3Icon, BellIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import clsx from "clsx";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Fragment, useMemo } from "react";
|
||||
import { Logo } from "../Logo";
|
||||
import AvatarPlaceholder from "@/images/avatar-placeholder.png";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Fragment } from "react";
|
||||
import LoadingSpinner from "../LoadingSpinner";
|
||||
import clsx from "clsx";
|
||||
import { Logo } from "../Logo";
|
||||
|
||||
export default function ProjectsLayout({ params, children }) {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function MeSettingsPage() {
|
||||
const [openNewApiKeyModal, setOpenNewApiKeyModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-auto mt-8 max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-gray-900">Account Settings</h1>
|
||||
</header>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import EmptyPageFiller from "@/components/EmptyPageFiller";
|
||||
import { deleteForm, useForms } from "@/lib/forms";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { CommandLineIcon, DocumentPlusIcon, PlusIcon, SquaresPlusIcon } from "@heroicons/react/24/outline";
|
||||
import { DocumentPlusIcon, PlusIcon } from "@heroicons/react/24/outline";
|
||||
import { EllipsisHorizontalIcon, TrashIcon } from "@heroicons/react/24/solid";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
@@ -11,7 +11,7 @@ import { Fragment, useState } from "react";
|
||||
import NewFormModal from "./NewFormModal";
|
||||
|
||||
export default function FormsList({ teamId }) {
|
||||
const { forms, mutateForms } = useForms(teamId);
|
||||
const { forms, mutateForms, isLoadingForms } = useForms(teamId);
|
||||
const [openNewFormModal, setOpenNewFormModal] = useState(false);
|
||||
|
||||
const newForm = async () => {
|
||||
|
||||
62
apps/hq/src/app/app/teams/[teamId]/forms/[formId]/layout.tsx
Normal file
62
apps/hq/src/app/app/teams/[teamId]/forms/[formId]/layout.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
import { BellIcon } from "@heroicons/react/24/outline";
|
||||
import clsx from "clsx";
|
||||
|
||||
export default function FormLayout({ params, children }) {
|
||||
const navigation = [
|
||||
{ name: "Form", href: `/teams/${params.teamId}/forms/${params.formId}/`, current: true },
|
||||
{ name: "Pipelines", href: `/teams/${params.teamId}/forms/${params.formId}/pipelines/`, current: false },
|
||||
{ name: "Results", href: `/teams/${params.teamId}/forms/${params.formId}/results/`, current: false },
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Disclosure as="header" className="bg-white shadow">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="px-2 sm:px-4 lg:divide-y lg:divide-gray-200 lg:px-8">
|
||||
<nav className="hidden lg:flex lg:space-x-8 lg:py-2" aria-label="Global">
|
||||
{navigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-gray-100 text-gray-900"
|
||||
: "text-gray-900 hover:bg-gray-50 hover:text-gray-900",
|
||||
"inline-flex items-center rounded-md py-2 px-3 text-sm font-medium"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel as="nav" className="lg:block" aria-label="Global">
|
||||
<div className="space-y-1 px-2 pt-2 pb-3">
|
||||
{navigation.map((item) => (
|
||||
<Disclosure.Button
|
||||
key={item.name}
|
||||
as="a"
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-gray-100 text-gray-900"
|
||||
: "text-gray-900 hover:bg-gray-50 hover:text-gray-900",
|
||||
"block rounded-md py-2 px-3 text-base font-medium"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
{item.name}
|
||||
</Disclosure.Button>
|
||||
))}
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -3,10 +3,56 @@
|
||||
import LoadingSpinner from "@/app/LoadingSpinner";
|
||||
import { useForm } from "@/lib/forms";
|
||||
import { useTeam } from "@/lib/teams";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { DocumentMagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { FaReact, FaVuejs } from "react-icons/fa";
|
||||
import { toast } from "react-toastify";
|
||||
import Prism from "prismjs";
|
||||
|
||||
require("prismjs/components/prism-javascript");
|
||||
|
||||
const getLibs = (formId: string) => [
|
||||
{
|
||||
id: "react",
|
||||
name: "React",
|
||||
href: `https://formbricks.com/docs/react-form-library/introduction`,
|
||||
bgColor: "bg-brand-dark",
|
||||
target: "_blank",
|
||||
icon: FaReact,
|
||||
},
|
||||
{
|
||||
id: "reactNative",
|
||||
name: "React Native",
|
||||
comingSoon: true,
|
||||
href: "#",
|
||||
disabled: true,
|
||||
icon: FaReact,
|
||||
},
|
||||
{
|
||||
id: "vue",
|
||||
name: "Vue.js",
|
||||
comingSoon: true,
|
||||
href: "#",
|
||||
disabled: true,
|
||||
icon: FaVuejs,
|
||||
},
|
||||
];
|
||||
|
||||
export default function FormsPage({ params }) {
|
||||
const { form, isLoadingForm, isErrorForm } = useForm(params.formId, params.teamId);
|
||||
const { team, isLoadingTeam, isErrorTeam } = useTeam(params.teamId);
|
||||
const libs = useMemo(() => {
|
||||
if (form) {
|
||||
return getLibs(form.id);
|
||||
}
|
||||
}, [form]);
|
||||
|
||||
useEffect(() => {
|
||||
Prism.highlightAll();
|
||||
}, []);
|
||||
|
||||
if (isLoadingForm || isLoadingTeam) {
|
||||
return (
|
||||
@@ -29,6 +75,131 @@ export default function FormsPage({ params }) {
|
||||
</span>
|
||||
</h1>
|
||||
</header>
|
||||
<div>
|
||||
<div className="mx-auto mt-8">
|
||||
<h1 className="text-ui-gray-dark text-3xl font-bold leading-tight">Connect your form</h1>
|
||||
</div>
|
||||
<div className="mt-4 mb-12">
|
||||
<p className="text-ui-gray-dark">
|
||||
To send all form submissions to this dashboard, update the form ID in the{" "}
|
||||
<code className="javascript">{"<snoopForm>"}</code> component.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-10">
|
||||
<div>
|
||||
<label htmlFor="formId" className="text-ui-gray-dark block text-base">
|
||||
Your form ID
|
||||
</label>
|
||||
<div className="mt-3">
|
||||
<input
|
||||
id="formId"
|
||||
type="text"
|
||||
className="text-md mb-3 w-full rounded-sm border-gray-300 shadow-sm disabled:bg-gray-100"
|
||||
value={form.id}
|
||||
disabled
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="w-full justify-center"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(form.id);
|
||||
toast("Copied form ID to clipboard");
|
||||
}}>
|
||||
copy
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-md bg-black p-8 font-light text-gray-200">
|
||||
<pre>
|
||||
<code className="language-js whitespace-pre-wrap">
|
||||
{`import { Form, Text, Email, Checkbox, Submit, sendToHQ } from "@formbricks/react";
|
||||
import "@formbricks/react/styles.css";
|
||||
|
||||
export default function WaitlistForm() {
|
||||
return (
|
||||
<Form id="${form.id}" onSubmit={sendToHQ}>
|
||||
<Text name="name" label="What's your name?" validation="required" />
|
||||
<Email
|
||||
name="email"
|
||||
label="What's your email address?"
|
||||
placeholder="you@example.com"
|
||||
validation="required|email"
|
||||
/>
|
||||
<Checkbox
|
||||
name="terms"
|
||||
label="Terms & Conditions"
|
||||
help="To use our service, please accept."
|
||||
validation="accepted"
|
||||
/>
|
||||
<Submit label="Let's go!" />
|
||||
</Form>
|
||||
);
|
||||
}`}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16">
|
||||
<h2 className="text-ui-gray-dark text-xl font-bold">Code your form</h2>
|
||||
<div className="mt-4 mb-12">
|
||||
<p className="text-ui-gray-dark">
|
||||
Build your form with the code library of your choice. Manage your data in this dashboard.
|
||||
</p>
|
||||
</div>
|
||||
<ul role="list" className="mt-3 grid grid-cols-1 gap-5 sm:grid-cols-2 sm:gap-6">
|
||||
{libs.map((lib) => (
|
||||
<Link
|
||||
key={lib.id}
|
||||
href={lib.href}
|
||||
className={clsx(
|
||||
"col-span-1 flex rounded-md shadow-sm",
|
||||
lib.disabled && "pointer-events-none"
|
||||
)}
|
||||
target={lib.target || ""}
|
||||
rel="noreferrer">
|
||||
<li
|
||||
className={clsx(
|
||||
lib.comingSoon ? "text-slate-500" : "text-slate-800 shadow-sm hover:text-black",
|
||||
"col-span-1 flex w-full rounded-md"
|
||||
)}>
|
||||
<div
|
||||
className={clsx(
|
||||
lib.bgColor || "bg-slate-300",
|
||||
"flex w-20 flex-shrink-0 items-center justify-center rounded-l-md text-sm font-medium text-white"
|
||||
)}>
|
||||
<lib.icon
|
||||
className={clsx(lib.comingSoon ? "text-slate-100" : "stroke-1 text-white", "h-10 w-10")}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
lib.comingSoon ? "border-dashed" : "",
|
||||
"flex flex-1 items-center justify-between truncate rounded-r-md bg-white"
|
||||
)}>
|
||||
<div className="inline-flex truncate px-4 py-6 text-lg">
|
||||
<p className="font-light">{lib.name}</p>
|
||||
{lib.comingSoon && (
|
||||
<div className="ml-3 rounded bg-teal-100 p-1 px-3">
|
||||
<p className="text-xs text-black">coming soon</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</Link>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className="my-12 text-center font-light text-slate-500">
|
||||
<p>
|
||||
Your form is running? Go to{" "}
|
||||
<Link href={`/forms/${form.id}/preview`} className="text-red underline">
|
||||
Pipelines
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTeam } from "@/lib/teams";
|
||||
import FormsList from "./FormsList";
|
||||
|
||||
export default function FormsPage({ params }) {
|
||||
const { forms, isLoadingForms, isErrorForms } = useForms(params.teamId);
|
||||
const { isLoadingForms, isErrorForms } = useForms(params.teamId);
|
||||
const { team, isLoadingTeam, isErrorTeam } = useTeam(params.teamId);
|
||||
|
||||
if (isLoadingForms || isLoadingTeam) {
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Cog8ToothIcon, RectangleStackIcon, UsersIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export default function Example({ children, params }) {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
@@ -39,14 +36,6 @@ export default function Example({ children, params }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*
|
||||
This example requires updating your template:
|
||||
|
||||
```
|
||||
<html class="h-full bg-gray-50">
|
||||
<body class="h-full overflow-hidden">
|
||||
```
|
||||
*/}
|
||||
<div className="flex h-full">
|
||||
{/* Narrow sidebar */}
|
||||
<div className="bg-brand-dark hidden w-28 overflow-y-auto bg-gradient-to-r md:block">
|
||||
@@ -56,7 +45,7 @@ export default function Example({ children, params }) {
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-brand-light text-white"
|
||||
: "hover:bg-brand-light text-teal-100 hover:text-white",
|
||||
@@ -64,7 +53,7 @@ export default function Example({ children, params }) {
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
className={clsx(
|
||||
item.current ? "text-white" : "text-teal-300 group-hover:text-white",
|
||||
"h-6 w-6"
|
||||
)}
|
||||
@@ -133,7 +122,7 @@ export default function Example({ children, params }) {
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-brand-dark text-white"
|
||||
: "hover:bg-brand-dark text-teal-100 hover:text-white",
|
||||
@@ -141,7 +130,7 @@ export default function Example({ children, params }) {
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
className={clsx(
|
||||
item.current ? "text-white" : "text-teal-300 group-hover:text-white",
|
||||
"mr-3 h-6 w-6"
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "@/styles/globals.css";
|
||||
import "@/styles/toastify.css";
|
||||
import "@/styles/prism.css";
|
||||
// include styles from the ui package
|
||||
|
||||
import SessionProvider from "./SessionProvider";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import useSWR from "swr";
|
||||
import { fetcher } from "./utils";
|
||||
|
||||
export const useForms = (teamId) => {
|
||||
export const useForms = (teamId: string) => {
|
||||
const { data, error, mutate } = useSWR(`/api/teams/${teamId}/forms`, fetcher);
|
||||
|
||||
return {
|
||||
@@ -35,7 +35,7 @@ export const persistForm = async (form) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const createForm = async (teamId, form = {}) => {
|
||||
export const createForm = async (teamId: string, form = {}) => {
|
||||
try {
|
||||
const res = await fetch(`/api/teams/${teamId}/forms`, {
|
||||
method: "POST",
|
||||
@@ -49,7 +49,7 @@ export const createForm = async (teamId, form = {}) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteForm = async (teamId, formId) => {
|
||||
export const deleteForm = async (teamId: string, formId: string) => {
|
||||
try {
|
||||
await fetch(`/api/teams/${teamId}/forms/${formId}`, {
|
||||
method: "DELETE",
|
||||
|
||||
@@ -9,15 +9,9 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.status(401).json({ message: "Not authenticated" });
|
||||
}
|
||||
|
||||
const teamId = parseInt(req.query.teamId.toString());
|
||||
if (isNaN(teamId)) {
|
||||
return res.status(400).json({ message: "Invalid teamId" });
|
||||
}
|
||||
const teamId = req.query.teamId.toString();
|
||||
|
||||
const formId = parseInt(req.query.formId.toString());
|
||||
if (isNaN(formId)) {
|
||||
return res.status(400).json({ message: "Invalid formId" });
|
||||
}
|
||||
const formId = req.query.formId.toString();
|
||||
|
||||
// check team permission
|
||||
const membership = await prisma.membership.findUnique({
|
||||
|
||||
@@ -9,10 +9,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.status(401).json({ message: "Not authenticated" });
|
||||
}
|
||||
|
||||
const teamId = parseInt(req.query.teamId.toString());
|
||||
if (isNaN(teamId)) {
|
||||
return res.status(400).json({ message: "Invalid teamId" });
|
||||
}
|
||||
const teamId = req.query.teamId.toString();
|
||||
|
||||
// check team permission
|
||||
const membership = await prisma.membership.findUnique({
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { getSessionOrUser, hashApiKey } from "@/lib/apiHelper";
|
||||
import { getSessionOrUser } from "@/lib/apiHelper";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { randomBytes } from "crypto";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { unstable_getServerSession } from "next-auth";
|
||||
|
||||
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Check Authentication
|
||||
@@ -11,10 +9,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.status(401).json({ message: "Not authenticated" });
|
||||
}
|
||||
|
||||
const teamId = parseInt(req.query.teamId.toString());
|
||||
if (isNaN(teamId)) {
|
||||
return res.status(400).json({ message: "Invalid teamId" });
|
||||
}
|
||||
const teamId = req.query.teamId.toString();
|
||||
|
||||
// GET /api/teams[teamId]
|
||||
// Get a specific team
|
||||
|
||||
90
apps/hq/src/styles/prism.css
Normal file
90
apps/hq/src/styles/prism.css
Normal file
@@ -0,0 +1,90 @@
|
||||
/* PrismJS 1.29.0
|
||||
https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+clike+javascript */
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #f8f8f2;
|
||||
background: 0 0;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
:not(pre) > code[class*="language-"],
|
||||
.token.doctype,
|
||||
.token.prolog {
|
||||
color: #8292a2;
|
||||
}
|
||||
.token.punctuation {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.token.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.token.constant,
|
||||
.token.deleted,
|
||||
.token.property,
|
||||
.token.symbol,
|
||||
.token.tag {
|
||||
color: #f92672;
|
||||
}
|
||||
.token.boolean,
|
||||
.token.number {
|
||||
color: #ae81ff;
|
||||
}
|
||||
.token.attr-name,
|
||||
.token.builtin,
|
||||
.token.char,
|
||||
.token.inserted,
|
||||
.token.selector,
|
||||
.token.string {
|
||||
color: #a6e22e;
|
||||
}
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.entity,
|
||||
.token.operator,
|
||||
.token.url,
|
||||
.token.variable {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.class-name,
|
||||
.token.function {
|
||||
color: #e6db74;
|
||||
}
|
||||
.token.keyword {
|
||||
color: #66d9ef;
|
||||
}
|
||||
.token.important,
|
||||
.token.regex {
|
||||
color: #fd971f;
|
||||
}
|
||||
.token.bold,
|
||||
.token.important {
|
||||
font-weight: 700;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
@@ -9,12 +9,12 @@ CREATE TYPE "IdentityProvider" AS ENUM ('EMAIL', 'GITHUB');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Pipeline" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "PipelineType" NOT NULL,
|
||||
"formId" INTEGER NOT NULL,
|
||||
"formId" TEXT NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"config" JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
@@ -26,7 +26,7 @@ CREATE TABLE "Customer" (
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"teamId" INTEGER NOT NULL,
|
||||
"teamId" TEXT NOT NULL,
|
||||
"data" JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
CONSTRAINT "Customer_pkey" PRIMARY KEY ("id","teamId")
|
||||
@@ -34,11 +34,11 @@ CREATE TABLE "Customer" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Form" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"teamId" INTEGER NOT NULL,
|
||||
"teamId" TEXT NOT NULL,
|
||||
"schema" JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
CONSTRAINT "Form_pkey" PRIMARY KEY ("id")
|
||||
@@ -46,12 +46,12 @@ CREATE TABLE "Form" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Submission" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"formId" INTEGER NOT NULL,
|
||||
"formId" TEXT NOT NULL,
|
||||
"customerId" TEXT,
|
||||
"teamId" INTEGER,
|
||||
"teamId" TEXT,
|
||||
"data" JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
CONSTRAINT "Submission_pkey" PRIMARY KEY ("id")
|
||||
@@ -59,7 +59,7 @@ CREATE TABLE "Submission" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Team" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
@@ -69,8 +69,8 @@ CREATE TABLE "Team" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Membership" (
|
||||
"teamId" INTEGER NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"teamId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"accepted" BOOLEAN NOT NULL DEFAULT false,
|
||||
"role" "MembershipRole" NOT NULL,
|
||||
|
||||
@@ -79,22 +79,22 @@ CREATE TABLE "Membership" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ApiKey" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"lastUsedAt" TIMESTAMP(3),
|
||||
"label" TEXT,
|
||||
"hashedKey" TEXT NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
@@ -111,7 +111,7 @@ CREATE TABLE "Account" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT,
|
||||
@@ -18,13 +18,13 @@ enum PipelineType {
|
||||
}
|
||||
|
||||
model Pipeline {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
type PipelineType
|
||||
form Form @relation(fields: [formId], references: [id], onDelete: Cascade)
|
||||
formId Int
|
||||
formId String
|
||||
enabled Boolean @default(false)
|
||||
config Json @default("{}")
|
||||
}
|
||||
@@ -34,7 +34,7 @@ model Customer {
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
teamId Int
|
||||
teamId String
|
||||
submissions Submission[]
|
||||
data Json @default("{}")
|
||||
|
||||
@@ -42,31 +42,31 @@ model Customer {
|
||||
}
|
||||
|
||||
model Form {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
label String
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
teamId Int
|
||||
teamId String
|
||||
schema Json @default("{}")
|
||||
submissions Submission[]
|
||||
pipelines Pipeline[]
|
||||
}
|
||||
|
||||
model Submission {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
form Form @relation(fields: [formId], references: [id], onDelete: Cascade)
|
||||
formId Int
|
||||
formId String
|
||||
customer Customer? @relation(fields: [customerId, teamId], references: [id, teamId])
|
||||
customerId String?
|
||||
teamId Int?
|
||||
teamId String?
|
||||
data Json @default("{}")
|
||||
}
|
||||
|
||||
model Team {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
@@ -83,9 +83,9 @@ enum MembershipRole {
|
||||
|
||||
model Membership {
|
||||
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
teamId Int
|
||||
teamId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId Int
|
||||
userId String
|
||||
accepted Boolean @default(false)
|
||||
role MembershipRole
|
||||
|
||||
@@ -93,13 +93,13 @@ model Membership {
|
||||
}
|
||||
|
||||
model ApiKey {
|
||||
id Int @id @unique @default(autoincrement())
|
||||
id String @id @unique @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
lastUsedAt DateTime?
|
||||
label String?
|
||||
hashedKey String @unique()
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId Int
|
||||
userId String
|
||||
}
|
||||
|
||||
enum IdentityProvider {
|
||||
@@ -108,11 +108,11 @@ enum IdentityProvider {
|
||||
}
|
||||
|
||||
model Account {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId Int
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
@@ -128,7 +128,7 @@ model Account {
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String?
|
||||
|
||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@@ -14,43 +14,6 @@ importers:
|
||||
tsx: 3.9.0
|
||||
turbo: 1.6.3
|
||||
|
||||
apps/examples:
|
||||
specifiers:
|
||||
'@formbricks/react': workspace:*
|
||||
'@formbricks/tailwind-config': workspace:*
|
||||
'@formbricks/tsconfig': workspace:*
|
||||
'@formbricks/ui': workspace:*
|
||||
'@heroicons/react': ^2.0.13
|
||||
'@types/node': ^18.11.9
|
||||
'@types/react': ^18.0.25
|
||||
'@types/react-dom': ^18.0.8
|
||||
autoprefixer: ^10.4.12
|
||||
eslint-config-formbricks: workspace:*
|
||||
next: latest
|
||||
postcss: ^8.4.18
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
tailwindcss: ^3.2.2
|
||||
typescript: ^4.8.4
|
||||
dependencies:
|
||||
'@formbricks/react': link:../../packages/react
|
||||
'@formbricks/ui': link:../../packages/ui
|
||||
'@heroicons/react': 2.0.13_react@18.2.0
|
||||
next: 13.0.5_biqbaboplfbrettd7655fr4n2y
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
devDependencies:
|
||||
'@formbricks/tailwind-config': link:../../packages/tailwind-config
|
||||
'@formbricks/tsconfig': link:../../packages/tsconfig
|
||||
'@types/node': 18.11.9
|
||||
'@types/react': 18.0.25
|
||||
'@types/react-dom': 18.0.9
|
||||
autoprefixer: 10.4.12_postcss@8.4.18
|
||||
eslint-config-formbricks: link:../../packages/eslint-config-formbricks
|
||||
postcss: 8.4.18
|
||||
tailwindcss: 3.2.4_postcss@8.4.18
|
||||
typescript: 4.9.3
|
||||
|
||||
apps/formbricks-com:
|
||||
specifiers:
|
||||
'@docsearch/react': ^3.3.0
|
||||
@@ -137,6 +100,7 @@ importers:
|
||||
next-auth: ^4.17.0
|
||||
nodemailer: ^6.8.0
|
||||
postcss: ^8.4.19
|
||||
prismjs: ^1.29.0
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
react-icons: ^4.6.0
|
||||
@@ -156,6 +120,7 @@ importers:
|
||||
next: 13.0.5_biqbaboplfbrettd7655fr4n2y
|
||||
next-auth: 4.17.0_2xoejpawkzgot77rbv5mbik6ve
|
||||
nodemailer: 6.8.0
|
||||
prismjs: 1.29.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-icons: 4.6.0_react@18.2.0
|
||||
@@ -12966,6 +12931,11 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/prismjs/1.29.0:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/process-nextick-args/2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
dev: true
|
||||
|
||||
Reference in New Issue
Block a user