Skip to content

Commit

Permalink
fix(dashboard): fixed view execution logs button triggering the workf…
Browse files Browse the repository at this point in the history
…low (#7335)

Co-authored-by: Dima Grossman <[email protected]>
  • Loading branch information
LetItRock and scopsy authored Dec 23, 2024
1 parent 1fed04e commit 29b820a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const digestRegularControlZodSchema = z
.object({
skip: z.object({}).catchall(z.unknown()).optional(),
amount: z.union([z.number().min(1), z.string().min(1)]),
unit: z.nativeEnum(TimeUnitEnum).default(TimeUnitEnum.SECONDS),
unit: z.nativeEnum(TimeUnitEnum),
digestKey: z.string().optional(),
lookBackWindow: z
.object({
amount: z.number().min(1),
unit: z.nativeEnum(TimeUnitEnum).default(TimeUnitEnum.SECONDS),
unit: z.nativeEnum(TimeUnitEnum),
})
.strict()
.optional(),
Expand Down
43 changes: 39 additions & 4 deletions apps/dashboard/src/components/activity/activity-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { useEffect, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { motion } from 'motion/react';
import { RiPlayCircleLine, RiRouteFill } from 'react-icons/ri';
import { IActivityJob, JobStatusEnum } from '@novu/shared';

import { ActivityJobItem } from './activity-job-item';
import { InlineToast } from '../primitives/inline-toast';
import { useFetchActivity } from '@/hooks/use-fetch-activity';
import { ActivityOverview } from './components/activity-overview';
import { cn } from '../../utils/ui';
import { IActivityJob } from '@novu/shared';
import { Skeleton } from '../primitives/skeleton';
import { QueryKeys } from '@/utils/query-keys';
import { useEnvironment } from '@/context/environment/hooks';

export interface ActivityPanelProps {
activityId: string;
activityId?: string;
onActivitySelect: (activityId: string) => void;
headerClassName?: string;
overviewHeaderClassName?: string;
Expand All @@ -21,7 +26,34 @@ export function ActivityPanel({
headerClassName,
overviewHeaderClassName,
}: ActivityPanelProps) {
const { activity, isPending, error } = useFetchActivity({ activityId });
const queryClient = useQueryClient();
const [shouldRefetch, setShouldRefetch] = useState(true);
const { currentEnvironment } = useEnvironment();
const { activity, isPending, error } = useFetchActivity(
{ activityId },
{
refetchInterval: shouldRefetch ? 1000 : false,
}
);

useEffect(() => {
if (!activity) return;

const isPending = activity.jobs?.some(
(job) =>
job.status === JobStatusEnum.PENDING ||
job.status === JobStatusEnum.QUEUED ||
job.status === JobStatusEnum.RUNNING ||
job.status === JobStatusEnum.DELAYED
);

// Only stop refetching if we have an activity and it's not pending
setShouldRefetch(isPending || !activity?.jobs?.length);

queryClient.invalidateQueries({
queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activityId],
});
}, [activity, queryClient, currentEnvironment, activityId]);

if (isPending) {
return (
Expand Down Expand Up @@ -77,7 +109,10 @@ export function ActivityPanel({
ctaClassName="text-foreground-950"
variant={'tip'}
ctaLabel="View Execution"
onCtaClick={() => {
onCtaClick={(e) => {
e.stopPropagation();
e.preventDefault();

if (activity._digestedNotificationId) {
onActivitySelect(activity._digestedNotificationId);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/primitives/inline-toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface InlineToastProps
title?: string;
description?: string | React.ReactNode;
ctaLabel?: string;
onCtaClick?: () => void;
onCtaClick?: React.MouseEventHandler<HTMLButtonElement>;
isCtaLoading?: boolean;
ctaClassName?: string;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,44 @@
import { ActivityPanel } from '@/components/activity/activity-panel';
import { useEffect, useState } from 'react';
import { JobStatusEnum } from '@novu/shared';
import { Loader2 } from 'lucide-react';

import { ActivityPanel } from '@/components/activity/activity-panel';
import { WorkflowTriggerInboxIllustration } from '../../icons/workflow-trigger-inbox';
import { useFetchActivities } from '../../../hooks/use-fetch-activities';
import { QueryKeys } from '../../../utils/query-keys';
import { useEnvironment } from '../../../context/environment/hooks';
import { useQueryClient } from '@tanstack/react-query';

type TestWorkflowLogsSidebarProps = {
transactionId?: string;
};

export const TestWorkflowLogsSidebar = ({ transactionId }: TestWorkflowLogsSidebarProps) => {
const queryClient = useQueryClient();
const { currentEnvironment } = useEnvironment();
const [parentActivityId, setParentActivityId] = useState<string | undefined>(undefined);
const [shouldRefetch, setShouldRefetch] = useState(true);
const { activities } = useFetchActivities(
{
filters: transactionId ? { transactionId } : undefined,
},
{
enabled: transactionId !== undefined,
enabled: !!transactionId,
refetchInterval: shouldRefetch ? 1000 : false,
}
);
const activityId: string | undefined = parentActivityId ?? activities?.[0]?._id;

useEffect(() => {
if (!activities?.length) return;

const activity = activities[0];
const isPending = activity.jobs?.some(
(job) =>
job.status === JobStatusEnum.PENDING ||
job.status === JobStatusEnum.QUEUED ||
job.status === JobStatusEnum.RUNNING ||
job.status === JobStatusEnum.DELAYED
);

// Only stop refetching if we have an activity and it's not pending
setShouldRefetch(isPending || !activity?.jobs?.length);

queryClient.invalidateQueries({
queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activity._id],
});
}, [activities]);
if (activityId) {
setShouldRefetch(false);
}
}, [activityId]);

// Reset refetch when transaction ID changes
useEffect(() => {
if (!transactionId) {
return;
}

setShouldRefetch(true);
setParentActivityId(undefined);
}, [transactionId]);

const activityId = activities?.[0]?._id;

return (
<aside className="flex h-full w-[500px] flex-col border-l">
{transactionId && !activityId ? (
Expand All @@ -65,7 +51,7 @@ export const TestWorkflowLogsSidebar = ({ transactionId }: TestWorkflowLogsSideb
) : activityId ? (
<ActivityPanel
activityId={activityId}
onActivitySelect={() => {}}
onActivitySelect={setParentActivityId}
headerClassName="h-[49px]"
overviewHeaderClassName="border-t-0"
/>
Expand Down
12 changes: 11 additions & 1 deletion apps/dashboard/src/hooks/use-fetch-activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@ import type { IActivity } from '@novu/shared';
import { useEnvironment } from '@/context/environment/hooks';
import { getNotification } from '@/api/activity';

export function useFetchActivity({ activityId }: { activityId?: string }) {
export function useFetchActivity(
{ activityId }: { activityId?: string },
{
refetchInterval = false,
refetchOnWindowFocus = false,
staleTime = 0,
}: { refetchInterval?: number | false; refetchOnWindowFocus?: boolean; staleTime?: number } = {}
) {
const { currentEnvironment } = useEnvironment();

const { data, isPending, error } = useQuery<{ data: IActivity }>({
queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activityId],
queryFn: () => getNotification(activityId!, currentEnvironment!),
enabled: !!currentEnvironment?._id && !!activityId,
refetchInterval,
refetchOnWindowFocus,
staleTime,
});

return {
Expand Down

0 comments on commit 29b820a

Please sign in to comment.