Edit.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. });
  84. const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
  85. const navigate = useNavigate();
  86. React.useEffect(() => {
  87. fetchTopicConfig({ clusterName, topicName });
  88. }, [fetchTopicConfig, clusterName, topicName]);
  89. React.useEffect(() => {
  90. if (isSubmitting && isTopicUpdated) {
  91. navigate('../');
  92. }
  93. }, [isSubmitting, isTopicUpdated, clusterName, navigate]);
  94. if (!isFetched || !topic || !topic.config) {
  95. return null;
  96. }
  97. if (!formInit) {
  98. methods.reset(defaultValues);
  99. formInit = true;
  100. }
  101. const config: TopicConfigByName = {
  102. byName: {},
  103. };
  104. topic.config.forEach((param) => {
  105. config.byName[param.name] = param;
  106. });
  107. const onSubmit = async (data: TopicFormDataRaw) => {
  108. updateTopic({ clusterName, topicName, form: data });
  109. setIsSubmitting(true); // Keep this action after updateTopic to prevent redirect before update.
  110. };
  111. return (
  112. <>
  113. <PageHeading text={`Edit ${topicName}`} />
  114. <EditWrapperStyled>
  115. <div>
  116. <FormProvider {...methods}>
  117. <TopicForm
  118. topicName={topicName}
  119. isSubmitting={isSubmitting}
  120. isEditing
  121. onSubmit={methods.handleSubmit(onSubmit)}
  122. />
  123. </FormProvider>
  124. {topic && (
  125. <DangerZoneContainer
  126. defaultPartitions={defaultValues.partitions}
  127. defaultReplicationFactor={
  128. defaultValues.replicationFactor || DEFAULTS.replicationFactor
  129. }
  130. />
  131. )}
  132. </div>
  133. </EditWrapperStyled>
  134. </>
  135. );
  136. };
  137. export default Edit;