Edit.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import React from 'react';
  2. import {
  3. ClusterName,
  4. TopicFormDataRaw,
  5. TopicName,
  6. TopicConfigByName,
  7. TopicWithDetailedInfo,
  8. TopicFormData,
  9. } from 'redux/interfaces';
  10. import { useForm, FormProvider } from 'react-hook-form';
  11. import TopicForm from 'components/Topics/shared/Form/TopicForm';
  12. import { RouteParamsClusterTopic } from 'lib/paths';
  13. import { useNavigate } from 'react-router-dom';
  14. import { yupResolver } from '@hookform/resolvers/yup';
  15. import { topicFormValidationSchema } from 'lib/yupExtended';
  16. import { TOPIC_CUSTOM_PARAMS_PREFIX, TOPIC_CUSTOM_PARAMS } from 'lib/constants';
  17. import styled from 'styled-components';
  18. import PageHeading from 'components/common/PageHeading/PageHeading';
  19. import { useAppSelector } from 'lib/hooks/redux';
  20. import { getFullTopic } from 'redux/reducers/topics/selectors';
  21. import useAppParams from 'lib/hooks/useAppParams';
  22. import DangerZoneContainer from './DangerZone/DangerZoneContainer';
  23. export interface Props {
  24. isFetched: boolean;
  25. isTopicUpdated: boolean;
  26. fetchTopicConfig: (payload: {
  27. clusterName: ClusterName;
  28. topicName: TopicName;
  29. }) => void;
  30. updateTopic: (payload: {
  31. clusterName: ClusterName;
  32. topicName: TopicName;
  33. form: TopicFormDataRaw;
  34. }) => void;
  35. }
  36. const EditWrapperStyled = styled.div`
  37. display: flex;
  38. justify-content: center;
  39. & > * {
  40. width: 800px;
  41. }
  42. `;
  43. export const DEFAULTS = {
  44. partitions: 1,
  45. replicationFactor: 1,
  46. minInSyncReplicas: 1,
  47. cleanupPolicy: 'delete',
  48. retentionBytes: -1,
  49. maxMessageBytes: 1000012,
  50. };
  51. const topicParams = (topic: TopicWithDetailedInfo | undefined) => {
  52. if (!topic) {
  53. return DEFAULTS;
  54. }
  55. const { name, replicationFactor } = topic;
  56. return {
  57. ...DEFAULTS,
  58. name,
  59. partitions: topic.partitionCount || DEFAULTS.partitions,
  60. replicationFactor,
  61. [TOPIC_CUSTOM_PARAMS_PREFIX]: topic.config
  62. ?.filter(
  63. (el) =>
  64. el.value !== el.defaultValue &&
  65. Object.keys(TOPIC_CUSTOM_PARAMS).includes(el.name)
  66. )
  67. .map((el) => ({ name: el.name, value: el.value })),
  68. };
  69. };
  70. let formInit = false;
  71. const Edit: React.FC<Props> = ({
  72. isFetched,
  73. isTopicUpdated,
  74. fetchTopicConfig,
  75. updateTopic,
  76. }) => {
  77. const { clusterName, topicName } = useAppParams<RouteParamsClusterTopic>();
  78. const topic = useAppSelector((state) => getFullTopic(state, topicName));
  79. const defaultValues = React.useMemo(() => topicParams(topic), [topic]);
  80. const methods = useForm<TopicFormData>({
  81. defaultValues,
  82. resolver: yupResolver(topicFormValidationSchema),
  83. mode: 'onChange',
  84. });
  85. const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
  86. const navigate = useNavigate();
  87. React.useEffect(() => {
  88. fetchTopicConfig({ clusterName, topicName });
  89. }, [fetchTopicConfig, clusterName, topicName]);
  90. React.useEffect(() => {
  91. if (isSubmitting && isTopicUpdated) {
  92. navigate('../');
  93. }
  94. }, [isSubmitting, isTopicUpdated, clusterName, navigate]);
  95. if (!isFetched || !topic || !topic.config) {
  96. return null;
  97. }
  98. if (!formInit) {
  99. methods.reset(defaultValues);
  100. formInit = true;
  101. }
  102. const config: TopicConfigByName = {
  103. byName: {},
  104. };
  105. topic.config.forEach((param) => {
  106. config.byName[param.name] = param;
  107. });
  108. const onSubmit = async (data: TopicFormDataRaw) => {
  109. updateTopic({ clusterName, topicName, form: data });
  110. setIsSubmitting(true); // Keep this action after updateTopic to prevent redirect before update.
  111. };
  112. return (
  113. <>
  114. <PageHeading text={`Edit ${topicName}`} />
  115. <EditWrapperStyled>
  116. <div>
  117. <FormProvider {...methods}>
  118. <TopicForm
  119. topicName={topicName}
  120. isSubmitting={isSubmitting}
  121. isEditing
  122. onSubmit={methods.handleSubmit(onSubmit)}
  123. />
  124. </FormProvider>
  125. {topic && (
  126. <DangerZoneContainer
  127. defaultPartitions={defaultValues.partitions}
  128. defaultReplicationFactor={
  129. defaultValues.replicationFactor || DEFAULTS.replicationFactor
  130. }
  131. />
  132. )}
  133. </div>
  134. </EditWrapperStyled>
  135. </>
  136. );
  137. };
  138. export default Edit;