From 8bee0cf4a22d18a0d3524ab6743b3abf5b0e6bb4 Mon Sep 17 00:00:00 2001 From: Zhaoxinxin <107842350+Liam-Zhao@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:54:32 +0800 Subject: [PATCH] feat: cluster scope adds hostnames (#356) Signed-off-by: zhaoxinxin <1186037180@qq.com> --- cypress/e2e/clusters/cluster.cy.ts | 8 + cypress/e2e/clusters/create-cluster.cy.ts | 17 ++ cypress/e2e/clusters/update-cluster.cy.ts | 21 ++ cypress/e2e/job/preheats/create-preheat.cy.ts | 4 +- cypress/e2e/seed-peers/seed-peers.cy.ts | 6 +- .../fixtures/clusters/cluster/cluster.json | 3 +- .../clusters/cluster/update-cluster.json | 3 +- cypress/fixtures/clusters/create-cluster.json | 33 ++- public/icons/cluster/cidrs.svg | 9 +- public/icons/cluster/hostname.svg | 9 +- public/icons/cluster/hostnames.svg | 6 + src/components/clusters/edit.tsx | 190 +++++++++++------- .../clusters/information.module.css | 37 +++- src/components/clusters/information.tsx | 104 +++++++++- src/components/clusters/new.tsx | 185 +++++++++++------ src/components/clusters/show.tsx | 1 + src/lib/api.ts | 6 + 17 files changed, 477 insertions(+), 165 deletions(-) create mode 100644 public/icons/cluster/hostnames.svg diff --git a/cypress/e2e/clusters/cluster.cy.ts b/cypress/e2e/clusters/cluster.cy.ts index 59a8a27b..18e10c8c 100644 --- a/cypress/e2e/clusters/cluster.cy.ts +++ b/cypress/e2e/clusters/cluster.cy.ts @@ -171,6 +171,10 @@ describe('Cluster', () => { cy.get('.MuiDialogContent-root').should('be.visible').and('contain', '10.0.0.0/8'); cy.get('body').click('topLeft'); + + cy.get(':nth-child(4) > .MuiPaper-root > .information_cidrsTags__4sKxa') + .should('be.visible') + .and('contain', 'cluster-1'); }); it('can display config', () => { @@ -373,6 +377,7 @@ describe('Cluster', () => { ).click(); cy.get('.information_clusterContainer__l8H8p > :nth-child(1) > .MuiTypography-subtitle1') + .scrollIntoView() .should('be.visible') .and('contain', 'cluster-10'); @@ -444,6 +449,7 @@ describe('Cluster', () => { ).click(); cy.get('.information_clusterContainer__l8H8p > :nth-child(1) > .MuiTypography-subtitle1') + .scrollIntoView() .should('be.visible') .and('contain', 'cluster-1'); @@ -516,6 +522,7 @@ describe('Cluster', () => { ).click(); cy.get('.information_clusterContainer__l8H8p > :nth-child(1) > .MuiTypography-subtitle1') + .scrollIntoView() .should('be.visible') .and('contain', 'cluster-10'); @@ -565,6 +572,7 @@ describe('Cluster', () => { ).click(); cy.get('.information_clusterContainer__l8H8p > :nth-child(1) > .MuiTypography-subtitle1') + .scrollIntoView() .should('be.visible') .and('contain', 'cluster-10'); diff --git a/cypress/e2e/clusters/create-cluster.cy.ts b/cypress/e2e/clusters/create-cluster.cy.ts index 60434ca6..1a9d249a 100644 --- a/cypress/e2e/clusters/create-cluster.cy.ts +++ b/cypress/e2e/clusters/create-cluster.cy.ts @@ -280,6 +280,7 @@ describe('Create cluster', () => { it('try to verify scopes', () => { const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const location = _.times(101, () => _.sample(characters)).join(''); + const hostnames = _.times(31, () => _.sample(characters)).join(''); // Name is a required attribute. cy.get('#name').type('cluster-12'); @@ -323,6 +324,22 @@ describe('Create cluster', () => { // Verification passed. cy.get('#cidrs-helper-text').should('not.exist'); + + // Should display cidrs the validation error message. + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root').type('sigma'); + cy.get('#save').click(); + cy.url().should('include', '/clusters/new'); + cy.get('#hostnames-helper-text') + .should('be.visible') + .and('contain', `Please press ENTER to end the Hostnames creation.`); + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root').type( + 'hostname{enter}', + ); + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root').type(hostnames); + + cy.get('#hostnames-helper-text') + .should('be.visible') + .and('contain', `Fill in the characters, the length is 1-30.`); }); it('try to verify config', () => { diff --git a/cypress/e2e/clusters/update-cluster.cy.ts b/cypress/e2e/clusters/update-cluster.cy.ts index f522b29c..7f25ba25 100644 --- a/cypress/e2e/clusters/update-cluster.cy.ts +++ b/cypress/e2e/clusters/update-cluster.cy.ts @@ -66,6 +66,11 @@ describe('Update cluster', () => { .and('contain', '192.168.0.0/16') .and('contain', '172.16.0.0/12'); + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root') + .should('contain', 'cluster-1') + .and('contain', 'cluster-2') + .and('contain', 'cluster-3'); + // Show config. cy.get('#seedPeerLoadLimit').should('have.value', 300); cy.get('#peerLoadLimit').should('have.value', 51); @@ -332,6 +337,22 @@ describe('Update cluster', () => { '192.168.40.0/24{enter}', ); cy.get('#cidrs-helper-text').should('not.exist'); + + // Should display hostnames the validation error message. + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root').type('cluster-2'); + + cy.get('#save').click(); + cy.url().should('include', '/clusters/1/edit'); + + // Show verification error message. + cy.get('#hostnames-helper-text') + .should('be.visible') + .and('contain', `Please press ENTER to end the Hostnames creation.`); + + cy.get(':nth-child(4) > .MuiAutocomplete-root > .MuiFormControl-root > .MuiInputBase-root').type( + 'cluster-1{enter}', + ); + cy.get('#hostnames-helper-text').should('not.exist'); }); it('try to verify config', () => { diff --git a/cypress/e2e/job/preheats/create-preheat.cy.ts b/cypress/e2e/job/preheats/create-preheat.cy.ts index 4ce3d86a..0e508c98 100644 --- a/cypress/e2e/job/preheats/create-preheat.cy.ts +++ b/cypress/e2e/job/preheats/create-preheat.cy.ts @@ -359,12 +359,12 @@ describe('Create preheat', () => { // Incorrect header value entered. cy.get('.new_headersValueInput__zn-9E > .MuiInputBase-root').type(value); - // Show header value verification error message. + // Show header value verification error message. cy.get('.new_headersValueInput__zn-9E > .MuiFormHelperText-root') .should('be.visible') .and('have.text', 'Fill in the characters, the length is 1-1000.'); - // Show header value verification error message. + // Show header value verification error message. cy.get('.new_headersValueInput__zn-9E > .MuiFormHelperText-root') .should('be.visible') .and('have.text', 'Fill in the characters, the length is 1-1000.'); diff --git a/cypress/e2e/seed-peers/seed-peers.cy.ts b/cypress/e2e/seed-peers/seed-peers.cy.ts index 06771d51..36b17be5 100644 --- a/cypress/e2e/seed-peers/seed-peers.cy.ts +++ b/cypress/e2e/seed-peers/seed-peers.cy.ts @@ -183,7 +183,7 @@ describe('Seed peers', () => { describe('pagination', () => { it('pagination updates results and page number', () => { - cy.get('#seed-peer-table').should('be.visible'); + cy.get('#seed-peer-table').scrollIntoView().should('be.visible'); // Check number of pagination. cy.get('#seed-peer-pagination > .MuiPagination-ul').children().should('have.length', 5); @@ -470,6 +470,8 @@ describe('Seed peers', () => { .should('be.visible') .and('contain', 'Inactive'); + cy.get(':nth-child(5) > :nth-child(9) > .MuiButtonBase-root').click(); + cy.intercept({ method: 'DELETE', url: '/api/v1/seed-peers/9' }, (req) => { req.reply({ statusCode: 200, @@ -488,8 +490,6 @@ describe('Seed peers', () => { }, ); - cy.get(':nth-child(5) > :nth-child(9) > .MuiButtonBase-root').click(); - cy.get('#deleteSeedPeer').click(); // Delete success message. diff --git a/cypress/fixtures/clusters/cluster/cluster.json b/cypress/fixtures/clusters/cluster/cluster.json index 2b258e58..16d81894 100644 --- a/cypress/fixtures/clusters/cluster/cluster.json +++ b/cypress/fixtures/clusters/cluster/cluster.json @@ -5,7 +5,8 @@ "scopes": { "idc": "Hangzhou|Shanghai|Beijing", "location": "China|Hang|Zhou", - "cidrs": ["10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12"] + "cidrs": ["10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12"], + "hostnames": ["cluster-1", "cluster-2", "cluster-3"] }, "scheduler_cluster_id": 1, "seed_peer_cluster_id": 1, diff --git a/cypress/fixtures/clusters/cluster/update-cluster.json b/cypress/fixtures/clusters/cluster/update-cluster.json index 3e7abdec..11114aae 100644 --- a/cypress/fixtures/clusters/cluster/update-cluster.json +++ b/cypress/fixtures/clusters/cluster/update-cluster.json @@ -5,7 +5,8 @@ "scopes": { "idc": "Hangzhou|Shanghai", "location": "China|Shang|Hai", - "cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "192.168.20.2"] + "cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "192.168.20.2"], + "hostnames": ["cluster-1"] }, "scheduler_cluster_id": 1, "seed_peer_cluster_id": 1, diff --git a/cypress/fixtures/clusters/create-cluster.json b/cypress/fixtures/clusters/create-cluster.json index 4e0c1b3d..d2dc033a 100644 --- a/cypress/fixtures/clusters/create-cluster.json +++ b/cypress/fixtures/clusters/create-cluster.json @@ -31,7 +31,8 @@ "scopes": { "idc": "", "location": "", - "cidrs": [] + "cidrs": [], + "hostnames": [] }, "scheduler_cluster_id": 2, "seed_peer_cluster_id": 2, @@ -56,7 +57,8 @@ "scopes": { "idc": "Korea", "location": "Seoul|Korea", - "cidrs": ["192.168.0.0/16", "172.16.0.0/12"] + "cidrs": ["192.168.0.0/16", "172.16.0.0/12"], + "hostnames": [] }, "scheduler_cluster_id": 3, "seed_peer_cluster_id": 3, @@ -81,7 +83,8 @@ "scopes": { "idc": "hz|dl", "location": "China|Hang|Zhou", - "cidrs": ["10.0.0.0/8", "192.168.0.0/16"] + "cidrs": ["10.0.0.0/8", "192.168.0.0/16"], + "hostnames": [] }, "scheduler_cluster_id": 4, "seed_peer_cluster_id": 4, @@ -106,7 +109,8 @@ "scopes": { "idc": "cq|cd", "location": "China|Chong|Qing", - "cidrs": ["10.0.0.0/8", "192.168.0.0/16"] + "cidrs": ["10.0.0.0/8", "192.168.0.0/16"], + "hostnames": [] }, "scheduler_cluster_id": 5, "seed_peer_cluster_id": 5, @@ -131,7 +135,8 @@ "scopes": { "idc": "cq|cd", "location": "China|Chong|Du", - "cidrs": ["10.0.0.0/8", "192.168.0.0/16"] + "cidrs": ["10.0.0.0/8", "192.168.0.0/16"], + "hostnames": [] }, "scheduler_cluster_id": 6, "seed_peer_cluster_id": 6, @@ -156,7 +161,8 @@ "scopes": { "idc": "cd", "location": "China|Cheng|Du", - "cidrs": ["10.0.0.0/8", "172.16.0.0/19"] + "cidrs": ["10.0.0.0/8", "172.16.0.0/19"], + "hostnames": [] }, "scheduler_cluster_id": 7, "seed_peer_cluster_id": 7, @@ -181,7 +187,8 @@ "scopes": { "idc": "js", "location": "China|Jiang|Su", - "cidrs": ["10.0.0.0/5", "172.16.0.0/19"] + "cidrs": ["10.0.0.0/5", "172.16.0.0/19"], + "hostnames": [] }, "scheduler_cluster_id": 8, "seed_peer_cluster_id": 8, @@ -206,7 +213,8 @@ "scopes": { "idc": "hz|hf", "location": "China|Hang|Zhou", - "cidrs": ["10.0.0.0/8", "192.168.0.0/16"] + "cidrs": ["10.0.0.0/8", "192.168.0.0/16"], + "hostnames": [] }, "scheduler_cluster_id": 9, "seed_peer_cluster_id": 9, @@ -231,7 +239,8 @@ "scopes": { "idc": "London", "location": "London|England", - "cidrs": ["192.168.255.255"] + "cidrs": ["192.168.255.255"], + "hostnames": [] }, "scheduler_cluster_id": 10, "seed_peer_cluster_id": 10, @@ -256,7 +265,8 @@ "scopes": { "idc": "Paris", "location": "Paris|France", - "cidrs": ["192.168.0.0", "10.0.0.0"] + "cidrs": ["192.168.0.0", "10.0.0.0"], + "hostnames": [] }, "scheduler_cluster_id": 11, "seed_peer_cluster_id": 11, @@ -281,7 +291,8 @@ "scopes": { "idc": "hz|sh", "location": "China|Hang|Zhou", - "cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] + "cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], + "hostnames": [] }, "scheduler_cluster_id": 12, "seed_peer_cluster_id": 12, diff --git a/public/icons/cluster/cidrs.svg b/public/icons/cluster/cidrs.svg index ecdff45b..8c1312a1 100644 --- a/public/icons/cluster/cidrs.svg +++ b/public/icons/cluster/cidrs.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/public/icons/cluster/hostname.svg b/public/icons/cluster/hostname.svg index 5b584edb..807ca332 100644 --- a/public/icons/cluster/hostname.svg +++ b/public/icons/cluster/hostname.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/public/icons/cluster/hostnames.svg b/public/icons/cluster/hostnames.svg new file mode 100644 index 00000000..7940c6a3 --- /dev/null +++ b/public/icons/cluster/hostnames.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/components/clusters/edit.tsx b/src/components/clusters/edit.tsx index 656c453e..6631a31e 100644 --- a/src/components/clusters/edit.tsx +++ b/src/components/clusters/edit.tsx @@ -34,7 +34,12 @@ export default function EditCluster() { const [locationError, setLocationError] = useState(false); const [idcError, setIDCError] = useState(false); const [cidrsError, setCIDRsError] = useState(false); + const [hostnamesError, setHostnamesError] = useState(false); const [loadingButton, setLoadingButton] = useState(false); + const cidrsOptions = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; + const [idcHelperText, setIDCHelperText] = useState('Fill in the characters, the length is 0-100.'); + const [cidrsHelperText, setCIDRsHelperText] = useState('Fill in the characters, the length is 0-1000.'); + const [hostnamesHelperText, setHostnamesHelperText] = useState('Fill in the characters, the length is 1-30.'); const [cluster, setCluster] = useState({ id: 0, name: '', @@ -43,6 +48,7 @@ export default function EditCluster() { idc: '', location: '', cidrs: [], + hostnames: [], }, scheduler_cluster_id: 0, seed_peer_cluster_id: 0, @@ -60,10 +66,6 @@ export default function EditCluster() { updated_at: '', is_default: false, }); - const cidrsOptions = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; - const [idcHelperText, setIDCHelperText] = useState('Fill in the characters, the length is 0-100.'); - const [cidrsHelperText, setCIDRsHelperText] = useState('Fill in the characters, the length is 0-1000.'); - const navigate = useNavigate(); const params = useParams(); @@ -73,7 +75,7 @@ export default function EditCluster() { id, peer_cluster_config: { load_limit }, scheduler_cluster_config: { candidate_parent_limit, filter_parent_limit }, - scopes: { idc, location, cidrs }, + scopes: { idc, location, cidrs, hostnames }, seed_peer_cluster_config, } = cluster; @@ -162,6 +164,7 @@ export default function EditCluster() { { name: 'idc', label: 'IDC', + enterMultiple: true, scopesFormProps: { value: cluster?.scopes?.idc ? cluster?.scopes?.idc.split('|') : [] || [], options: [], @@ -189,7 +192,22 @@ export default function EditCluster() { placeholder: 'Please enter IDC', error: idcError, helperText: idcError ? idcHelperText : '', - + endadornment: ( + + + + ), onKeyDown: (e: any) => { if (e.keyCode === 13) { e.preventDefault(); @@ -207,6 +225,7 @@ export default function EditCluster() { { name: 'cidrs', label: 'CIDRs', + enterMultiple: true, scopesFormProps: { value: cidrs || [], options: cidrsOptions, @@ -234,7 +253,24 @@ export default function EditCluster() { placeholder: 'Please enter CIDRs', error: cidrsError, helperText: cidrsError ? cidrsHelperText : '', - + endadornment: ( + + + + ), onKeyDown: (e: any) => { if (e.keyCode === 13) { e.preventDefault(); @@ -249,6 +285,71 @@ export default function EditCluster() { return reg.test(value); }, }, + { + name: 'hostnames', + label: 'Hostnames', + enterMultiple: true, + scopesFormProps: { + value: hostnames, + options: [], + + onChange: (_e: any, newValue: any) => { + if (!scopesForm[3].formProps.error) { + setCluster({ ...cluster, scopes: { ...cluster.scopes, hostnames: newValue } }); + } + }, + + onInputChange: (e: any) => { + setHostnamesHelperText('Fill in the characters, the length is 0-1000.'); + changeValidate(e.target.value, scopesForm[3]); + }, + + renderTags: (value: any, getTagProps: any) => + value.map((option: any, index: any) => ( + + )), + }, + + formProps: { + id: 'hostnames', + label: 'Hostnames', + name: 'hostnames', + placeholder: 'Please enter Hostnames', + error: hostnamesError, + helperText: hostnamesError ? hostnamesHelperText : '', + endadornment: ( + + + + ), + onKeyDown: (e: any) => { + if (e.keyCode === 13) { + e.preventDefault(); + } + }, + }, + + syncError: false, + setError: setHostnamesError, + + validate: (value: string) => { + const reg = /^(.{1,30})$/; + return reg.test(value); + }, + }, ]; const configForm = [ @@ -435,6 +536,7 @@ export default function EditCluster() { const data = new FormData(event.currentTarget); const idcText = event.currentTarget.elements.idc.value; const cidrsText = event.currentTarget.elements.cidrs.value; + const hostnamesText = event.currentTarget.elements.hostnames.value; if (idcText) { setIDCHelperText('Please press ENTER to end the IDC creation.'); @@ -452,6 +554,14 @@ export default function EditCluster() { setCIDRsHelperText('Fill in the characters, the length is 0-100.'); } + if (hostnamesText) { + setHostnamesHelperText('Please press ENTER to end the Hostnames creation.'); + setHostnamesError(true); + } else { + setHostnamesError(false); + setHostnamesHelperText('Fill in the characters, the length is 1-30.'); + } + informationForm.forEach((item) => { const value = data.get(item.formProps.name); item.setError(!item.validate(value as string)); @@ -477,7 +587,8 @@ export default function EditCluster() { !scopesForm.filter((item) => item.syncError).length && !configForm.filter((item) => item.syncError).length && Boolean(!idcText) && - Boolean(!cidrsText), + Boolean(!cidrsText) && + Boolean(!hostnamesText), ); const formdata = { @@ -494,6 +605,7 @@ export default function EditCluster() { cidrs: cidrs, idc: String(idc), location: String(location), + hostnames: hostnames, }, seed_peer_cluster_config: { load_limit: Number(seed_peer_cluster_config.load_limit), @@ -612,7 +724,7 @@ export default function EditCluster() { {scopesForm.map((item) => { return ( - {item.label === 'CIDRs' ? ( + {item.enterMultiple ? ( - {params.InputProps.endAdornment} - - - - - ), - }} - color="success" - {...item.formProps} - /> - )} - /> - ) : item.label === 'IDC' ? ( - ( - - {params.InputProps.endAdornment} - - - - - ), + endAdornment: item.formProps.endadornment, }} color="success" {...item.formProps} diff --git a/src/components/clusters/information.module.css b/src/components/clusters/information.module.css index ad18f889..a7e98934 100644 --- a/src/components/clusters/information.module.css +++ b/src/components/clusters/information.module.css @@ -35,12 +35,26 @@ display: flex; flex-direction: column; justify-content: space-between; - width: 65%; + width: 60%; align-items: stretch; } +.scopesCard { + width: 50%; + padding-right: 0.6rem; +} + +.scopesCard:nth-child(1) { + padding-bottom: 0.6rem !important; +} + +.scopesCard:nth-child(2) { + padding-bottom: 0.6rem !important; +} + .scopesContentContainer { display: flex; + flex-wrap: wrap; height: 100%; } @@ -86,12 +100,16 @@ .cidrsContainer { height: 100%; - padding: 1.4rem; + padding: 1rem; display: flex; flex-direction: column; justify-content: space-between; } +.cidrsTitleWrapper { + padding-bottom: 1rem; +} + .cidrsTitle { display: flex; align-items: center; @@ -122,26 +140,29 @@ } .configRightContainer { - width: 35%; + width: 40%; max-width: 55rem; + display: flex; + flex-direction: column; } .configContainer { display: flex; align-items: center; - margin-bottom: 0.6rem; + margin-bottom: 1rem; } .configListContainer { - padding-left: 1rem; - padding-right: 1rem; + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-around; } .configContent { + padding: 1rem; display: flex; justify-content: space-between; - margin-top: 0.6rem; - margin-bottom: 0.6rem; } .idcDialogContainer { diff --git a/src/components/clusters/information.tsx b/src/components/clusters/information.tsx index 428c74d6..929cc3f4 100644 --- a/src/components/clusters/information.tsx +++ b/src/components/clusters/information.tsx @@ -12,6 +12,7 @@ export default function Information(props: { cluster: getClusterResponse; isLoad const { cluster, isLoading } = props; const [openCIDRs, setOpenCIDRs] = useState(false); const [openIDC, setOpenIDC] = useState(false); + const [openHostnames, setOpenHostnames] = useState(false); const [showSchedulerClusterIDCopyIcon, setShowSchedulerClusterIDCopyIcon] = useState(false); const [showSeedPeerClusterIDCopyIcon, setShowSeedPeerClusterIDCopyIcon] = useState(false); const [, setCopyToClipboard] = useCopyToClipboard(); @@ -220,7 +221,7 @@ export default function Information(props: { cluster: getClusterResponse; isLoad - + Scopes @@ -235,8 +236,8 @@ export default function Information(props: { cluster: getClusterResponse; isLoad - - + + @@ -262,7 +263,7 @@ export default function Information(props: { cluster: getClusterResponse; isLoad - + @@ -355,7 +356,7 @@ export default function Information(props: { cluster: getClusterResponse; isLoad - + @@ -363,7 +364,7 @@ export default function Information(props: { cluster: getClusterResponse; isLoad CIDRs @@ -444,6 +445,97 @@ export default function Information(props: { cluster: getClusterResponse; isLoad + + + + + + Hostnames + + + + + + + + + + {isLoading ? ( + + ) : ( + + {cluster?.scopes?.hostnames?.length > 0 ? ( + <> + + + + + {cluster?.scopes?.hostnames[0]} + + + + {cluster?.scopes?.hostnames?.length > 1 ? ( + + + + {cluster?.scopes?.hostnames[1]} + + + + ) : ( + <> + )} + + {cluster?.scopes?.hostnames?.length > 2 ? ( + { + setOpenHostnames(true); + }} + > + + + ) : ( + <> + )} + + ) : ( + + - + + )} + + )} + + { + setOpenHostnames(false); + }} + > + Hostnames + + {cluster?.scopes?.hostnames?.map((item: any, id: any) => ( + + + + + {item} + + + + ))} + + + diff --git a/src/components/clusters/new.tsx b/src/components/clusters/new.tsx index df0b8ba6..50c57c58 100644 --- a/src/components/clusters/new.tsx +++ b/src/components/clusters/new.tsx @@ -20,6 +20,7 @@ import CancelIcon from '@mui/icons-material/Cancel'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { useNavigate } from 'react-router-dom'; import { createCluster } from '../../lib/api'; +import { hostname } from 'os'; export default function NewCluster() { const [successMessage, setSuccessMessage] = useState(false); @@ -34,12 +35,15 @@ export default function NewCluster() { const [locationError, setLocationError] = useState(false); const [idcError, setIDCError] = useState(false); const [cidrsError, setCIDRsError] = useState(false); + const [hostnamesError, setHostnamesError] = useState(false); const [cidrs, setCIDRs] = useState([]); const [idc, setIDC] = useState([]); + const [hostnames, setHostnames] = useState([]); const [loadingButton, setLoadingButton] = useState(false); const cidrsOptions = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; const [idcHelperText, setIDCHelperText] = useState('Fill in the characters, the length is 0-100.'); const [cidrsHelperText, setCIDRsHelperText] = useState('Fill in the characters, the length is 0-1000.'); + const [hostnamesHelperText, setHostnamesHelperText] = useState('Fill in the characters, the length is 1-30.'); const navigate = useNavigate(); const informationForm = [ @@ -129,6 +133,7 @@ export default function NewCluster() { { name: 'idc', label: 'IDC', + enterMultiple: true, scopesFormProps: { value: idc, options: [], @@ -156,7 +161,24 @@ export default function NewCluster() { placeholder: 'Please enter IDC', error: idcError, helperText: idcError ? idcHelperText : '', - + endadornment: ( + <> + + + + + ), onKeyDown: (e: any) => { if (e.keyCode === 13) { e.preventDefault(); @@ -175,6 +197,7 @@ export default function NewCluster() { { name: 'cidrs', label: 'CIDRs', + enterMultiple: true, scopesFormProps: { value: cidrs, options: cidrsOptions, @@ -204,6 +227,24 @@ export default function NewCluster() { error: cidrsError, helperText: cidrsError ? cidrsHelperText : '', + endadornment: ( + + + + ), onKeyDown: (e: any) => { if (e.keyCode === 13) { e.preventDefault(); @@ -219,6 +260,71 @@ export default function NewCluster() { return reg.test(value); }, }, + { + name: 'hostnames', + label: 'Hostnames', + enterMultiple: true, + scopesFormProps: { + value: hostnames, + options: [], + + onChange: (_e: any, newValue: any) => { + if (!scopesForm[3].formProps.error) { + setHostnames(newValue); + } + }, + + onInputChange: (e: any) => { + setHostnamesHelperText('Fill in the characters, the length is 1-30.'); + changeValidate(e.target.value, scopesForm[3]); + }, + + renderTags: (value: any, getTagProps: any) => + value.map((option: any, index: any) => ( + + )), + }, + + formProps: { + id: 'hostnames', + label: 'Hostnames', + name: 'hostnames', + placeholder: 'Please enter Hostnames', + error: hostnamesError, + helperText: hostnamesError ? hostnamesHelperText : '', + endadornment: ( + + + + ), + onKeyDown: (e: any) => { + if (e.keyCode === 13) { + e.preventDefault(); + } + }, + }, + + syncError: false, + setError: setHostnamesError, + + validate: (value: string) => { + const reg = /^(.{1,30})$/; + return reg.test(value); + }, + }, ]; const configForm = [ @@ -383,6 +489,7 @@ export default function NewCluster() { const filterParentLimit = event.currentTarget.elements.filterParentLimit.value; const idcText = event.currentTarget.elements.idc.value; const cidrsText = event.currentTarget.elements.cidrs.value; + const hostnamesText = event.currentTarget.elements.hostnames.value; if (idcText) { setIDCHelperText('Please press ENTER to end the IDC creation.'); @@ -397,7 +504,15 @@ export default function NewCluster() { setCIDRsError(true); } else { setCIDRsError(false); - setCIDRsHelperText('Fill in the characters, the length is 0-100.'); + setCIDRsHelperText('Fill in the characters, the length is 0-1000.'); + } + + if (hostnamesText) { + setHostnamesHelperText('Please press ENTER to end the Hostnames creation.'); + setHostnamesError(true); + } else { + setHostnamesError(false); + setHostnamesHelperText('Fill in the characters, the length is 1-30.'); } informationForm.forEach((item) => { @@ -427,7 +542,8 @@ export default function NewCluster() { !scopesForm.filter((item) => item.syncError).length && !configForm.filter((item) => item.syncError).length && Boolean(!idcText) && - Boolean(!cidrsText), + Boolean(!cidrsText) && + Boolean(!hostnamesText), ); const formData = { @@ -448,6 +564,7 @@ export default function NewCluster() { cidrs: cidrs, idc: idcs, location: location, + hostnames: hostnames, }, }; @@ -560,47 +677,7 @@ export default function NewCluster() { {scopesForm.map((item) => { return ( - {item.label === 'CIDRs' ? ( - ( - - {params.InputProps.endAdornment} - - - - - ), - }} - color="success" - {...item.formProps} - /> - )} - /> - ) : item.label === 'IDC' ? ( + {item.enterMultiple ? ( - {params.InputProps.endAdornment} - - - - - ), + endAdornment: item.formProps.endadornment, }} color="success" {...item.formProps} diff --git a/src/components/clusters/show.tsx b/src/components/clusters/show.tsx index a6350e2c..632eb038 100644 --- a/src/components/clusters/show.tsx +++ b/src/components/clusters/show.tsx @@ -86,6 +86,7 @@ export default function ShowCluster() { idc: '', location: '', cidrs: [], + hostnames: [], }, scheduler_cluster_id: 0, seed_peer_cluster_id: 0, diff --git a/src/lib/api.ts b/src/lib/api.ts index 373c7e1e..3636d9b7 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -175,6 +175,7 @@ export interface getClustersResponse { idc: string; location: string; cidrs: Array; + hostnames: Array; }; created_at: string; is_default: boolean; @@ -197,6 +198,7 @@ export interface getClusterResponse { idc: string; location: string; cidrs: Array; + hostnames: Array; }; scheduler_cluster_id: number; seed_peer_cluster_id: number; @@ -239,6 +241,7 @@ interface createClusterRequest { cidrs: Array; idc: string; location: string; + hostnames: Array; }; } @@ -263,6 +266,7 @@ interface createClusterResponse { cidrs: Array; idc: string; location: string; + hostnames: Array; }; } @@ -286,6 +290,7 @@ interface updateClusterRequset { cidrs: Array; idc: string; location: string; + hostnames: Array; }; seed_peer_cluster_config: { load_limit: number; @@ -313,6 +318,7 @@ interface updateClusterResponse { cidrs: Array; idc: string; location: string; + hostnames: Array; }; }