diff --git a/src/client/components/ActivityDetail.tsx b/src/client/components/ActivityDetail.tsx index 2dd0466f..172d47b6 100644 --- a/src/client/components/ActivityDetail.tsx +++ b/src/client/components/ActivityDetail.tsx @@ -1,11 +1,11 @@ -import { Accordion, Alert, Box, Group, Portal, Code, Collapsible, Flex, HStack, Separator, Menu, Skeleton, SkeletonText, Span, Stack, useAccordionItemContext, type BadgeProps, type MenuItemProps, type MenuSelectionDetails, useClipboard } from '@chakra-ui/react'; +import { Accordion, Alert, Dialog, Box, Text, Group, Portal, Code, Collapsible, Flex, HStack, Separator, Menu, Skeleton, SkeletonText, Span, Stack, useAccordionItemContext, type BadgeProps, type MenuItemProps, type MenuSelectionDetails, useClipboard, CloseButton, Button } from '@chakra-ui/react'; import { useSSEContext, useSSEEvent } from "@flamefrontend/sse-runtime-react"; import { useMutation, useQuery, useQueryClient, type InfiniteData } from '@tanstack/react-query'; import React, { Fragment, useCallback, useEffect, useState, type ComponentProps } from "react"; import { LuChevronRight } from "react-icons/lu"; import type { MarkOptional } from "ts-essentials"; import type { ComponentsApiJson, MsSseEvent, PaginatedResponse, PlayApiCommonDetailed, QueryPlaysOptsJson, SortPlaysByProps } from "../../core/Api"; -import { INGRESS_QUEUE, type ComponentType, type Second } from "../../core/Atomic"; +import { INGRESS_QUEUE, type ComponentType, type QueueContext, type Second } from "../../core/Atomic"; import { tanQueries, useQueryWatcher } from "../queries"; import { activityTimelineHasIssue } from "../utils/ComponentUtils"; import { ActivityTimeline } from "./ActivityTimeline"; @@ -26,11 +26,7 @@ type UseActivityQueryOptions = { activity?: ActivitySummaryProps['activity'] refetchOnMount?: boolean | 'always' } -export function useActivityQuery( - componentId: number, - activityUid: string, - options: UseActivityQueryOptions = {} -) { +export const useActivityQuery = (componentId: number, activityUid: string, options: UseActivityQueryOptions = {}) => { const { msQuery, activity: preloadedActivity, @@ -84,7 +80,7 @@ export function useActivityQuery( }); return { activity, isPending, isError, error }; -} +}; export interface ActivityDetailProps { activity: PlayApiCommonDetailed componentType: ComponentType @@ -309,12 +305,46 @@ export const ActivityDetailFetchable = (props: ActivityDetailFetchableProps) => return } -const playStateMenuItem = (Icon: IconType, value: string, name?: string) => (props: Pick = {}) => { - return ({name ?? capitalize(value)}); -} +const RetryWithDialog = (props: {open: boolean, setOpen: (open: boolean) => void}) => + ( props.setOpen(e.open)} + > + + + + + + + + + Retry With Options... + + + + Retry the Play using the options below: + + + + + + + + + + + ) + +const playStateMenuItem = (Icon: IconType, value: string, name?: string) => (props: Pick = {}) => ( + {name ?? capitalize(value)} +) const MenuItemRetry = playStateMenuItem(RetryIcon, 'retry'); -const MenuItemRetryWith = playStateMenuItem(RetryIcon, 'retry', 'Retry With...'); +const MenuItemRetryWith = playStateMenuItem(RetryIcon, 'retryWith', 'Retry With...'); const MenuItemCancel = playStateMenuItem(StopIconRaw, 'cancel'); const MenuItemTrash = playStateMenuItem(TrashIconRaw, 'delete'); const MenuItemDebug = playStateMenuItem(DebugIcon, 'debug'); @@ -333,25 +363,46 @@ export const ActivityStateActions = (props: {activity: PlayApiCommonDetailed}) = let menuItems: React.JSX.Element[] = []; const badgeProps: BadgeProps = {}; + const [retryOpen, setRetryOpen] = useState(false); + const clipboard = useClipboard({value: JSON.stringify(props.activity)}); const {mutate, isPending, variables, isSuccess} = useMutation({ mutationKey: ['playAction', props.activity.uid], - mutationFn: () => ky.post(`/api/components/${props.activity.componentId}/state`,{ - json: {reason: 'User initiated from UI'} - }) + mutationFn: (data: {action: string, context?: QueueContext}) => { + if(data.action === 'queue') { + return ky.post(`/api/components/${props.activity.componentId}/plays/${props.activity.uid}/state`,{ + json: data.context ?? {} + }); + } + const realAction = data.action === 'cancel' ? 'queue' : data.action; + return ky.delete(`/api/components/${props.activity.componentId}/plays/${props.activity.uid}/${realAction}`); + } }); const menuCb = useCallback((select: MenuSelectionDetails) => { - if(select.value === 'debug') { - clipboard.copy(); - toaster.create({ - title: 'Copied debug data to clipboard', - type: 'success' - }); + switch(select.value) { + case 'debug': + clipboard.copy(); + toaster.create({ + title: 'Copied debug data to clipboard', + type: 'success' + }); + break; + case 'retry': + mutate({action: 'queue'}); + break; + case 'retryWith': + setRetryOpen(true); + break; + case 'cancel': + mutate({action: 'cancel'}); + break; + case 'finish': + mutate({action: 'dead'}); + break; } - //mutate(); - },[mutate, props.activity]); + },[mutate,clipboard, setRetryOpen]); const { activity: { @@ -359,21 +410,20 @@ export const ActivityStateActions = (props: {activity: PlayApiCommonDetailed}) = } = {} } = props; const hasDeadQueue = queueStates.some(x => x.queueName === INGRESS_QUEUE && x.retries > 0); - //badgeProps.paddingRight = 0; switch(props.activity.state) { case 'queued': - primaryAction = ; + primaryAction = mutate({action: 'cancel'})}/>; menuItems = [,]; break; case 'failed': - primaryAction = ; - menuItems = [, ,]; + primaryAction = mutate({action: 'queue'})}/>; + menuItems = [, ,]; if(!hasDeadQueue) { menuItems.unshift() } break default: - primaryAction = ; + primaryAction = mutate({action: 'queue'})}/>; menuItems = [, ,]; break @@ -405,6 +455,7 @@ export const ActivityStateActions = (props: {activity: PlayApiCommonDetailed}) = } return ( + @@ -476,8 +527,7 @@ export const ActivityCollapsible = (props: ActivitySummaryProps & { key?: string ) } -export const ActivitySummarySkeleton = () => { - return ( +export const ActivitySummarySkeleton = () => ( { - ); -} \ No newline at end of file + ) \ No newline at end of file diff --git a/src/client/components/icons/ChakraIcons.tsx b/src/client/components/icons/ChakraIcons.tsx index 01e5515d..a9031885 100644 --- a/src/client/components/icons/ChakraIcons.tsx +++ b/src/client/components/icons/ChakraIcons.tsx @@ -209,7 +209,7 @@ export const ExclamationCircleIcon = makeChakraIcon(LuCircleAlert); export const ExclamationTriangleIcon = makeChakraIcon(LuTriangleAlert); -export const TimelineIndicatorIconQueued = makeChakraIcon(LuClock);s +export const TimelineIndicatorIconQueued = makeChakraIcon(LuClock); export const SparkleIcon = makeChakraIcon(LuSparkles);