This is an automated email from the ASF dual-hosted git repository.

likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 7a8ac58d7 feat(config-ui): display time zone on next run time (#4596)
7a8ac58d7 is described below

commit 7a8ac58d7e754c6f07e0083072892c95544a179d
Author: 青湛 <[email protected]>
AuthorDate: Tue Mar 7 11:53:10 2023 +0800

    feat(config-ui): display time zone on next run time (#4596)
    
    Co-authored-by: Yumeng Wang <[email protected]>
---
 config-ui/src/config/cron.ts                       |  32 ++++---
 config-ui/src/images/cron-help.png                 | Bin 41582 -> 0 bytes
 .../blueprint/components/sync-policy/index.tsx     | 100 ++++++++++++---------
 .../blueprint/components/sync-policy/styled.ts     |  24 ++---
 4 files changed, 84 insertions(+), 72 deletions(-)

diff --git a/config-ui/src/config/cron.ts b/config-ui/src/config/cron.ts
index 2648e1388..3b5f5f0b9 100644
--- a/config-ui/src/config/cron.ts
+++ b/config-ui/src/config/cron.ts
@@ -19,28 +19,31 @@
 import parser from 'cron-parser';
 
 export const cronPresets = [
-  {
-    label: 'Hourly',
-    config: '59 * * * *',
-    description: 'At minute 59 on every day-of-week from Monday through 
Sunday.',
-  },
   {
     label: 'Daily',
     config: '0 0 * * *',
-    description: 'At 00:00 (Midnight) on every day-of-week from Monday through 
Sunday.',
+    description: '(at 00:00 AM) ',
   },
   {
     label: 'Weekly',
     config: '0 0 * * 1',
-    description: 'At 00:00 (Midnight) on Monday.',
+    description: '(on Monday at 00:00 AM) ',
   },
   {
     label: 'Monthly',
     config: '0 0 1 * *',
-    description: 'At 00:00 (Midnight) on day-of-month 1.',
+    description: '(on first day of the month at 00:00 AM) ',
   },
 ];
 
+const getNextTime = (config: string) => {
+  try {
+    return parser.parseExpression(config, { tz: 'utc' }).next().toString();
+  } catch {
+    return null;
+  }
+};
+
 export const getCron = (isManual: boolean, config: string) => {
   if (isManual) {
     return {
@@ -58,34 +61,37 @@ export const getCron = (isManual: boolean, config: string) 
=> {
     ? {
         ...preset,
         value: preset.config,
-        nextTime: parser.parseExpression(preset.config).next().toString(),
+        nextTime: getNextTime(preset.config),
       }
     : {
         label: 'Custom',
         value: 'custom',
         description: 'Custom',
         config,
-        nextTime: parser.parseExpression(config).next().toString(),
+        nextTime: getNextTime(config),
       };
 };
 
 export const getCronOptions = () => {
   return [
     {
-      label: 'Manual',
       value: 'manual',
+      label: 'Manual',
+      subLabel: '',
     },
   ]
     .concat(
       cronPresets.map((cp) => ({
-        label: cp.label,
         value: cp.config,
+        label: cp.label,
+        subLabel: cp.description,
       })),
     )
     .concat([
       {
-        label: 'Custom',
         value: 'custom',
+        label: 'Custom',
+        subLabel: '',
       },
     ]);
 };
diff --git a/config-ui/src/images/cron-help.png 
b/config-ui/src/images/cron-help.png
deleted file mode 100644
index 8b1777c90..000000000
Binary files a/config-ui/src/images/cron-help.png and /dev/null differ
diff --git a/config-ui/src/pages/blueprint/components/sync-policy/index.tsx 
b/config-ui/src/pages/blueprint/components/sync-policy/index.tsx
index bb405967e..a7d2a4e9f 100644
--- a/config-ui/src/pages/blueprint/components/sync-policy/index.tsx
+++ b/config-ui/src/pages/blueprint/components/sync-policy/index.tsx
@@ -17,11 +17,10 @@
  */
 
 import React, { useMemo } from 'react';
-import { Checkbox, Icon, InputGroup, Position, Radio, RadioGroup } from 
'@blueprintjs/core';
-import { Popover2 } from '@blueprintjs/popover2';
+import dayjs from 'dayjs';
+import { Checkbox, FormGroup, InputGroup, Radio, RadioGroup } from 
'@blueprintjs/core';
 
 import { getCron, getCronOptions } from '@/config';
-import CronHelp from '@/images/cron-help.png';
 
 import StartFromSelector from './start-from-selector';
 import * as S from './styled';
@@ -49,6 +48,7 @@ export const SyncPolicy = ({
   onChangeSkipOnFail,
   onChangeTimeAfter,
 }: Props) => {
+  const [mintue, hour, day, month, week] = useMemo(() => cronConfig.split(' 
'), [cronConfig]);
   const cron = useMemo(() => getCron(isManual, cronConfig), [isManual, 
cronConfig]);
 
   const options = useMemo(() => getCronOptions(), []);
@@ -75,46 +75,60 @@ export const SyncPolicy = ({
           <StartFromSelector value={timeAfter} onChange={onChangeTimeAfter} />
         </div>
       )}
-      <div className="block">
-        <h3>Frequency</h3>
-        <p>Blueprints will run recurringly based on the sync frequency.</p>
-        <p style={{ margin: '10px 0' }}>{cron.description}</p>
-        <RadioGroup selectedValue={cron.value} 
onChange={handleChangeFrequency}>
-          {options.map(({ label, value }) => (
-            <Radio key={value} label={label} value={value} />
-          ))}
-        </RadioGroup>
-        {cron.value === 'custom' && (
-          <S.Input>
-            <InputGroup value={cronConfig} onChange={(e) => 
onChangeCronConfig(e.target.value)} />
-            <Popover2
-              position={Position.RIGHT}
-              content={
-                <S.Help>
-                  <div className="title">
-                    <Icon icon="help" />
-                    <span>Cron Expression Format</span>
-                  </div>
-                  <p>
-                    Need Help? &mdash; For additional information on 
<strong>Crontab</strong>, please reference the{' '}
-                    <a
-                      
href="https://man7.org/linux/man-pages/man5/crontab.5.html";
-                      rel="noreferrer"
-                      target="_blank"
-                      style={{ textDecoration: 'underline' }}
-                    >
-                      Crontab Linux manual
-                    </a>
-                    .
-                  </p>
-                  <img src={CronHelp} alt="" />
-                </S.Help>
-              }
-            >
-              <Icon icon="help" size={14} style={{ marginLeft: '10px', 
transition: 'none' }} />
-            </Popover2>
-          </S.Input>
-        )}
+      <div className="block" style={{ display: 'flex' }}>
+        <div className="left" style={{ flex: '0 0 500px' }}>
+          <h3>Frequency</h3>
+          <p>
+            Blueprints will run on creation and recurringly based on the 
schedule. The time shown below is your LOCAL
+            time.
+          </p>
+          <RadioGroup selectedValue={cron.value} 
onChange={handleChangeFrequency}>
+            {options.map(({ value, label, subLabel }) => (
+              <Radio key={value} label={`${label} ${subLabel}`} value={value} 
/>
+            ))}
+          </RadioGroup>
+          {cron.value === 'custom' && (
+            <>
+              <S.Input>
+                <FormGroup label="Minute">
+                  <InputGroup
+                    value={mintue}
+                    onChange={(e) => onChangeCronConfig([e.target.value, hour, 
day, month, week].join(' '))}
+                  />
+                </FormGroup>
+                <FormGroup label="Hour">
+                  <InputGroup
+                    value={hour}
+                    onChange={(e) => onChangeCronConfig([mintue, 
e.target.value, day, month, week].join(' '))}
+                  />
+                </FormGroup>
+                <FormGroup label="Day">
+                  <InputGroup
+                    value={day}
+                    onChange={(e) => onChangeCronConfig([mintue, hour, 
e.target.value, month, week].join(' '))}
+                  />
+                </FormGroup>
+                <FormGroup label="Month">
+                  <InputGroup
+                    value={month}
+                    onChange={(e) => onChangeCronConfig([mintue, hour, day, 
e.target.value, week].join(' '))}
+                  />
+                </FormGroup>
+                <FormGroup label="Week">
+                  <InputGroup
+                    value={week}
+                    onChange={(e) => onChangeCronConfig([mintue, hour, day, 
month, e.target.value].join(' '))}
+                  />
+                </FormGroup>
+              </S.Input>
+              {!cron.nextTime && <S.Error>Invalid Cron code, please enter 
again.</S.Error>}
+            </>
+          )}
+        </div>
+        <div className="right">
+          <h3>Next Run Time:</h3>
+          <h4>{cron.nextTime ? dayjs(cron.nextTime).format('YYYY-MM-DD HH:mm 
A') : 'N/A'}</h4>
+        </div>
       </div>
       <div className="block">
         <h3>Running Policy</h3>
diff --git a/config-ui/src/pages/blueprint/components/sync-policy/styled.ts 
b/config-ui/src/pages/blueprint/components/sync-policy/styled.ts
index cea6c13ad..41c3b55f3 100644
--- a/config-ui/src/pages/blueprint/components/sync-policy/styled.ts
+++ b/config-ui/src/pages/blueprint/components/sync-policy/styled.ts
@@ -27,28 +27,20 @@ export const Wrapper = styled.div`
 export const Input = styled.div`
   display: flex;
   align-items: center;
-`;
-
-export const Help = styled.div`
-  padding: 10px;
-  width: 300px;
-  font-size: 12px;
-
-  .title {
-    margin-bottom: 10px;
-    font-size: 14px;
-    font-weight: 700px;
 
-    span.bp4-icon {
-      margin-right: 4px;
-    }
+  .bp4-form-group + .bp4-form-group {
+    margin-left: 8px;
   }
 
-  img {
-    width: 100%;
+  .bp4-input {
+    width: 60px;
   }
 `;
 
+export const Error = styled.div`
+  color: #e34040;
+`;
+
 export const FromTimeWrapper = styled.div`
   .quick-selection {
     margin-bottom: 16px;

Reply via email to