Add link on snack bar (#9873)

Add link to workflow execution 

<img width="639" alt="Capture d’écran 2025-01-27 à 18 15 34"
src="https://github.com/user-attachments/assets/f2a1b3b5-7bf6-4b33-bba7-bf8907f6bcc6"
/>
This commit is contained in:
Thomas Trompette
2025-01-28 11:26:20 +01:00
committed by GitHub
parent af8d22ee99
commit 069c34cd7b
7 changed files with 55 additions and 11 deletions

View File

@ -26,7 +26,6 @@ export const useTestWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataI
runWorkflowVersion({ runWorkflowVersion({
workflowVersionId: workflowWithCurrentVersion.currentVersion.id, workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
workflowName: workflowWithCurrentVersion.name,
}); });
}; };

View File

@ -71,7 +71,6 @@ export const useWorkflowRunRecordActions = ({
await runWorkflowVersion({ await runWorkflowVersion({
workflowVersionId: activeWorkflowVersion.id, workflowVersionId: activeWorkflowVersion.id,
workflowName: name,
payload: selectedRecord, payload: selectedRecord,
}); });
}, },

View File

@ -49,7 +49,6 @@ export const useWorkflowRunActions = () => {
onClick: async () => { onClick: async () => {
await runWorkflowVersion({ await runWorkflowVersion({
workflowVersionId: activeWorkflowVersion.id, workflowVersionId: activeWorkflowVersion.id,
workflowName: name,
}); });
}, },
}); });

View File

@ -3,6 +3,7 @@ import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { isUndefined } from '@sniptt/guards'; import { isUndefined } from '@sniptt/guards';
import { ComponentPropsWithoutRef, ReactNode, useMemo } from 'react'; import { ComponentPropsWithoutRef, ReactNode, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { import {
IconAlertTriangle, IconAlertTriangle,
IconInfoCircle, IconInfoCircle,
@ -31,6 +32,10 @@ export type SnackBarProps = Pick<ComponentPropsWithoutRef<'div'>, 'id'> & {
duration?: number; duration?: number;
icon?: ReactNode; icon?: ReactNode;
message: string; message: string;
link?: {
href: string;
text: string;
};
detailedMessage?: string; detailedMessage?: string;
onCancel?: () => void; onCancel?: () => void;
onClose?: () => void; onClose?: () => void;
@ -101,6 +106,20 @@ const StyledDescription = styled.div`
width: 200px; width: 200px;
`; `;
const StyledLink = styled(Link)`
display: block;
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
padding-left: ${({ theme }) => theme.spacing(6)};
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 200px;
&:hover {
color: ${({ theme }) => theme.font.color.secondary};
}
`;
const defaultAriaLabelByVariant: Record<SnackBarVariant, string> = { const defaultAriaLabelByVariant: Record<SnackBarVariant, string> = {
[SnackBarVariant.Default]: 'Alert', [SnackBarVariant.Default]: 'Alert',
[SnackBarVariant.Error]: 'Error', [SnackBarVariant.Error]: 'Error',
@ -117,6 +136,7 @@ export const SnackBar = ({
id, id,
message, message,
detailedMessage, detailedMessage,
link,
onCancel, onCancel,
onClose, onClose,
role = 'status', role = 'status',
@ -205,6 +225,7 @@ export const SnackBar = ({
{detailedMessage && ( {detailedMessage && (
<StyledDescription>{detailedMessage}</StyledDescription> <StyledDescription>{detailedMessage}</StyledDescription>
)} )}
{link && <StyledLink to={link.href}>{link.text}</StyledLink>}
</StyledContainer> </StyledContainer>
); );
}; };

View File

@ -46,7 +46,15 @@ export const SnackBarProvider = ({ children }: React.PropsWithChildren) => {
<StyledSnackBarContainer> <StyledSnackBarContainer>
<AnimatePresence> <AnimatePresence>
{snackBarInternal.queue.map( {snackBarInternal.queue.map(
({ duration, icon, id, message, detailedMessage, variant }) => ( ({
duration,
icon,
id,
message,
detailedMessage,
variant,
link,
}) => (
<motion.div <motion.div
key={id} key={id}
variants={variants} variants={variants}
@ -57,7 +65,14 @@ export const SnackBarProvider = ({ children }: React.PropsWithChildren) => {
layout layout
> >
<SnackBar <SnackBar
{...{ duration, icon, message, detailedMessage, variant }} {...{
duration,
icon,
message,
detailedMessage,
variant,
link,
}}
onClose={() => handleSnackBarClose(id)} onClose={() => handleSnackBarClose(id)}
/> />
</motion.div> </motion.div>

View File

@ -68,7 +68,6 @@ export const RecordShowPageWorkflowHeader = ({
await runWorkflowVersion({ await runWorkflowVersion({
workflowVersionId: workflowWithCurrentVersion.currentVersion.id, workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
workflowName: workflowWithCurrentVersion.name,
}); });
}} }}
/> />

View File

@ -3,7 +3,6 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion'; import { RUN_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/runWorkflowVersion';
import { useApolloClient, useMutation } from '@apollo/client'; import { useApolloClient, useMutation } from '@apollo/client';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { capitalize } from 'twenty-shared';
import { IconSettingsAutomation } from 'twenty-ui'; import { IconSettingsAutomation } from 'twenty-ui';
import { import {
RunWorkflowVersionMutation, RunWorkflowVersionMutation,
@ -25,18 +24,27 @@ export const useRunWorkflowVersion = () => {
const runWorkflowVersion = async ({ const runWorkflowVersion = async ({
workflowVersionId, workflowVersionId,
workflowName,
payload, payload,
}: { }: {
workflowVersionId: string; workflowVersionId: string;
workflowName: string;
payload?: Record<string, any>; payload?: Record<string, any>;
}) => { }) => {
await mutate({ const { data } = await mutate({
variables: { input: { workflowVersionId, payload } }, variables: { input: { workflowVersionId, payload } },
}); });
enqueueSnackBar(`${capitalize(workflowName)} starting...`, { const workflowRunId = data?.runWorkflowVersion?.workflowRunId;
if (!workflowRunId) {
enqueueSnackBar('Workflow run failed', {
variant: SnackBarVariant.Error,
});
return;
}
const link = `/object/workflowRun/${workflowRunId}`;
enqueueSnackBar('Workflow is running...', {
variant: SnackBarVariant.Success, variant: SnackBarVariant.Success,
icon: ( icon: (
<IconSettingsAutomation <IconSettingsAutomation
@ -44,6 +52,10 @@ export const useRunWorkflowVersion = () => {
color={theme.snackBar.success.color} color={theme.snackBar.success.color}
/> />
), ),
link: {
href: link,
text: 'View execution details',
},
}); });
}; };