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:
@ -26,7 +26,6 @@ export const useTestWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataI
|
|||||||
|
|
||||||
runWorkflowVersion({
|
runWorkflowVersion({
|
||||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
workflowName: workflowWithCurrentVersion.name,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,6 @@ export const useWorkflowRunRecordActions = ({
|
|||||||
|
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: activeWorkflowVersion.id,
|
workflowVersionId: activeWorkflowVersion.id,
|
||||||
workflowName: name,
|
|
||||||
payload: selectedRecord,
|
payload: selectedRecord,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -49,7 +49,6 @@ export const useWorkflowRunActions = () => {
|
|||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: activeWorkflowVersion.id,
|
workflowVersionId: activeWorkflowVersion.id,
|
||||||
workflowName: name,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -68,7 +68,6 @@ export const RecordShowPageWorkflowHeader = ({
|
|||||||
|
|
||||||
await runWorkflowVersion({
|
await runWorkflowVersion({
|
||||||
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
|
||||||
workflowName: workflowWithCurrentVersion.name,
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user