diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormSelectFieldInput.tsx
index cac2cb6beb..738d8e558e 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormSelectFieldInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormSelectFieldInput.tsx
@@ -3,6 +3,7 @@ import { FormFieldInputInnerContainer } from '@/object-record/record-field/ui/fo
import { FormFieldInputRowContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputRowContainer';
import { VariableChipStandalone } from '@/object-record/record-field/ui/form-types/components/VariableChipStandalone';
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
+import { InputHint } from '@/ui/input/components/InputHint';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
@@ -18,6 +19,7 @@ import { type SelectOption } from 'twenty-ui/input';
type FormSelectFieldInputProps = {
label?: string;
+ hint?: string;
defaultValue: string | undefined;
onChange: (value: string | null) => void;
VariablePicker?: VariablePickerComponent;
@@ -27,6 +29,7 @@ type FormSelectFieldInputProps = {
export const FormSelectFieldInput = ({
label,
+ hint,
defaultValue,
onChange,
VariablePicker,
@@ -162,6 +165,7 @@ export const FormSelectFieldInput = ({
/>
)}
+ {hint && {hint}}
);
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelper.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelper.tsx
index 5d6471076c..0816fbf1bf 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelper.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelper.tsx
@@ -1,56 +1,15 @@
import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat';
import { InputHint } from '@/ui/input/components/InputHint';
-import { InputLabel } from '@/ui/input/components/InputLabel';
import type { WorkflowCronTrigger } from '@/workflow/types/Workflow';
-import { describeCronExpression } from '@/workflow/workflow-trigger/utils/cron-to-human/describeCronExpression';
+import { convertScheduleToCronExpression } from '@/workflow/workflow-trigger/utils/cron-to-human/utils/convertScheduleToCronExpression';
+import { getTriggerScheduleDescription } from '@/workflow/workflow-trigger/utils/getTriggerScheduleDescription';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { CronExpressionParser } from 'cron-parser';
-import { type Locale } from 'date-fns';
import { useRecoilValue } from 'recoil';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { formatDateTimeString } from '~/utils/string/formatDateTimeString';
-const convertScheduleToCronExpression = (
- trigger: WorkflowCronTrigger,
-): string | null => {
- switch (trigger.settings.type) {
- case 'CUSTOM':
- return trigger.settings.pattern;
- case 'DAYS':
- return `${trigger.settings.schedule.minute} ${trigger.settings.schedule.hour} */${trigger.settings.schedule.day} * *`;
- case 'HOURS':
- return `${trigger.settings.schedule.minute} */${trigger.settings.schedule.hour} * * *`;
- case 'MINUTES':
- return `*/${trigger.settings.schedule.minute} * * * *`;
- default:
- return null;
- }
-};
-
-const getTriggerScheduleDescription = (
- trigger: WorkflowCronTrigger,
- localeCatalog?: Locale,
-): string | null => {
- const cronExpression = convertScheduleToCronExpression(trigger);
-
- if (!cronExpression) {
- return null;
- }
-
- try {
- return describeCronExpression(
- cronExpression,
- { use24HourTimeFormat: true },
- localeCatalog,
- );
- } catch (error) {
- const errorMessage =
- error instanceof Error ? error.message : t`Invalid cron expression`;
- return errorMessage;
- }
-};
-
const getNextExecutions = (cronExpression: string): Date[] => {
try {
const interval = CronExpressionParser.parse(cronExpression, {
@@ -70,37 +29,48 @@ const StyledContainer = styled.div`
`;
const StyledSection = styled.div`
+ background-color: ${({ theme }) => theme.background.transparent.lighter};
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ border-radius: ${({ theme }) => theme.border.radius.sm};
+ box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
+ padding: ${({ theme }) => theme.spacing(3)} ${({ theme }) => theme.spacing(4)};
`;
const StyledScheduleDescription = styled.div`
- color: ${({ theme }) => theme.font.color.primary};
+ color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
`;
-const StyledScheduleSubtext = styled.div`
- color: ${({ theme }) => theme.font.color.tertiary};
- font-size: ${({ theme }) => theme.font.size.xs};
+const StyledScheduleTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.primary};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ margin-bottom: ${({ theme }) => theme.spacing(1)};
`;
const StyledExecutionItem = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
- font-family: monospace;
- font-size: ${({ theme }) => theme.font.size.xs};
- margin-bottom: ${({ theme }) => theme.spacing(0.5)};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ margin-top: ${({ theme }) => theme.spacing(0.5)};
`;
type CronExpressionHelperProps = {
trigger: WorkflowCronTrigger;
isVisible?: boolean;
+ isScheduleVisible?: boolean;
+ isUpcomingExecutionVisible?: boolean;
};
export const CronExpressionHelper = ({
trigger,
isVisible = true,
+ isScheduleVisible = true,
+ isUpcomingExecutionVisible = true,
}: CronExpressionHelperProps) => {
const { timeZone, dateFormat, timeFormat } = useDateTimeFormat();
const dateLocale = useRecoilValue(dateLocaleState);
@@ -143,19 +113,17 @@ export const CronExpressionHelper = ({
return (
-
- {t`Schedule`}
-
- {customDescription}
-
-
- {t`Schedule runs in UTC timezone.`}
-
-
-
- {nextExecutions.length > 0 && (
+ {isScheduleVisible && (
- {t`Upcoming execution times (${timeZone})`}
+ {t`Schedule`}
+
+ {customDescription}
+
+
+ )}
+ {nextExecutions.length > 0 && isUpcomingExecutionVisible && (
+
+ {t`Upcoming execution time (${timeZone})`}
{nextExecutions.slice(0, 3).map((execution, index) => (
{formatDateTimeString({
diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelperLazy.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelperLazy.tsx
index eec2c9d2d7..37a0cc44d1 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelperLazy.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/CronExpressionHelperLazy.tsx
@@ -10,11 +10,15 @@ const CronExpressionHelperComponent = lazy(() =>
type CronExpressionHelperLazyProps = {
trigger: WorkflowCronTrigger;
isVisible?: boolean;
+ isScheduleVisible?: boolean;
+ isUpcomingExecutionVisible?: boolean;
};
export const CronExpressionHelperLazy = ({
trigger,
isVisible = true,
+ isScheduleVisible = true,
+ isUpcomingExecutionVisible = true,
}: CronExpressionHelperLazyProps) => {
if (!isVisible) {
return null;
@@ -22,7 +26,12 @@ export const CronExpressionHelperLazy = ({
return (
-
+
);
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
index 97a479251b..9c1fae2f88 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm.tsx
@@ -1,25 +1,30 @@
import { SidePanelHeader } from '@/command-menu/components/SidePanelHeader';
import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput';
+import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
-import { Select } from '@/ui/input/components/Select';
-import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { type WorkflowCronTrigger } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { CronExpressionHelperLazy } from '@/workflow/workflow-trigger/components/CronExpressionHelperLazy';
-import { CRON_TRIGGER_INTERVAL_OPTIONS } from '@/workflow/workflow-trigger/constants/CronTriggerIntervalOptions';
+import {
+ CRON_TRIGGER_INTERVAL_OPTIONS,
+ type CronTriggerInterval,
+} from '@/workflow/workflow-trigger/constants/CronTriggerIntervalOptions';
import { getCronTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getCronTriggerDefaultSettings';
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel';
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { getTriggerIconColor } from '@/workflow/workflow-trigger/utils/getTriggerIconColor';
+import { getTriggerScheduleDescription } from '@/workflow/workflow-trigger/utils/getTriggerScheduleDescription';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { isNumber } from '@sniptt/guards';
import { useState } from 'react';
+import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { useIcons } from 'twenty-ui/display';
+import { dateLocaleState } from '~/localization/states/dateLocaleState';
type WorkflowEditTriggerCronFormProps = {
trigger: WorkflowCronTrigger;
@@ -51,6 +56,7 @@ export const WorkflowEditTriggerCronForm = ({
const theme = useTheme();
const [errorMessages, setErrorMessages] = useState({});
const [errorMessagesVisible, setErrorMessagesVisible] = useState(false);
+ const dateLocale = useRecoilValue(dateLocaleState);
const { getIcon } = useIcons();
@@ -60,6 +66,11 @@ export const WorkflowEditTriggerCronForm = ({
const headerTitle = trigger.name ?? defaultLabel;
const headerType = getTriggerHeaderType(trigger);
+ const customDescription = getTriggerScheduleDescription(
+ trigger,
+ dateLocale.localeCatalog,
+ );
+
const onBlur = () => {
setErrorMessagesVisible(true);
};
@@ -85,30 +96,30 @@ export const WorkflowEditTriggerCronForm = ({
iconTooltip={getTriggerDefaultLabel(trigger)}
/>
-