Enhancement/improve time to retain usability v2 (#35)
* enhancement/improve-time-to-retain-usability * add-btn-controls-for-time-to-retain-for-topics
This commit is contained in:
parent
3c5b46bd76
commit
2d45c488f9
5 changed files with 168 additions and 103 deletions
|
@ -1,16 +1,17 @@
|
||||||
import React from 'react';
|
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 { useForm, FormContext, ErrorMessage } from 'react-hook-form';
|
||||||
|
|
||||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||||
import CustomParamsContainer from "./CustomParams/CustomParamsContainer";
|
|
||||||
import TimeToRetain from './TimeToRetain';
|
|
||||||
import { clusterTopicsPath } from 'lib/paths';
|
import { clusterTopicsPath } from 'lib/paths';
|
||||||
import {
|
import { TOPIC_NAME_VALIDATION_PATTERN, BYTES_IN_GB } from 'lib/constants';
|
||||||
TOPIC_NAME_VALIDATION_PATTERN,
|
import CustomParamsContainer from './CustomParams/CustomParamsContainer';
|
||||||
BYTES_IN_GB,
|
import TimeToRetain from './TimeToRetain';
|
||||||
} from 'lib/constants';
|
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
|
@ -21,29 +22,32 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const New: React.FC<Props> = ({
|
const New: React.FC<Props> = ({
|
||||||
clusterName,
|
clusterName,
|
||||||
isTopicCreated,
|
isTopicCreated,
|
||||||
createTopic,
|
createTopic,
|
||||||
redirectToTopicPath,
|
redirectToTopicPath,
|
||||||
resetUploadedState
|
resetUploadedState,
|
||||||
}) => {
|
}) => {
|
||||||
const methods = useForm<TopicFormData>();
|
const methods = useForm<TopicFormData>();
|
||||||
const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
|
const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(() => {
|
||||||
() => {
|
if (isSubmitting && isTopicCreated) {
|
||||||
if (isSubmitting && isTopicCreated) {
|
const { name } = methods.getValues();
|
||||||
const {name} = methods.getValues();
|
redirectToTopicPath(clusterName, name);
|
||||||
redirectToTopicPath(clusterName, name);
|
}
|
||||||
}
|
}, [
|
||||||
},
|
isSubmitting,
|
||||||
[isSubmitting, isTopicCreated, redirectToTopicPath, clusterName, methods.getValues],
|
isTopicCreated,
|
||||||
);
|
redirectToTopicPath,
|
||||||
|
clusterName,
|
||||||
|
methods.getValues,
|
||||||
|
]);
|
||||||
|
|
||||||
const onSubmit = async (data: TopicFormData) => {
|
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
|
// 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
|
// 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
|
// going to object page on the second creation. Resetting loaded state is workaround, need to tweak loader logic
|
||||||
resetUploadedState();
|
resetUploadedState();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
createTopic(clusterName, data);
|
createTopic(clusterName, data);
|
||||||
|
@ -53,9 +57,11 @@ const New: React.FC<Props> = ({
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<div className="level">
|
<div className="level">
|
||||||
<div className="level-item level-left">
|
<div className="level-item level-left">
|
||||||
<Breadcrumb links={[
|
<Breadcrumb
|
||||||
{href: clusterTopicsPath(clusterName), label: 'All Topics'},
|
links={[
|
||||||
]}>
|
{ href: clusterTopicsPath(clusterName), label: 'All Topics' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
New Topic
|
New Topic
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,9 +72,7 @@ const New: React.FC<Props> = ({
|
||||||
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-three-quarters">
|
<div className="column is-three-quarters">
|
||||||
<label className="label">
|
<label className="label">Topic Name *</label>
|
||||||
Topic Name *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
placeholder="Topic Name"
|
placeholder="Topic Name"
|
||||||
|
@ -84,72 +88,76 @@ const New: React.FC<Props> = ({
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={methods.errors} name="name"/>
|
<ErrorMessage errors={methods.errors} name="name" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<label className="label">
|
<label className="label">Number of partitions *</label>
|
||||||
Number of partitions *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Number of partitions"
|
placeholder="Number of partitions"
|
||||||
defaultValue="1"
|
defaultValue="1"
|
||||||
ref={methods.register({required: 'Number of partitions is required.'})}
|
ref={methods.register({
|
||||||
|
required: 'Number of partitions is required.',
|
||||||
|
})}
|
||||||
name="partitions"
|
name="partitions"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={methods.errors} name="partitions"/>
|
<ErrorMessage errors={methods.errors} name="partitions" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<label className="label">
|
<label className="label">Replication Factor *</label>
|
||||||
Replication Factor *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Replication Factor"
|
placeholder="Replication Factor"
|
||||||
defaultValue="1"
|
defaultValue="1"
|
||||||
ref={methods.register({required: 'Replication Factor is required.'})}
|
ref={methods.register({
|
||||||
|
required: 'Replication Factor is required.',
|
||||||
|
})}
|
||||||
name="replicationFactor"
|
name="replicationFactor"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={methods.errors} name="replicationFactor"/>
|
<ErrorMessage
|
||||||
|
errors={methods.errors}
|
||||||
|
name="replicationFactor"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<label className="label">
|
<label className="label">Min In Sync Replicas *</label>
|
||||||
Min In Sync Replicas *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Replication Factor"
|
placeholder="Replication Factor"
|
||||||
defaultValue="1"
|
defaultValue="1"
|
||||||
ref={methods.register({required: 'Min In Sync Replicas is required.'})}
|
ref={methods.register({
|
||||||
|
required: 'Min In Sync Replicas is required.',
|
||||||
|
})}
|
||||||
name="minInSyncReplicas"
|
name="minInSyncReplicas"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={methods.errors} name="minInSyncReplicas"/>
|
<ErrorMessage
|
||||||
|
errors={methods.errors}
|
||||||
|
name="minInSyncReplicas"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-one-third">
|
<div className="column is-one-third">
|
||||||
<label className="label">
|
<label className="label">Cleanup policy</label>
|
||||||
Cleanup policy
|
|
||||||
</label>
|
|
||||||
<div className="select is-block">
|
<div className="select is-block">
|
||||||
<select
|
<select
|
||||||
defaultValue={CleanupPolicy.Delete}
|
defaultValue={CleanupPolicy.Delete}
|
||||||
|
@ -157,12 +165,8 @@ const New: React.FC<Props> = ({
|
||||||
ref={methods.register}
|
ref={methods.register}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
<option value={CleanupPolicy.Delete}>
|
<option value={CleanupPolicy.Delete}>Delete</option>
|
||||||
Delete
|
<option value={CleanupPolicy.Compact}>Compact</option>
|
||||||
</option>
|
|
||||||
<option value={CleanupPolicy.Compact}>
|
|
||||||
Compact
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -172,9 +176,7 @@ const New: React.FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="column is-one-third">
|
<div className="column is-one-third">
|
||||||
<label className="label">
|
<label className="label">Max size on disk in GB</label>
|
||||||
Max size on disk in GB
|
|
||||||
</label>
|
|
||||||
<div className="select is-block">
|
<div className="select is-block">
|
||||||
<select
|
<select
|
||||||
defaultValue={-1}
|
defaultValue={-1}
|
||||||
|
@ -182,21 +184,11 @@ const New: React.FC<Props> = ({
|
||||||
ref={methods.register}
|
ref={methods.register}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
<option value={-1}>
|
<option value={-1}>Not Set</option>
|
||||||
Not Set
|
<option value={BYTES_IN_GB}>1 GB</option>
|
||||||
</option>
|
<option value={BYTES_IN_GB * 10}>10 GB</option>
|
||||||
<option value={BYTES_IN_GB}>
|
<option value={BYTES_IN_GB * 20}>20 GB</option>
|
||||||
1 GB
|
<option value={BYTES_IN_GB * 50}>50 GB</option>
|
||||||
</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -204,26 +196,33 @@ const New: React.FC<Props> = ({
|
||||||
|
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<label className="label">
|
<label className="label">Maximum message size in bytes *</label>
|
||||||
Maximum message size in bytes *
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue="1000012"
|
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"
|
name="maxMessageBytes"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={methods.errors} name="maxMessageBytes"/>
|
<ErrorMessage
|
||||||
|
errors={methods.errors}
|
||||||
|
name="maxMessageBytes"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CustomParamsContainer isSubmitting={isSubmitting} />
|
<CustomParamsContainer isSubmitting={isSubmitting} />
|
||||||
|
|
||||||
<input type="submit" className="button is-primary" disabled={isSubmitting}/>
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="button is-primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormContext>
|
</FormContext>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,53 +1,52 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import prettyMilliseconds from 'pretty-ms';
|
import prettyMilliseconds from 'pretty-ms';
|
||||||
import { useFormContext, ErrorMessage } from 'react-hook-form';
|
import { useFormContext, ErrorMessage } from 'react-hook-form';
|
||||||
import { MILLISECONDS_IN_WEEK } from 'lib/constants';
|
import { MILLISECONDS_IN_WEEK, MILLISECONDS_IN_SECOND } from 'lib/constants';
|
||||||
|
import TimeToRetainBtns from './TimeToRetainBtns';
|
||||||
const MILLISECONDS_IN_SECOND = 1000;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimeToRetain: React.FC<Props> = ({
|
const TimeToRetain: React.FC<Props> = ({ isSubmitting }) => {
|
||||||
isSubmitting,
|
|
||||||
}) => {
|
|
||||||
const { register, errors, watch } = useFormContext();
|
const { register, errors, watch } = useFormContext();
|
||||||
const defaultValue = MILLISECONDS_IN_WEEK;
|
const defaultValue = MILLISECONDS_IN_WEEK;
|
||||||
const name: string = 'retentionMs';
|
const name = 'retentionMs';
|
||||||
const watchedValue: any = watch(name, defaultValue.toString());
|
const watchedValue = watch(name, defaultValue.toString());
|
||||||
|
|
||||||
const valueHint = React.useMemo(() => {
|
const valueHint = React.useMemo(() => {
|
||||||
const value = parseInt(watchedValue, 10);
|
const value = parseInt(watchedValue, 10);
|
||||||
return value >= MILLISECONDS_IN_SECOND ? prettyMilliseconds(value) : false;
|
return value >= MILLISECONDS_IN_SECOND ? prettyMilliseconds(value) : false;
|
||||||
}, [watchedValue])
|
}, [watchedValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label className="label">
|
<label
|
||||||
Time to retain data (in ms)
|
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>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
|
id="timeToRetain"
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
name={name}
|
name={name}
|
||||||
ref={register(
|
ref={register({
|
||||||
{ min: { value: -1, message: 'must be greater than or equal to -1' }}
|
min: { value: -1, message: 'must be greater than or equal to -1' },
|
||||||
)}
|
})}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="help is-danger">
|
<p className="help is-danger">
|
||||||
<ErrorMessage errors={errors} name={name}/>
|
<ErrorMessage errors={errors} name={name} />
|
||||||
</p>
|
</p>
|
||||||
{
|
|
||||||
valueHint &&
|
<TimeToRetainBtns name={name} value={watchedValue} />
|
||||||
<p className="help is-info">
|
|
||||||
{valueHint}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default TimeToRetain;
|
export default TimeToRetain;
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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 TOPIC_NAME_VALIDATION_PATTERN = RegExp(/^[.,A-Za-z0-9_-]+$/);
|
||||||
|
|
||||||
export const MILLISECONDS_IN_WEEK = 604_800_000;
|
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;
|
export const BYTES_IN_GB = 1_073_741_824;
|
||||||
|
|
Loading…
Add table
Reference in a new issue