FormDebugger.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. import React, { Component } from "react";
  2. import styled from "styled-components";
  3. import AceEditor from "react-ace";
  4. import PorterFormWrapper from "./PorterFormWrapper";
  5. import CheckboxRow from "components/form-components/CheckboxRow";
  6. import InputRow from "components/form-components/InputRow";
  7. import yaml from "js-yaml";
  8. import "shared/ace-porter-theme";
  9. import "ace-builds/src-noconflict/mode-text";
  10. import Heading from "../form-components/Heading";
  11. import Helper from "../form-components/Helper";
  12. type PropsType = {
  13. goBack: () => void;
  14. };
  15. type StateType = {
  16. rawYaml: string;
  17. showBonusTabs: boolean;
  18. showStateDebugger: boolean;
  19. valuesToOverride: any;
  20. checkbox_a: boolean;
  21. input_a: string;
  22. isReadOnly: boolean;
  23. };
  24. const tabOptions = [
  25. { value: "a", label: "Bonus Tab A" },
  26. { value: "b", label: "Bonus Tab B" },
  27. ];
  28. export default class FormDebugger extends Component<PropsType, StateType> {
  29. state = {
  30. rawYaml: initYaml,
  31. showBonusTabs: false,
  32. showStateDebugger: true,
  33. valuesToOverride: {
  34. checkbox_a: {
  35. value: true,
  36. },
  37. } as any,
  38. checkbox_a: true,
  39. input_a: "",
  40. isReadOnly: false,
  41. };
  42. renderTabContents = (currentTab: string) => {
  43. return (
  44. <TabWrapper>
  45. {this.state.rawYaml.toString().slice(0, 300) || "No raw YAML inputted."}
  46. </TabWrapper>
  47. );
  48. };
  49. aceEditorRef = React.createRef<AceEditor>();
  50. render() {
  51. let formData = {};
  52. try {
  53. formData = yaml.load(this.state.rawYaml);
  54. } catch (err: any) {
  55. console.log("YAML parsing error.");
  56. }
  57. return (
  58. <StyledFormDebugger>
  59. <Button onClick={this.props.goBack}>
  60. <i className="material-icons">keyboard_backspace</i>
  61. Back
  62. </Button>
  63. <Heading isAtTop={true}>✨ Form.yaml Editor</Heading>
  64. <Helper>Write and test form.yaml free of consequence.</Helper>
  65. <EditorWrapper>
  66. <AceEditor
  67. ref={this.aceEditorRef}
  68. mode="yaml"
  69. value={this.state.rawYaml}
  70. theme="porter"
  71. onChange={(e: string) => this.setState({ rawYaml: e })}
  72. name="codeEditor"
  73. editorProps={{ $blockScrolling: true }}
  74. height="450px"
  75. width="100%"
  76. style={{
  77. borderRadius: "5px",
  78. border: "1px solid #ffffff22",
  79. marginTop: "27px",
  80. marginBottom: "27px",
  81. }}
  82. showPrintMargin={false}
  83. showGutter={true}
  84. highlightActiveLine={true}
  85. />
  86. </EditorWrapper>
  87. <CheckboxRow
  88. label="Show form state debugger"
  89. checked={this.state.showStateDebugger}
  90. toggle={() =>
  91. this.setState({ showStateDebugger: !this.state.showStateDebugger })
  92. }
  93. />
  94. <CheckboxRow
  95. label="Read-only"
  96. checked={this.state.isReadOnly}
  97. toggle={() =>
  98. this.setState({
  99. isReadOnly: !this.state.isReadOnly,
  100. })
  101. }
  102. />
  103. <CheckboxRow
  104. label="Include non-form dummy tabs"
  105. checked={this.state.showBonusTabs}
  106. toggle={() =>
  107. this.setState({ showBonusTabs: !this.state.showBonusTabs })
  108. }
  109. />
  110. <CheckboxRow
  111. label="checkbox_a"
  112. checked={this.state.checkbox_a}
  113. toggle={() =>
  114. this.setState({
  115. checkbox_a: !this.state.checkbox_a,
  116. // Override the form value for checkbox_a
  117. valuesToOverride: {
  118. ...this.state.valuesToOverride,
  119. checkbox_a: {
  120. value: !this.state.checkbox_a,
  121. },
  122. },
  123. })
  124. }
  125. />
  126. <InputRow
  127. type="string"
  128. value={this.state.input_a}
  129. setValue={(x: string) =>
  130. this.setState({
  131. input_a: x,
  132. // Override the form value for input_a
  133. valuesToOverride: {
  134. ...this.state.valuesToOverride,
  135. input_a: {
  136. value: x,
  137. },
  138. },
  139. })
  140. }
  141. label={"input_a"}
  142. placeholder="ex: override text"
  143. />
  144. <Heading>🎨 Rendered Form</Heading>
  145. <Br />
  146. <PorterFormWrapper
  147. showStateDebugger={this.state.showStateDebugger}
  148. formData={formData}
  149. valuesToOverride={{
  150. input_a: this.state.valuesToOverride?.input_a?.value,
  151. }}
  152. isReadOnly={this.state.isReadOnly}
  153. onSubmit={(vars) => {
  154. alert("check console output");
  155. console.log(vars);
  156. }}
  157. rightTabOptions={this.state.showBonusTabs ? tabOptions : []}
  158. renderTabContents={this.renderTabContents}
  159. saveButtonText={"Test Submit"}
  160. />
  161. </StyledFormDebugger>
  162. );
  163. }
  164. }
  165. const Br = styled.div`
  166. width: 100%;
  167. height: 12px;
  168. `;
  169. const TabWrapper = styled.div`
  170. background: #ffffff11;
  171. height: 200px;
  172. width: 100%;
  173. border-radius: 8px;
  174. display: flex;
  175. align-items: center;
  176. justify-content: center;
  177. font-size: 13px;
  178. overflow: auto;
  179. padding: 50px;
  180. `;
  181. const EditorWrapper = styled.div`
  182. .ace_editor,
  183. .ace_editor * {
  184. font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas",
  185. monospace !important;
  186. font-size: 12px !important;
  187. font-weight: 400 !important;
  188. letter-spacing: 0 !important;
  189. }
  190. `;
  191. const StyledFormDebugger = styled.div`
  192. position: relative;
  193. `;
  194. const Button = styled.div`
  195. display: flex;
  196. flex-direction: row;
  197. align-items: center;
  198. justify-content: space-between;
  199. font-size: 13px;
  200. cursor: pointer;
  201. font-family: "Work Sans", sans-serif;
  202. border-radius: 20px;
  203. color: white;
  204. height: 35px;
  205. margin-left: -2px;
  206. padding: 0px 8px;
  207. width: 85px;
  208. float: right;
  209. padding-bottom: 1px;
  210. font-weight: 500;
  211. padding-right: 15px;
  212. overflow: hidden;
  213. white-space: nowrap;
  214. text-overflow: ellipsis;
  215. cursor: pointer;
  216. border: 2px solid #969fbbaa;
  217. :hover {
  218. background: #ffffff11;
  219. }
  220. > i {
  221. color: white;
  222. width: 18px;
  223. height: 18px;
  224. color: #969fbbaa;
  225. font-weight: 600;
  226. font-size: 14px;
  227. border-radius: 20px;
  228. display: flex;
  229. align-items: center;
  230. margin-right: 5px;
  231. justify-content: center;
  232. }
  233. `;
  234. const initYaml = `name: Web
  235. hasSource: true
  236. includeHiddenFields: true
  237. tabs:
  238. - name: main
  239. label: Main
  240. sections:
  241. - name: section_one
  242. contents:
  243. - type: heading
  244. label: Container Settings
  245. - type: variable
  246. variable: showStartCommand
  247. settings:
  248. default: true
  249. - name: command
  250. show_if: showStartCommand
  251. contents:
  252. - type: subtitle
  253. name: command_description
  254. label: (Optional) Set a start command for this service.
  255. - type: string-input
  256. label: Start Command
  257. placeholder: "ex: sh ./script.sh"
  258. variable: container.command
  259. - name: section_one_cont
  260. contents:
  261. - type: subtitle
  262. label: Specify the port your application is running on.
  263. - type: number-input
  264. variable: container.port
  265. label: Container Port
  266. placeholder: "ex: 80"
  267. settings:
  268. default: 80
  269. - type: heading
  270. label: Deploy Webhook
  271. - type: checkbox
  272. variable: auto_deploy
  273. label: Auto-deploy when webhook is called.
  274. settings:
  275. default: true
  276. - name: network
  277. contents:
  278. - type: heading
  279. label: Network Settings
  280. - type: subtitle
  281. label: For containers that you do not want to expose to external traffic (e.g. databases and add-ons), you may make them accessible only to other internal services running within the same cluster.
  282. - type: checkbox
  283. variable: ingress.enabled
  284. label: Expose to external traffic
  285. settings:
  286. default: true
  287. - name: domain_toggle
  288. show_if: ingress.enabled
  289. contents:
  290. - type: subtitle
  291. label: Assign custom domain to your deployment. You must first create an A record in your domain provider that points to your cluster load balancer's IP address for this.
  292. - type: checkbox
  293. variable: ingress.custom_domain
  294. label: Configure Custom Domain
  295. settings:
  296. default: false
  297. - name: domain_name
  298. show_if: ingress.custom_domain
  299. contents:
  300. - type: array-input
  301. variable: ingress.hosts
  302. label: Domain Name
  303. - name: do_wildcard
  304. show_if:
  305. and:
  306. - ingress.custom_domain
  307. - currentCluster.service.is_do
  308. contents:
  309. - type: subtitle
  310. label: If you're hosting on Digital Ocean and have enabled the wildcard domains from the 'HTTPS Issuer', you can use a wildcard certificate.
  311. - type: checkbox
  312. variable: ingress.wildcard
  313. label: Use Wildcard Certificate
  314. - name: resources
  315. label: Resources
  316. sections:
  317. - name: main_section
  318. contents:
  319. - type: heading
  320. label: Resources
  321. - type: subtitle
  322. label: Configure resources assigned to this container.
  323. - type: number-input
  324. label: RAM
  325. variable: resources.requests.memory
  326. placeholder: "ex: 256"
  327. settings:
  328. unit: Mi
  329. default: 256
  330. - type: number-input
  331. label: CPU
  332. variable: resources.requests.cpu
  333. placeholder: "ex: 100"
  334. settings:
  335. unit: m
  336. default: 100
  337. - type: number-input
  338. label: Replicas
  339. variable: replicaCount
  340. placeholder: "ex: 1"
  341. settings:
  342. default: 1
  343. - type: checkbox
  344. variable: autoscaling.enabled
  345. label: Enable autoscaling
  346. settings:
  347. default: false
  348. - name: autoscaler
  349. show_if: autoscaling.enabled
  350. contents:
  351. - type: number-input
  352. label: Minimum Replicas
  353. variable: autoscaling.minReplicas
  354. placeholder: "ex: 1"
  355. settings:
  356. default: 1
  357. - type: number-input
  358. label: Maximum Replicas
  359. variable: autoscaling.maxReplicas
  360. placeholder: "ex: 10"
  361. settings:
  362. default: 10
  363. - type: number-input
  364. label: Target CPU Utilization
  365. variable: autoscaling.targetCPUUtilizationPercentage
  366. placeholder: "ex: 50"
  367. settings:
  368. omitUnitFromValue: true
  369. unit: "%"
  370. default: 50
  371. - type: number-input
  372. label: Target RAM Utilization
  373. variable: autoscaling.targetMemoryUtilizationPercentage
  374. placeholder: "ex: 50"
  375. settings:
  376. omitUnitFromValue: true
  377. unit: "%"
  378. default: 50
  379. - name: env
  380. label: Environment
  381. sections:
  382. - name: env_vars
  383. contents:
  384. - type: heading
  385. label: Environment Variables
  386. - type: subtitle
  387. label: Set environment variables for your secrets and environment-specific configuration.
  388. - type: env-key-value-array
  389. label:
  390. variable: container.env.normal
  391. - name: advanced
  392. label: Advanced
  393. sections:
  394. - name: ingress_annotations
  395. contents:
  396. - type: heading
  397. label: Ingress Custom Annotations
  398. - type: subtitle
  399. label: Assign custom annotations to Ingress. These annotations will overwrite the annotations Porter assigns by default.
  400. - type: key-value-array
  401. variable: ingress.annotations
  402. settings:
  403. default: {}
  404. - name: health_check
  405. contents:
  406. - type: heading
  407. label: Custom Health Checks
  408. - type: subtitle
  409. label: Define custom health check endpoints to ensure zero down-time deployments.
  410. - type: checkbox
  411. variable: health.enabled
  412. label: Enable Custom Health Checks
  413. settings:
  414. default: false
  415. - name: health_check_endpoint
  416. show_if: health.enabled
  417. contents:
  418. - type: string-input
  419. label: Health Check Endpoint
  420. variable: health.path
  421. placeholder: "ex: /healthz"
  422. settings:
  423. default: /healthz
  424. - type: heading
  425. label: Custom Health Check Rules
  426. - type: subtitle
  427. label: Configure how many times a health check will be performed before deeming the container as failed.
  428. - type: number-input
  429. label: Failure Threshold
  430. variable: health.failureThreshold
  431. placeholder: "ex: 3"
  432. - type: subtitle
  433. label: Configure the interval at which health check is repeated in the case of failure.
  434. - type: number-input
  435. label: Repeat Interval
  436. variable: health.periodSeconds
  437. placeholder: "ex: 30"
  438. - name: persistence_toggle
  439. contents:
  440. - type: heading
  441. label: Persistent Disks
  442. - type: subtitle
  443. label: Attach persistent disks to your deployment to retain data across releases.
  444. - type: checkbox
  445. label: Enable Persistence
  446. variable: pvc.enabled
  447. - name: persistent_storage
  448. show_if: pvc.enabled
  449. contents:
  450. - type: number-input
  451. label: Persistent Storage
  452. variable: pvc.storage
  453. placeholder: "ex: 20"
  454. settings:
  455. unit: Gi
  456. default: 20
  457. - type: string-input
  458. label: Mount Path
  459. variable: pvc.mountPath
  460. placeholder: "ex: /mypath"
  461. settings:
  462. default: /mypath
  463. - name: termination_grace_period
  464. contents:
  465. - type: heading
  466. label: Termination Grace Period
  467. - type: subtitle
  468. label: Specify how much time app processes have to gracefully shut down on SIGTERM.
  469. - type: number-input
  470. label: Termination Grace Period (seconds)
  471. variable: terminationGracePeriodSeconds
  472. placeholder: "ex: 30"
  473. settings:
  474. default: 30
  475. - name: container_hooks
  476. contents:
  477. - type: heading
  478. label: Container hooks
  479. - type: subtitle
  480. label: (Optional) Set post start or pre stop commands for this service.
  481. - type: string-input
  482. label: Post start command
  483. placeholder: "ex: /bin/sh ./myscript.sh"
  484. variable: container.lifecycle.postStart
  485. - type: string-input
  486. label: Pre stop command
  487. placeholder: "ex: /bin/sh ./myscript.sh"
  488. variable: container.lifecycle.preStop
  489. - name: cloud_sql_toggle
  490. show_if: currentCluster.service.is_gcp
  491. contents:
  492. - type: heading
  493. label: Google Cloud SQL
  494. - type: subtitle
  495. label: Securely connect to Google Cloud SQL (GKE only).
  496. - type: checkbox
  497. variable: cloudsql.enabled
  498. label: Enable Google Cloud SQL Proxy
  499. settings:
  500. default: false
  501. - name: cloud_sql_contents
  502. show_if: cloudsql.enabled
  503. contents:
  504. - type: string-input
  505. label: Instance Connection Name
  506. variable: cloudsql.connectionName
  507. placeholder: "ex: project-123:us-east1:pachyderm"
  508. - type: number-input
  509. label: DB Port
  510. variable: cloudsql.dbPort
  511. placeholder: "ex: 5432"
  512. settings:
  513. default: 5432
  514. - type: string-input
  515. label: Service Account JSON
  516. variable: cloudsql.serviceAccountJSON
  517. placeholder: "ex: { <SERVICE_ACCOUNT_JSON> }"`;