Skip to content

Commit 15a5d25

Browse files
Trigger page revamped
1 parent 4539f6c commit 15a5d25

File tree

13 files changed

+149
-121
lines changed

13 files changed

+149
-121
lines changed

src/actions/user/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import { stripe } from "@/lib/stripe";
1010
export const onCurrentUser = async () => {
1111
try {
1212
const user = await currentUser();
13-
if (!user) return redirect("/sign-in");
13+
if (!user) {
14+
throw new Error("No authenticated user found");
15+
}
1416
return user;
1517
} catch (error) {
1618
console.error("Authentication error:", error);
17-
return redirect("/sign-in");
19+
redirect("/sign-in");
1820
}
1921
};
2022

src/app/(protected)/dashboard/[slug]/automations/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const Page = async ({ params }: Props) => {
4242
<div className="w-full lg:w-10/12 xl:w-6/12 p-5 rounded-xl flex flex-col bg-[#F6F7F9] gap-y-3">
4343
<div className="flex gap-x-2">
4444
<Warning />
45-
When...
45+
<strong>When...</strong>
4646
</div>
4747
<Trigger id={params.id} />
4848
</div>

src/components/global/activate-automation-button/index.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,23 @@ const ActivateAutomationButton = ({ id }: Props) => {
2525
}
2626
}, [data?.data?.active, isPending]);
2727

28+
const isActivationAllowed = React.useMemo(() => {
29+
if (!data?.data) return false;
30+
if (!data.data.trigger || data.data.trigger.length === 0) return false;
31+
if (!data.data.keywords || data.data.keywords.length === 0) return false;
32+
return true;
33+
}, [data?.data]);
34+
2835
const handleStateChange = (checked: boolean) => {
36+
if (!isActivationAllowed && checked) return;
2937
setOptimisticState(checked);
3038
mutate({ state: checked });
3139
};
3240

3341
return (
3442
<div className="flex items-center space-x-2">
3543
<Switch
36-
disabled={isPending}
44+
disabled={isPending || !isActivationAllowed}
3745
checked={optimisticState}
3846
onCheckedChange={handleStateChange}
3947
className="data-[state=checked]:hover:bg-green-600 data-[state=checked]:bg-green-500 data-[state=unchecked]:bg-slate-400 h-6 w-11 mx-4 [&>span]:data-[state=checked]:translate-x-6 [&>span]:bg-white transition-colors duration-200"

src/components/global/automations/post/node.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ const PostNode = ({ id }: Props) => {
1717
data.data.posts.length > 0 && (
1818
<div className="w-10/12 lg:w-8/12 relative xl:w-4/12 p-5 rounded-xl flex flex-col bg-[#1D1D1D] gap-y-3">
1919
<div className="absolute h-20 left-1/2 bottom-full flex flex-col items-center z-50">
20-
<span className="h-[9px] w-[9px] bg-connector/10 rounded-full" />
20+
<span className="h-[9px] w-[9px] bg-black rounded-full" />
2121
<Separator
2222
orientation="vertical"
23-
className="bottom-full flex-1 border-[1px] border-connector/10"
23+
className="bottom-full flex-1 border-[1px] border-dashed border-black"
2424
/>
25-
<span className="h-[9px] w-[9px] bg-connector/10 rounded-full" />
25+
<span className="h-[9px] w-[9px] bg-black rounded-full" />
2626
</div>
2727
<div className="flex gap-x-2">
2828
<Warning />

src/components/global/automations/then/node.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ const ThenNode = ({ id }: Props) => {
1616
return !data?.data?.listener ? (
1717
<></>
1818
) : (
19-
<div className="w-full lg:w-10/12 relative xl:w-6/12 p-5 rounded-xl flex flex-col bg-[#1D1D1D] gap-y-3">
19+
<div className="w-full lg:w-10/12 relative xl:w-6/12 p-5 rounded-xl flex flex-col bg-[#F6F7F9] gap-y-3">
2020
<div className="absolute h-20 left-1/2 bottom-full flex flex-col items-center z-50">
21-
<span className="h-[9px] w-[9px] bg-connector/10 rounded-full" />
21+
<span className="h-[9px] w-[9px] bg-black rounded-full" />
2222
<Separator
2323
orientation="vertical"
24-
className="bottom-full flex-1 border-[1px] border-connector/10"
24+
className="bottom-full flex-1 border-[1px] border-dashed border-black"
2525
/>
26-
<span className="h-[9px] w-[9px] bg-connector/10 rounded-full" />
26+
<span className="h-[9px] w-[9px] bg-black rounded-full" />
2727
</div>
2828
<div className="flex gap-x-2">
2929
<Warning />
30-
Then...
30+
<strong>Then...</strong>
3131
</div>
32-
<div className="bg-background-80 p-3 rounded-xl flex flex-col gap-y-2">
32+
<div className="bg-[#ededef] hover:bg-[#dfdfdf] p-3 rounded-xl flex flex-col gap-y-2">
3333
<div className="flex gap-x-2 items-center">
3434
{data.data.listener.listener === "MESSAGE" ? (
3535
<PlaneBlue />

src/components/global/automations/then/then-action.tsx

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useListener } from "@/hooks/use-automations";
22
import React from "react";
33
import TriggerButton from "../trigger-button";
44
import { AUTOMATION_LISTENERS } from "@/constants/automation";
5-
import { SubscriptionPlan } from "../../subscription-plan";
65
import { cn } from "@/lib/utils";
76
import { Textarea } from "@/components/ui/textarea";
87
import { Input } from "@/components/ui/input";
@@ -23,72 +22,54 @@ const ThenAction = ({ id }: Props) => {
2322
isPending,
2423
} = useListener(id);
2524
const { data } = useQueryUser();
26-
let isPro = data?.data?.subscription?.plan === "PRO";
25+
const isPro = data?.data?.subscription?.plan === "PRO";
2726

2827
return (
2928
<TriggerButton label="Then">
30-
<div className="flex flex-col gap-y-2 ">
31-
{AUTOMATION_LISTENERS.map((listener) =>
32-
listener.type === "SMARTAI" ? (
33-
<button
34-
onClick={() => onSetListener(listener.type)}
35-
key={listener.id}
36-
disabled={!isPro}
37-
className={cn(
38-
"text-left",
39-
Listener === listener.type
40-
? "bg-gradient-to-br from-[#3352CC] to-[#1C2D70]"
41-
: "bg-background-80",
42-
"p-3 rounded-xl flex flex-col gap-y-2 cursor-pointer hover:opacity-80 transition duration-100",
43-
!isPro && "cursor-not-allowed"
44-
)}
45-
>
46-
<div className="flex gap-x-2 items-center">
47-
{listener.icon}
48-
<p>{listener.label}</p>
49-
</div>
50-
{isPro ? (
51-
<p className="text-sm">{listener.description}</p>
52-
) : (
53-
<p>(Upgrade to use this feature).</p>
54-
)}
55-
</button>
56-
) : (
57-
<button
58-
onClick={() => onSetListener(listener.type)}
59-
key={listener.id}
60-
className={cn(
61-
"text-left",
62-
Listener === listener.type
63-
? "bg-gradient-to-br from-[#3352CC] to-[#1C2D70]"
64-
: "bg-background-80",
65-
"p-3 rounded-xl flex flex-col gap-y-2 cursor-pointer hover:opacity-80 transition duration-100"
66-
)}
67-
>
68-
<div className="flex gap-x-2 items-center">
69-
{listener.icon}
70-
<p>{listener.label}</p>
71-
</div>
72-
<p className="text-sm">{listener.description}</p>
73-
</button>
74-
)
75-
)}
76-
<form onSubmit={onFormSubmit} className="flex flex-col gap-y-2">
29+
<div className="flex flex-col gap-y-3">
30+
{AUTOMATION_LISTENERS.map((listener) => (
31+
<button
32+
onClick={() => onSetListener(listener.type)}
33+
key={listener.id}
34+
disabled={listener.type === "SMARTAI" && !isPro}
35+
className={cn(
36+
"text-left p-4 rounded-xl flex flex-col gap-y-2 transition-all duration-200",
37+
Listener === listener.type
38+
? "bg-primary text-white shadow-md"
39+
: "bg-gray-50 hover:bg-gray-100",
40+
listener.type === "SMARTAI" && !isPro && "opacity-50 cursor-not-allowed"
41+
)}
42+
>
43+
<div className="flex gap-x-2 items-center">
44+
{listener.icon}
45+
<p className="font-medium">{listener.label}</p>
46+
</div>
47+
<p className="text-sm opacity-90">
48+
{listener.type === "SMARTAI" && !isPro
49+
? "(Upgrade to use this feature)"
50+
: listener.description}
51+
</p>
52+
</button>
53+
))}
54+
<form onSubmit={onFormSubmit} className="flex flex-col gap-y-3 mt-2">
7755
<Textarea
7856
placeholder={
7957
Listener === "SMARTAI"
80-
? "Add a prompt that your smart ai can use..."
81-
: "Add a message you want send to your customers"
58+
? "Add a prompt that your smart AI can use..."
59+
: "Add a message you want to send to your customers"
8260
}
8361
{...register("prompt")}
84-
className="bg-background-80 outline-none border-none ring-0 focus:ring-0"
62+
className="min-h-[100px] bg-gray-50 border-gray-200 focus:border-primary focus:ring-primary"
8563
/>
8664
<Input
8765
{...register("reply")}
8866
placeholder="Add a reply for comments (Optional)"
89-
className="bg-background-80 outline-none border-none ring-0 focus:ring-0"
67+
className="bg-gray-50 border-gray-200 focus:border-primary focus:ring-primary"
9068
/>
91-
<Button className="bg-gradient-to-br w-full from-[#3352CC] font-medium text-white to-[#1C2D70]">
69+
<Button
70+
className="w-full bg-primary hover:bg-primary/90 text-white font-medium"
71+
disabled={isPending}
72+
>
9273
<Loader state={isPending}>Add listener</Loader>
9374
</Button>
9475
</form>

src/components/global/automations/trigger-button/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react'
22
import PopOver from '../../popover'
3-
import { Divide } from 'lucide-react'
43
import { BlueAddIcon } from '@/icons'
54

65
type Props = {
@@ -13,9 +12,9 @@ const TriggerButton = ({ children, label }: Props) => {
1312
<PopOver
1413
className="w-[400px]"
1514
trigger={
16-
<div className="border-2 border-dashed w-full border-[#3352cc] hover:opacity-80 cursor-pointer transition duration-100 rounded-xl flex gap-x-2 justify-center items-center p-5 mt-4">
15+
<div className="border-2 border-dashed w-full border-primary/60 hover:border-primary hover:bg-primary/5 cursor-pointer transition-all duration-200 rounded-xl flex gap-x-2 justify-center items-center p-5 mt-4">
1716
<BlueAddIcon />
18-
<p className="text-[#768BDD] font-bold">{label}</p>
17+
<p className="text-primary font-semibold">{label}</p>
1918
</div>
2019
}
2120
>

src/components/global/automations/trigger/active.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const ActiveTrigger = ({ keywords, type, automationId }: Props) => {
1717
console.log(type);
1818

1919
return (
20-
<div className="bg-background-80 p-3 rounded-xl w-full">
20+
<div className="bg-[#ededef] hover:bg-[#dfdfdf] transition-colors duration-200 p-3 rounded-xl w-full">
2121
<div className="flex gap-x-2 items-center">
2222
{type === "COMMENT" ? (
2323
<InstagramBlue />

src/components/global/automations/trigger/index.tsx

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"use client";
2+
3+
// Import necessary dependencies and components
24
import { useQueryAutomation } from "@/hooks/user-queries";
35
import React from "react";
46
import ActiveTrigger from "./active";
@@ -12,82 +14,109 @@ import Keywords from "./keywords";
1214
import { Button } from "@/components/ui/button";
1315
import Loader from "../../loader";
1416

17+
// Define the component's props interface
1518
type Props = {
16-
id: string;
19+
id: string; // Automation ID
1720
};
1821

22+
// Trigger component handles the automation trigger selection and display
1923
const Trigger = ({ id }: Props) => {
2024
const { types, onSetTrigger, onSaveTrigger, isPending } = useTriggers(id);
2125
const { data } = useQueryAutomation(id);
26+
const [hasKeywords, setHasKeywords] = React.useState(false);
27+
const [keywordInputValue, setKeywordInputValue] = React.useState("");
28+
29+
React.useEffect(() => {
30+
const handleKeywordChange = (event: CustomEvent<{ keyword: string; hasKeywords: boolean }>) => {
31+
setKeywordInputValue(event.detail.keyword);
32+
setHasKeywords(event.detail.hasKeywords);
33+
};
34+
35+
window.addEventListener('keywordChange', handleKeywordChange as EventListener);
36+
return () => {
37+
window.removeEventListener('keywordChange', handleKeywordChange as EventListener);
38+
};
39+
}, []);
40+
41+
// If triggers exist, display the active trigger configuration
2242
if (data?.data && data?.data?.trigger.length > 0) {
2343
return (
2444
<div className="flex flex-col gap-y-6 items-center">
45+
{/* Display the first trigger type */}
2546
<ActiveTrigger
2647
type={data.data.trigger[0].type as "DM" | "COMMENT" | "KEYWORDS"}
2748
automationId={id}
2849
/>
2950

51+
{/* If there's a second trigger, display it with an 'or' separator */}
3052
{data?.data?.trigger.length > 1 && (
3153
<>
3254
<div className="relative w-6/12 my-4">
33-
<p className="absolute transform px-2 -translate-y-1/2 top-1/2 -translate-x-1/2 left-1/2">
55+
<p className="absolute transform px-2 -translate-y-1/2 top-1/2 -translate-x-1/2 left-1/2 text-gray-500 bg-white">
3456
or
3557
</p>
36-
<Separator
37-
orientation="horizontal"
38-
className="border-muted border-[1px]"
39-
/>
58+
<Separator className="border-gray-200" />
4059
</div>
4160
<ActiveTrigger
4261
type={data.data.trigger[1].type as "DM" | "COMMENT" | "KEYWORDS"}
4362
automationId={id}
4463
/>
4564
</>
4665
)}
66+
67+
{/* Display keywords section with separator */}
4768
<div className="relative w-6/12 my-4">
48-
<p className="absolute transform px-2 -translate-y-1/2 top-1/2 -translate-x-1/2 left-1/2">
69+
<p className="absolute transform px-2 -translate-y-1/2 top-1/2 -translate-x-1/2 left-1/2 text-gray-500 bg-white">
4970
with key words
5071
</p>
51-
<Separator
52-
orientation="horizontal"
53-
className="border-muted border-[1px]"
54-
/>
72+
<Separator className="border-gray-200" />
5573
</div>
74+
75+
{/* Display keywords trigger */}
5676
<ActiveTrigger
5777
type={"KEYWORDS"}
5878
keywords={data.data.keywords}
5979
automationId={id}
6080
/>
81+
82+
{/* Show ThenAction component if no listener is configured */}
6183
{!data.data.listener && <ThenAction id={id} />}
6284
</div>
6385
);
6486
}
87+
88+
// If no triggers exist, display the trigger selection interface
6589
return (
6690
<TriggerButton label="Add Trigger">
67-
<div className="flex flex-col gap-y-2">
91+
<div className="flex flex-col gap-y-3">
92+
{/* Map through available trigger types */}
6893
{AUTOMATION_TRIGGERS.map((trigger) => (
6994
<div
7095
key={trigger.id}
7196
onClick={() => onSetTrigger(trigger.type)}
7297
className={cn(
73-
"hover:opacity-80 text-white rounded-xl flex cursor-pointer flex-col p-3 gap-y-2",
98+
"p-4 rounded-xl flex cursor-pointer flex-col gap-y-2 transition-all duration-200 border",
7499
!types?.find((t) => t === trigger.type)
75-
? "bg-background-80"
76-
: "bg-gradient-to-br from-[#3352CC] font-medium to-[#1C2D70]"
100+
? "border-black hover:bg-gray-50"
101+
: "border-green-500 border-2"
77102
)}
78103
>
79104
<div className="flex gap-x-2 items-center">
80105
{trigger.icon}
81-
<p className="font-bold">{trigger.label}</p>
106+
<p className="font-medium">{trigger.label}</p>
82107
</div>
83-
<p className="text-sm font-light">{trigger.description}</p>
108+
<p className="text-sm opacity-90">{trigger.description}</p>
84109
</div>
85110
))}
111+
112+
{/* Keywords input component */}
86113
<Keywords id={id} />
114+
115+
{/* Save trigger button with loading state */}
87116
<Button
88117
onClick={onSaveTrigger}
89-
disabled={types?.length === 0}
90-
className="bg-gradient-to-br from-[#3352CC] font-medium text-white to-[#1C2D70]"
118+
disabled={types?.length === 0 || (!hasKeywords && !keywordInputValue)}
119+
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium"
91120
>
92121
<Loader state={isPending}>Create Trigger</Loader>
93122
</Button>

0 commit comments

Comments
 (0)