Trigger changes (#232)

* new external trigger page is shown if no external triggers are setup yet

* make one time trigger default to new trigger

* default to new trigger in recurring triggers

* remove back button if no triggers are setup yet
This commit is contained in:
arkml 2025-09-07 19:15:16 +05:30 committed by GitHub
parent d899966107
commit f772088e90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 137 additions and 29 deletions

View file

@ -25,7 +25,15 @@ const commonCronExamples = [
{ label: "Monthly on the 1st at midnight", value: "0 0 1 * *" },
];
export function CreateRecurringJobRuleForm({ projectId }: { projectId: string }) {
export function CreateRecurringJobRuleForm({
projectId,
onBack,
hasExistingTriggers = true
}: {
projectId: string;
onBack?: () => void;
hasExistingTriggers?: boolean;
}) {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [messages, setMessages] = useState<FormMessage[]>([
@ -89,7 +97,11 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string })
input: { messages: convertedMessages },
cron: cronExpression,
});
router.push(`/projects/${projectId}/manage-triggers?tab=recurring`);
if (onBack) {
onBack();
} else {
router.push(`/projects/${projectId}/manage-triggers?tab=recurring`);
}
} catch (error) {
console.error("Failed to create recurring job rule:", error);
alert("Failed to create recurring job rule");
@ -102,11 +114,23 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string })
<Panel
title={
<div className="flex items-center gap-3">
<Link href={`/projects/${projectId}/manage-triggers?tab=recurring`}>
<Button variant="secondary" size="sm" startContent={<ArrowLeftIcon className="w-4 h-4" />} className="whitespace-nowrap">
{hasExistingTriggers && onBack ? (
<Button
variant="secondary"
size="sm"
startContent={<ArrowLeftIcon className="w-4 h-4" />}
className="whitespace-nowrap"
onClick={onBack}
>
Back
</Button>
</Link>
) : hasExistingTriggers ? (
<Link href={`/projects/${projectId}/manage-triggers?tab=recurring`}>
<Button variant="secondary" size="sm" startContent={<ArrowLeftIcon className="w-4 h-4" />} className="whitespace-nowrap">
Back
</Button>
</Link>
) : null}
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
CREATE RECURRING JOB RULE
</div>

View file

@ -8,7 +8,8 @@ import { listRecurringJobRules, deleteRecurringJobRule } from "@/app/actions/rec
import { z } from "zod";
import { ListedRecurringRuleItem } from "@/src/application/repositories/recurring-job-rules.repository.interface";
import { isToday, isThisWeek, isThisMonth } from "@/lib/utils/date";
import { PlusIcon, Trash2 } from "lucide-react";
import { PlusIcon, Trash2, ArrowLeftIcon } from "lucide-react";
import { CreateRecurringJobRuleForm } from "./create-recurring-job-rule-form";
type ListedItem = z.infer<typeof ListedRecurringRuleItem>;
@ -19,6 +20,7 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
const [loadingMore, setLoadingMore] = useState<boolean>(false);
const [hasMore, setHasMore] = useState<boolean>(false);
const [deletingRule, setDeletingRule] = useState<string | null>(null);
const [showCreateForm, setShowCreateForm] = useState<boolean>(false);
const fetchPage = useCallback(async (cursorArg?: string | null) => {
const res = await listRecurringJobRules({ projectId, cursor: cursorArg ?? undefined, limit: 20 });
@ -39,6 +41,12 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
return () => { ignore = true; };
}, [fetchPage]);
useEffect(() => {
if (!loading && items.length === 0 && !showCreateForm) {
setShowCreateForm(true);
}
}, [loading, items.length, showCreateForm]);
const loadMore = useCallback(async () => {
if (!cursor) return;
setLoadingMore(true);
@ -49,6 +57,24 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
setLoadingMore(false);
}, [cursor, fetchPage]);
const handleCreateNew = () => {
setShowCreateForm(true);
};
const handleBackToList = () => {
setShowCreateForm(false);
// Reload the list in case new triggers were created
const reload = async () => {
setLoading(true);
const res = await fetchPage(null);
setItems(res.items);
setCursor(res.nextCursor);
setHasMore(Boolean(res.nextCursor));
setLoading(false);
};
reload();
};
const handleDeleteRule = async (ruleId: string) => {
if (!window.confirm('Are you sure you want to delete this recurring trigger?')) {
return;
@ -125,6 +151,10 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
return cron;
};
if (showCreateForm) {
return <CreateRecurringJobRuleForm projectId={projectId} onBack={handleBackToList} hasExistingTriggers={items.length > 0} />;
}
return (
<Panel
title={
@ -134,11 +164,14 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
}
rightActions={
<div className="flex items-center gap-3">
<Link href={`/projects/${projectId}/manage-triggers/recurring/new`}>
<Button size="sm" className="whitespace-nowrap" startContent={<PlusIcon className="w-4 h-4" />}>
New Recurring Trigger
</Button>
</Link>
<Button
size="sm"
className="whitespace-nowrap"
startContent={<PlusIcon className="w-4 h-4" />}
onClick={handleCreateNew}
>
New Recurring Trigger
</Button>
</div>
}
>

View file

@ -205,6 +205,12 @@ export function TriggersTab({ projectId }: { projectId: string }) {
}
}, [showCreateFlow, loadTriggers]);
useEffect(() => {
if (!loading && !error && triggers.length === 0 && !showCreateFlow) {
setShowCreateFlow(true);
}
}, [loading, error, triggers.length, showCreateFlow]);
useEffect(() => {
// No-op: trigger names are now derived from slug locally
}, [triggers]);
@ -457,14 +463,16 @@ export function TriggersTab({ projectId }: { projectId: string }) {
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
Select a Toolkit to Create Trigger
</h3>
<Button
variant="secondary"
onClick={handleBackToList}
startContent={<ArrowLeftIcon className="w-4 h-4" />}
className="whitespace-nowrap"
>
Back to Triggers
</Button>
{triggers.length > 0 && (
<Button
variant="secondary"
onClick={handleBackToList}
startContent={<ArrowLeftIcon className="w-4 h-4" />}
className="whitespace-nowrap"
>
Back to Triggers
</Button>
)}
</div>
<SelectComposioToolkit

View file

@ -16,7 +16,7 @@ type FormMessage = {
content: string;
};
export function CreateScheduledJobRuleForm({ projectId }: { projectId: string }) {
export function CreateScheduledJobRuleForm({ projectId, onBack, hasExistingTriggers = true }: { projectId: string; onBack?: () => void; hasExistingTriggers?: boolean }) {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [messages, setMessages] = useState<FormMessage[]>([
@ -90,7 +90,11 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string })
input: { messages: convertedMessages },
scheduledTime: scheduledTimeString,
});
router.push(`/projects/${projectId}/manage-triggers?tab=scheduled`);
if (onBack) {
onBack();
} else {
router.push(`/projects/${projectId}/manage-triggers?tab=scheduled`);
}
} catch (error) {
console.error("Failed to create scheduled job rule:", error);
alert("Failed to create scheduled job rule");
@ -105,11 +109,17 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string })
<Panel
title={
<div className="flex items-center gap-3">
<Link href={`/projects/${projectId}/manage-triggers?tab=scheduled`}>
<Button variant="secondary" size="sm" startContent={<ArrowLeftIcon className="w-4 h-4" />} className="whitespace-nowrap">
{hasExistingTriggers && onBack ? (
<Button variant="secondary" size="sm" startContent={<ArrowLeftIcon className="w-4 h-4" />} className="whitespace-nowrap" onClick={onBack}>
Back
</Button>
</Link>
) : hasExistingTriggers ? (
<Link href={`/projects/${projectId}/manage-triggers?tab=scheduled`}>
<Button variant="secondary" size="sm" startContent={<ArrowLeftIcon className="w-4 h-4" />} className="whitespace-nowrap">
Back
</Button>
</Link>
) : null}
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
CREATE SCHEDULED JOB RULE
</div>

View file

@ -9,6 +9,7 @@ import { z } from "zod";
import { ListedRuleItem } from "@/src/application/repositories/scheduled-job-rules.repository.interface";
import { isToday, isThisWeek, isThisMonth } from "@/lib/utils/date";
import { PlusIcon, Trash2 } from "lucide-react";
import { CreateScheduledJobRuleForm } from "./create-scheduled-job-rule-form";
type ListedItem = z.infer<typeof ListedRuleItem>;
@ -19,6 +20,7 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
const [loadingMore, setLoadingMore] = useState<boolean>(false);
const [hasMore, setHasMore] = useState<boolean>(false);
const [deletingRule, setDeletingRule] = useState<string | null>(null);
const [showCreateFlow, setShowCreateFlow] = useState(false);
const fetchPage = useCallback(async (cursorArg?: string | null) => {
const res = await listScheduledJobRules({ projectId, cursor: cursorArg ?? undefined, limit: 20 });
@ -39,6 +41,12 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
return () => { ignore = true; };
}, [fetchPage]);
useEffect(() => {
if (!loading && items.length === 0 && !showCreateFlow) {
setShowCreateFlow(true);
}
}, [loading, items.length, showCreateFlow]);
const loadMore = useCallback(async () => {
if (!cursor) return;
setLoadingMore(true);
@ -67,6 +75,29 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
}
};
const handleCreateNew = () => {
setShowCreateFlow(true);
};
const handleBackToList = () => {
setShowCreateFlow(false);
// Reload the list to show any newly created triggers
const loadTriggers = async () => {
try {
setLoading(true);
const response = await fetchPage(null);
setItems(response.items);
setCursor(response.nextCursor);
setHasMore(Boolean(response.nextCursor));
} catch (err: any) {
console.error('Error loading triggers:', err);
} finally {
setLoading(false);
}
};
loadTriggers();
};
const sections = useMemo(() => {
const groups: Record<string, ListedItem[]> = {
Today: [],
@ -103,6 +134,10 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
return date.toLocaleString();
};
if (showCreateFlow) {
return <CreateScheduledJobRuleForm projectId={projectId} onBack={handleBackToList} hasExistingTriggers={items.length > 0} />;
}
return (
<Panel
title={
@ -112,11 +147,9 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
}
rightActions={
<div className="flex items-center gap-3">
<Link href={`/projects/${projectId}/manage-triggers/scheduled/new`}>
<Button size="sm" className="whitespace-nowrap" startContent={<PlusIcon className="w-4 h-4" />}>
New One-time Trigger
</Button>
</Link>
<Button size="sm" className="whitespace-nowrap" startContent={<PlusIcon className="w-4 h-4" />} onClick={handleCreateNew}>
New One-time Trigger
</Button>
</div>
}
>