PorterForm.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import React, { useContext, useState } from "react";
  2. import {
  3. Section,
  4. FormField,
  5. InputField,
  6. CheckboxField,
  7. KeyValueArrayField,
  8. ArrayInputField,
  9. SelectField,
  10. ServiceIPListField,
  11. ResourceListField,
  12. } from "./types";
  13. import TabRegion, { TabOption } from "../TabRegion";
  14. import Heading from "../form-components/Heading";
  15. import Helper from "../form-components/Helper";
  16. import Input from "./field-components/Input";
  17. import { PorterFormContext } from "./PorterFormContextProvider";
  18. import Checkbox from "./field-components/Checkbox";
  19. import KeyValueArray from "./field-components/KeyValueArray";
  20. import styled from "styled-components";
  21. import SaveButton from "../SaveButton";
  22. import ArrayInput from "./field-components/ArrayInput";
  23. import Select from "./field-components/Select";
  24. import ServiceIPList from "./field-components/ServiceIPList";
  25. import ResourceList from "./field-components/ResourceList";
  26. import VeleroForm from "./field-components/VeleroForm";
  27. interface Props {
  28. leftTabOptions?: TabOption[];
  29. rightTabOptions?: TabOption[];
  30. renderTabContents?: (
  31. currentTab: string,
  32. submitValues?: any
  33. ) => React.ReactElement;
  34. saveButtonText?: string;
  35. isReadOnly?: boolean;
  36. isInModal?: boolean;
  37. color?: string;
  38. addendum?: any;
  39. saveValuesStatus?: string;
  40. showStateDebugger?: boolean;
  41. currentTab: string;
  42. setCurrentTab: (nt: string) => void;
  43. }
  44. const PorterForm: React.FC<Props> = (props) => {
  45. const {
  46. formData,
  47. isReadOnly,
  48. validationInfo,
  49. onSubmit,
  50. formState,
  51. } = useContext(PorterFormContext);
  52. const { currentTab, setCurrentTab } = props;
  53. const renderSectionField = (field: FormField): JSX.Element => {
  54. const bundledProps = {
  55. ...field,
  56. isReadOnly,
  57. };
  58. switch (field.type) {
  59. case "heading":
  60. return <Heading>{field.label}</Heading>;
  61. case "subtitle":
  62. return <Helper>{field.label}</Helper>;
  63. case "input":
  64. return <Input {...(bundledProps as InputField)} />;
  65. case "checkbox":
  66. return <Checkbox {...(bundledProps as CheckboxField)} />;
  67. case "key-value-array":
  68. return <KeyValueArray {...(bundledProps as KeyValueArrayField)} />;
  69. case "array-input":
  70. return <ArrayInput {...(bundledProps as ArrayInputField)} />;
  71. case "select":
  72. return <Select {...(bundledProps as SelectField)} />;
  73. case "service-ip-list":
  74. return <ServiceIPList {...(bundledProps as ServiceIPListField)} />;
  75. case "resource-list":
  76. return <ResourceList {...(bundledProps as ResourceListField)} />;
  77. case "velero-create-backup":
  78. return <VeleroForm />;
  79. }
  80. return <p>Not Implemented: {(field as any).type}</p>;
  81. };
  82. const renderSection = (section: Section): JSX.Element => {
  83. return (
  84. <>
  85. {section.contents?.map((field, i) => {
  86. return (
  87. <React.Fragment key={field.id}>
  88. {renderSectionField(field)}
  89. </React.Fragment>
  90. );
  91. })}
  92. </>
  93. );
  94. };
  95. const getTabOptions = (): TabOption[] => {
  96. let options = (props.leftTabOptions || [])
  97. .concat(
  98. formData?.tabs?.map((tab) => {
  99. return { label: tab.label, value: tab.name };
  100. })
  101. )
  102. .concat(props.rightTabOptions || []);
  103. return options.filter((x) => x !== undefined);
  104. };
  105. const showSaveButton = (): boolean => {
  106. if (props.isReadOnly) {
  107. return false;
  108. }
  109. let returnVal = true;
  110. props.leftTabOptions?.forEach((tab: any) => {
  111. if (tab.value === currentTab) {
  112. returnVal = false;
  113. }
  114. });
  115. props.rightTabOptions?.forEach((tab: any) => {
  116. if (tab.value === currentTab) {
  117. returnVal = false;
  118. }
  119. });
  120. return returnVal;
  121. };
  122. const renderTab = (): JSX.Element => {
  123. if (!formData) {
  124. return props.renderTabContents(currentTab);
  125. }
  126. const tab = formData.tabs?.filter((tab) => tab.name == currentTab)[0];
  127. // Handle external tab
  128. if (!tab) {
  129. return props.renderTabContents ? (
  130. props.renderTabContents(currentTab)
  131. ) : (
  132. <></>
  133. );
  134. }
  135. return (
  136. <StyledPorterForm showSave={showSaveButton()}>
  137. {tab.sections?.map((section) => {
  138. return (
  139. <React.Fragment key={section.name}>
  140. {renderSection(section)}
  141. </React.Fragment>
  142. );
  143. })}
  144. </StyledPorterForm>
  145. );
  146. };
  147. const isDisabled = () => {
  148. if (props.saveValuesStatus == "loading") {
  149. return true;
  150. }
  151. return isReadOnly || !validationInfo.validated;
  152. };
  153. const renderSaveStatus = (): string => {
  154. if (isDisabled() && props.saveValuesStatus !== "loading") {
  155. return "Missing required fields";
  156. }
  157. return props.saveValuesStatus;
  158. };
  159. return (
  160. <>
  161. <TabRegion
  162. addendum={props.addendum}
  163. color={props.color}
  164. options={getTabOptions()}
  165. currentTab={currentTab}
  166. setCurrentTab={setCurrentTab}
  167. suppressAnimation={true}
  168. >
  169. {renderTab()}
  170. </TabRegion>
  171. <br />
  172. {showSaveButton() && (
  173. <SaveButton
  174. text={props.saveButtonText || "Deploy"}
  175. onClick={onSubmit}
  176. makeFlush={!props.isInModal}
  177. status={
  178. validationInfo.validated ? renderSaveStatus() : validationInfo.error
  179. }
  180. disabled={isDisabled()}
  181. />
  182. )}
  183. {props.showStateDebugger && (
  184. <Pre>{JSON.stringify(formState, undefined, 2)}</Pre>
  185. )}
  186. <Spacer />
  187. </>
  188. );
  189. };
  190. export default PorterForm;
  191. const Pre = styled.pre`
  192. font-size: 13px;
  193. color: #aaaabb;
  194. `;
  195. const Spacer = styled.div`
  196. height: 50px;
  197. `;
  198. const StyledPorterForm = styled.div<{ showSave?: boolean }>`
  199. width: 100%;
  200. height: ${(props) => (props.showSave ? "calc(100% - 50px)" : "100%")};
  201. background: #ffffff11;
  202. color: #ffffff;
  203. padding: 0px 35px 25px;
  204. position: relative;
  205. border-radius: 8px;
  206. font-size: 13px;
  207. overflow: auto;
  208. `;