Browse Source

Enhancement/improve time to retain usability v2 (#35)

* enhancement/improve-time-to-retain-usability

* add-btn-controls-for-time-to-retain-for-topics
Azat Gataullin 5 years ago
parent
commit
2d45c488f9

+ 80 - 81
kafka-ui-react-app/src/components/Topics/New/New.tsx

@@ -1,16 +1,17 @@
 import React from 'react';
-import { ClusterName, CleanupPolicy, TopicFormData, TopicName } from 'redux/interfaces';
+import {
+  ClusterName,
+  CleanupPolicy,
+  TopicFormData,
+  TopicName,
+} from 'redux/interfaces';
 import { useForm, FormContext, ErrorMessage } from 'react-hook-form';
 
 import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
-import CustomParamsContainer from "./CustomParams/CustomParamsContainer";
-import TimeToRetain from './TimeToRetain';
 import { clusterTopicsPath } from 'lib/paths';
-import {
-  TOPIC_NAME_VALIDATION_PATTERN,
-  BYTES_IN_GB,
-} from 'lib/constants';
-
+import { TOPIC_NAME_VALIDATION_PATTERN, BYTES_IN_GB } from 'lib/constants';
+import CustomParamsContainer from './CustomParams/CustomParamsContainer';
+import TimeToRetain from './TimeToRetain';
 
 interface Props {
   clusterName: ClusterName;
@@ -21,29 +22,32 @@ interface Props {
 }
 
 const New: React.FC<Props> = ({
-                                clusterName,
-                                isTopicCreated,
-                                createTopic,
-                                redirectToTopicPath,
-                                resetUploadedState
-                              }) => {
+  clusterName,
+  isTopicCreated,
+  createTopic,
+  redirectToTopicPath,
+  resetUploadedState,
+}) => {
   const methods = useForm<TopicFormData>();
   const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
 
-  React.useEffect(
-    () => {
-      if (isSubmitting && isTopicCreated) {
-        const {name} = methods.getValues();
-        redirectToTopicPath(clusterName, name);
-      }
-    },
-    [isSubmitting, isTopicCreated, redirectToTopicPath, clusterName, methods.getValues],
-  );
+  React.useEffect(() => {
+    if (isSubmitting && isTopicCreated) {
+      const { name } = methods.getValues();
+      redirectToTopicPath(clusterName, name);
+    }
+  }, [
+      isSubmitting,
+      isTopicCreated,
+      redirectToTopicPath,
+      clusterName,
+      methods.getValues,
+    ]);
 
   const onSubmit = async (data: TopicFormData) => {
-    //TODO: need to fix loader. After success loading the first time, we won't wait for creation any more, because state is
-    //loaded, and we will try to get entity immediately after pressing the button, and we will receive null
-    //going to object page on the second creation. Resetting loaded state is workaround, need to tweak loader logic
+    // TODO: need to fix loader. After success loading the first time, we won't wait for creation any more, because state is
+    // loaded, and we will try to get entity immediately after pressing the button, and we will receive null
+    // going to object page on the second creation. Resetting loaded state is workaround, need to tweak loader logic
     resetUploadedState();
     setIsSubmitting(true);
     createTopic(clusterName, data);
@@ -53,9 +57,11 @@ const New: React.FC<Props> = ({
     <div className="section">
       <div className="level">
         <div className="level-item level-left">
-          <Breadcrumb links={[
-            {href: clusterTopicsPath(clusterName), label: 'All Topics'},
-          ]}>
+          <Breadcrumb
+            links={[
+              { href: clusterTopicsPath(clusterName), label: 'All Topics' },
+            ]}
+          >
             New Topic
           </Breadcrumb>
         </div>
@@ -66,9 +72,7 @@ const New: React.FC<Props> = ({
           <form onSubmit={methods.handleSubmit(onSubmit)}>
             <div className="columns">
               <div className="column is-three-quarters">
-                <label className="label">
-                  Topic Name *
-                </label>
+                <label className="label">Topic Name *</label>
                 <input
                   className="input"
                   placeholder="Topic Name"
@@ -84,72 +88,76 @@ const New: React.FC<Props> = ({
                   disabled={isSubmitting}
                 />
                 <p className="help is-danger">
-                  <ErrorMessage errors={methods.errors} name="name"/>
+                  <ErrorMessage errors={methods.errors} name="name" />
                 </p>
               </div>
 
               <div className="column">
-                <label className="label">
-                  Number of partitions *
-                </label>
+                <label className="label">Number of partitions *</label>
                 <input
                   className="input"
                   type="number"
                   placeholder="Number of partitions"
                   defaultValue="1"
-                  ref={methods.register({required: 'Number of partitions is required.'})}
+                  ref={methods.register({
+                    required: 'Number of partitions is required.',
+                  })}
                   name="partitions"
                   disabled={isSubmitting}
                 />
                 <p className="help is-danger">
-                  <ErrorMessage errors={methods.errors} name="partitions"/>
+                  <ErrorMessage errors={methods.errors} name="partitions" />
                 </p>
               </div>
             </div>
 
             <div className="columns">
               <div className="column">
-                <label className="label">
-                  Replication Factor *
-                </label>
+                <label className="label">Replication Factor *</label>
                 <input
                   className="input"
                   type="number"
                   placeholder="Replication Factor"
                   defaultValue="1"
-                  ref={methods.register({required: 'Replication Factor is required.'})}
+                  ref={methods.register({
+                    required: 'Replication Factor is required.',
+                  })}
                   name="replicationFactor"
                   disabled={isSubmitting}
                 />
                 <p className="help is-danger">
-                  <ErrorMessage errors={methods.errors} name="replicationFactor"/>
+                  <ErrorMessage
+                    errors={methods.errors}
+                    name="replicationFactor"
+                  />
                 </p>
               </div>
 
               <div className="column">
-                <label className="label">
-                  Min In Sync Replicas *
-                </label>
+                <label className="label">Min In Sync Replicas *</label>
                 <input
                   className="input"
                   type="number"
                   placeholder="Replication Factor"
                   defaultValue="1"
-                  ref={methods.register({required: 'Min In Sync Replicas is required.'})}
+                  ref={methods.register({
+                    required: 'Min In Sync Replicas is required.',
+                  })}
                   name="minInSyncReplicas"
                   disabled={isSubmitting}
                 />
                 <p className="help is-danger">
-                  <ErrorMessage errors={methods.errors} name="minInSyncReplicas"/>
+                  <ErrorMessage
+                    errors={methods.errors}
+                    name="minInSyncReplicas"
+                  />
                 </p>
               </div>
             </div>
 
             <div className="columns">
               <div className="column is-one-third">
-                <label className="label">
-                  Cleanup policy
-                </label>
+                <label className="label">Cleanup policy</label>
                 <div className="select is-block">
                   <select
                     defaultValue={CleanupPolicy.Delete}
@@ -157,12 +165,8 @@ const New: React.FC<Props> = ({
                     ref={methods.register}
                     disabled={isSubmitting}
                   >
-                    <option value={CleanupPolicy.Delete}>
-                      Delete
-                    </option>
-                    <option value={CleanupPolicy.Compact}>
-                      Compact
-                    </option>
+                    <option value={CleanupPolicy.Delete}>Delete</option>
+                    <option value={CleanupPolicy.Compact}>Compact</option>
                   </select>
                 </div>
               </div>
@@ -172,9 +176,7 @@ const New: React.FC<Props> = ({
               </div>
 
               <div className="column is-one-third">
-                <label className="label">
-                  Max size on disk in GB
-                </label>
+                <label className="label">Max size on disk in GB</label>
                 <div className="select is-block">
                   <select
                     defaultValue={-1}
@@ -182,21 +184,11 @@ const New: React.FC<Props> = ({
                     ref={methods.register}
                     disabled={isSubmitting}
                   >
-                    <option value={-1}>
-                      Not Set
-                    </option>
-                    <option value={BYTES_IN_GB}>
-                      1 GB
-                    </option>
-                    <option value={BYTES_IN_GB * 10}>
-                      10 GB
-                    </option>
-                    <option value={BYTES_IN_GB * 20}>
-                      20 GB
-                    </option>
-                    <option value={BYTES_IN_GB * 50}>
-                      50 GB
-                    </option>
+                    <option value={-1}>Not Set</option>
+                    <option value={BYTES_IN_GB}>1 GB</option>
+                    <option value={BYTES_IN_GB * 10}>10 GB</option>
+                    <option value={BYTES_IN_GB * 20}>20 GB</option>
+                    <option value={BYTES_IN_GB * 50}>50 GB</option>
                   </select>
                 </div>
               </div>
@@ -204,26 +196,33 @@ const New: React.FC<Props> = ({
 
             <div className="columns">
               <div className="column">
-                <label className="label">
-                  Maximum message size in bytes *
-                </label>
+                <label className="label">Maximum message size in bytes *</label>
                 <input
                   className="input"
                   type="number"
                   defaultValue="1000012"
-                  ref={methods.register({required: 'Maximum message size in bytes is required'})}
+                  ref={methods.register({
+                    required: 'Maximum message size in bytes is required',
+                  })}
                   name="maxMessageBytes"
                   disabled={isSubmitting}
                 />
                 <p className="help is-danger">
-                  <ErrorMessage errors={methods.errors} name="maxMessageBytes"/>
+                  <ErrorMessage
+                    errors={methods.errors}
+                    name="maxMessageBytes"
+                  />
                 </p>
               </div>
             </div>
 
             <CustomParamsContainer isSubmitting={isSubmitting} />
 
-            <input type="submit" className="button is-primary" disabled={isSubmitting}/>
+            <input
+              type="submit"
+              className="button is-primary"
+              disabled={isSubmitting}
+            />
           </form>
         </FormContext>
       </div>

+ 21 - 22
kafka-ui-react-app/src/components/Topics/New/TimeToRetain.tsx

@@ -1,53 +1,52 @@
 import React from 'react';
 import prettyMilliseconds from 'pretty-ms';
 import { useFormContext, ErrorMessage } from 'react-hook-form';
-import { MILLISECONDS_IN_WEEK } from 'lib/constants';
-
-const MILLISECONDS_IN_SECOND = 1000;
+import { MILLISECONDS_IN_WEEK, MILLISECONDS_IN_SECOND } from 'lib/constants';
+import TimeToRetainBtns from './TimeToRetainBtns';
 
 interface Props {
   isSubmitting: boolean;
 }
 
-const TimeToRetain: React.FC<Props> = ({
-  isSubmitting,
-}) => {
+const TimeToRetain: React.FC<Props> = ({ isSubmitting }) => {
   const { register, errors, watch } = useFormContext();
   const defaultValue = MILLISECONDS_IN_WEEK;
-  const name: string = 'retentionMs';
-  const watchedValue: any = watch(name, defaultValue.toString());
+  const name = 'retentionMs';
+  const watchedValue = watch(name, defaultValue.toString());
 
   const valueHint = React.useMemo(() => {
     const value = parseInt(watchedValue, 10);
     return value >= MILLISECONDS_IN_SECOND ? prettyMilliseconds(value) : false;
-  }, [watchedValue])
+  }, [watchedValue]);
 
   return (
     <>
-      <label className="label">
-        Time to retain data (in ms)
+      <label
+        className="label is-flex"
+        style={{ justifyContent: 'space-between' }}
+      >
+        <div>Time to retain data (in ms)</div>
+        {valueHint && <span className="has-text-info">{valueHint}</span>}
       </label>
       <input
         className="input"
+        id="timeToRetain"
         type="number"
         defaultValue={defaultValue}
         name={name}
-        ref={register(
-          { min: { value: -1, message: 'must be greater than or equal to -1' }}
-        )}
+        ref={register({
+          min: { value: -1, message: 'must be greater than or equal to -1' },
+        })}
         disabled={isSubmitting}
       />
+
       <p className="help is-danger">
-        <ErrorMessage errors={errors} name={name}/>
+        <ErrorMessage errors={errors} name={name} />
       </p>
-      {
-        valueHint &&
-        <p className="help is-info">
-          {valueHint}
-        </p>
-      }
+
+      <TimeToRetainBtns name={name} value={watchedValue} />
     </>
   );
-}
+};
 
 export default TimeToRetain;

+ 29 - 0
kafka-ui-react-app/src/components/Topics/New/TimeToRetainBtn.tsx

@@ -0,0 +1,29 @@
+import React from 'react';
+import { useFormContext } from 'react-hook-form';
+import cx from 'classnames';
+import { MILLISECONDS_IN_WEEK } from 'lib/constants';
+
+interface Props {
+  inputName: string;
+  text: string;
+  value: number;
+}
+
+const TimeToRetainBtn: React.FC<Props> = ({ inputName, text, value }) => {
+  const { setValue, watch } = useFormContext();
+  const watchedValue = watch(inputName, MILLISECONDS_IN_WEEK.toString());
+
+  return (
+    <button
+      type="button"
+      className={cx('button', {
+        'is-info': watchedValue === value.toString(),
+      })}
+      onClick={() => setValue(inputName, value)}
+    >
+      {text}
+    </button>
+  );
+};
+
+export default TimeToRetainBtn;

+ 36 - 0
kafka-ui-react-app/src/components/Topics/New/TimeToRetainBtns.tsx

@@ -0,0 +1,36 @@
+import React from 'react';
+import { MILLISECONDS_IN_DAY } from 'lib/constants';
+import TimeToRetainBtn from './TimeToRetainBtn';
+
+interface Props {
+  name: string;
+  value: string;
+}
+
+const TimeToRetainBtns: React.FC<Props> = ({ name }) => (
+  <div className="buttons are-small">
+    <TimeToRetainBtn
+      text="12h"
+      inputName={name}
+      value={MILLISECONDS_IN_DAY / 2}
+    />
+    <TimeToRetainBtn text="1d" inputName={name} value={MILLISECONDS_IN_DAY} />
+    <TimeToRetainBtn
+      text="2d"
+      inputName={name}
+      value={MILLISECONDS_IN_DAY * 2}
+    />
+    <TimeToRetainBtn
+      text="7d"
+      inputName={name}
+      value={MILLISECONDS_IN_DAY * 7}
+    />
+    <TimeToRetainBtn
+      text="4w"
+      inputName={name}
+      value={MILLISECONDS_IN_DAY * 7 * 24}
+    />
+  </div>
+);
+
+export default TimeToRetainBtns;

+ 2 - 0
kafka-ui-react-app/src/lib/constants.ts

@@ -11,5 +11,7 @@ export const BASE_URL = process.env.REACT_APP_API_URL;
 export const TOPIC_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
 
 export const MILLISECONDS_IN_WEEK = 604_800_000;
+export const MILLISECONDS_IN_DAY = 86_400_000;
+export const MILLISECONDS_IN_SECOND = 1_000;
 
 export const BYTES_IN_GB = 1_073_741_824;