mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-16 19:51:40 -05:00
add functionality to make events editable after creating them (#245)
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui";
|
||||
import { ErrorComponent } from "@formbricks/ui";
|
||||
import { Input } from "@formbricks/ui";
|
||||
import { Label } from "@formbricks/ui";
|
||||
import { RadioGroup, RadioGroupItem } from "@formbricks/ui";
|
||||
import { useEventClass, useEventClasses } from "@/lib/eventClasses/eventClasses";
|
||||
import { useEventClassMutation } from "@/lib/eventClasses/mutateEventClasses";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import type { NoCodeConfig, Event } from "@formbricks/types/events";
|
||||
import { testURLmatch } from "./testURLmatch";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import clsx from "clsx";
|
||||
|
||||
interface EventSettingsTabProps {
|
||||
environmentId: string;
|
||||
@@ -17,8 +22,12 @@ interface EventSettingsTabProps {
|
||||
export default function EventSettingsTab({ environmentId, eventClassId, setOpen }: EventSettingsTabProps) {
|
||||
const { eventClass, isLoadingEventClass, isErrorEventClass } = useEventClass(environmentId, eventClassId);
|
||||
|
||||
const { register, handleSubmit } = useForm({
|
||||
defaultValues: { name: eventClass.name, description: eventClass.description },
|
||||
const { register, handleSubmit, control, watch } = useForm({
|
||||
defaultValues: {
|
||||
name: eventClass.name,
|
||||
description: eventClass.description,
|
||||
noCodeConfig: eventClass.noCodeConfig,
|
||||
},
|
||||
});
|
||||
const { triggerEventClassMutate, isMutatingEventClass } = useEventClassMutation(
|
||||
environmentId,
|
||||
@@ -28,11 +37,41 @@ export default function EventSettingsTab({ environmentId, eventClassId, setOpen
|
||||
const { mutateEventClasses } = useEventClasses(environmentId);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
await triggerEventClassMutate(data);
|
||||
const filteredNoCodeConfig = filterNoCodeConfig(data.noCodeConfig as NoCodeConfig);
|
||||
|
||||
const updatedData: Event = {
|
||||
...data,
|
||||
noCodeConfig: filteredNoCodeConfig,
|
||||
type: "noCode",
|
||||
} as Event;
|
||||
|
||||
await triggerEventClassMutate(updatedData);
|
||||
mutateEventClasses();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const filterNoCodeConfig = (noCodeConfig: NoCodeConfig): NoCodeConfig => {
|
||||
const { type } = noCodeConfig;
|
||||
return {
|
||||
type,
|
||||
[type]: noCodeConfig[type],
|
||||
};
|
||||
};
|
||||
|
||||
const [testUrl, setTestUrl] = useState("");
|
||||
const [isMatch, setIsMatch] = useState("");
|
||||
|
||||
const handleMatchClick = () => {
|
||||
const match = testURLmatch(
|
||||
testUrl,
|
||||
watch("noCodeConfig.[pageUrl].value"),
|
||||
watch("noCodeConfig.[pageUrl].rule")
|
||||
);
|
||||
setIsMatch(match);
|
||||
if (match === "yes") toast.success("Your survey would be shown on this URL.");
|
||||
if (match === "no") toast.error("Your survey would not be shown.");
|
||||
};
|
||||
|
||||
if (isLoadingEventClass) return <LoadingSpinner />;
|
||||
if (isErrorEventClass) return <ErrorComponent />;
|
||||
|
||||
@@ -61,37 +100,152 @@ export default function EventSettingsTab({ environmentId, eventClassId, setOpen
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="my-6">
|
||||
<div className="">
|
||||
<Label>Action Type</Label>
|
||||
{eventClass.type === "code" ? (
|
||||
<p className="text-sm text-slate-600">
|
||||
This is a code action. Please make changes in your code base.
|
||||
</p>
|
||||
) : eventClass.type === "noCode" ? (
|
||||
<div>
|
||||
<Label className="mb-3 mt-1 block font-normal text-slate-500">
|
||||
You cannot change the action type. Please add a new action instead.
|
||||
</Label>
|
||||
<RadioGroup defaultValue={eventClass.noCodeConfig.type} className="flex">
|
||||
<div className="flex items-center space-x-2 rounded-lg bg-slate-50 p-3">
|
||||
<RadioGroupItem disabled value="pageUrl" id="pageUrl" className="bg-slate-50" />
|
||||
<Label htmlFor="pageUrl" className="flex items-center text-slate-400">
|
||||
Page URL
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 rounded-lg bg-slate-50 p-3">
|
||||
<RadioGroupItem disabled value="innerHtml" id="innerHtml" className="bg-slate-50" />
|
||||
<Label htmlFor="innerHtml" className="flex items-center text-slate-400">
|
||||
Inner Text
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 rounded-lg bg-slate-50 p-3">
|
||||
<RadioGroupItem disabled value="cssSelector" id="cssSelector" className="bg-slate-50" />
|
||||
<Label htmlFor="cssSelector" className="flex items-center text-slate-400">
|
||||
CSS Selector
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<div className="flex justify-between rounded-lg">
|
||||
<div className="w-full space-y-4">
|
||||
<Controller
|
||||
name="noCodeConfig.type"
|
||||
defaultValue={"pageUrl"}
|
||||
control={control}
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<RadioGroup className="flex" onValueChange={onChange} onBlur={onBlur} value={value}>
|
||||
<div className="flex items-center space-x-2 rounded-lg border border-slate-200 p-3">
|
||||
<RadioGroupItem value="pageUrl" id="pageUrl" className="bg-slate-50" />
|
||||
<Label htmlFor="pageUrl" className="flex cursor-pointer items-center">
|
||||
Page URL
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 rounded-lg border border-slate-200 p-3">
|
||||
<RadioGroupItem value="innerHtml" id="innerHtml" className="bg-slate-50" />
|
||||
<Label htmlFor="innerHtml" className="flex cursor-pointer items-center">
|
||||
Inner Text
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 rounded-lg border border-slate-200 p-3">
|
||||
<RadioGroupItem value="cssSelector" id="cssSelector" className="bg-slate-50" />
|
||||
<Label htmlFor="cssSelector" className="flex cursor-pointer items-center">
|
||||
CSS Selector
|
||||
</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
)}
|
||||
/>
|
||||
{(watch("noCodeConfig.type") === "pageUrl" || !watch("noCodeConfig.type")) && (
|
||||
<>
|
||||
<div className="grid w-full grid-cols-3 gap-x-8">
|
||||
<div className="col-span-1">
|
||||
<Label>URL</Label>
|
||||
<Controller
|
||||
name="noCodeConfig.pageUrl.rule"
|
||||
defaultValue={"exactMatch"}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
/* onValueChange={(e) => {
|
||||
setMatchType(e as MatchType);
|
||||
setIsMatch("default");
|
||||
}} */
|
||||
{...field}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select match type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="exactMatch">Exactly matches</SelectItem>
|
||||
<SelectItem value="contains">Contains</SelectItem>
|
||||
<SelectItem value="startsWith">Starts with</SelectItem>
|
||||
<SelectItem value="endsWith">Ends with</SelectItem>
|
||||
<SelectItem value="notMatch">Does not exactly match</SelectItem>
|
||||
<SelectItem value="notContains">Does not contain</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 flex w-full items-end">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="e.g. https://app.formbricks.com/dashboard"
|
||||
{...register("noCodeConfig.[pageUrl].value", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="mb-2 block">Test Your URL</Label>
|
||||
<div className=" rounded bg-slate-50 p-4">
|
||||
<Label className="font-normal text-slate-500">
|
||||
Enter a URL to see if it matches your event URL
|
||||
</Label>
|
||||
<div className="mt-1 flex">
|
||||
<Input
|
||||
type="text"
|
||||
value={testUrl}
|
||||
onChange={(e) => {
|
||||
setTestUrl(e.target.value);
|
||||
setIsMatch("default");
|
||||
}}
|
||||
className={clsx(
|
||||
isMatch === "yes"
|
||||
? "border-green-500 bg-green-50"
|
||||
: isMatch === "no"
|
||||
? "border-red-200 bg-red-50"
|
||||
: isMatch === "default"
|
||||
? "border-slate-200 bg-white"
|
||||
: null
|
||||
)}
|
||||
placeholder="Paste the URL you want the event to trigger on"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className="ml-2 whitespace-nowrap"
|
||||
onClick={() => {
|
||||
handleMatchClick();
|
||||
}}>
|
||||
Test Match
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{watch("noCodeConfig.type") === "innerHtml" && (
|
||||
<div className="grid w-full grid-cols-3 gap-x-8">
|
||||
<div className="col-span-1">
|
||||
<Label>Inner Text</Label>
|
||||
</div>
|
||||
<div className="col-span-3 flex w-full items-end">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="e.g. 'Install App'"
|
||||
{...register("noCodeConfig.innerHtml.value", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{watch("noCodeConfig.type") === "cssSelector" && (
|
||||
<div className="grid w-full grid-cols-3 gap-x-8">
|
||||
<div className="col-span-1">
|
||||
<Label>CSS Tag</Label>
|
||||
</div>
|
||||
<div className="col-span-3 flex w-full items-end">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="e.g. #install-button"
|
||||
{...register("noCodeConfig.cssSelector.value", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : eventClass.type === "automatic" ? (
|
||||
<p className="text-sm text-slate-600">
|
||||
@@ -99,7 +253,7 @@ export default function EventSettingsTab({ environmentId, eventClassId, setOpen
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-slate-200 pt-6">
|
||||
<div className="flex justify-between border-t border-slate-200 py-6">
|
||||
<div>
|
||||
<Button variant="secondary" href="https://formbricks.com/docs" target="_blank">
|
||||
Read Docs
|
||||
|
||||
Reference in New Issue
Block a user