Browse Source

ref(crons): Show human readable cron tab text in sidebar (#48400)

Following these mocks:

https://www.figma.com/file/cfcxnhwGet0cHZKEZXY5kV/Crons?node-id=0-1&t=U7hLl4STsy9eaXZr-0

Before:
<img width="357" alt="image"
src="https://user-images.githubusercontent.com/9372512/235797592-8c5921f3-d5d7-4952-a044-5aad261b1921.png">

After:
<img width="364" alt="image"
src="https://user-images.githubusercontent.com/9372512/235797542-301caeb0-61ba-4383-a589-caa7b9ebc890.png">
David Wang 1 year ago
parent
commit
5ba4d7da36

+ 21 - 21
static/app/views/monitors/components/detailsSidebar.tsx

@@ -14,31 +14,18 @@ import {getFormattedDate} from 'sentry/utils/dates';
 import {DEFAULT_MAX_RUNTIME} from 'sentry/views/monitors/components/monitorForm';
 import MonitorIcon from 'sentry/views/monitors/components/monitorIcon';
 import {
-  IntervalConfig,
   Monitor,
   MonitorEnvironment,
   MonitorStatus,
   ScheduleType,
 } from 'sentry/views/monitors/types';
+import {scheduleAsText} from 'sentry/views/monitors/utils';
 
 interface Props {
   monitor: Monitor;
   monitorEnv?: MonitorEnvironment;
 }
 
-function getIntervalScheduleText(schedule: IntervalConfig['schedule']) {
-  const [n, period] = schedule;
-  const intervalTextMap = {
-    minute: tn('Every %s minute', 'Every %s minutes', n),
-    hour: tn('Every %s hour', 'Every %s hours', n),
-    day: tn('Every %s day', 'Every %s days', n),
-    week: tn('Every %s week', 'Every %s weeks', n),
-    month: tn('Every %s month', 'Every %s months', n),
-    year: tn('Every %s year', 'Every %s years', n),
-  };
-  return intervalTextMap[period];
-}
-
 export default function DetailsSidebar({monitorEnv, monitor}: Props) {
   const {checkin_margin, schedule, schedule_type, max_runtime, timezone} = monitor.config;
 
@@ -81,12 +68,13 @@ export default function DetailsSidebar({monitorEnv, monitor}: Props) {
       </CheckIns>
       <SectionHeading>{t('Schedule')}</SectionHeading>
       <Schedule>
-        <MonitorIcon status={MonitorStatus.OK} size={12} />
-        <Text>
-          {schedule_type === ScheduleType.CRONTAB
-            ? schedule
-            : getIntervalScheduleText(schedule as IntervalConfig['schedule'])}
-        </Text>
+        <Text>{scheduleAsText(monitor.config)}</Text>
+        {schedule_type === ScheduleType.CRONTAB && (
+          <CrontabText>({schedule})</CrontabText>
+        )}
+      </Schedule>
+      <SectionHeading>{t('Thresholds')}</SectionHeading>
+      <Thresholds>
         <MonitorIcon status={MonitorStatus.MISSED_CHECKIN} size={12} />
         <Text>
           {defined(checkin_margin)
@@ -101,7 +89,7 @@ export default function DetailsSidebar({monitorEnv, monitor}: Props) {
             max_runtime ?? DEFAULT_MAX_RUNTIME
           )}
         </Text>
-      </Schedule>
+      </Thresholds>
       <SectionHeading>{t('Cron Details')}</SectionHeading>
       <KeyValueTable>
         <KeyValueTableRow keyName={t('Monitor Slug')} value={slug} />
@@ -124,6 +112,18 @@ const CheckIns = styled('div')`
 `;
 
 const Schedule = styled('div')`
+  margin-bottom: ${space(2)};
+  display: flex;
+  flex-wrap: wrap;
+  gap: ${space(1)};
+`;
+
+const CrontabText = styled(Text)`
+  font-family: ${p => p.theme.text.familyMono};
+  color: ${p => p.theme.subText};
+`;
+
+const Thresholds = styled('div')`
   display: grid;
   grid-template-columns: max-content 1fr;
   margin-bottom: ${space(2)};

+ 3 - 42
static/app/views/monitors/components/row.tsx

@@ -12,20 +12,14 @@ import Text from 'sentry/components/text';
 import TextOverflow from 'sentry/components/textOverflow';
 import TimeSince from 'sentry/components/timeSince';
 import {IconEllipsis} from 'sentry/icons';
-import {t, tct, tn} from 'sentry/locale';
+import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {Organization} from 'sentry/types';
 import useApi from 'sentry/utils/useApi';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
-import {crontabAsText} from 'sentry/views/monitors/utils';
+import {scheduleAsText} from 'sentry/views/monitors/utils';
 
-import {
-  Monitor,
-  MonitorConfig,
-  MonitorEnvironment,
-  MonitorStatus,
-  ScheduleType,
-} from '../types';
+import {Monitor, MonitorEnvironment, MonitorStatus} from '../types';
 
 import {MonitorBadge} from './monitorBadge';
 
@@ -36,39 +30,6 @@ interface MonitorRowProps {
   monitorEnv?: MonitorEnvironment;
 }
 
-function scheduleAsText(config: MonitorConfig) {
-  // Crontab format uses cronstrue
-  if (config.schedule_type === ScheduleType.CRONTAB) {
-    const parsedSchedule = crontabAsText(config.schedule);
-    return parsedSchedule ?? t('Unknown schedule');
-  }
-
-  // Interval format is simpler
-  const [value, timeUnit] = config.schedule;
-
-  if (timeUnit === 'minute') {
-    return tn('Every minute', 'Every %s minutes', value);
-  }
-
-  if (timeUnit === 'hour') {
-    return tn('Every hour', 'Every %s hours', value);
-  }
-
-  if (timeUnit === 'day') {
-    return tn('Every day', 'Every %s days', value);
-  }
-
-  if (timeUnit === 'week') {
-    return tn('Every week', 'Every %s weeks', value);
-  }
-
-  if (timeUnit === 'month') {
-    return tn('Every month', 'Every %s months', value);
-  }
-
-  return t('Unknown schedule');
-}
-
 function MonitorRow({monitor, monitorEnv, organization, onDelete}: MonitorRowProps) {
   const api = useApi();
   const lastCheckin = monitorEnv?.lastCheckIn ? (

+ 35 - 0
static/app/views/monitors/utils.tsx

@@ -1,6 +1,8 @@
 import cronstrue from 'cronstrue';
 
+import {t, tn} from 'sentry/locale';
 import {shouldUse24Hours} from 'sentry/utils/dates';
+import {MonitorConfig, ScheduleType} from 'sentry/views/monitors/types';
 
 export function crontabAsText(crontabInput: string | null): string | null {
   if (!crontabInput) {
@@ -18,3 +20,36 @@ export function crontabAsText(crontabInput: string | null): string | null {
 
   return parsedSchedule;
 }
+
+export function scheduleAsText(config: MonitorConfig) {
+  // Crontab format uses cronstrue
+  if (config.schedule_type === ScheduleType.CRONTAB) {
+    const parsedSchedule = crontabAsText(config.schedule);
+    return parsedSchedule ?? t('Unknown schedule');
+  }
+
+  // Interval format is simpler
+  const [value, timeUnit] = config.schedule;
+
+  if (timeUnit === 'minute') {
+    return tn('Every minute', 'Every %s minutes', value);
+  }
+
+  if (timeUnit === 'hour') {
+    return tn('Every hour', 'Every %s hours', value);
+  }
+
+  if (timeUnit === 'day') {
+    return tn('Every day', 'Every %s days', value);
+  }
+
+  if (timeUnit === 'week') {
+    return tn('Every week', 'Every %s weeks', value);
+  }
+
+  if (timeUnit === 'month') {
+    return tn('Every month', 'Every %s months', value);
+  }
+
+  return t('Unknown schedule');
+}