Integration survey (#194)
* add integrations tab with surveys * add new horizontal navbar --------- Co-authored-by: knugget <johannes@knugget.de>
@@ -51,24 +51,24 @@ export function Logo(props) {
|
||||
<path
|
||||
d="M21 93.6416H46.3585V106.321C46.3585 113.323 40.6818 119 33.6792 119C26.6767 119 21 113.323 21 106.321V93.6416Z"
|
||||
fill="black"
|
||||
fill-opacity="0.1"
|
||||
fillOpacity="0.1"
|
||||
/>
|
||||
<path
|
||||
d="M21 55C21 43.9543 29.9543 35 41 35H71.717C78.7195 35 84.3962 40.6767 84.3962 47.6792C84.3962 54.6818 78.7195 60.3585 71.717 60.3585H21V55Z"
|
||||
fill="black"
|
||||
fill-opacity="0.1"
|
||||
fillOpacity="0.1"
|
||||
/>
|
||||
<path
|
||||
d="M21 64.3208H71.717C78.7195 64.3208 84.3962 69.9975 84.3962 77C84.3962 84.0026 78.7195 89.6793 71.717 89.6793H21V64.3208Z"
|
||||
fill="black"
|
||||
fill-opacity="0.1"
|
||||
fillOpacity="0.1"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_2810_6010)">
|
||||
<path
|
||||
d="M22.3303 13.158C32.2638 3.46307 57.4526 13.158 57.4526 13.158H22.3303C19.8905 15.5391 18.3709 19.09 18.3709 24.2415C18.3709 50.3672 46.6715 59.8676 46.6715 78.0764C46.6715 95.9014 19.5515 106.898 18.4081 131.119H57.4526C57.4526 131.119 18.3709 158.037 18.3709 132.703C18.3709 132.169 18.3835 131.641 18.4081 131.119H1.18848L4.55759 13.158H22.3303Z"
|
||||
fill="black"
|
||||
fill-opacity="0.1"
|
||||
fillOpacity="0.1"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
@@ -79,7 +79,7 @@ export function Logo(props) {
|
||||
<circle cx="13.0756" cy="47.6793" r="23.7736" fill="#00C4B8" />
|
||||
</g>
|
||||
</g>
|
||||
<line x1="123.75" y1="33" x2="123.75" y2="120" stroke="#CBD5E1" stroke-width="1.5" />
|
||||
<line x1="123.75" y1="33" x2="123.75" y2="120" stroke="#CBD5E1" strokeWidth="1.5" />
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_d_2810_6010"
|
||||
@@ -88,8 +88,8 @@ export function Logo(props) {
|
||||
width="60.4526"
|
||||
height="108"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
colorInterpolationFilters="sRGB">
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
@@ -110,8 +110,8 @@ export function Logo(props) {
|
||||
width="87.5471"
|
||||
height="87.5471"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
colorInterpolationFilters="sRGB">
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="10" result="effect1_foregroundBlur_2810_6010" />
|
||||
</filter>
|
||||
@@ -122,8 +122,8 @@ export function Logo(props) {
|
||||
width="87.5471"
|
||||
height="87.5471"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
colorInterpolationFilters="sRGB">
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="10" result="effect1_foregroundBlur_2810_6010" />
|
||||
</filter>
|
||||
@@ -134,8 +134,8 @@ export function Logo(props) {
|
||||
x2="20.9978"
|
||||
y2="105.964"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00E6CA" />
|
||||
<stop offset="1" stop-color="#00C4B8" />
|
||||
<stop stopColor="#00E6CA" />
|
||||
<stop offset="1" stopColor="#00C4B8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2810_6010"
|
||||
@@ -144,8 +144,8 @@ export function Logo(props) {
|
||||
x2="21"
|
||||
y2="77.1838"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00E6CA" />
|
||||
<stop offset="1" stop-color="#00C4B8" />
|
||||
<stop stopColor="#00E6CA" />
|
||||
<stop offset="1" stopColor="#00C4B8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2810_6010"
|
||||
@@ -154,8 +154,8 @@ export function Logo(props) {
|
||||
x2="21"
|
||||
y2="47.863"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00E6CA" />
|
||||
<stop offset="1" stop-color="#00C4B8" />
|
||||
<stop stopColor="#00E6CA" />
|
||||
<stop offset="1" stopColor="#00C4B8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_2810_6010"
|
||||
@@ -164,8 +164,8 @@ export function Logo(props) {
|
||||
x2="20.9978"
|
||||
y2="105.964"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFE1" />
|
||||
<stop offset="1" stop-color="#01E0C6" />
|
||||
<stop stopColor="#00FFE1" />
|
||||
<stop offset="1" stopColor="#01E0C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_2810_6010"
|
||||
@@ -174,8 +174,8 @@ export function Logo(props) {
|
||||
x2="21"
|
||||
y2="77.1838"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFE1" />
|
||||
<stop offset="1" stop-color="#01E0C6" />
|
||||
<stop stopColor="#00FFE1" />
|
||||
<stop offset="1" stopColor="#01E0C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_2810_6010"
|
||||
@@ -184,8 +184,8 @@ export function Logo(props) {
|
||||
x2="21"
|
||||
y2="47.863"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFE1" />
|
||||
<stop offset="1" stop-color="#01E0C6" />
|
||||
<stop stopColor="#00FFE1" />
|
||||
<stop offset="1" stopColor="#01E0C6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
@@ -12,6 +12,7 @@ export default function TabNavigation({ tabs, currentTab, setCurrentTab }) {
|
||||
id="tabs"
|
||||
name="tabs"
|
||||
className="block w-full rounded-md border-gray-300 focus:border-teal-500 focus:ring-teal-500"
|
||||
onChange={(e) => setCurrentTab(e.target.value)}
|
||||
defaultValue={currentTab}>
|
||||
{tabs.map((tab) => (
|
||||
<option key={tab.name}>{tab.name}</option>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Button } from "@formbricks/ui";
|
||||
|
||||
interface EngineButtonsProps {
|
||||
allowSkip: boolean;
|
||||
skipAction: () => void;
|
||||
autoSubmit: boolean;
|
||||
}
|
||||
|
||||
export function EngineButtons({ allowSkip, skipAction, autoSubmit }: EngineButtonsProps) {
|
||||
return (
|
||||
<div className="mx-auto mt-8 flex w-full">
|
||||
{allowSkip && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
type="button"
|
||||
className="transition-all ease-in-out hover:scale-105"
|
||||
onClick={() => skipAction()}>
|
||||
Skip
|
||||
</Button>
|
||||
)}
|
||||
{!autoSubmit && (
|
||||
<Button variant="primary" type="submit" className="ml-2 transition-all ease-in-out hover:scale-105">
|
||||
Submit
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import clsx from "clsx";
|
||||
import { EngineButtons } from "./EngineButtons";
|
||||
|
||||
interface IconCheckboxProps {
|
||||
element: any;
|
||||
page: any;
|
||||
register: any;
|
||||
field: any;
|
||||
allowSkip: boolean;
|
||||
skipAction: () => void;
|
||||
autoSubmit: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export default function IconCheckbox({
|
||||
element,
|
||||
page,
|
||||
allowSkip,
|
||||
register,
|
||||
autoSubmit,
|
||||
skipAction,
|
||||
loading,
|
||||
}: IconCheckboxProps) {
|
||||
return (
|
||||
<div className={clsx(loading && "formbricks-pulse-animation")}>
|
||||
<div className="flex flex-col justify-center">
|
||||
<label className="pb-6 text-center text-lg font-bold text-slate-600 dark:text-slate-300 sm:text-xl md:text-2xl">
|
||||
{element.label}
|
||||
</label>
|
||||
<fieldset className="space-y-5">
|
||||
<legend className="sr-only">{element.label}</legend>
|
||||
<div className="flex max-w-5xl space-x-3 px-2">
|
||||
{element.options &&
|
||||
element.options.map((option) => (
|
||||
<label
|
||||
htmlFor={`${page.id}-${element.id}-${option.value}`}
|
||||
key={`${page.id}-${element.id}-${option.value}`}>
|
||||
<div className="drop-shadow-card duration-120 relative w-32 cursor-default rounded-lg border border-gray-200 bg-white p-6 text-center transition-all ease-in-out hover:scale-105">
|
||||
<div className="absolute right-4 top-3">
|
||||
<input
|
||||
id={`${page.id}-${element.id}-${option.value}`}
|
||||
aria-describedby={`${page.id}-${element.id}-${option.value}-description`}
|
||||
type="checkbox"
|
||||
value={option.value}
|
||||
className="text-brand-dark focus:ring-brand-dark border-brand-dark h-5 w-5 rounded border-2 bg-slate-100"
|
||||
{...register(element.name!)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-auto my-4 h-12 w-12">
|
||||
{option.frontend?.icon && <option.frontend.icon />}
|
||||
</div>
|
||||
<span className="mt-3 mb-1 text-sm text-slate-500">{option.label}</span>
|
||||
<p
|
||||
id={`${page.id}-${element.id}-${option.value}-description`}
|
||||
className="mt-1 text-xs text-slate-600 dark:text-slate-400">
|
||||
{option.frontend.description}
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<EngineButtons allowSkip={allowSkip} skipAction={skipAction} autoSubmit={autoSubmit} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import {
|
||||
SegmentLogoImage,
|
||||
MixpanelLogoImage,
|
||||
AmplitudeLogoImage,
|
||||
PosthogLogoImage,
|
||||
JuneLogoImage,
|
||||
HeapLogoImage,
|
||||
HubspotLogoImage,
|
||||
SalesforceLogoImage,
|
||||
CustomerIOLogoImage,
|
||||
BrazeLogoImage,
|
||||
} from "./LogoImages";
|
||||
import IconCheckbox from "./IconCheckbox";
|
||||
import ThankYouPage from "./ThankYouPage";
|
||||
import { useSession } from "next-auth/react";
|
||||
import LoadingSpinner from "../LoadingSpinner";
|
||||
import { FormbricksEngine } from "@formbricks/engine-react";
|
||||
|
||||
export const DataInSurvey = () => {
|
||||
const { data: session, status } = useSession();
|
||||
if (status === "loading") return <LoadingSpinner />;
|
||||
|
||||
return (
|
||||
<FormbricksEngine
|
||||
formbricksUrl={
|
||||
process.env.NODE_ENV === "production" ? "https://app.formbricks.com" : "http://localhost:3000"
|
||||
}
|
||||
formId={
|
||||
process.env.NODE_ENV === "production" ? "cldvn1r6x0002s00gnw01lj40" : "cldvkpx11000019a0aoucngcb"
|
||||
}
|
||||
customer={{ email: session.user.email }}
|
||||
onFinished={({ submission }) => {
|
||||
console.log(submission);
|
||||
}}
|
||||
schema={{
|
||||
config: {
|
||||
progressBar: false,
|
||||
},
|
||||
pages: [
|
||||
{
|
||||
id: "DataInPage",
|
||||
config: {
|
||||
autoSubmit: false,
|
||||
},
|
||||
elements: [
|
||||
{
|
||||
id: "dataIn",
|
||||
type: "radio",
|
||||
name: "dataIn",
|
||||
options: [
|
||||
{ label: "Segment", value: "segmentIn", frontend: { icon: SegmentLogoImage } },
|
||||
{
|
||||
label: "Mixpanel",
|
||||
value: "mixpanel",
|
||||
frontend: { icon: MixpanelLogoImage },
|
||||
},
|
||||
{ label: "Amplitude", value: "amplitude", frontend: { icon: AmplitudeLogoImage } },
|
||||
{ label: "PostHog", value: "posthog", frontend: { icon: PosthogLogoImage } },
|
||||
{ label: "Heap", value: "heap", frontend: { icon: HeapLogoImage } },
|
||||
{ label: "June", value: "june", frontend: { icon: JuneLogoImage } },
|
||||
],
|
||||
component: IconCheckbox,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "thankYouPage",
|
||||
endScreen: true,
|
||||
elements: [
|
||||
{
|
||||
id: "thankYou",
|
||||
type: "html",
|
||||
component: ThankYouPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const DataOutSurvey = () => {
|
||||
const { data: session, status } = useSession();
|
||||
if (status === "loading") return <LoadingSpinner />;
|
||||
|
||||
return (
|
||||
<FormbricksEngine
|
||||
formbricksUrl={
|
||||
process.env.NODE_ENV === "production" ? "https://app.formbricks.com" : "http://localhost:3000"
|
||||
}
|
||||
formId={
|
||||
process.env.NODE_ENV === "production" ? "cldvn4fk30003s00gum3rkvma" : "cldvku70u000319a0xokj8cku"
|
||||
}
|
||||
customer={{ email: session.user.email }}
|
||||
schema={{
|
||||
config: {
|
||||
progressBar: false,
|
||||
},
|
||||
pages: [
|
||||
{
|
||||
id: "DataInPage",
|
||||
config: {
|
||||
autoSubmit: false,
|
||||
},
|
||||
elements: [
|
||||
{
|
||||
id: "dataIn",
|
||||
type: "checkbox",
|
||||
name: "dataIn",
|
||||
options: [
|
||||
{ label: "Segment", value: "segmentOut", frontend: { icon: SegmentLogoImage } },
|
||||
{
|
||||
label: "Hubspot",
|
||||
value: "hubspot",
|
||||
frontend: { icon: HubspotLogoImage },
|
||||
},
|
||||
{ label: "customer.io", value: "customerio", frontend: { icon: CustomerIOLogoImage } },
|
||||
{ label: "Salesforce", value: "salesforce", frontend: { icon: SalesforceLogoImage } },
|
||||
{ label: "braze", value: "braze", frontend: { icon: BrazeLogoImage } },
|
||||
],
|
||||
component: IconCheckbox,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "thankYouPage",
|
||||
endScreen: true,
|
||||
elements: [
|
||||
{
|
||||
id: "thankYou",
|
||||
type: "html",
|
||||
component: ThankYouPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { DataInSurvey, DataOutSurvey } from "./IntegrationSurveys";
|
||||
|
||||
export default function FormsPage({}) {
|
||||
return (
|
||||
<div className="mx-auto py-8 sm:px-6 lg:px-8">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl font-bold leading-tight tracking-tight text-slate-900">Integrations</h1>
|
||||
</header>
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-bold text-slate-700">Data-In: Pre-Segmentation</h2>
|
||||
<p className="text-slate-400">Sync cohorts of users to ask questions based on your analytics data.</p>
|
||||
<DataInSurvey />
|
||||
</div>
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-bold text-slate-700">Data-Out: User Profiles and CRM</h2>
|
||||
<p className="text-slate-400">Enrich user data on other platforms.</p>
|
||||
<DataOutSurvey />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import SegmentLogo from "@/images/logos/segment-logo.png";
|
||||
import MixpanelLogo from "@/images/logos/mixpanel-logo.png";
|
||||
import AmplitudeLogo from "@/images/logos/amplitude-logo.png";
|
||||
import PosthogLogo from "@/images/logos/posthog-logo.png";
|
||||
import HeapLogo from "@/images/logos/heap-logo.png";
|
||||
import JuneLogo from "@/images/logos/june-logo.png";
|
||||
import CustomerIOLogo from "@/images/logos/customerio-logo.png";
|
||||
import SalesforceLogo from "@/images/logos/salesforce-logo.png";
|
||||
import BrazeLogo from "@/images/logos/braze-logo.png";
|
||||
import HubspotLogo from "@/images/logos/hubspot-logo.png";
|
||||
|
||||
import Image from "next/image";
|
||||
|
||||
export function SegmentLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={SegmentLogo} {...props} />;
|
||||
}
|
||||
|
||||
export function MixpanelLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={MixpanelLogo} {...props} />;
|
||||
}
|
||||
|
||||
export function AmplitudeLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={AmplitudeLogo} {...props} />;
|
||||
}
|
||||
export function PosthogLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={PosthogLogo} {...props} />;
|
||||
}
|
||||
export function HeapLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={HeapLogo} {...props} />;
|
||||
}
|
||||
export function JuneLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={JuneLogo} {...props} />;
|
||||
}
|
||||
export function SalesforceLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={SalesforceLogo} {...props} />;
|
||||
}
|
||||
export function BrazeLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={BrazeLogo} {...props} />;
|
||||
}
|
||||
export function HubspotLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={HubspotLogo} {...props} />;
|
||||
}
|
||||
export function CustomerIOLogoImage(props: any) {
|
||||
return <Image alt="Segment Logo" src={CustomerIOLogo} {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function ThankYouPage() {
|
||||
return (
|
||||
<div className="mt-6 mb-10 max-w-2xl rounded-lg border border-gray-200 bg-white p-6 text-center">
|
||||
<h3 className="text-brand-dark mt-2 text-lg font-bold">Thank you!</h3>
|
||||
<p className="py-2 text-sm text-slate-400">
|
||||
We'll send you a message once we built these integrations.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,163 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import { Logo } from "@/components/Logo";
|
||||
import { CustomersIcon, FormIcon } from "@formbricks/ui";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { CustomersIcon, DashboardIcon, FormIcon } from "@formbricks/ui";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useRouter } from "next/router";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export default function LayoutWrapperOrganisation({ children }) {
|
||||
interface LayoutWrapperOrganisationProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function LayoutWrapperOrganisation({ children }: LayoutWrapperOrganisationProps) {
|
||||
const router = useRouter();
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
const sidebarNavigation = useMemo(
|
||||
const navigation = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: "Forms",
|
||||
href: `/organisations/${router.query.organisationId}/forms`,
|
||||
icon: FormIcon,
|
||||
current: pathname.includes("/form"),
|
||||
current: router.pathname.includes("/form"),
|
||||
},
|
||||
{
|
||||
name: "Customers",
|
||||
href: `/organisations/${router.query.organisationId}/customers`,
|
||||
icon: CustomersIcon,
|
||||
current: pathname.includes("/customers"),
|
||||
current: router.pathname.includes("/customers"),
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
href: `/organisations/${router.query.organisationId}/integrations`,
|
||||
icon: DashboardIcon,
|
||||
current: router.pathname.includes("/integrations"),
|
||||
},
|
||||
/* {
|
||||
name: "Settings",
|
||||
href: `/organisations/${router.query.organisationId}/settings`,
|
||||
icon: Cog8ToothIcon,
|
||||
current: pathname.includes("/settings"),
|
||||
}, */
|
||||
],
|
||||
[router.query, pathname]
|
||||
[router]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full">
|
||||
{/* Narrow sidebar */}
|
||||
<div className="hidden overflow-y-auto border-r border-gray-200 bg-white bg-gradient-to-r sm:w-40 md:block xl:w-64">
|
||||
<div className="flex w-full flex-col items-center py-6">
|
||||
<div className="w-full flex-1 space-y-2 px-2">
|
||||
{sidebarNavigation.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-gray-100 text-gray-900"
|
||||
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
||||
"group flex items-center rounded-md px-2 py-2 text-sm font-medium"
|
||||
)}>
|
||||
<item.icon
|
||||
className={clsx(
|
||||
item.current ? "text-gray-500" : "text-gray-400 group-hover:text-gray-500",
|
||||
"mr-3 h-6 w-6 flex-shrink-0"
|
||||
<div>
|
||||
<Disclosure as="header" className="bg-white shadow">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="mx-auto w-full px-2 sm:px-4 lg:divide-y lg:divide-gray-200 lg:px-8">
|
||||
<nav className="py-2" aria-label="Global">
|
||||
<div className="relative z-10 flex items-center lg:hidden">
|
||||
{/* Mobile menu button */}
|
||||
<Disclosure.Button className="focus:ring-brand inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset">
|
||||
<span className="sr-only">Open menu</span>
|
||||
{open ? (
|
||||
<XMarkIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
) : (
|
||||
<Bars3Icon className="block h-6 w-6" aria-hidden="true" />
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<div className="hidden lg:flex lg:space-x-4">
|
||||
{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.icon className="mr-3 h-5 w-5" />
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu */}
|
||||
<Transition.Root show={mobileMenuOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-20 md:hidden" onClose={setMobileMenuOpen}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity ease-linear duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0">
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-75" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-40 flex">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition ease-in-out duration-300 transform"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full">
|
||||
<Dialog.Panel className="bg-brand-light relative flex w-full max-w-xs flex-1 flex-col pt-5 pb-4">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-in-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in-out duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0">
|
||||
<div className="absolute top-1 right-0 -mr-14 p-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-12 w-12 items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-white"
|
||||
onClick={() => setMobileMenuOpen(false)}>
|
||||
<XMarkIcon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
<span className="sr-only">Close sidebar</span>
|
||||
</button>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
<div className="flex flex-shrink-0 items-center px-4">
|
||||
<Logo className="h-8 w-auto" />
|
||||
</div>
|
||||
<div className="mt-5 h-0 flex-1 overflow-y-auto px-2">
|
||||
<nav className="flex h-full flex-col">
|
||||
<div className="space-y-1">
|
||||
{sidebarNavigation.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={clsx(
|
||||
item.current
|
||||
? "bg-brand-dark text-white"
|
||||
: "hover:bg-brand-dark text-teal-100 hover:text-white",
|
||||
"group flex items-center rounded-md py-2 px-3 text-sm font-medium"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
<item.icon
|
||||
className={clsx(
|
||||
item.current ? "text-white" : "text-teal-300 group-hover:text-white",
|
||||
"mr-3 h-6 w-6"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
<div className="w-14 flex-shrink-0" aria-hidden="true">
|
||||
{/* Dummy element to force sidebar to shrink to fit close icon */}
|
||||
<Disclosure.Panel as="nav" className="lg:hidden" 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>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
|
||||
{/* Content area */}
|
||||
<div className="flex flex-1 flex-col overflow-hidden">
|
||||
{/* Main content */}
|
||||
<div className="flex flex-1 items-stretch overflow-hidden">
|
||||
<main className="flex-1 overflow-y-auto">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
<div className="px-4 sm:px-6 lg:px-8">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -16,6 +16,17 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
else if (req.method === "POST") {
|
||||
const schema = req.body;
|
||||
|
||||
// find form
|
||||
const form = await prisma.form.findUnique({
|
||||
where: {
|
||||
id: formId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!form) {
|
||||
return res.status(404).json({ error: `Form with id "${formId}" not found` });
|
||||
}
|
||||
|
||||
// create form in db
|
||||
await prisma.form.update({
|
||||
where: {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import IntegrationsPage from "@/components/integrations/IntegrationsPage";
|
||||
import LayoutApp from "@/components/layout/LayoutApp";
|
||||
import LayoutWrapperOrganisation from "@/components/layout/LayoutWrapperOrganisation";
|
||||
|
||||
export default function IntegrationPage({}) {
|
||||
return (
|
||||
<LayoutApp>
|
||||
<LayoutWrapperOrganisation>
|
||||
<IntegrationsPage />
|
||||
</LayoutWrapperOrganisation>
|
||||
</LayoutApp>
|
||||
);
|
||||
}
|
||||
@@ -148,6 +148,7 @@ export function EnginePage({
|
||||
{element.name ? (
|
||||
<ElementComponent
|
||||
element={element}
|
||||
page={page}
|
||||
control={control}
|
||||
register={register}
|
||||
onSubmit={() => handleSubmitElement()}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
export function DashboardIcon(props: any) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
|
||||
<defs />
|
||||
<rect x={0.5} y={16.5} width={10} height={7} rx={1} fill="#00e6ca" />
|
||||
<path d="M5.5,16.5h-4a1,1,0,0,0-1,1v5a1,1,0,0,0,1,1h4Z" fill="#c4f0eb" />
|
||||
<rect
|
||||
x={13.5}
|
||||
y={10.5}
|
||||
width={10}
|
||||
height={13}
|
||||
rx={1}
|
||||
transform="translate(37 34) rotate(180)"
|
||||
fill="#00e6ca"
|
||||
/>
|
||||
<path d="M18.5,10.5h-4a1,1,0,0,0-1,1v11a1,1,0,0,0,1,1h4Z" fill="#c4f0eb" />
|
||||
<rect
|
||||
x={13.5}
|
||||
y={0.5}
|
||||
width={10}
|
||||
height={7}
|
||||
rx={1}
|
||||
transform="translate(37 8) rotate(180)"
|
||||
fill="#00e6ca"
|
||||
/>
|
||||
<path d="M18.5.5h-4a1,1,0,0,0-1,1v5a1,1,0,0,0,1,1h4Z" fill="#c4f0eb" />
|
||||
<rect x={0.5} y={0.5} width={10} height={13} rx={1} fill="#00e6ca" />
|
||||
<path d="M5.5.5h-4a1,1,0,0,0-1,1v11a1,1,0,0,0,1,1h4Z" fill="#c4f0eb" />
|
||||
<rect
|
||||
x={0.5}
|
||||
y={16.5}
|
||||
width={10}
|
||||
height={7}
|
||||
rx={1}
|
||||
fill="none"
|
||||
stroke="#0f172a"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<rect
|
||||
x={13.5}
|
||||
y={10.5}
|
||||
width={10}
|
||||
height={13}
|
||||
rx={1}
|
||||
transform="translate(37 34) rotate(180)"
|
||||
fill="none"
|
||||
stroke="#0f172a"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<rect
|
||||
x={13.5}
|
||||
y={0.5}
|
||||
width={10}
|
||||
height={7}
|
||||
rx={1}
|
||||
transform="translate(37 8) rotate(180)"
|
||||
fill="none"
|
||||
stroke="#0f172a"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<rect
|
||||
x={0.5}
|
||||
y={0.5}
|
||||
width={10}
|
||||
height={13}
|
||||
rx={1}
|
||||
fill="none"
|
||||
stroke="#0f172a"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -37,3 +37,4 @@ export * from "./icons/VeryDisappointedIcon";
|
||||
export * from "./icons/SomewhatDisappointedIcon";
|
||||
export * from "./icons/NotDisappointedIcon";
|
||||
export * from "./icons/ArchiveIcon";
|
||||
export * from "./icons/DashboardIcon";
|
||||
|
||||