github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/webui/src/lib/components/repositoryCreateForm.jsx (about) 1 import React, {useEffect, useRef, useState} from 'react'; 2 import Form from "react-bootstrap/Form"; 3 import Alert from "react-bootstrap/Alert"; 4 import {Warnings} from "./controls"; 5 import {FloatingLabel} from "react-bootstrap"; 6 import Accordion from "react-bootstrap/Accordion"; 7 8 const DEFAULT_BLOCKSTORE_EXAMPLE = "e.g. s3://example-bucket/"; 9 const DEFAULT_BLOCKSTORE_VALIDITY_REGEX = new RegExp(`^s3://`); 10 11 export const RepositoryCreateForm = ({ id, config, onSubmit, formValid, setFormValid, error = null, sampleRepoChecked = false }) => { 12 const repoValidityRegex = /^[a-z0-9][a-z0-9-]{2,62}$/; 13 14 const [repoValid, setRepoValid] = useState(null); 15 const defaultNamespacePrefix = config.default_namespace_prefix 16 17 const [storageNamespaceValid, setStorageNamespaceValid] = useState(defaultNamespacePrefix ? true : null); 18 const [defaultBranchValid, setDefaultBranchValid] = useState(true); 19 20 const storageNamespaceField = useRef(null); 21 const defaultBranchField = useRef(null); 22 const repoNameField = useRef(null); 23 const sampleDataCheckbox = useRef(null); 24 25 useEffect(() => { 26 if (sampleDataCheckbox.current) { 27 sampleDataCheckbox.current.checked = sampleRepoChecked; 28 } 29 }, [sampleRepoChecked, sampleDataCheckbox.current]); 30 31 32 const onRepoNameChange = () => { 33 const isRepoValid = repoValidityRegex.test(repoNameField.current.value); 34 setRepoValid(isRepoValid); 35 setFormValid(isRepoValid && storageNamespaceValid && defaultBranchValid); 36 if (defaultNamespacePrefix) { 37 storageNamespaceField.current.value = defaultNamespacePrefix + repoNameField.current.value 38 checkStorageNamespaceValidity() 39 } 40 }; 41 42 const checkStorageNamespaceValidity = () => { 43 const isStorageNamespaceValid = storageNamespaceValidityRegex.test(storageNamespaceField.current.value); 44 setStorageNamespaceValid(isStorageNamespaceValid); 45 setFormValid(isStorageNamespaceValid && defaultBranchValid && repoValidityRegex.test(repoNameField.current.value)); 46 }; 47 48 const checkDefaultBranchValidity = () => { 49 const isBranchValid = defaultBranchField.current.value.length; 50 setDefaultBranchValid(isBranchValid); 51 setFormValid(isBranchValid && storageNamespaceValid && repoValid); 52 }; 53 54 const storageType = config.blockstore_type 55 const storageNamespaceValidityRegexStr = config ? config.blockstore_namespace_ValidityRegex : DEFAULT_BLOCKSTORE_VALIDITY_REGEX; 56 const storageNamespaceValidityRegex = RegExp(storageNamespaceValidityRegexStr); 57 const storageNamespaceExample = config ? config.blockstore_namespace_example : DEFAULT_BLOCKSTORE_EXAMPLE; 58 59 useEffect(() => { 60 if (repoNameField.current) { 61 repoNameField.current.focus(); 62 } 63 }, []); 64 65 const sampleCheckbox = ( 66 <Form.Group controlId="sampleData" className="mt-3"> 67 <Form.Check ref={sampleDataCheckbox} type="checkbox" label="Add sample data, hooks, and configuration" /> 68 </Form.Group> 69 ); 70 71 const flattenForm = !defaultNamespacePrefix; 72 73 const basicSettings = ( 74 <> 75 <Form.Group className="mb-3"> 76 <Form.Text> 77 A repository contains all of your objects, including the revision history. <a href="https://docs.lakefs.io/understand/model.html#repository" target="_blank" rel="noopener noreferrer">Learn more.</a> 78 </Form.Text> 79 </Form.Group> 80 <Form.Group controlId="id"> 81 <FloatingLabel label="Repository ID" controlId="repositryIdControl"> 82 <Form.Control type="text" ref={repoNameField} onChange={onRepoNameChange} placeholder="my-data-lake"/> 83 </FloatingLabel> 84 {repoValid === false && 85 <Form.Text className="text-danger"> 86 Min 3 characters. Only lowercase alphanumeric characters and {'\'-\''} allowed. 87 </Form.Text> 88 } 89 </Form.Group> 90 91 {!flattenForm && sampleCheckbox} 92 </> 93 ) 94 95 const advancedSettings = ( 96 <> 97 <Form.Group controlId="defaultBranch" className="mt-3"> 98 <FloatingLabel label="Default Branch Name" controlId="defaultBranchNameCtrl"> 99 <Form.Control type="text" ref={defaultBranchField} defaultValue={"main"} placeholder="main" onChange={checkDefaultBranchValidity}/> 100 </FloatingLabel> 101 {defaultBranchValid === false && 102 <Form.Text className="text-danger"> 103 Invalid Branch. 104 </Form.Text> 105 } 106 </Form.Group> 107 <Form.Group className="mt-3"> 108 <FloatingLabel label="Storage Namespace" controlId="storageNamespaceCtrl"> 109 <Form.Control type="text" ref={storageNamespaceField} placeholder={storageNamespaceExample} onChange={checkStorageNamespaceValidity} aria-describedby="namespaceHelpText"/> 110 </FloatingLabel> 111 <Form.Text muted id="namespaceHelpText"> 112 Where should data be stored? (e.g. <code>{storageNamespaceExample}</code>) 113 </Form.Text> 114 {storageNamespaceValid === false && 115 <p> 116 <Form.Text className="text-danger"> 117 {"Can only create repository with storage type: " + storageType} 118 </Form.Text> 119 </p> 120 } 121 </Form.Group> 122 {flattenForm && sampleCheckbox} 123 </> 124 ); 125 126 const creationForm = (defaultNamespacePrefix) ? ( 127 <> 128 <section className="repository-creation-expanded"> 129 {basicSettings} 130 </section> 131 <Accordion className="repository-creation-accordion"> 132 <Accordion.Item eventKey={"1"}> 133 <Accordion.Header>Advanced Settings</Accordion.Header> 134 <Accordion.Body> 135 {advancedSettings} 136 </Accordion.Body> 137 </Accordion.Item> 138 </Accordion> 139 </> 140 ) : ( 141 <section className="repository-creation-expanded"> 142 {basicSettings} 143 {advancedSettings} 144 </section> 145 ) 146 147 return ( 148 <Form id={id} onSubmit={(e) => { 149 e.preventDefault(); 150 if (!formValid) { 151 return; 152 } 153 onSubmit({ 154 name: repoNameField.current.value, 155 storage_namespace: storageNamespaceField.current.value, 156 default_branch: defaultBranchField.current.value, 157 sample_data: sampleDataCheckbox.current.checked, 158 }); 159 }}> 160 <h4 className="mb-3">Create A New Repository</h4> 161 162 {creationForm} 163 164 {config?.warnings && 165 <Warnings warnings={config.warnings}/> 166 } 167 168 {error && 169 <div className="mb-3"> 170 <Alert variant={"danger"}>{error.message}</Alert> 171 </div> 172 } 173 </Form> 174 ); 175 }