diff --git a/cypress/e2e/clusters/cluster.cy.ts b/cypress/e2e/clusters/cluster.cy.ts index ffa3715..6b2e1d9 100644 --- a/cypress/e2e/clusters/cluster.cy.ts +++ b/cypress/e2e/clusters/cluster.cy.ts @@ -214,11 +214,11 @@ describe('Cluster', () => { // Click the copy scheduler cluster id icon. cy.get('#copy-scheduler-cluster-id').click(); cy.get('#schedulerClusterIDCopyIcon').should('exist'); - cy.get('#schedulerClusterIDTooltip').should('exist'); + cy.get('#schedulerClusterIDTooltip').should('not.exist'); cy.wait(1000); // Display successful copy icon. - cy.get('#schedulerClusterIDCopyIcon').should('not.exist'); + cy.get('#schedulerClusterIDCopyIcon').should('exist'); cy.get('#schedulerClusterIDTooltip').should('not.exist'); // Let's check the copied text. @@ -230,11 +230,11 @@ describe('Cluster', () => { // Click the copy seed peer cluster id icon. cy.get('#copy-seed-peer-cluster-id').click(); cy.get('#seedPeerClusterIDCopyIcon').should('exist'); - cy.get('#seedPeerClusterIDTooltip').should('exist'); + cy.get('#seedPeerClusterIDTooltip').should('not.exist'); cy.wait(1000); // Display successful copy icon. - cy.get('#seedPeerClusterIDCopyIcon').should('not.exist'); + cy.get('#seedPeerClusterIDCopyIcon').should('exist'); cy.get('#seedPeerClusterIDTooltip').should('not.exist'); // Let's check the copied text. diff --git a/cypress/e2e/clusters/clusters.cy.ts b/cypress/e2e/clusters/clusters.cy.ts index 9be4181..3389e09 100644 --- a/cypress/e2e/clusters/clusters.cy.ts +++ b/cypress/e2e/clusters/clusters.cy.ts @@ -43,7 +43,7 @@ describe('Clusters', () => { ); cy.visit('/clusters'); - cy.viewport(1440, 1080); + cy.viewport(1440, 1480); }); describe('when data is loaded', () => { @@ -86,7 +86,7 @@ describe('Clusters', () => { cy.get('#default-cluster-1') .should('be.visible') .and('contain', 'Default') - .and('have.css', 'background-color', 'rgb(46, 143, 121)'); + .and('have.css', 'background-color', 'rgb(31, 125, 83)'); // Show cluster name. cy.get('#cluster-name-1').should('be.visible').and('contain', 'cluster-1'); @@ -108,7 +108,7 @@ describe('Clusters', () => { cy.get('#default-cluster-2') .should('be.visible') .and('contain', 'Non-Default') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); }); }); @@ -183,8 +183,6 @@ describe('Clusters', () => { describe('pagination', () => { it('pagination updates results and page number', () => { - cy.get('.Mui-selected').invoke('text').should('eq', 'Cluster1'); - // Check number of pagination. cy.get('#clusterPagination > .MuiPagination-ul').children().should('have.length', 7); @@ -193,8 +191,6 @@ describe('Clusters', () => { }); it('when pagination changes, different page results are rendered', () => { - cy.get('.Mui-selected').invoke('text').should('eq', 'Cluster1'); - // Go to last page. cy.get('.MuiPagination-ul > :nth-child(3) > .MuiButtonBase-root').click(); @@ -205,7 +201,7 @@ describe('Clusters', () => { cy.get('#default-cluster-8') .should('be.visible') .and('contain', 'Non-Default') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); cy.get('#cluster-name-8').should('be.visible').and('contain', 'cluster-8'); @@ -268,7 +264,7 @@ describe('Clusters', () => { cy.get('#clusterPagination > .MuiPagination-ul .Mui-selected').should('have.text', '5'); - cy.get('#clusters').children().should('have.length', 1); + cy.get('#clustersCard').children().should('have.length', 1); cy.get('#cluster-name-37').should('have.text', 'cluster-37'); @@ -384,7 +380,7 @@ describe('Clusters', () => { cy.get('#free-solo-demo').type('cluster-47'); // No clusters card. - cy.get('#clusters').should('not.exist'); + cy.get('#clustersCard').should('not.exist'); // Pagination has been hidden. cy.get('#clusterPagination > .MuiPagination-ul').should('not.exist'); diff --git a/cypress/e2e/clusters/create-cluster.cy.ts b/cypress/e2e/clusters/create-cluster.cy.ts index 3504202..e8ba95b 100644 --- a/cypress/e2e/clusters/create-cluster.cy.ts +++ b/cypress/e2e/clusters/create-cluster.cy.ts @@ -192,7 +192,7 @@ describe('Create cluster', () => { // Show error message. cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Conflict'); cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); - cy.get('.MuiPaper-root').should('not.exist'); + cy.get('.MuiPaper-message').should('not.exist'); }); it('click the `CANCEL button', () => { diff --git a/cypress/e2e/developer/tokens/create-token.cy.ts b/cypress/e2e/developer/tokens/create-token.cy.ts index 583b259..067503f 100644 --- a/cypress/e2e/developer/tokens/create-token.cy.ts +++ b/cypress/e2e/developer/tokens/create-token.cy.ts @@ -27,7 +27,7 @@ describe('Create token', () => { cy.visit('/developer/personal-access-tokens'); // Click the `ADD PERSONAL ACCESS TOKENS` button. - cy.get('.css-1qdyvok > .MuiButtonBase-root').click(); + cy.get('#new-tokens-button').click(); // Then I see that the current page is the developer/personal-access-tokens/new! cy.url().should('include', '/developer/personal-access-tokens/new'); @@ -140,7 +140,7 @@ describe('Create token', () => { // Show error message. cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Conflict'); cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); - cy.get('.MuiPaper-root').should('not.exist'); + cy.get('.MuiAlert-message').should('not.exist'); }); it('click the `CANCEL button', () => { diff --git a/cypress/e2e/developer/tokens/tokens.cy.ts b/cypress/e2e/developer/tokens/tokens.cy.ts index 1c943a0..fdfab7a 100644 --- a/cypress/e2e/developer/tokens/tokens.cy.ts +++ b/cypress/e2e/developer/tokens/tokens.cy.ts @@ -25,12 +25,7 @@ describe('Tokens', () => { it('when data is loaded', () => { cy.get('[data-testid="isloading"]').should('be.exist'); - cy.get('.css-1qdyvok > .MuiTypography-root').should('have.text', 'Personal access tokens'); - - cy.get('.MuiList-root > :nth-child(2) > .MuiButtonBase-root > .MuiTypography-root').click(); - - // Whether the style selected by menu is Personal access tokens. - cy.get('.MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root').should('have.class', 'Mui-selected'); + cy.get('#token-title').should('have.text', 'Personal access tokens'); // Show token name. cy.get('#root-11').should('be.visible').and('have.text', 'root-11'); @@ -69,7 +64,7 @@ describe('Tokens', () => { }, ); - cy.get('.MuiPaper-root').should('be.visible').and('have.text', `You don't have any tokens.`); + cy.get('#no-tokens').should('be.visible').and('have.text', `You don't have any tokens.`); }); it('should handle API error response', () => { @@ -97,7 +92,7 @@ describe('Tokens', () => { cy.get('#tokens-list').should('not.exist'); // Show You don't have any preheat tokens. - cy.get('.MuiPaper-root').should('be.visible').and('have.text', `You don't have any tokens.`); + cy.get('#no-tokens').should('be.visible').and('have.text', `You don't have any tokens.`); }); describe('pagination', () => { @@ -120,9 +115,9 @@ describe('Tokens', () => { cy.get('#tokens-pagination > .MuiPagination-ul .Mui-selected').should('have.text', '2'); // Show token name. - cy.get('.MuiTypography-inherit').should('be.visible').and('have.text', 'root-1'); + cy.get('#root-1').should('be.visible').and('have.text', 'root-1'); - cy.get('span.css-189ppmh-MuiTypography-root').should('be.visible').and('have.text', 'Thu, Dec 1 2033.'); + cy.get('#expired-at-1').should('be.visible').and('have.text', 'Thu, Dec 1 2033.'); }); it('when you click refresh, the paginated results and page numbers remain unchanged.', () => { diff --git a/cypress/e2e/job/preheats/create-preheat.cy.ts b/cypress/e2e/job/preheats/create-preheat.cy.ts index bdce6b0..7ccd5dc 100644 --- a/cypress/e2e/job/preheats/create-preheat.cy.ts +++ b/cypress/e2e/job/preheats/create-preheat.cy.ts @@ -46,7 +46,7 @@ describe('Create preheat', () => { it('can create preheat', () => { cy.visit('/jobs/preheats'); - cy.get('.css-1g5t85q > .MuiButtonBase-root').click(); + cy.get('#new-preheat').click(); cy.url().should('include', '/jobs/preheats/new'); @@ -321,7 +321,6 @@ describe('Create preheat', () => { it('try to verify header', () => { const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const key = _.times(101, () => _.sample(characters)).join(''); - const value = _.times(1001, () => _.sample(characters)).join(''); // Select a cluster. cy.get('#select-cluster').click(); @@ -344,13 +343,11 @@ describe('Create preheat', () => { // 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.'); + .and('have.text', 'Fill in the characters, the length is 1-10000.'); // Verification passed. cy.get('.new_headersKeyInput__aZcds > .MuiInputBase-root > .MuiInputBase-input').type('key'); cy.get('.new_headersKeyInput__aZcds > .MuiFormHelperText-root').should('not.exist'); - cy.get('.new_headersValueInput__zn-9E > .MuiInputBase-root').type('value'); - cy.get('.new_headersValueInput__zn-9E > .MuiFormHelperText-root').should('not.exist'); // Incorrect header key entered. cy.get('.new_headersKeyInput__aZcds > .MuiInputBase-root > .MuiInputBase-input').clear(); @@ -371,19 +368,6 @@ describe('Create preheat', () => { cy.get('.new_headersKeyInput__aZcds > .MuiInputBase-root > .MuiInputBase-input').type('key'); cy.get('.new_headersKeyInput__aZcds > .MuiFormHelperText-root').should('not.exist'); - // Incorrect header value entered. - cy.get('.new_headersValueInput__zn-9E > .MuiInputBase-root').type(value); - - // 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. - cy.get('.new_headersValueInput__zn-9E > .MuiFormHelperText-root') - .should('be.visible') - .and('have.text', 'Fill in the characters, the length is 1-1000.'); - cy.get('#save').click(); // Preheat creation failed, the page is still in preheat/new! diff --git a/cypress/e2e/job/preheats/preheat.cy.ts b/cypress/e2e/job/preheats/preheat.cy.ts index 3b4fa71..2fbcbc6 100644 --- a/cypress/e2e/job/preheats/preheat.cy.ts +++ b/cypress/e2e/job/preheats/preheat.cy.ts @@ -44,14 +44,14 @@ describe('Preheat', () => { it('click the breadcrumb', () => { cy.get('[data-testid="isloading"]').should('be.exist'); - cy.get(':nth-child(6) > .css-1mlhis1 > .css-1ozvesn > .MuiTypography-root > .MuiBox-root').click(); + cy.get('#preheat-6').click(); cy.url().should('include', '/jobs/preheats/6'); // Check for breadcrumb. - cy.get('.MuiBreadcrumbs-ol > :nth-child(3) > .MuiTypography-root').should('exist').and('contain', 'preheats'); + cy.get(':nth-child(3) > .MuiTypography-root').should('exist').and('contain', 'Preheat'); - cy.get('.MuiBreadcrumbs-ol > :nth-child(3) > .MuiTypography-root').click(); + cy.get(':nth-child(3) > .MuiTypography-root').click(); cy.get('[data-testid="isloading"]').should('not.exist'); @@ -244,7 +244,7 @@ describe('Preheat', () => { }); it('unable to display breadcrumb', () => { - cy.get('.MuiBreadcrumbs-ol').should('be.visible').and('contain', 'preheats'); + cy.get('.MuiBreadcrumbs-ol').should('be.visible').and('contain', 'Preheat'); cy.get('.MuiBreadcrumbs-ol > :nth-child(5) > .MuiTypography-root').should('have.text', '-'); }); @@ -303,7 +303,7 @@ describe('Preheat', () => { }); it('unable to display breadcrumb', () => { - cy.get('.MuiBreadcrumbs-ol').should('be.visible').and('contain', 'preheats'); + cy.get('.MuiBreadcrumbs-ol').should('be.visible').and('contain', 'Preheat'); cy.get('.MuiBreadcrumbs-ol > :nth-child(5) > .MuiTypography-root').should('have.text', '-'); }); diff --git a/cypress/e2e/job/preheats/preheats.cy.ts b/cypress/e2e/job/preheats/preheats.cy.ts index e3f9c3f..ba35709 100644 --- a/cypress/e2e/job/preheats/preheats.cy.ts +++ b/cypress/e2e/job/preheats/preheats.cy.ts @@ -67,30 +67,32 @@ describe('Preheats', () => { ).as('preheats'); cy.get('[data-testid="isloading"]').should('be.exist'); cy.wait(120000); + // Executed every 3 seconds, it should be executed 2 times after 6 seconds. cy.get('@preheats').then(() => { expect(interceptCount).to.be.greaterThan(0); expect(interceptCount).to.be.closeTo(2, 1); }); + cy.get('[data-testid="isloading"]').should('not.exist'); cy.get('.MuiList-root > :nth-child(3) > .MuiButtonBase-root').click(); - // Whether the style selected by menu is Preheat. - cy.get( - ':nth-child(3) > .MuiCollapse-root > .MuiCollapse-wrapper > .MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root', - ).should('have.class', 'Mui-selected'); - cy.get('.css-1g5t85q > .MuiTypography-root').should('be.visible').and('have.text', 'Preheats'); + // The preheating status is displayed as PENDING. cy.get('#PENDING-11').should('exist'); cy.get('#id-11').should('have.text', 11); + // cy.get('#created_at-11').should('have.text', '2023-03-23 16:29:18'); cy.get('#description-11').should('have.text', 'This is a preheat task with status pending'); + // The preheating status is displayed as FAILURE. cy.get('#FAILURE-10').should('exist'); cy.get('#description-10').should('have.text', 'This is a preheat task with status failure'); + // The preheating status is displayed as SUCCESS. cy.get('#list-8 > .css-1mlhis1').should('exist').find('#SUCCESS-8').should('exist'); cy.get('#description-8').should('have.text', 'This is a preheat task with status success'); }); + it('should display preheat success list', () => { cy.intercept( { @@ -113,6 +115,7 @@ describe('Preheats', () => { cy.get('#preheats-list').children().should('have.length', 6); cy.get('#preheat-pagination').should('not.exist'); }); + it('should display preheat failure list', () => { cy.intercept( { @@ -134,6 +137,7 @@ describe('Preheats', () => { // Check how many preheat tasks are in success failure. cy.get('#preheats-list').children().should('have.length', 4); }); + it('should display preheat pending list', () => { let interceptCount = 0; cy.intercept( @@ -288,7 +292,7 @@ describe('Preheats', () => { cy.get('#list-1').should('exist').find('#SUCCESS-1').should('exist'); // Go to show preheat page. - cy.get('#preheat-1 > .MuiBox-root').click(); + cy.get('#preheat-1').click(); // Then I see that the current page is the show update personal-access-tokens. cy.url().should('include', '/jobs/preheats/1'); diff --git a/cypress/e2e/job/task/clear.cy.ts b/cypress/e2e/job/task/clear.cy.ts index 5e80fd5..149baae 100644 --- a/cypress/e2e/job/task/clear.cy.ts +++ b/cypress/e2e/job/task/clear.cy.ts @@ -16,6 +16,20 @@ describe('Clear', () => { it('when no data is loaded', () => { cy.get('#no-task').should('not.exist'); + cy.get('#light').should('exist'); + cy.get('#no-task-image').should('exist'); + + cy.get('#dark').click(); + // Dark mode should show a different no-task-image. + + cy.get('.Mui-selected').invoke('text').should('contain', 'Dark'); + + cy.get('.Mui-selected').invoke('text').should('not.contain', 'Light'); + + cy.get('#no-task-image').should('not.exist'); + + cy.get('#dark-no-task-image').should('exist'); + cy.intercept( { method: 'post', @@ -299,6 +313,12 @@ describe('Clear', () => { expect(interceptCount).to.be.closeTo(2, 1); }); + // Executed every 1 minute and once after 1 minute. + cy.get('@cache').then(() => { + expect(interceptCount).to.be.greaterThan(0); + expect(interceptCount).to.be.closeTo(2, 1); + }); + cy.intercept( { method: 'GET', @@ -314,13 +334,6 @@ describe('Clear', () => { // Preheat API error response after three seconds. cy.wait(60000); - - // Show error message. - cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Unauthorized'); - - // Close error message. - cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); - cy.get('.MuiAlert-message').should('not.exist'); }); it('Delete cache API error response', () => { diff --git a/cypress/e2e/job/task/execution.cy.ts b/cypress/e2e/job/task/execution.cy.ts index 4b40d91..03c941e 100644 --- a/cypress/e2e/job/task/execution.cy.ts +++ b/cypress/e2e/job/task/execution.cy.ts @@ -43,12 +43,12 @@ describe('Executions', () => { // Show isloading. cy.get('[data-testid="isloading"]').should('be.exist'); - cy.get('#execution-9 > .MuiBox-root').click(); + cy.get('#execution-9').click(); cy.url().should('include', '/jobs/task/executions/9'); // Check for breadcrumb. - cy.get('.MuiBreadcrumbs-ol').should('exist').and('contain', 'executions').and('contain', '9'); + cy.get('.MuiBreadcrumbs-ol').should('exist').and('contain', 'Executions').and('contain', '9'); cy.get('.MuiBreadcrumbs-ol > :nth-child(5) > .MuiTypography-root').click(); diff --git a/cypress/e2e/job/task/executions.cy.ts b/cypress/e2e/job/task/executions.cy.ts index 998109d..eda6ddb 100644 --- a/cypress/e2e/job/task/executions.cy.ts +++ b/cypress/e2e/job/task/executions.cy.ts @@ -84,13 +84,6 @@ describe('Executions', () => { cy.get('.MuiList-root > :nth-child(3) > .MuiButtonBase-root').click(); - // Whether the style selected by menu is Task. - cy.get( - ':nth-child(3) > .MuiCollapse-root > .MuiCollapse-wrapper > .MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root', - ).should('have.class', 'Mui-selected'); - - cy.get('.MuiList-root > .Mui-selected').should('be.visible').and('have.text', 'Task'); - // The executions status is displayed as PENDING. cy.get('#list-11 > .css-1mlhis1').should('exist').find('#PENDING-11').should('exist'); cy.get('#id-11').should('have.text', 11); @@ -318,7 +311,7 @@ describe('Executions', () => { cy.get('#list-1').should('exist').find('#SUCCESS-1').should('exist'); // Go to show executions page. - cy.get('#execution-1 > .MuiBox-root').click(); + cy.get('#execution-1').click(); // Then I see that the current page is the show update personal-access-tokens. cy.url().should('include', '/jobs/task/executions/1'); diff --git a/cypress/e2e/menu/menu.cy.ts b/cypress/e2e/menu/menu.cy.ts index 2229fe0..a0e8e1d 100644 --- a/cypress/e2e/menu/menu.cy.ts +++ b/cypress/e2e/menu/menu.cy.ts @@ -3,14 +3,39 @@ import seedPeers from '../../fixtures/seed-peers/seed-peers.json'; import schedulers from '../../fixtures/schedulers/schedulers.json'; describe('Menu', () => { + beforeEach(() => { + cy.viewport(1440, 1080); + }); + it('user not signin', () => { // redirect when not signin. cy.visit('/'); // Then I see that the current page is the signin! cy.url().should('include', '/signin'); + }); - cy.viewport(1440, 1080); + it('should switch to dark mode', () => { + cy.guestSignin(); + cy.visit('/'); + + // Click the Toggle Dark button. + cy.get('#dark').click(); + + cy.get('.Mui-selected').invoke('text').should('contain', 'Dark'); + + cy.get('.Mui-selected').invoke('text').should('not.contain', 'Light'); + + cy.get('#main').should('have.css', 'background-color', 'rgb(31, 36, 48)'); + + // Click the Toggle Light button. + cy.get('#light').click(); + + cy.get('.Mui-selected').invoke('text').should('not.contain', 'Dark'); + + cy.get('.Mui-selected').invoke('text').should('contain', 'Light'); + + cy.get('#main').should('have.css', 'background-color', 'rgb(244, 246, 248)'); }); describe('try to signin as guest user', () => { @@ -32,32 +57,36 @@ describe('Menu', () => { }); it('should navigate to the tokens page', () => { - cy.get('.MuiList-root > :nth-child(2) > .MuiButtonBase-root').click(); - cy.get('.MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root').click(); + cy.get('#developer').click(); + cy.get('#personal-access-tokens').click(); // Then I see that the current page is the tokens! cy.url().should('include', '/developer/personal-access-tokens'); // The selected menu is tokens. - cy.get('.MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root').should('have.class', 'Mui-selected'); + cy.get('#personal-access-tokens').should('have.class', 'Mui-selected'); + + cy.get('#dragonfly').click(); + // Then I see that the current page is the clusters! + cy.url().should('include', '/clusters'); }); it('should navigate to the preheats page and task page', () => { - cy.get('.MuiList-root > :nth-child(3) > .MuiButtonBase-root').click(); - cy.get('[href="/jobs/preheats"]').click(); + cy.get('#jobs').click(); + cy.get('#preheats').click(); // Then I see that the current page is the preheats! cy.url().should('include', '/jobs/preheats'); // The selected menu is preheats. - cy.get('[href="/jobs/preheats"]').should('have.class', 'Mui-selected'); + cy.get('#preheats').should('have.class', 'Mui-selected'); - cy.get('[href="/jobs/task/clear"]').click(); + cy.get('#task').click(); // Then I see that the current page is the task! cy.url().should('include', '/jobs/task/clear'); - cy.get('[href="/jobs/task/clear"]').should('have.class', 'Mui-selected'); + cy.get('#task').should('have.class', 'Mui-selected'); }); }); @@ -114,46 +143,42 @@ describe('Menu', () => { }); it('should navigate to the tokens page', () => { - cy.get('.MuiList-root > :nth-child(2) > .MuiButtonBase-root').click(); - cy.get('.MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root').click(); + cy.get('#developer').click(); + cy.get('#personal-access-tokens').click(); // Then I see that the current page is the tokens! cy.url().should('include', '/developer/personal-access-tokens'); // The selected menu is tokens. - cy.get('.MuiCollapse-wrapperInner > .MuiList-root > .MuiButtonBase-root').should('have.class', 'Mui-selected'); + cy.get('#personal-access-tokens').should('have.class', 'Mui-selected'); }); it('should navigate to the preheats page and task page', () => { - cy.get('.MuiList-root > :nth-child(3) > .MuiButtonBase-root').click(); - cy.get('[href="/jobs/preheats"]').click(); - - // Show page loading. - cy.get('#page-loading').should('be.visible'); + cy.get('#jobs').click(); + cy.get('#preheats').click(); // Then I see that the current page is the preheats! cy.url().should('include', '/jobs/preheats'); // The selected menu is preheats. - cy.get('[href="/jobs/preheats"]').should('have.class', 'Mui-selected'); + cy.get('#preheats').should('have.class', 'Mui-selected'); - cy.get('[href="/jobs/task/clear"]').click(); + cy.get('#task').click(); // Then I see that the current page is the task! cy.url().should('include', '/jobs/task/clear'); - // The selected menu is task. - cy.get('[href="/jobs/task/clear"]').should('have.class', 'Mui-selected'); + cy.get('#task').should('have.class', 'Mui-selected'); }); it('should navigate to the users page', () => { - cy.get('[href="/users"]').click(); + cy.get('#users').click(); // Then I see that the current page is the users! cy.url().should('include', '/users'); // The selected menu is users. - cy.get('[href="/users"]').should('have.class', 'Mui-selected'); + cy.get('#users').should('have.class', 'Mui-selected'); }); it('should navigate to the profile page', () => { @@ -167,6 +192,42 @@ describe('Menu', () => { cy.url().should('include', '/profile'); }); + it('The menu should be smaller', () => { + // The menu should be smaller. + cy.get('#closure').should('exist').click(); + cy.get('#expand').should('exist'); + cy.get('#closure').should('not.exist'); + + // Go to tokens page. + cy.get('#developer').click(); + + cy.get('#personal-access-tokens').click(); + + // Then I see that the current page is the tokens! + cy.url().should('include', '/developer/personal-access-tokens'); + + // Go to jobs page. + cy.get('#jobs').click(); + cy.get('#preheats').click(); + + // Then I see that the current page is the preheats! + cy.url().should('include', '/jobs/preheats'); + + // Go to task page. + cy.get('#jobs').click(); + cy.get('#task').click(); + + // Then I see that the current page is the task! + cy.url().should('include', '/jobs/task/clear'); + + // Go to user page. + + cy.get('#users').click(); + + // Then I see that the current page is the users! + cy.url().should('include', '/users'); + }); + it('can logout', () => { cy.intercept( { diff --git a/cypress/e2e/peers/peers.cy.ts b/cypress/e2e/peers/peers.cy.ts index 100ef86..438ec57 100644 --- a/cypress/e2e/peers/peers.cy.ts +++ b/cypress/e2e/peers/peers.cy.ts @@ -250,8 +250,6 @@ describe('Peers', () => { }); describe('refresh', () => { - beforeEach(() => {}); - it('can refresh peers and return new data', () => { cy.get('#total').should('have.text', 10); cy.get('#git-version').should('have.text', 4); @@ -261,7 +259,7 @@ describe('Peers', () => { // Click refresh button. cy.get('#refresh').click(); - cy.get('.css-70qvj9 > .MuiTypography-root').should('be.visible').and('have.text', 'Refresh'); + cy.get('#refresh-title').should('be.visible').and('have.text', 'Refresh'); cy.get('#cancel').click(); cy.get('#refresh').click(); @@ -316,7 +314,7 @@ describe('Peers', () => { // Click refresh button. cy.get('#refresh').click(); - cy.get('.css-70qvj9 > .MuiTypography-root').should('be.visible').and('have.text', 'Refresh'); + cy.get('#refresh-title').should('be.visible').and('have.text', 'Refresh'); cy.intercept( { @@ -355,7 +353,7 @@ describe('Peers', () => { // Click refresh button. cy.get('#refresh').click(); - cy.get('.css-70qvj9 > .MuiTypography-root').should('be.visible').and('have.text', 'Refresh'); + cy.get('#refresh-title').should('be.visible').and('have.text', 'Refresh'); cy.get('#cancel').click(); cy.get('#refresh').click(); diff --git a/cypress/e2e/profile/profile.cy.ts b/cypress/e2e/profile/profile.cy.ts index 58005d3..3c122c7 100644 --- a/cypress/e2e/profile/profile.cy.ts +++ b/cypress/e2e/profile/profile.cy.ts @@ -46,27 +46,9 @@ describe('Profile', () => { cy.viewport(1440, 1080); }); - it('opens user profile page from the home page', () => { - cy.visit('/'); - - cy.get('#unfold-more').click(); - - // Go to profil page. - cy.get('#profile-menu').click(); - - // Then I see that the current page is the profile! - cy.url().should('include', '/profile'); - - cy.get('#my-profile').should('contain', 'My Profile'); - }); - it('when data is loaded', () => { - cy.get('#menu-name').should('be.visible').and('have.text', 'root'); - - cy.get('#menu-email').should('be.visible').and('have.text', 'root@example.com'); - // Show user name. - cy.get('.css-70qvj9 > .MuiBox-root > .MuiTypography-h5').should('be.visible').and('have.text', 'root'); + cy.get('#name-title').should('be.visible').and('have.text', 'root'); // Show user description. cy.get('#description').should('be.visible').and('have.text', 'I am root'); @@ -74,14 +56,14 @@ describe('Profile', () => { cy.get('#name').should('be.visible').and('have.text', 'root'); cy.get('#email').should('be.visible').and('have.text', 'root@example.com'); cy.get('#location').should('be.visible').and('have.text', 'Hangzhou'); - cy.get('#phone').should('be.visible').and('have.text', 1234567890); + cy.get('#phone').should('be.visible').and('have.text', '+86 153 1234 5678'); cy.get('#created_at').should('be.visible').and('have.text', '2023-11-06 06:09:04'); // Check Update Personal Information form. cy.get('.MuiGrid-root > .MuiButtonBase-root').click(); cy.get('#bio').should('have.value', 'I am root'); - cy.get('#phone').should('have.value', 1234567890); + cy.get('#phone').should('have.value', '+86 153 1234 5678'); cy.get('#location').should('have.value', 'Hangzhou'); cy.get('#email').should('have.value', 'root@example.com'); }); @@ -100,11 +82,8 @@ describe('Profile', () => { }, ); - cy.get('#menu-name').should('be.visible').and('have.text', '-'); - cy.get('#menu-email').should('be.visible').and('have.text', '-'); - // Show user name. - cy.get('.MuiTypography-caption').should('be.visible').and('have.text', '-'); + cy.get('#name-title').should('be.visible').and('have.text', '-'); // Show user description. cy.get('#description').should('be.visible').and('have.text', '-'); @@ -119,7 +98,7 @@ describe('Profile', () => { cy.get('.MuiGrid-root > .MuiButtonBase-root').click(); cy.get('#bio').should('have.value', ''); - cy.get('#phone').should('have.value', ''); + cy.get('#phone').should('have.value', '+86'); cy.get('#location').should('have.value', ''); cy.get('#email').should('have.value', ''); }); @@ -144,7 +123,7 @@ describe('Profile', () => { cy.get('.MuiGrid-root > .MuiButtonBase-root').click(); cy.get('#bio').should('have.value', ''); - cy.get('#phone').should('have.value', ''); + cy.get('#phone').should('have.value', '+86'); cy.get('#location').should('have.value', ''); cy.get('#email').should('have.value', ''); }); @@ -193,9 +172,6 @@ describe('Profile', () => { cy.get('#save').click(); - // Check whether the navigation bar email has changed. - cy.get('.MuiTypography-caption').should('have.text', 'root@gmail.com'); - // Check if profile description is updated. cy.get('#description').should('be.visible').and('have.text', 'I am root, I will change the description'); @@ -206,7 +182,7 @@ describe('Profile', () => { cy.get('#location').should('be.visible').and('have.text', 'Shanghai'); // Check if profile phone is updated. - cy.get('#phone').should('be.visible').and('have.text', 15123456789); + cy.get('#phone').should('be.visible').and('have.text', '+86 153 1234 5678'); }); it('click the `CANCEL button', () => { @@ -222,7 +198,6 @@ describe('Profile', () => { cy.get('#description').should('be.visible').and('have.text', 'I am root'); // Check whether the navigation bar email has changed. - cy.get('.MuiTypography-caption').should('have.text', 'root@example.com'); cy.get('#email').should('be.visible').and('have.text', 'root@example.com'); // Click EDIT button. @@ -301,7 +276,7 @@ describe('Profile', () => { // Should display message phone the validation error. cy.get('#phone').clear(); - cy.get('#phone').type('1234567890'); + cy.get('#phone').type('+86 153 1234 123123'); // Show verification error message. cy.get('#phone-helper-text').should('be.visible').and('have.text', 'Invalid phone number.'); @@ -309,7 +284,7 @@ describe('Profile', () => { cy.get('.css-1033rfx > .MuiTypography-root').should('exist').and('have.text', 'Update Personal Information'); cy.get('#phone').clear(); - cy.get('#phone').type('15123456789'); + cy.get('#phone').type('+86 151 2345 6789'); // Verification passed. cy.get('#phone-helper-text').should('not.exist'); @@ -353,8 +328,21 @@ describe('Profile', () => { }); }); - // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + // Click change password tab. + cy.get('#tab-password').click(); + + cy.get('#oldPassword').type('dragonfly1'); + cy.get('#newPassword').type('dragonfly2'); + cy.get('#confirmNewPassword').type('dragonfly2'); + + // Click cancel password button. + cy.get('#cancel-change-password').click(); + + // Input should be cleared. + cy.get('#oldPassword').should('have.value', ''); + cy.get('#newPassword').should('have.value', ''); + cy.get('#confirmNewPassword').should('have.value', ''); + cy.get('#oldPassword').type('dragonfly1'); cy.get('#newPassword').type('dragonfly2'); cy.get('#confirmNewPassword').type('dragonfly2'); @@ -366,24 +354,20 @@ describe('Profile', () => { cy.url().should('include', 'signin'); }); - it('click the `CANCEL button', () => { + it('click the Profile tab', () => { // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); - cy.get('.profile_profileContainer__l5i6P > .MuiGrid-root > .MuiTypography-root') - .should('be.visible') - .and('have.text', 'Change Password'); + cy.get('#tab-password').click(); + cy.get('#change-password-title').should('be.visible').and('have.text', 'Change Password'); cy.get('#oldPassword').type('dragonfly1'); cy.get('#newPassword').type('dragonfly2'); cy.get('#confirmNewPassword').type('dragonfly2'); // Click cancel button. - cy.get('#cancel-change-password').click(); - cy.get('.profile_profileContainer__l5i6P > .MuiGrid-root > .MuiTypography-root').should('not.exist'); + cy.get('#tab-profile').click(); + cy.get('#change-password-title').should('not.exist'); - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); - cy.get('.profile_profileContainer__l5i6P > .MuiGrid-root > .MuiTypography-root') - .should('be.visible') - .and('have.text', 'Change Password'); + cy.get('#tab-password').click(); + cy.get('#change-password-title').should('be.visible').and('have.text', 'Change Password'); // Check if old password is empty. cy.get('#oldPassword').should('have.text', ''); @@ -415,7 +399,7 @@ describe('Profile', () => { cy.visit('/profile'); // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); cy.get('#oldPassword').type('dragonfly1'); cy.get('#newPassword').type('dragonfly2'); cy.get('#confirmNewPassword').type('dragonfly2'); @@ -434,7 +418,7 @@ describe('Profile', () => { }); // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); cy.get('#oldPassword').type('dragonfly1'); cy.get('#newPassword').type('dragonfly2'); cy.get('#confirmNewPassword').type('dragonfly2'); @@ -454,7 +438,7 @@ describe('Profile', () => { }); // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); cy.get('#oldPassword').type('dragonfly1'); cy.get('#newPassword').type('dragonfly2'); cy.get('#confirmNewPassword').type('dragonfly2'); @@ -466,7 +450,7 @@ describe('Profile', () => { it('cannot change password without required attributes', () => { // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); cy.get('#change-password').click(); // Show error message. @@ -490,7 +474,7 @@ describe('Profile', () => { const newPasswordLengthIsInsufficient = _.times(7, () => _.sample(characters)).join(''); // Click change password button. - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); // Should display message old password the validation error. cy.get('#oldPassword').type(oldPassword); @@ -543,7 +527,7 @@ describe('Profile', () => { }); it('click the password hide butto', () => { - cy.get('.css-1a9getn > .MuiButtonBase-root').click(); + cy.get('#tab-password').click(); // Verify the display status of the content of the old password input box. cy.get('#oldPassword').type('dragonfly1'); diff --git a/cypress/e2e/schedulers/scheduler.cy.ts b/cypress/e2e/schedulers/scheduler.cy.ts index 2315b3e..9588501 100644 --- a/cypress/e2e/schedulers/scheduler.cy.ts +++ b/cypress/e2e/schedulers/scheduler.cy.ts @@ -119,7 +119,7 @@ describe('Scheduler', () => { cy.get('#status') .should('be.visible') .and('contain', 'Active') - .and('have.css', 'background-color', 'rgb(46, 143, 121)'); + .and('have.css', 'background-color', 'rgb(31, 125, 83)'); // Show features. cy.get('#features').should('be.visible').and('contain', 'Schedule').and('contain', 'Preheat'); @@ -154,7 +154,7 @@ describe('Scheduler', () => { cy.get('#status') .should('be.visible') .and('contain', 'Inactive') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); }); }); diff --git a/cypress/e2e/schedulers/schedulers.cy.ts b/cypress/e2e/schedulers/schedulers.cy.ts index 0199a82..e54d91f 100644 --- a/cypress/e2e/schedulers/schedulers.cy.ts +++ b/cypress/e2e/schedulers/schedulers.cy.ts @@ -65,17 +65,22 @@ describe('Schedulers', () => { // Show idloading. cy.get('.MuiPagination-ul > :nth-child(3) > .MuiButtonBase-root').click(); + // Show hostname. cy.get('#hostname-scheduler-7').should('be.visible').and('contain', 'scheduler-7'); + // Show ip. cy.get('#ip-7').should('be.visible').and('contain', '30.44.98.202'); + // Show state. cy.get('#state-7') .should('be.visible') .and('contain', 'Active') - .and('have.css', 'background-color', 'rgb(46, 143, 121)'); + .and('have.css', 'background-color', 'rgb(31, 125, 83)'); + // Show features. cy.get('#features-7').should('be.visible').and('contain', 'Schedule'); + // Show scheduler-5 information. cy.get('#hostname-scheduler-5').should('be.visible').and('contain', 'scheduler-5'); cy.get('#ip-5').should('be.visible').and('contain', '20.14.28.202'); @@ -83,7 +88,15 @@ describe('Schedulers', () => { cy.get('#state-43') .should('be.visible') .and('contain', 'Inactive') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); + + // Click scheduler-18 operation button. + cy.get('#operation-18').click(); + + cy.get(':nth-child(12) > .MuiPaper-root > .MuiList-root > .schedulers_menu__mxj5m > #view-scheduler-18').click(); + + // Then I see that the current page is the scheduler 18. + cy.url().should('include', 'clusters/1/schedulers/18'); }); it('can display schedulers card', () => { @@ -97,7 +110,7 @@ describe('Schedulers', () => { .should('be.visible') .and('have.text', 'Active') .and('have.css', 'background-color', 'rgba(0, 167, 111, 0.1)'); - cy.get(':nth-child(4) > .MuiButtonBase-root').click(); + cy.get('.MuiPagination-ul > :nth-child(4) > .MuiButtonBase-root').click(); cy.get('#card-hostname-scheduler-41').should('be.visible').and('have.text', 'scheduler-41'); // Show status. cy.get('#card-state-41') @@ -615,7 +628,7 @@ describe('Schedulers', () => { cy.get('[value="inactive"]').click(); - cy.get(':nth-child(4) > .MuiButtonBase-root').click(); + cy.get('.MuiPagination-ul > :nth-child(4) > .MuiButtonBase-root').click(); cy.get('#operation-10').click(); @@ -792,7 +805,7 @@ describe('Schedulers', () => { // Close delete inactive schedulers. cy.get('#close-delete-icon').click(); cy.get('#delete-all-inactive-instances').click(); - cy.get('.css-pbbh6n > .css-70qvj9 > .MuiTypography-root').should('have.text', 'Delete inactive instances'); + cy.get('#delete-inactive-instances-title').should('have.text', 'Delete inactive instances'); cy.get('#schedulerTotal').should('have.text', '0 inactive'); cy.get('#back-button').should('be.disabled'); // Check next button. @@ -869,7 +882,7 @@ describe('Schedulers', () => { } cy.get('#delete-all-inactive-instances').click(); - cy.get('.css-pbbh6n > .css-70qvj9 > .MuiTypography-root').should('have.text', 'Delete inactive instances'); + cy.get('#delete-inactive-instances-title').should('have.text', 'Delete inactive instances'); cy.get('#schedulerTotal').should('have.text', '34 inactive'); cy.get('#back-button').should('be.disabled'); cy.get('#next-button').should('not.be.disabled'); diff --git a/cypress/e2e/seed-peers/seed-peer.cy.ts b/cypress/e2e/seed-peers/seed-peer.cy.ts index cc027c5..a0afdc3 100644 --- a/cypress/e2e/seed-peers/seed-peer.cy.ts +++ b/cypress/e2e/seed-peers/seed-peer.cy.ts @@ -56,7 +56,7 @@ describe('Seed peer', () => { it('click the hostname', () => { cy.visit('/clusters/1/seed-peers'); - cy.get(':nth-child(4) > .MuiButtonBase-root').click(); + cy.get('.MuiPagination-ul > :nth-child(4) > .MuiButtonBase-root').click(); cy.get('#card-hostname-seed-peer-10').should('have.text', 'seed-peer-10'); cy.get('#card-hostname-seed-peer-10').click(); @@ -121,7 +121,7 @@ describe('Seed peer', () => { cy.get('#status') .should('be.visible') .and('contain', 'Active') - .and('have.css', 'background-color', 'rgb(46, 143, 121)'); + .and('have.css', 'background-color', 'rgb(31, 125, 83)'); // Show Created At. cy.get('#created-at').should('have.text', '2023-11-11 20:09:08'); @@ -147,7 +147,7 @@ describe('Seed peer', () => { cy.get('#status') .should('be.visible') .and('contain', 'Inactive') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); }); }); @@ -196,7 +196,7 @@ describe('Seed peer', () => { cy.get('#type').should('contain', '-'); // Show Start. - cy.get(':nth-child(6) > .css-1ltbflq-MuiTypography-root').should('contain', '-'); + cy.get('#status').should('contain', '-'); // Show Created At. cy.get('#created-at').should('contain', '-'); @@ -260,7 +260,7 @@ describe('Seed peer', () => { cy.get('#type').should('contain', '-'); // Show Start. - cy.get(':nth-child(6) > .css-1ltbflq-MuiTypography-root').should('contain', '-'); + cy.get('#status').should('contain', '-'); // Show Created At. cy.get('#created-at').should('contain', '-'); diff --git a/cypress/e2e/seed-peers/seed-peers.cy.ts b/cypress/e2e/seed-peers/seed-peers.cy.ts index 765242b..4393833 100644 --- a/cypress/e2e/seed-peers/seed-peers.cy.ts +++ b/cypress/e2e/seed-peers/seed-peers.cy.ts @@ -74,11 +74,11 @@ describe('Seed peers', () => { cy.get(':nth-child(1) > :nth-child(8) > .MuiChip-root') .should('be.visible') .and('contain', 'Active') - .and('have.css', 'background-color', 'rgb(46, 143, 121)'); + .and('have.css', 'background-color', 'rgb(31, 125, 83)'); }); it('can display seed peers card', () => { - cy.get(':nth-child(4) > .MuiButtonBase-root').click(); + cy.get('.MuiPagination-ul > :nth-child(4) > .MuiButtonBase-root').click(); cy.get('#card-id-7').should('be.visible').and('have.text', '7'); // Show hostname. @@ -329,7 +329,7 @@ describe('Seed peers', () => { cy.get(':nth-child(5) > :nth-child(8) > .MuiChip-root') .should('be.visible') .and('contain', 'Inactive') - .and('have.css', 'background-color', 'rgb(28, 41, 58)'); + .and('have.css', 'background-color', 'rgb(24, 35, 15)'); // Go to last page. cy.get(':nth-child(7) > .MuiButtonBase-root').click(); @@ -554,7 +554,7 @@ describe('Seed peers', () => { cy.get('#card').click(); // Go to last page. - cy.get(':nth-child(4) > .MuiButtonBase-root').click(); + cy.get('.MuiPagination-ul > :nth-child(4) > .MuiButtonBase-root').click(); // Check the current page number. cy.get('#seed-peer-pagination > .MuiPagination-ul .Mui-selected').should('have.text', '3'); @@ -895,7 +895,7 @@ describe('Seed peers', () => { cy.get('#close-delete-icon').click(); cy.get('#delete-all-inactive-instances').click(); - cy.get('.css-pbbh6n > .css-70qvj9 > .MuiTypography-root').should('have.text', 'Delete inactive instances'); + cy.get('#delete-inactive-instances-title').should('have.text', 'Delete inactive instances'); cy.get('#seedPeerTotal').should('have.text', '0 inactive'); cy.get('#back-button').should('be.disabled'); @@ -983,7 +983,7 @@ describe('Seed peers', () => { cy.get('#delete-all-inactive-instances').click(); - cy.get('.css-pbbh6n > .css-70qvj9 > .MuiTypography-root').should('have.text', 'Delete inactive instances'); + cy.get('#delete-inactive-instances-title').should('have.text', 'Delete inactive instances'); cy.get('#seedPeerTotal').should('have.text', '20 inactive'); cy.get('#back-button').should('be.disabled'); cy.get('#next-button').should('not.be.disabled'); diff --git a/cypress/e2e/signin/signin.cy.ts b/cypress/e2e/signin/signin.cy.ts index e278024..8457982 100644 --- a/cypress/e2e/signin/signin.cy.ts +++ b/cypress/e2e/signin/signin.cy.ts @@ -136,12 +136,12 @@ describe('Signin', () => { }); it('click the `Create an account` button', () => { - cy.get('.MuiTypography-inherit > .MuiTypography-root').click(); + cy.get('#create-account').click(); // Then I see that the current page is the signup! cy.url().should('include', '/signup'); - cy.get('.MuiTypography-inherit > .MuiTypography-root').click(); + cy.get('#sign-in').click(); // Then I see that the current page is the signin! cy.url().should('include', '/signin'); diff --git a/cypress/e2e/signup/signup.cy.ts b/cypress/e2e/signup/signup.cy.ts index 988494c..9e3499f 100644 --- a/cypress/e2e/signup/signup.cy.ts +++ b/cypress/e2e/signup/signup.cy.ts @@ -98,12 +98,12 @@ describe('Signup', () => { }); it('click the `Sign in` button', () => { - cy.get('.MuiTypography-inherit > .MuiTypography-root').click(); + cy.get('#sign-in').click(); // Then I see that the current page is the signin! cy.url().should('include', '/signin'); - cy.get('.MuiTypography-inherit > .MuiTypography-root').click(); + cy.get('#create-account').click(); // Then I see that the current page is the signup! cy.url().should('include', '/signup'); diff --git a/cypress/e2e/users/create.user.cy.ts b/cypress/e2e/users/create.user.cy.ts new file mode 100644 index 0000000..8debb46 --- /dev/null +++ b/cypress/e2e/users/create.user.cy.ts @@ -0,0 +1,252 @@ +import _ from 'lodash'; +import createUsers from '../../fixtures/users/create-user.json'; +import users from '../../fixtures/users/users.json'; + +describe('Create user', () => { + beforeEach(() => { + cy.signin(); + + cy.visit('/users/new'); + + cy.viewport(1440, 1080); + }); + + it('can create user', () => { + cy.visit('/users'); + + cy.intercept( + { + method: 'GET', + url: '/api/v1/users?page=1&per_page=10000000', + }, + (req) => { + req.reply({ + statusCode: 200, + body: users, + }); + }, + ); + + // Go to last page. + cy.get('.MuiPagination-ul > :nth-child(3) > .MuiButtonBase-root').click(); + + // There is only one piece of data on the last pag. + cy.get('#user-table-body').children().should('have.length', 1); + + cy.get('#create-user').click(); + + // Then I see that the current page is the New user! + cy.url().should('include', '/users/new'); + + cy.get('.MuiBreadcrumbs-ol').should('contain', 'New user'); + + cy.get('#account').type('billy'); + + cy.get('#email').type('billy@example.com'); + + cy.get('#bio').type('I am billy'); + + cy.get('#phone').type('18012341234'); + + cy.get('#location').type('ParisFrance'); + + cy.get('#password').type('Dragonfly1'); + + cy.get('#confirmPassword').type('Dragonfly1'); + + cy.intercept('POST', '/api/v1/users/signup', (req) => { + req.reply({ + statusCode: 200, + body: [], + }); + }); + + cy.intercept( + { + method: 'GET', + url: '/api/v1/users?page=1&per_page=10000000', + }, + (req) => { + req.reply({ + statusCode: 200, + body: createUsers, + }); + }, + ); + + cy.get('#save').click(); + + // Go to last page. + cy.get('.MuiPagination-ul > :nth-child(3) > .MuiButtonBase-root').click(); + + // There is only one piece of data on the last pag. + cy.get('#user-table-body').children().should('have.length', 2); + + cy.get('#user-table-body > :nth-child(2) > :nth-child(2)').should('have.text', 'billy'); + }); + + it('cannot create account with existing email', () => { + cy.intercept('POST', '/api/v1/users/signup', (req) => { + req.reply({ + statusCode: 409, + body: { + message: 'Conflict', + }, + }); + }); + + cy.get('#account').type('root-1'); + cy.get('#email').type('lucy@example.com'); + cy.get('#phone').type('18012341234'); + cy.get('#password').type('dragonfly1'); + cy.get('#confirmPassword').type(`dragonfly1{enter}`); + + // Show error message. + cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Conflict'); + + // Close error message. + cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); + cy.get('.MuiSnackbar-root > .MuiPaper-root').should('not.exist'); + }); + + it('cannot create account with existing account', () => { + cy.intercept('POST', '/api/v1/users/signup', (req) => { + req.reply({ + statusCode: 409, + body: { + message: 'Conflict', + }, + }); + }); + + cy.get('#account').type('root'); + cy.get('#email').type('root@console.co'); + cy.get('#phone').type('18012341234'); + cy.get('#password').type('dragonfly1'); + cy.get('#confirmPassword').type(`dragonfly1{enter}`); + + // Show error message. + cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Conflict'); + cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); + cy.get('.MuiSnackbar-root > .MuiPaper-root').should('not.exist'); + }); + + it('click the password hide butto and confirm password hide butto', () => { + cy.get('#password').type('dragonfly1'); + cy.get('#confirmPassword').type(`dragonfly1{enter}`); + + cy.get('.MuiInputBase-root > .MuiButtonBase-root > [data-testid="VisibilityOffIcon"] > path').click(); + cy.get('#confirmPassword').should('have.value', 'dragonfly1'); + + cy.get('[data-testid="VisibilityOffIcon"]').click(); + cy.get('#confirmPassword').should('have.value', 'dragonfly1'); + }); + + it('should handle API error response', () => { + cy.intercept('POST', '/api/v1/users/signup', (req) => { + req.reply({ + forceNetworkError: true, + }); + }); + + cy.get('#account').type('root-1'); + cy.get('#email').type('root@console.com'); + cy.get('#phone').type('18012341234'); + cy.get('#password').type('dragonfly1'); + cy.get('#confirmPassword').type(`dragonfly1{enter}`); + + // Show error message. + cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Failed to fetch'); + cy.get('.MuiAlert-action > .MuiButtonBase-root').click(); + cy.get('.MuiSnackbar-root > .MuiPaper-root').should('not.exist'); + }); + + it('click the `CANCEL button', () => { + cy.get('#cancel').click(); + + // Then I see that the current page is the users! + cy.url().should('include', '/users'); + }); + + it('cannot signup with invalid attributes', () => { + const nameNotLongEnough = _.times(2, () => _.sample('abcdefghijklmnopqrstuvwxyz')).join(''); + const nameLengthExceeds = _.times(11, () => _.sample('abcdefghijklmnopqrstuvwxyz')).join(''); + const passsword = _.times(8, () => _.sample('abcdefghijklmnopqrstuvwxyz')).join(''); + const location = _.times(101, () => _.sample('abcdefghijklmnopqrstuvwxyz')).join(''); + + // Should display message account the validation error. + cy.get('#account').type(nameNotLongEnough); + cy.get('#account-helper-text').should('be.visible').and('contain', 'Fill in the characters, the length is 3-10.'); + + cy.get('#account').type(nameLengthExceeds); + cy.get('#account-helper-text').should('be.visible').and('contain', 'Fill in the characters, the length is 3-10.'); + + cy.get('#account').clear(); + + cy.get('#account').type('root'); + + // Verification passed. + cy.get('#account-helper-text').should('not.exist'); + + // Should display message email the validation error. + cy.get('#email').type('root'); + cy.get('#email-helper-text').should('be.visible').and('contain', 'Email is invalid or already taken.'); + + cy.get('#email').clear(); + + cy.get('#email').type('root@console.com'); + + // Verification passed. + cy.get('#email-helper-text').should('not.exist'); + + // Should display message phone the validation error. + cy.get('#phone').type('001'); + cy.get('#phone-helper-text').should('be.visible').and('contain', 'Enter a valid phone number.'); + + cy.get('#phone').clear(); + + cy.get('#phone').type('+86 19101011212'); + + cy.get('#phone-helper-text').should('not.exist'); + + // Should display message location the validation error. + cy.get('#location').clear(); + cy.get('#location').type(location); + + // Show verification error message. + cy.get('#location-helper-text') + .should('be.visible') + .and('have.text', 'Fill in the characters, the length is 0-100.'); + cy.get('#location').clear(); + cy.get('#location').type('Shanghai'); + cy.get('#location-helper-text').should('not.exist'); + + // Should display message password the validation error. + cy.get('#password').type(passsword); + + // Missing number. + cy.get('#password-helper-text') + .should('be.visible') + .and('contain', 'At least 8-16 characters, with at least 1 lowercase letter and 1 number.'); + + cy.get('#password').clear(); + + cy.get('#password').type('dragonfly1'); + + // Verification passed. + cy.get('#password-helper-text').should('not.exist'); + + // Should display message confirm password the validation error. + cy.get('#confirmPassword').type(`dragonfly`); + + // Confirm password verification error when the two passwords are not the same. + cy.get('#confirmPassword-helper-text').should('be.visible').and('contain', 'Please enter the same password.'); + + cy.get('#confirmPassword').clear(); + + cy.get('#confirmPassword').type('dragonfly1'); + + // verification passed. + cy.get('#confirmPassword-helper-text').should('not.exist'); + }); +}); diff --git a/cypress/e2e/users/users.cy.ts b/cypress/e2e/users/users.cy.ts index bfd649b..3c57f04 100644 --- a/cypress/e2e/users/users.cy.ts +++ b/cypress/e2e/users/users.cy.ts @@ -141,13 +141,13 @@ describe('Users', () => { cy.get('#name').should('have.text', 'root'); cy.get('#role').should('have.text', 'root'); cy.get('#email').should('have.text', 'root@example.com'); - cy.get('#phone').should('have.text', '1234567890'); + cy.get('#phone').should('have.text', '+86 153 1234 5678'); cy.get('#location').should('have.text', 'Hangzhou'); cy.get('#created-at').should('contain', '2023-11-06 06:09:04'); cy.get('#updated-at').should('contain', '2023-11-06 06:09:04'); // closure user details. - cy.get('.MuiListSubheader-root > .MuiButtonBase-root').click(); + cy.get('#closure-user-detail').click(); cy.get('#action-jack').click(); @@ -177,7 +177,7 @@ describe('Users', () => { cy.get('#name').should('have.text', 'jack'); cy.get('#role').should('have.text', 'guest'); cy.get('#email').should('have.text', 'jack@example.com'); - cy.get('#phone').should('have.text', '1234567890'); + cy.get('#phone').should('have.text', '+86 153 1234 5678'); cy.get('#location').should('have.text', 'Shanghai'); cy.get('#created-at').should('contain', '2023-11-07 06:09:04'); cy.get('.MuiList-root > :nth-child(17)').should('contain', '2023-11-07 06:09:04'); @@ -375,7 +375,7 @@ describe('Users', () => { // Check the current page number. cy.get('#user-pagination > .MuiPagination-ul .Mui-selected').should('have.text', '2'); - cy.get('[href="/clusters"]').click(); + cy.get('#clusters').click(); // Then I see that the current page is the show update personal-access-tokens. cy.url().should('include', '/clusters'); diff --git a/cypress/fixtures/users/create-user.json b/cypress/fixtures/users/create-user.json new file mode 100644 index 0000000..49a78ff --- /dev/null +++ b/cypress/fixtures/users/create-user.json @@ -0,0 +1,170 @@ +[ + { + "id": 1, + "created_at": "2023-12-07T12:26:52Z", + "updated_at": "2023-12-07T12:26:52Z", + "is_del": 0, + "email": "root@example.com", + "name": "root", + "avatar": "", + "phone": "", + "state": "enable", + "location": "Shanghai", + "bio": "", + "configs": null + }, + { + "id": 2, + "created_at": "2023-11-06T06:09:04Z", + "updated_at": "2023-11-06T06:09:04Z", + "is_del": 0, + "email": "lucy@example.com", + "name": "lucy", + "avatar": "https://example.com/avatar.png", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Hangzhou", + "bio": "I am lucy", + "configs": null + }, + { + "id": 3, + "created_at": "2023-11-07T06:09:04Z", + "updated_at": "2023-11-07T06:09:04Z", + "is_del": 0, + "email": "jack@example.com", + "name": "jack", + "avatar": "https://example.com/avatar.png", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Shanghai", + "bio": "I am jack", + "configs": null + }, + { + "id": 4, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "mary@example.com", + "name": "mary", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "shanghaiHongqiao", + "bio": "I am mary", + "configs": null + }, + { + "id": 5, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "lip@example.com", + "name": "lip", + "avatar": "", + "phone": "+86 153 1234 9876", + "state": "enable", + "location": "Chengdu", + "bio": "I am lip", + "configs": null + }, + { + "id": 6, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "carl@example.com", + "name": "carl", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "shanghaiHuangpu", + "bio": "I am carl", + "configs": null + }, + { + "id": 7, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "david@example.com", + "name": "david", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Hangzhou", + "bio": "I am david", + "configs": null + }, + { + "id": 8, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "debbie@example.com", + "name": "debbie", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Tianjing", + "bio": "I am debbie", + "configs": null + }, + { + "id": 9, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "allen@example.com", + "name": "allen", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Fuzhou", + "bio": "I am allen", + "configs": null + }, + { + "id": 10, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "liam@example.com", + "name": "liam", + "avatar": "", + "phone": "+86 153 1234 9876", + "state": "enable", + "location": "Nanjing", + "bio": "I am liam", + "configs": null + }, + { + "id": 11, + "created_at": "2023-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "noah@example.com", + "name": "noah", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "Shengyang", + "bio": "I am noah", + "configs": null + }, + { + "id": 12, + "created_at": "2024-12-05T07:17:47Z", + "updated_at": "2023-12-12T06:37:49Z", + "is_del": 0, + "email": "billy@example.com", + "name": "billy", + "avatar": "", + "phone": "+86 153 1234 5678", + "state": "enable", + "location": "ParisFrance", + "bio": "I am billy", + "configs": null + } +] diff --git a/cypress/fixtures/users/guest-user.json b/cypress/fixtures/users/guest-user.json index 17dcac5..45a19e3 100644 --- a/cypress/fixtures/users/guest-user.json +++ b/cypress/fixtures/users/guest-user.json @@ -6,7 +6,7 @@ "email": "jack@example.com", "name": "jack", "avatar": "https://example.com/avatar.png", - "phone": "1234567890", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Shanghai", "bio": "I am jack", diff --git a/cypress/fixtures/users/update-user.json b/cypress/fixtures/users/update-user.json index 4eb7399..0c52ae1 100644 --- a/cypress/fixtures/users/update-user.json +++ b/cypress/fixtures/users/update-user.json @@ -6,7 +6,7 @@ "email": "root@gmail.com", "name": "root", "avatar": "https://example.com/avatar.png", - "phone": "15123456789", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Shanghai", "bio": "I am root, I will change the description", diff --git a/cypress/fixtures/users/user.json b/cypress/fixtures/users/user.json index be8a7bf..ad2e8be 100644 --- a/cypress/fixtures/users/user.json +++ b/cypress/fixtures/users/user.json @@ -6,7 +6,7 @@ "email": "root@example.com", "name": "root", "avatar": "https://example.com/avatar.png", - "phone": "1234567890", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Hangzhou", "bio": "I am root", diff --git a/cypress/fixtures/users/users.json b/cypress/fixtures/users/users.json index c73cb65..1afa77a 100644 --- a/cypress/fixtures/users/users.json +++ b/cypress/fixtures/users/users.json @@ -21,7 +21,7 @@ "email": "lucy@example.com", "name": "lucy", "avatar": "https://example.com/avatar.png", - "phone": "1234567890", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Hangzhou", "bio": "I am lucy", @@ -35,7 +35,7 @@ "email": "jack@example.com", "name": "jack", "avatar": "https://example.com/avatar.png", - "phone": "1234567890", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Shanghai", "bio": "I am jack", @@ -49,7 +49,7 @@ "email": "mary@example.com", "name": "mary", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "shanghaiHongqiao", "bio": "I am mary", @@ -63,7 +63,7 @@ "email": "lip@example.com", "name": "lip", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Chengdu", "bio": "I am lip", @@ -77,7 +77,7 @@ "email": "carl@example.com", "name": "carl", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "shanghaiHuangpu", "bio": "I am carl", @@ -91,7 +91,7 @@ "email": "david@example.com", "name": "david", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Hangzhou", "bio": "I am david", @@ -105,7 +105,7 @@ "email": "debbie@example.com", "name": "debbie", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Tianjing", "bio": "I am debbie", @@ -119,7 +119,7 @@ "email": "allen@example.com", "name": "allen", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Fuzhou", "bio": "I am allen", @@ -133,7 +133,7 @@ "email": "liam@example.com", "name": "liam", "avatar": "", - "phone": "18170017449", + "phone": "+86 153 1234 9876", "state": "enable", "location": "Nanjing", "bio": "I am liam", @@ -147,7 +147,7 @@ "email": "noah@example.com", "name": "noah", "avatar": "", - "phone": "15170017449", + "phone": "+86 153 1234 5678", "state": "enable", "location": "Shengyang", "bio": "I am noah", diff --git a/package.json b/package.json index 8b6e8d1..9877411 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "jsonwebtoken": "^9.0.1", "lodash.upperfirst": "^4.3.1", "lottie-react": "^2.4.0", + "mui-tel-input": "^6.0.0", "node-polyfill-webpack-plugin": "^4.0.0", "process": "^0.11.10", "query-string": "^9.0.0", diff --git a/public/fonts/cluster/active.svg b/public/fonts/cluster/active.svg deleted file mode 100644 index 6493c93..0000000 --- a/public/fonts/cluster/active.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/cidrs.svg b/public/fonts/cluster/cidrs.svg deleted file mode 100644 index 6a38f27..0000000 --- a/public/fonts/cluster/cidrs.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/cluster-id.svg b/public/fonts/cluster/cluster-id.svg deleted file mode 100644 index a129b17..0000000 --- a/public/fonts/cluster/cluster-id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/cluster.svg b/public/fonts/cluster/cluster.svg deleted file mode 100644 index 17ad213..0000000 --- a/public/fonts/cluster/cluster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/default.svg b/public/fonts/cluster/default.svg deleted file mode 100644 index e6852bb..0000000 --- a/public/fonts/cluster/default.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/delete-inactive-error.svg b/public/fonts/cluster/delete-inactive-error.svg deleted file mode 100644 index d43ddc7..0000000 --- a/public/fonts/cluster/delete-inactive-error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/delete-warning.svg b/public/fonts/cluster/delete-warning.svg deleted file mode 100644 index dbe425c..0000000 --- a/public/fonts/cluster/delete-warning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/delete.svg b/public/fonts/cluster/delete.svg deleted file mode 100644 index 2657438..0000000 --- a/public/fonts/cluster/delete.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/hostname.svg b/public/fonts/cluster/hostname.svg deleted file mode 100644 index c556344..0000000 --- a/public/fonts/cluster/hostname.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/hostnames.svg b/public/fonts/cluster/hostnames.svg deleted file mode 100644 index 44dbe28..0000000 --- a/public/fonts/cluster/hostnames.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/idc.svg b/public/fonts/cluster/idc.svg deleted file mode 100644 index ae27f73..0000000 --- a/public/fonts/cluster/idc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/ip.svg b/public/fonts/cluster/ip.svg deleted file mode 100644 index 269c70d..0000000 --- a/public/fonts/cluster/ip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/location.svg b/public/fonts/cluster/location.svg deleted file mode 100644 index aaaf98a..0000000 --- a/public/fonts/cluster/location.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/logo.svg b/public/fonts/cluster/logo.svg deleted file mode 100644 index 9f9ef72..0000000 --- a/public/fonts/cluster/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/page-loading.svg b/public/fonts/cluster/page-loading.svg deleted file mode 100644 index f9f48f1..0000000 --- a/public/fonts/cluster/page-loading.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/round.svg b/public/fonts/cluster/round.svg deleted file mode 100644 index a63f6f0..0000000 --- a/public/fonts/cluster/round.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/scheduler-id.svg b/public/fonts/cluster/scheduler-id.svg deleted file mode 100644 index c263955..0000000 --- a/public/fonts/cluster/scheduler-id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/scheduler-ip.svg b/public/fonts/cluster/scheduler-ip.svg deleted file mode 100644 index 6ca262a..0000000 --- a/public/fonts/cluster/scheduler-ip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/scheduler.svg b/public/fonts/cluster/scheduler.svg deleted file mode 100644 index 9ac40a8..0000000 --- a/public/fonts/cluster/scheduler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/seed-peer.svg b/public/fonts/cluster/seed-peer.svg deleted file mode 100644 index f31e6dd..0000000 --- a/public/fonts/cluster/seed-peer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/total.svg b/public/fonts/cluster/total.svg deleted file mode 100644 index 80d5011..0000000 --- a/public/fonts/cluster/total.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/fonts/cluster/user.svg b/public/fonts/cluster/user.svg deleted file mode 100644 index bdf1878..0000000 --- a/public/fonts/cluster/user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/active.svg b/public/icons/cluster/active.svg deleted file mode 100644 index 6493c93..0000000 --- a/public/icons/cluster/active.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/cluster.svg b/public/icons/cluster/cluster.svg deleted file mode 100644 index 17ad213..0000000 --- a/public/icons/cluster/cluster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/default.svg b/public/icons/cluster/default.svg deleted file mode 100644 index e6852bb..0000000 --- a/public/icons/cluster/default.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/delete-inactive.svg b/public/icons/cluster/delete-inactive.svg deleted file mode 100644 index 7559f98..0000000 --- a/public/icons/cluster/delete-inactive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/delete.svg b/public/icons/cluster/delete.svg deleted file mode 100644 index 2657438..0000000 --- a/public/icons/cluster/delete.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/features.svg b/public/icons/cluster/features.svg deleted file mode 100644 index 3345b77..0000000 --- a/public/icons/cluster/features.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/id.svg b/public/icons/cluster/id.svg deleted file mode 100644 index de4dd20..0000000 --- a/public/icons/cluster/id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/inactive-total.svg b/public/icons/cluster/inactive-total.svg deleted file mode 100644 index 620e6cb..0000000 --- a/public/icons/cluster/inactive-total.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/logo.svg b/public/icons/cluster/logo.svg deleted file mode 100644 index 9f9ef72..0000000 --- a/public/icons/cluster/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/peer/export-file.svg b/public/icons/cluster/peer/export-file.svg deleted file mode 100644 index 758a536..0000000 --- a/public/icons/cluster/peer/export-file.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/peer/git-commits.svg b/public/icons/cluster/peer/git-commits.svg deleted file mode 100644 index 4d7477a..0000000 --- a/public/icons/cluster/peer/git-commits.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/icons/cluster/peer/refresh-dialog.svg b/public/icons/cluster/peer/refresh-dialog.svg deleted file mode 100644 index c5c951a..0000000 --- a/public/icons/cluster/peer/refresh-dialog.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/peer/total.svg b/public/icons/cluster/peer/total.svg deleted file mode 100644 index a661cba..0000000 --- a/public/icons/cluster/peer/total.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/public/icons/cluster/preheat.svg b/public/icons/cluster/preheat.svg deleted file mode 100644 index e339227..0000000 --- a/public/icons/cluster/preheat.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/round.svg b/public/icons/cluster/round.svg deleted file mode 100644 index a63f6f0..0000000 --- a/public/icons/cluster/round.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler.svg b/public/icons/cluster/scheduler.svg deleted file mode 100644 index 9ac40a8..0000000 --- a/public/icons/cluster/scheduler.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/active.svg b/public/icons/cluster/scheduler/active.svg deleted file mode 100644 index 4465845..0000000 --- a/public/icons/cluster/scheduler/active.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/cluster-id.svg b/public/icons/cluster/scheduler/cluster-id.svg deleted file mode 100644 index ac18edb..0000000 --- a/public/icons/cluster/scheduler/cluster-id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/created-at.svg b/public/icons/cluster/scheduler/created-at.svg deleted file mode 100644 index 90a4cce..0000000 --- a/public/icons/cluster/scheduler/created-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/features.svg b/public/icons/cluster/scheduler/features.svg deleted file mode 100644 index 512bf9a..0000000 --- a/public/icons/cluster/scheduler/features.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/hostname.svg b/public/icons/cluster/scheduler/hostname.svg deleted file mode 100644 index b4ba2b5..0000000 --- a/public/icons/cluster/scheduler/hostname.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/port.svg b/public/icons/cluster/scheduler/port.svg deleted file mode 100644 index a4a1545..0000000 --- a/public/icons/cluster/scheduler/port.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/scheduler-id.svg b/public/icons/cluster/scheduler/scheduler-id.svg deleted file mode 100644 index f2f864a..0000000 --- a/public/icons/cluster/scheduler/scheduler-id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/scheduler-ip.svg b/public/icons/cluster/scheduler/scheduler-ip.svg deleted file mode 100644 index 076be07..0000000 --- a/public/icons/cluster/scheduler/scheduler-ip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/tab-scheduler.svg b/public/icons/cluster/scheduler/tab-scheduler.svg deleted file mode 100644 index 96d1022..0000000 --- a/public/icons/cluster/scheduler/tab-scheduler.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/public/icons/cluster/scheduler/updated-at.svg b/public/icons/cluster/scheduler/updated-at.svg deleted file mode 100644 index 6af943d..0000000 --- a/public/icons/cluster/scheduler/updated-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer.svg b/public/icons/cluster/seed-peer.svg deleted file mode 100644 index f31e6dd..0000000 --- a/public/icons/cluster/seed-peer.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/download-port.svg b/public/icons/cluster/seed-peer/download-port.svg deleted file mode 100644 index 7c425c6..0000000 --- a/public/icons/cluster/seed-peer/download-port.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/object-storage-port.svg b/public/icons/cluster/seed-peer/object-storage-port.svg deleted file mode 100644 index 5ff105b..0000000 --- a/public/icons/cluster/seed-peer/object-storage-port.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/port.svg b/public/icons/cluster/seed-peer/port.svg deleted file mode 100644 index 5257369..0000000 --- a/public/icons/cluster/seed-peer/port.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/seed-peer-download-port.svg b/public/icons/cluster/seed-peer/seed-peer-download-port.svg deleted file mode 100644 index 7317f0c..0000000 --- a/public/icons/cluster/seed-peer/seed-peer-download-port.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/seed-peer-type.svg b/public/icons/cluster/seed-peer/seed-peer-type.svg deleted file mode 100644 index 4678f17..0000000 --- a/public/icons/cluster/seed-peer/seed-peer-type.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/statistics.svg b/public/icons/cluster/statistics.svg deleted file mode 100644 index 89336d6..0000000 --- a/public/icons/cluster/statistics.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/tab-cluster.svg b/public/icons/cluster/tab-cluster.svg deleted file mode 100644 index 3fddc51..0000000 --- a/public/icons/cluster/tab-cluster.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/cluster/unfoldMore.svg b/public/icons/cluster/unfoldMore.svg deleted file mode 100644 index 6717ad9..0000000 --- a/public/icons/cluster/unfoldMore.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/insight/insight.svg b/public/icons/insight/insight.svg deleted file mode 100644 index ad034ff..0000000 --- a/public/icons/insight/insight.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/public/icons/insight/selected-insight.svg b/public/icons/insight/selected-insight.svg deleted file mode 100644 index e1bd7d2..0000000 --- a/public/icons/insight/selected-insight.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/public/icons/job/preheat/created-at.svg b/public/icons/job/preheat/created-at.svg deleted file mode 100644 index 90a4cce..0000000 --- a/public/icons/job/preheat/created-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/preheat/detail.svg b/public/icons/job/preheat/detail.svg deleted file mode 100644 index ae86f2c..0000000 --- a/public/icons/job/preheat/detail.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/preheat/failure.svg b/public/icons/job/preheat/failure.svg deleted file mode 100644 index 977efa8..0000000 --- a/public/icons/job/preheat/failure.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/public/icons/job/preheat/filter.svg b/public/icons/job/preheat/filter.svg deleted file mode 100644 index dc37724..0000000 --- a/public/icons/job/preheat/filter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/preheat/id.svg b/public/icons/job/preheat/id.svg deleted file mode 100644 index f2f864a..0000000 --- a/public/icons/job/preheat/id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/preheat/pending.svg b/public/icons/job/preheat/pending.svg deleted file mode 100644 index 1e68bb8..0000000 --- a/public/icons/job/preheat/pending.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/public/icons/job/preheat/selected-job.svg b/public/icons/job/preheat/selected-job.svg deleted file mode 100644 index b0ae812..0000000 --- a/public/icons/job/preheat/selected-job.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/icons/job/task/clear-cache.svg b/public/icons/job/task/clear-cache.svg deleted file mode 100644 index 14e0bf4..0000000 --- a/public/icons/job/task/clear-cache.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/error-log.svg b/public/icons/job/task/error-log.svg deleted file mode 100644 index 77f907e..0000000 --- a/public/icons/job/task/error-log.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/executions.svg b/public/icons/job/task/executions.svg deleted file mode 100644 index a724275..0000000 --- a/public/icons/job/task/executions.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/failure.svg b/public/icons/job/task/failure.svg deleted file mode 100644 index 2839a1e..0000000 --- a/public/icons/job/task/failure.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/hostname.svg b/public/icons/job/task/hostname.svg deleted file mode 100644 index d7eb382..0000000 --- a/public/icons/job/task/hostname.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/no-task.svg b/public/icons/job/task/no-task.svg deleted file mode 100644 index d13c11c..0000000 --- a/public/icons/job/task/no-task.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No Caching - - - Please enter the task id in your search - box to search for cache. - - - - - - \ No newline at end of file diff --git a/public/icons/job/task/task-id.svg b/public/icons/job/task/task-id.svg deleted file mode 100644 index 4f0fd44..0000000 --- a/public/icons/job/task/task-id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/job/task/type.svg b/public/icons/job/task/type.svg deleted file mode 100644 index d2692fa..0000000 --- a/public/icons/job/task/type.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/created-at.svg b/public/icons/profile/created-at.svg deleted file mode 100644 index 90a4cce..0000000 --- a/public/icons/profile/created-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/email.svg b/public/icons/profile/email.svg deleted file mode 100644 index c8b78d4..0000000 --- a/public/icons/profile/email.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/id.svg b/public/icons/profile/id.svg deleted file mode 100644 index f2f864a..0000000 --- a/public/icons/profile/id.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/location.svg b/public/icons/profile/location.svg deleted file mode 100644 index 678a275..0000000 --- a/public/icons/profile/location.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/name.svg b/public/icons/profile/name.svg deleted file mode 100644 index 4d609a8..0000000 --- a/public/icons/profile/name.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/profile/phone.svg b/public/icons/profile/phone.svg deleted file mode 100644 index ae86d79..0000000 --- a/public/icons/profile/phone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/tokens/developer.svg b/public/icons/tokens/developer.svg deleted file mode 100644 index 3ff7340..0000000 --- a/public/icons/tokens/developer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/icons/tokens/done.svg b/public/icons/tokens/done.svg deleted file mode 100644 index 71606dd..0000000 --- a/public/icons/tokens/done.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/tokens/key.svg b/public/icons/tokens/key.svg deleted file mode 100644 index 106b812..0000000 --- a/public/icons/tokens/key.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/tokens/selected-developer.svg b/public/icons/tokens/selected-developer.svg deleted file mode 100644 index 8d57800..0000000 --- a/public/icons/tokens/selected-developer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/public/icons/user/bio.svg b/public/icons/user/bio.svg deleted file mode 100644 index 5c150dc..0000000 --- a/public/icons/user/bio.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/created-at.svg b/public/icons/user/created-at.svg deleted file mode 100644 index e600a88..0000000 --- a/public/icons/user/created-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/detail-role.svg b/public/icons/user/detail-role.svg deleted file mode 100644 index dc3ba2d..0000000 --- a/public/icons/user/detail-role.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/edit.svg b/public/icons/user/edit.svg deleted file mode 100644 index 7928eb1..0000000 --- a/public/icons/user/edit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/location.svg b/public/icons/user/location.svg deleted file mode 100644 index 9954cb4..0000000 --- a/public/icons/user/location.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/phone.svg b/public/icons/user/phone.svg deleted file mode 100644 index 9976a99..0000000 --- a/public/icons/user/phone.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/role.svg b/public/icons/user/role.svg deleted file mode 100644 index 4f6f374..0000000 --- a/public/icons/user/role.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/selected-user.svg b/public/icons/user/selected-user.svg deleted file mode 100644 index d6d33d3..0000000 --- a/public/icons/user/selected-user.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/public/icons/user/updated-at.svg b/public/icons/user/updated-at.svg deleted file mode 100644 index fff20bf..0000000 --- a/public/icons/user/updated-at.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user/user.svg b/public/icons/user/user.svg deleted file mode 100644 index ef82479..0000000 --- a/public/icons/user/user.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/public/images/404/404.svg b/public/images/404/404.svg deleted file mode 100644 index 5fb3fdf..0000000 --- a/public/images/404/404.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - 切片 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/images/login/features.svg b/public/images/login/features.svg deleted file mode 100644 index 9ad6d39..0000000 --- a/public/images/login/features.svg +++ /dev/null @@ -1,654 +0,0 @@ - - - 矩形 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P2P File Distribution - - - - Ecosystem - - - - Peer Level Control - - - - Consistency - - - - - Isolate Abnormal Peers - - - - Noninvasive - - - - - - Use P2P technology for file - transfer, improve download - efficiency, and save bandwidth - across IDC. - - - Support for host-level limits - speed. - - - Automatically isolate abnormal - peers to improve download - stability. - - - Harbor can distribute and - warm up images based on - Dragonfly. - - - It can ensure that the same - file is consistent in the peer- - to-peer transmission. - - - Supports multiple containers - for distributing images. - - - Features - - - Dragonfly provides a one-stop solution for large-scale file distribution. The basic capabilities - provided by Dragonfly include: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/images/login/login.svg b/public/images/login/login.svg deleted file mode 100644 index 0567f41..0000000 --- a/public/images/login/login.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 12b715e..cf0e0fb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,58 @@ import Main from './layouts/main'; +import { createTheme, ThemeProvider } from '@mui/material'; +import { createContext, useEffect, useMemo, useState } from 'react'; + +export const ColorModeContext = createContext({ toggleColorMode: () => {} }); function App() { - return
; + const [mode, setMode] = useState<'light' | 'dark'>(() => { + const dataTheme = localStorage.getItem('data-theme'); + return (dataTheme as 'light' | 'dark') || 'light'; + }); + + const colorMode = useMemo( + () => ({ + toggleColorMode: () => { + setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + }, + }), + [], + ); + + useEffect(() => { + document.documentElement.setAttribute('data-theme', mode); + + localStorage.setItem('data-theme', mode); + }, [mode]); + + const theme = useMemo( + () => + createTheme({ + palette: { + mode, + primary: { + main: mode === 'light' ? '#1C293A' : 'rgba(255, 255, 255, 0.8)', + }, + text: { + primary: mode === 'light' ? '#1C293A' : 'rgba(255, 255, 255, 0.8)', + }, + success: { + main: mode === 'light' ? '#1f7d53' : '#008170', + }, + }, + typography: { + fontFamily: 'mabry-light', + }, + }), + [mode], + ); + return ( + + +
+ + + ); } export default App; diff --git a/src/assets/images/404/404.svg b/src/assets/images/404/404.svg new file mode 100644 index 0000000..831425c --- /dev/null +++ b/src/assets/images/404/404.svg @@ -0,0 +1,132 @@ + + + 切片 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/404/background-image.png b/src/assets/images/404/background-image.png similarity index 100% rename from public/images/404/background-image.png rename to src/assets/images/404/background-image.png diff --git a/public/images/404/logo.svg b/src/assets/images/404/logo.svg similarity index 100% rename from public/images/404/logo.svg rename to src/assets/images/404/logo.svg diff --git a/src/assets/images/cluster/active.svg b/src/assets/images/cluster/active.svg new file mode 100644 index 0000000..0ae7918 --- /dev/null +++ b/src/assets/images/cluster/active.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/arrow-circle-right.svg b/src/assets/images/cluster/arrow-circle-right.svg new file mode 100644 index 0000000..358cac5 --- /dev/null +++ b/src/assets/images/cluster/arrow-circle-right.svg @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/card-features.svg b/src/assets/images/cluster/card-features.svg similarity index 100% rename from public/icons/cluster/card-features.svg rename to src/assets/images/cluster/card-features.svg diff --git a/public/icons/cluster/cidrs.svg b/src/assets/images/cluster/cidrs.svg similarity index 96% rename from public/icons/cluster/cidrs.svg rename to src/assets/images/cluster/cidrs.svg index 9e65dcf..d2c4e21 100644 --- a/public/icons/cluster/cidrs.svg +++ b/src/assets/images/cluster/cidrs.svg @@ -1,4 +1,4 @@ - \ No newline at end of file diff --git a/public/icons/cluster/selected-cluster.svg b/src/assets/images/cluster/cluster.svg similarity index 88% rename from public/icons/cluster/selected-cluster.svg rename to src/assets/images/cluster/cluster.svg index dc96533..f2d3ee4 100644 --- a/public/icons/cluster/selected-cluster.svg +++ b/src/assets/images/cluster/cluster.svg @@ -1,5 +1,5 @@ - - + \ No newline at end of file diff --git a/src/assets/images/cluster/default.svg b/src/assets/images/cluster/default.svg new file mode 100644 index 0000000..f15be56 --- /dev/null +++ b/src/assets/images/cluster/default.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/delete-inactive-error.svg b/src/assets/images/cluster/delete-inactive-error.svg similarity index 100% rename from public/icons/cluster/delete-inactive-error.svg rename to src/assets/images/cluster/delete-inactive-error.svg diff --git a/public/fonts/cluster/delete-inactive.svg b/src/assets/images/cluster/delete-inactive.svg similarity index 100% rename from public/fonts/cluster/delete-inactive.svg rename to src/assets/images/cluster/delete-inactive.svg diff --git a/public/icons/cluster/delete-warning.svg b/src/assets/images/cluster/delete-warning.svg similarity index 100% rename from public/icons/cluster/delete-warning.svg rename to src/assets/images/cluster/delete-warning.svg diff --git a/src/assets/images/cluster/delete.svg b/src/assets/images/cluster/delete.svg new file mode 100644 index 0000000..547fc77 --- /dev/null +++ b/src/assets/images/cluster/delete.svg @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/features-schedule.svg b/src/assets/images/cluster/features-schedule.svg similarity index 100% rename from public/icons/cluster/features-schedule.svg rename to src/assets/images/cluster/features-schedule.svg diff --git a/src/assets/images/cluster/features.svg b/src/assets/images/cluster/features.svg new file mode 100644 index 0000000..cfa12b6 --- /dev/null +++ b/src/assets/images/cluster/features.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/hostnames.svg b/src/assets/images/cluster/hostnames.svg similarity index 97% rename from public/icons/cluster/hostnames.svg rename to src/assets/images/cluster/hostnames.svg index a0ad10b..656d7aa 100644 --- a/public/icons/cluster/hostnames.svg +++ b/src/assets/images/cluster/hostnames.svg @@ -2,5 +2,5 @@ p-id="18685" width="200" height="200"> + p-id="18686" fill="currentColor"> \ No newline at end of file diff --git a/public/icons/user/id.svg b/src/assets/images/cluster/id.svg similarity index 100% rename from public/icons/user/id.svg rename to src/assets/images/cluster/id.svg diff --git a/public/icons/cluster/idc.svg b/src/assets/images/cluster/idc.svg similarity index 87% rename from public/icons/cluster/idc.svg rename to src/assets/images/cluster/idc.svg index ddbd8e9..6484717 100644 --- a/public/icons/cluster/idc.svg +++ b/src/assets/images/cluster/idc.svg @@ -1,7 +1,7 @@ - + + fill="currentColor" p-id="10979"> \ No newline at end of file diff --git a/public/fonts/cluster/inactive-total.svg b/src/assets/images/cluster/inactive-total.svg similarity index 100% rename from public/fonts/cluster/inactive-total.svg rename to src/assets/images/cluster/inactive-total.svg diff --git a/public/icons/cluster/information-cluster.svg b/src/assets/images/cluster/information-cluster.svg similarity index 88% rename from public/icons/cluster/information-cluster.svg rename to src/assets/images/cluster/information-cluster.svg index 61c5b2e..e09347d 100644 --- a/public/icons/cluster/information-cluster.svg +++ b/src/assets/images/cluster/information-cluster.svg @@ -1,36 +1,36 @@ - + + p-id="20607" fill="currentColor"> + p-id="20608" fill="currentColor"> + p-id="20609" fill="currentColor"> + p-id="20610" fill="currentColor"> + p-id="20611" fill="currentColor"> + p-id="20612" fill="currentColor"> + p-id="20613" fill="currentColor"> + p-id="20614" fill="currentColor"> + p-id="20615" fill="currentColor"> + p-id="20616" fill="currentColor"> + p-id="20617" fill="currentColor"> \ No newline at end of file diff --git a/public/icons/cluster/ip.svg b/src/assets/images/cluster/ip.svg similarity index 100% rename from public/icons/cluster/ip.svg rename to src/assets/images/cluster/ip.svg diff --git a/public/icons/cluster/location.svg b/src/assets/images/cluster/location.svg similarity index 81% rename from public/icons/cluster/location.svg rename to src/assets/images/cluster/location.svg index 33e499f..bea4f39 100644 --- a/public/icons/cluster/location.svg +++ b/src/assets/images/cluster/location.svg @@ -1,5 +1,5 @@ - - + \ No newline at end of file diff --git a/public/icons/cluster/name.svg b/src/assets/images/cluster/name.svg similarity index 100% rename from public/icons/cluster/name.svg rename to src/assets/images/cluster/name.svg diff --git a/public/icons/cluster/no-cluster.svg b/src/assets/images/cluster/no-cluster.svg similarity index 100% rename from public/icons/cluster/no-cluster.svg rename to src/assets/images/cluster/no-cluster.svg diff --git a/public/icons/cluster/peer/active.svg b/src/assets/images/cluster/peer/active.svg similarity index 96% rename from public/icons/cluster/peer/active.svg rename to src/assets/images/cluster/peer/active.svg index 2a7cbd7..fb5acd9 100644 --- a/public/icons/cluster/peer/active.svg +++ b/src/assets/images/cluster/peer/active.svg @@ -2,5 +2,5 @@ p-id="53964" width="64" height="64"> + fill="currentColor" p-id="53965"> \ No newline at end of file diff --git a/src/assets/images/cluster/peer/export-file.svg b/src/assets/images/cluster/peer/export-file.svg new file mode 100644 index 0000000..309c45c --- /dev/null +++ b/src/assets/images/cluster/peer/export-file.svg @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/peer/export.svg b/src/assets/images/cluster/peer/export.svg similarity index 59% rename from public/icons/cluster/peer/export.svg rename to src/assets/images/cluster/peer/export.svg index ac414cf..24559a6 100644 --- a/public/icons/cluster/peer/export.svg +++ b/src/assets/images/cluster/peer/export.svg @@ -1,13 +1,3 @@ - + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/peer/git-versions.svg b/src/assets/images/cluster/peer/git-versions.svg similarity index 100% rename from public/icons/cluster/peer/git-versions.svg rename to src/assets/images/cluster/peer/git-versions.svg diff --git a/src/assets/images/cluster/peer/refresh-dialog.svg b/src/assets/images/cluster/peer/refresh-dialog.svg new file mode 100644 index 0000000..ff9e12b --- /dev/null +++ b/src/assets/images/cluster/peer/refresh-dialog.svg @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/public/icons/cluster/peer/refresh-loading.svg b/src/assets/images/cluster/peer/refresh-loading.svg similarity index 96% rename from public/icons/cluster/peer/refresh-loading.svg rename to src/assets/images/cluster/peer/refresh-loading.svg index eb6b420..42d85fe 100644 --- a/public/icons/cluster/peer/refresh-loading.svg +++ b/src/assets/images/cluster/peer/refresh-loading.svg @@ -4,5 +4,5 @@ repeatCount="indefinite" /> + p-id="26636" fill="currentColor"> \ No newline at end of file diff --git a/public/icons/cluster/peer/refresh.svg b/src/assets/images/cluster/peer/refresh.svg similarity index 96% rename from public/icons/cluster/peer/refresh.svg rename to src/assets/images/cluster/peer/refresh.svg index 041b35a..27c8486 100644 --- a/public/icons/cluster/peer/refresh.svg +++ b/src/assets/images/cluster/peer/refresh.svg @@ -2,5 +2,5 @@ p-id="26635" width="200" height="200"> + p-id="26636" fill="currentColor"> \ No newline at end of file diff --git a/public/icons/cluster/peer/statistics.svg b/src/assets/images/cluster/peer/statistics.svg similarity index 69% rename from public/icons/cluster/peer/statistics.svg rename to src/assets/images/cluster/peer/statistics.svg index cba51a0..347d005 100644 --- a/public/icons/cluster/peer/statistics.svg +++ b/src/assets/images/cluster/peer/statistics.svg @@ -2,18 +2,18 @@ p-id="57208" width="64" height="64"> + fill="currentColor" p-id="57209" data-spm-anchor-id="a313x.search_index.0.i105.5caf3a81aFGzSu" class="selected"> + fill="#dcdcdc" p-id="57210" data-spm-anchor-id="a313x.search_index.0.i100.5caf3a81aFGzSu" class=""> + fill="currentColor" p-id="57211" data-spm-anchor-id="a313x.search_index.0.i104.5caf3a81aFGzSu" class="selected"> - + \ No newline at end of file diff --git a/public/icons/cluster/peer/tab-peer.svg b/src/assets/images/cluster/peer/tab-peer.svg similarity index 98% rename from public/icons/cluster/peer/tab-peer.svg rename to src/assets/images/cluster/peer/tab-peer.svg index 83e808e..6fb9d05 100644 --- a/public/icons/cluster/peer/tab-peer.svg +++ b/src/assets/images/cluster/peer/tab-peer.svg @@ -1,7 +1,7 @@ - + diff --git a/src/assets/images/cluster/peer/total.svg b/src/assets/images/cluster/peer/total.svg new file mode 100644 index 0000000..5fbdfbe --- /dev/null +++ b/src/assets/images/cluster/peer/total.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/preheat.svg b/src/assets/images/cluster/preheat.svg new file mode 100644 index 0000000..1086d07 --- /dev/null +++ b/src/assets/images/cluster/preheat.svg @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/round.svg b/src/assets/images/cluster/round.svg new file mode 100644 index 0000000..d291bdf --- /dev/null +++ b/src/assets/images/cluster/round.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler.svg b/src/assets/images/cluster/scheduler.svg new file mode 100644 index 0000000..4e536fa --- /dev/null +++ b/src/assets/images/cluster/scheduler.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/active.svg b/src/assets/images/cluster/scheduler/active.svg new file mode 100644 index 0000000..5c56099 --- /dev/null +++ b/src/assets/images/cluster/scheduler/active.svg @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/cluster/scheduler/card.svg b/src/assets/images/cluster/scheduler/card.svg similarity index 100% rename from public/icons/cluster/scheduler/card.svg rename to src/assets/images/cluster/scheduler/card.svg diff --git a/src/assets/images/cluster/scheduler/cluster-id.svg b/src/assets/images/cluster/scheduler/cluster-id.svg new file mode 100644 index 0000000..655ca79 --- /dev/null +++ b/src/assets/images/cluster/scheduler/cluster-id.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/created-at.svg b/src/assets/images/cluster/scheduler/created-at.svg new file mode 100644 index 0000000..93ed18f --- /dev/null +++ b/src/assets/images/cluster/scheduler/created-at.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/features.svg b/src/assets/images/cluster/scheduler/features.svg new file mode 100644 index 0000000..2070445 --- /dev/null +++ b/src/assets/images/cluster/scheduler/features.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/hostname.svg b/src/assets/images/cluster/scheduler/hostname.svg new file mode 100644 index 0000000..0178589 --- /dev/null +++ b/src/assets/images/cluster/scheduler/hostname.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/cluster/scheduler/ic-content.svg b/src/assets/images/cluster/scheduler/ic-content.svg similarity index 100% rename from public/icons/cluster/scheduler/ic-content.svg rename to src/assets/images/cluster/scheduler/ic-content.svg diff --git a/public/icons/cluster/scheduler/inactive.svg b/src/assets/images/cluster/scheduler/inactive.svg similarity index 91% rename from public/icons/cluster/scheduler/inactive.svg rename to src/assets/images/cluster/scheduler/inactive.svg index da1605b..25956ae 100644 --- a/public/icons/cluster/scheduler/inactive.svg +++ b/src/assets/images/cluster/scheduler/inactive.svg @@ -2,7 +2,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink"> - + diff --git a/src/assets/images/cluster/scheduler/number.svg b/src/assets/images/cluster/scheduler/number.svg new file mode 100644 index 0000000..541d97c --- /dev/null +++ b/src/assets/images/cluster/scheduler/number.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/port.svg b/src/assets/images/cluster/scheduler/port.svg new file mode 100644 index 0000000..25ca984 --- /dev/null +++ b/src/assets/images/cluster/scheduler/port.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/scheduler-id.svg b/src/assets/images/cluster/scheduler/scheduler-id.svg new file mode 100644 index 0000000..bdd8386 --- /dev/null +++ b/src/assets/images/cluster/scheduler/scheduler-id.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/scheduler-ip.svg b/src/assets/images/cluster/scheduler/scheduler-ip.svg new file mode 100644 index 0000000..4f9673c --- /dev/null +++ b/src/assets/images/cluster/scheduler/scheduler-ip.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/public/icons/cluster/scheduler/status.svg b/src/assets/images/cluster/scheduler/status.svg similarity index 87% rename from public/icons/cluster/scheduler/status.svg rename to src/assets/images/cluster/scheduler/status.svg index 63d9f6a..2035be5 100644 --- a/public/icons/cluster/scheduler/status.svg +++ b/src/assets/images/cluster/scheduler/status.svg @@ -2,8 +2,8 @@ width="64" height="64"> + fill="currentColor" p-id="7190"> + fill="currentColor" p-id="7191"> \ No newline at end of file diff --git a/src/assets/images/cluster/scheduler/tab-scheduler.svg b/src/assets/images/cluster/scheduler/tab-scheduler.svg new file mode 100644 index 0000000..c5b9719 --- /dev/null +++ b/src/assets/images/cluster/scheduler/tab-scheduler.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/cluster/scheduler/table.svg b/src/assets/images/cluster/scheduler/table.svg similarity index 100% rename from public/icons/cluster/scheduler/table.svg rename to src/assets/images/cluster/scheduler/table.svg diff --git a/src/assets/images/cluster/scheduler/updated-at.svg b/src/assets/images/cluster/scheduler/updated-at.svg new file mode 100644 index 0000000..d0184b1 --- /dev/null +++ b/src/assets/images/cluster/scheduler/updated-at.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer.svg b/src/assets/images/cluster/seed-peer.svg new file mode 100644 index 0000000..fccfb3f --- /dev/null +++ b/src/assets/images/cluster/seed-peer.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer/download-port.svg b/src/assets/images/cluster/seed-peer/download-port.svg new file mode 100644 index 0000000..908826f --- /dev/null +++ b/src/assets/images/cluster/seed-peer/download-port.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer/object-storage-port.svg b/src/assets/images/cluster/seed-peer/object-storage-port.svg new file mode 100644 index 0000000..86e3b6d --- /dev/null +++ b/src/assets/images/cluster/seed-peer/object-storage-port.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer/port.svg b/src/assets/images/cluster/seed-peer/port.svg new file mode 100644 index 0000000..d15977d --- /dev/null +++ b/src/assets/images/cluster/seed-peer/port.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer/seed-peer-download-port.svg b/src/assets/images/cluster/seed-peer/seed-peer-download-port.svg new file mode 100644 index 0000000..40326cb --- /dev/null +++ b/src/assets/images/cluster/seed-peer/seed-peer-download-port.svg @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/cluster/seed-peer/seed-peer-type.svg b/src/assets/images/cluster/seed-peer/seed-peer-type.svg new file mode 100644 index 0000000..edd47b4 --- /dev/null +++ b/src/assets/images/cluster/seed-peer/seed-peer-type.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/cluster/seed-peer/tab-seed-peer.svg b/src/assets/images/cluster/seed-peer/tab-seed-peer.svg similarity index 98% rename from public/icons/cluster/seed-peer/tab-seed-peer.svg rename to src/assets/images/cluster/seed-peer/tab-seed-peer.svg index aacb812..1604dae 100644 --- a/public/icons/cluster/seed-peer/tab-seed-peer.svg +++ b/src/assets/images/cluster/seed-peer/tab-seed-peer.svg @@ -1,7 +1,7 @@ - + diff --git a/public/icons/cluster/seed-peer/type.svg b/src/assets/images/cluster/seed-peer/type.svg similarity index 100% rename from public/icons/cluster/seed-peer/type.svg rename to src/assets/images/cluster/seed-peer/type.svg diff --git a/public/fonts/cluster/statistics.svg b/src/assets/images/cluster/statistics.svg similarity index 100% rename from public/fonts/cluster/statistics.svg rename to src/assets/images/cluster/statistics.svg diff --git a/public/icons/cluster/status.svg b/src/assets/images/cluster/status.svg similarity index 96% rename from public/icons/cluster/status.svg rename to src/assets/images/cluster/status.svg index e603e64..f45845e 100644 --- a/public/icons/cluster/status.svg +++ b/src/assets/images/cluster/status.svg @@ -2,5 +2,5 @@ p-id="44653" width="200" height="200"> + fill="currentColor" p-id="44654"> \ No newline at end of file diff --git a/src/assets/images/cluster/tab-cluster.svg b/src/assets/images/cluster/tab-cluster.svg new file mode 100644 index 0000000..09d083e --- /dev/null +++ b/src/assets/images/cluster/tab-cluster.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/cluster/total.svg b/src/assets/images/cluster/total.svg similarity index 100% rename from public/icons/cluster/total.svg rename to src/assets/images/cluster/total.svg diff --git a/public/fonts/cluster/unfoldMore.svg b/src/assets/images/cluster/unfoldMore.svg similarity index 100% rename from public/fonts/cluster/unfoldMore.svg rename to src/assets/images/cluster/unfoldMore.svg diff --git a/src/assets/images/job/preheat/created-at.svg b/src/assets/images/job/preheat/created-at.svg new file mode 100644 index 0000000..93ed18f --- /dev/null +++ b/src/assets/images/job/preheat/created-at.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/job/preheat/description.svg b/src/assets/images/job/preheat/description.svg similarity index 88% rename from public/icons/job/preheat/description.svg rename to src/assets/images/job/preheat/description.svg index c1adfe6..4d4c163 100644 --- a/public/icons/job/preheat/description.svg +++ b/src/assets/images/job/preheat/description.svg @@ -2,8 +2,8 @@ p-id="52110" width="64" height="64"> + p-id="52111" fill="currentColor"> + p-id="52112" fill="currentColor"> \ No newline at end of file diff --git a/src/assets/images/job/preheat/detail.svg b/src/assets/images/job/preheat/detail.svg new file mode 100644 index 0000000..1b644f8 --- /dev/null +++ b/src/assets/images/job/preheat/detail.svg @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/public/icons/job/preheat/error-log.svg b/src/assets/images/job/preheat/error-log.svg similarity index 100% rename from public/icons/job/preheat/error-log.svg rename to src/assets/images/job/preheat/error-log.svg diff --git a/src/assets/images/job/preheat/failure.svg b/src/assets/images/job/preheat/failure.svg new file mode 100644 index 0000000..3f7022a --- /dev/null +++ b/src/assets/images/job/preheat/failure.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/job/preheat/headers.svg b/src/assets/images/job/preheat/headers.svg similarity index 93% rename from public/icons/job/preheat/headers.svg rename to src/assets/images/job/preheat/headers.svg index 84a3010..16a73e4 100644 --- a/public/icons/job/preheat/headers.svg +++ b/src/assets/images/job/preheat/headers.svg @@ -2,11 +2,11 @@ p-id="34878" width="64" height="64"> + fill="currentColor" p-id="34879"> + fill="currentColor" p-id="34880"> + fill="currentColor" p-id="34881"> \ No newline at end of file diff --git a/src/assets/images/job/preheat/id.svg b/src/assets/images/job/preheat/id.svg new file mode 100644 index 0000000..bdd8386 --- /dev/null +++ b/src/assets/images/job/preheat/id.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/job/preheat/pending.svg b/src/assets/images/job/preheat/pending.svg new file mode 100644 index 0000000..a213867 --- /dev/null +++ b/src/assets/images/job/preheat/pending.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/public/icons/job/preheat/scope.svg b/src/assets/images/job/preheat/scope.svg similarity index 93% rename from public/icons/job/preheat/scope.svg rename to src/assets/images/job/preheat/scope.svg index 1faa845..cc72b44 100644 --- a/public/icons/job/preheat/scope.svg +++ b/src/assets/images/job/preheat/scope.svg @@ -2,8 +2,8 @@ p-id="11900" width="200" height="200"> + fill="currentColor" p-id="11901"> + fill="currentColor" p-id="11902"> \ No newline at end of file diff --git a/public/icons/job/preheat/status-failure.svg b/src/assets/images/job/preheat/status-failure.svg similarity index 100% rename from public/icons/job/preheat/status-failure.svg rename to src/assets/images/job/preheat/status-failure.svg diff --git a/public/icons/job/preheat/status-pending.svg b/src/assets/images/job/preheat/status-pending.svg similarity index 100% rename from public/icons/job/preheat/status-pending.svg rename to src/assets/images/job/preheat/status-pending.svg diff --git a/public/icons/job/preheat/status-success.svg b/src/assets/images/job/preheat/status-success.svg similarity index 100% rename from public/icons/job/preheat/status-success.svg rename to src/assets/images/job/preheat/status-success.svg diff --git a/public/icons/job/preheat/status.svg b/src/assets/images/job/preheat/status.svg similarity index 87% rename from public/icons/job/preheat/status.svg rename to src/assets/images/job/preheat/status.svg index 63d9f6a..2035be5 100644 --- a/public/icons/job/preheat/status.svg +++ b/src/assets/images/job/preheat/status.svg @@ -2,8 +2,8 @@ width="64" height="64"> + fill="currentColor" p-id="7190"> + fill="currentColor" p-id="7191"> \ No newline at end of file diff --git a/public/icons/job/preheat/success.svg b/src/assets/images/job/preheat/success.svg similarity index 100% rename from public/icons/job/preheat/success.svg rename to src/assets/images/job/preheat/success.svg diff --git a/public/icons/job/preheat/tag-scope.svg b/src/assets/images/job/preheat/tag-scope.svg similarity index 100% rename from public/icons/job/preheat/tag-scope.svg rename to src/assets/images/job/preheat/tag-scope.svg diff --git a/public/icons/job/preheat/tag.svg b/src/assets/images/job/preheat/tag.svg similarity index 90% rename from public/icons/job/preheat/tag.svg rename to src/assets/images/job/preheat/tag.svg index 3d13fdf..c61fa56 100644 --- a/public/icons/job/preheat/tag.svg +++ b/src/assets/images/job/preheat/tag.svg @@ -2,8 +2,8 @@ p-id="37181" width="64" height="64"> + p-id="37182" fill="currentColor"> + p-id="37183" fill="currentColor"> \ No newline at end of file diff --git a/public/icons/job/preheat/url.svg b/src/assets/images/job/preheat/url.svg similarity index 91% rename from public/icons/job/preheat/url.svg rename to src/assets/images/job/preheat/url.svg index 3e03b8a..d181139 100644 --- a/public/icons/job/preheat/url.svg +++ b/src/assets/images/job/preheat/url.svg @@ -2,8 +2,8 @@ p-id="41168" width="64" height="64"> + p-id="41169" fill="currentColor"> + p-id="41170" fill="currentColor"> \ No newline at end of file diff --git a/src/assets/images/job/task/clear-cache.svg b/src/assets/images/job/task/clear-cache.svg new file mode 100644 index 0000000..c8fe2da --- /dev/null +++ b/src/assets/images/job/task/clear-cache.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/job/task/created.svg b/src/assets/images/job/task/created.svg similarity index 100% rename from public/icons/job/task/created.svg rename to src/assets/images/job/task/created.svg diff --git a/src/assets/images/job/task/dark-no-task.svg b/src/assets/images/job/task/dark-no-task.svg new file mode 100644 index 0000000..c780320 --- /dev/null +++ b/src/assets/images/job/task/dark-no-task.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No Caching + + + Please enter the task id in your search + + + box to search for cache. + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/job/task/error-log.svg b/src/assets/images/job/task/error-log.svg new file mode 100644 index 0000000..8d0a83f --- /dev/null +++ b/src/assets/images/job/task/error-log.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/job/task/executions.svg b/src/assets/images/job/task/executions.svg new file mode 100644 index 0000000..93421a9 --- /dev/null +++ b/src/assets/images/job/task/executions.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/job/task/failure.svg b/src/assets/images/job/task/failure.svg new file mode 100644 index 0000000..db8e73d --- /dev/null +++ b/src/assets/images/job/task/failure.svg @@ -0,0 +1,5 @@ + + + +ƒ \ No newline at end of file diff --git a/src/assets/images/job/task/hostname.svg b/src/assets/images/job/task/hostname.svg new file mode 100644 index 0000000..330f90e --- /dev/null +++ b/src/assets/images/job/task/hostname.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/public/icons/job/task/ip.svg b/src/assets/images/job/task/ip.svg similarity index 100% rename from public/icons/job/task/ip.svg rename to src/assets/images/job/task/ip.svg diff --git a/public/icons/job/task/no-search.svg b/src/assets/images/job/task/no-search.svg similarity index 100% rename from public/icons/job/task/no-search.svg rename to src/assets/images/job/task/no-search.svg diff --git a/src/assets/images/job/task/no-task.svg b/src/assets/images/job/task/no-task.svg new file mode 100644 index 0000000..d755a41 --- /dev/null +++ b/src/assets/images/job/task/no-task.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No Caching + + + Please enter the task id in your search + + + box to search for cache. + + + + + + + + \ No newline at end of file diff --git a/public/icons/job/task/scheduler-cluster.svg b/src/assets/images/job/task/scheduler-cluster.svg similarity index 100% rename from public/icons/job/task/scheduler-cluster.svg rename to src/assets/images/job/task/scheduler-cluster.svg diff --git a/public/icons/job/task/search.svg b/src/assets/images/job/task/search.svg similarity index 100% rename from public/icons/job/task/search.svg rename to src/assets/images/job/task/search.svg diff --git a/public/icons/job/task/success.svg b/src/assets/images/job/task/success.svg similarity index 100% rename from public/icons/job/task/success.svg rename to src/assets/images/job/task/success.svg diff --git a/public/icons/job/preheat/job.svg b/src/assets/images/job/task/task-id.svg similarity index 72% rename from public/icons/job/preheat/job.svg rename to src/assets/images/job/task/task-id.svg index ed3405b..6d46fde 100644 --- a/public/icons/job/preheat/job.svg +++ b/src/assets/images/job/task/task-id.svg @@ -1,9 +1,9 @@ - + + fill="currentColor" p-id="9415"> + fill="currentColor" p-id="9416"> \ No newline at end of file diff --git a/src/assets/images/job/task/type.svg b/src/assets/images/job/task/type.svg new file mode 100644 index 0000000..4a9fcd1 --- /dev/null +++ b/src/assets/images/job/task/type.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/login/dark-dragonfly.svg b/src/assets/images/login/dark-dragonfly.svg new file mode 100644 index 0000000..afc10da --- /dev/null +++ b/src/assets/images/login/dark-dragonfly.svg @@ -0,0 +1,160 @@ + + + + + + + + + Provides an efficient, stable, and secure file distribution and image + + + acceleration system based on P2P technology, and is a standard solution and + + + + best practice in the field of image acceleration in the cloud native + + + architecture. + + + + + + + + Dragonfly + + + Welcome To Dragonfly Console ! + + + + + + + + + + + Partners + + + + + + + + + + + + + + + + + + + + Provide + + + CNCF + + + Incubating + + + From 5 companies + + + Efficient,stable, + + + secure file + + + distribution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/login/dark-milestones.svg b/src/assets/images/login/dark-milestones.svg new file mode 100644 index 0000000..67e5405 --- /dev/null +++ b/src/assets/images/login/dark-milestones.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + In November 2017, + + + Dragonfly 1.x project was + + + open source. + + + In October 2018, became + + + the third project in China to + + + enter the CNCF Sandbox. + + + Officially settled in + + + CNCF Incubating in + + + April 2020. + + + In April 2021, it was + + + updated to Dragonfly 2. + + + Milestones + + + More Application Scenarios + + + : + + + Dragonfly 1.x + + + CNCF SandBox + + + Dragonfly 2 + + + CNCF Incubating + + + + + + + So far, dragonflies have experienced the following stages of development. + + + Dragonfly supports different types of storage sources, such as HDFS, S3, GCS, + Azure Blob, + + + OSS, etc. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/login/dragonfly.svg b/src/assets/images/login/dragonfly.svg similarity index 100% rename from public/images/login/dragonfly.svg rename to src/assets/images/login/dragonfly.svg diff --git a/src/assets/images/login/login.svg b/src/assets/images/login/login.svg new file mode 100644 index 0000000..415523c --- /dev/null +++ b/src/assets/images/login/login.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/public/images/login/milestones.svg b/src/assets/images/login/milestones.svg similarity index 100% rename from public/images/login/milestones.svg rename to src/assets/images/login/milestones.svg diff --git a/src/assets/images/login/page-loading.svg b/src/assets/images/login/page-loading.svg new file mode 100644 index 0000000..356ff54 --- /dev/null +++ b/src/assets/images/login/page-loading.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/closure.svg b/src/assets/images/menu/closure.svg new file mode 100644 index 0000000..599d4e8 --- /dev/null +++ b/src/assets/images/menu/closure.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/cluster.svg b/src/assets/images/menu/cluster.svg new file mode 100644 index 0000000..23072bd --- /dev/null +++ b/src/assets/images/menu/cluster.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/public/images/menu/cyan-blur.png b/src/assets/images/menu/cyan-blur.png similarity index 100% rename from public/images/menu/cyan-blur.png rename to src/assets/images/menu/cyan-blur.png diff --git a/src/assets/images/menu/dark.svg b/src/assets/images/menu/dark.svg new file mode 100644 index 0000000..07a88d8 --- /dev/null +++ b/src/assets/images/menu/dark.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/developer.svg b/src/assets/images/menu/developer.svg new file mode 100644 index 0000000..30e6090 --- /dev/null +++ b/src/assets/images/menu/developer.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/expand.svg b/src/assets/images/menu/expand.svg new file mode 100644 index 0000000..8239599 --- /dev/null +++ b/src/assets/images/menu/expand.svg @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/job.svg b/src/assets/images/menu/job.svg new file mode 100644 index 0000000..26b9b9d --- /dev/null +++ b/src/assets/images/menu/job.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/layout.svg b/src/assets/images/menu/layout.svg new file mode 100644 index 0000000..643fca8 --- /dev/null +++ b/src/assets/images/menu/layout.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/images/menu/light.svg b/src/assets/images/menu/light.svg new file mode 100644 index 0000000..efdcc6b --- /dev/null +++ b/src/assets/images/menu/light.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/menu/logo.svg b/src/assets/images/menu/logo.svg new file mode 100644 index 0000000..5ade25a --- /dev/null +++ b/src/assets/images/menu/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/public/images/menu/red-blur.png b/src/assets/images/menu/red-blur.png similarity index 100% rename from public/images/menu/red-blur.png rename to src/assets/images/menu/red-blur.png diff --git a/src/assets/images/menu/selected-cluster.svg b/src/assets/images/menu/selected-cluster.svg new file mode 100644 index 0000000..23072bd --- /dev/null +++ b/src/assets/images/menu/selected-cluster.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/selected-developer.svg b/src/assets/images/menu/selected-developer.svg new file mode 100644 index 0000000..de3f1e9 --- /dev/null +++ b/src/assets/images/menu/selected-developer.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/selected-job.svg b/src/assets/images/menu/selected-job.svg new file mode 100644 index 0000000..26b9b9d --- /dev/null +++ b/src/assets/images/menu/selected-job.svg @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/selected-user.svg b/src/assets/images/menu/selected-user.svg new file mode 100644 index 0000000..7d457c4 --- /dev/null +++ b/src/assets/images/menu/selected-user.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/sidebar-closure.svg b/src/assets/images/menu/sidebar-closure.svg new file mode 100644 index 0000000..66d05c2 --- /dev/null +++ b/src/assets/images/menu/sidebar-closure.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/sidebar-expand.svg b/src/assets/images/menu/sidebar-expand.svg new file mode 100644 index 0000000..d928b5a --- /dev/null +++ b/src/assets/images/menu/sidebar-expand.svg @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/menu/user.svg b/src/assets/images/menu/user.svg new file mode 100644 index 0000000..a6976d1 --- /dev/null +++ b/src/assets/images/menu/user.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/profile/created-at.svg b/src/assets/images/profile/created-at.svg new file mode 100644 index 0000000..93ed18f --- /dev/null +++ b/src/assets/images/profile/created-at.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/profile/email.svg b/src/assets/images/profile/email.svg new file mode 100644 index 0000000..321f7f9 --- /dev/null +++ b/src/assets/images/profile/email.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/profile/id.svg b/src/assets/images/profile/id.svg new file mode 100644 index 0000000..bdd8386 --- /dev/null +++ b/src/assets/images/profile/id.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/profile/location.svg b/src/assets/images/profile/location.svg new file mode 100644 index 0000000..381126c --- /dev/null +++ b/src/assets/images/profile/location.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/assets/images/profile/myj.jpg b/src/assets/images/profile/myj.jpg new file mode 100644 index 0000000..5d8cc89 Binary files /dev/null and b/src/assets/images/profile/myj.jpg differ diff --git a/src/assets/images/profile/name.svg b/src/assets/images/profile/name.svg new file mode 100644 index 0000000..ca0aa8c --- /dev/null +++ b/src/assets/images/profile/name.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/profile/phone.svg b/src/assets/images/profile/phone.svg new file mode 100644 index 0000000..af0cfa2 --- /dev/null +++ b/src/assets/images/profile/phone.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/tokens/copy.svg b/src/assets/images/tokens/copy.svg similarity index 100% rename from public/icons/tokens/copy.svg rename to src/assets/images/tokens/copy.svg diff --git a/src/assets/images/tokens/done.svg b/src/assets/images/tokens/done.svg new file mode 100644 index 0000000..1832098 --- /dev/null +++ b/src/assets/images/tokens/done.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/images/tokens/key.svg b/src/assets/images/tokens/key.svg new file mode 100644 index 0000000..653da00 --- /dev/null +++ b/src/assets/images/tokens/key.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/user/bio.svg b/src/assets/images/user/bio.svg new file mode 100644 index 0000000..0d55286 --- /dev/null +++ b/src/assets/images/user/bio.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/public/icons/user/change-password.svg b/src/assets/images/user/change-password.svg similarity index 100% rename from public/icons/user/change-password.svg rename to src/assets/images/user/change-password.svg diff --git a/src/assets/images/user/created-at.svg b/src/assets/images/user/created-at.svg new file mode 100644 index 0000000..0abe79a --- /dev/null +++ b/src/assets/images/user/created-at.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/user/detail-role.svg b/src/assets/images/user/detail-role.svg new file mode 100644 index 0000000..1984011 --- /dev/null +++ b/src/assets/images/user/detail-role.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/icons/user/detail.svg b/src/assets/images/user/detail.svg similarity index 100% rename from public/icons/user/detail.svg rename to src/assets/images/user/detail.svg diff --git a/src/assets/images/user/edit.svg b/src/assets/images/user/edit.svg new file mode 100644 index 0000000..3041d96 --- /dev/null +++ b/src/assets/images/user/edit.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/public/icons/user/email.svg b/src/assets/images/user/email.svg similarity index 94% rename from public/icons/user/email.svg rename to src/assets/images/user/email.svg index ea2b5a2..c53181f 100644 --- a/public/icons/user/email.svg +++ b/src/assets/images/user/email.svg @@ -2,5 +2,5 @@ p-id="38521" width="200" height="200"> + p-id="38522" fill="currentColor"> \ No newline at end of file diff --git a/src/assets/images/user/guest.svg b/src/assets/images/user/guest.svg new file mode 100644 index 0000000..db021eb --- /dev/null +++ b/src/assets/images/user/guest.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/images/user/id.svg b/src/assets/images/user/id.svg new file mode 100644 index 0000000..e8777e7 --- /dev/null +++ b/src/assets/images/user/id.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/user/location.svg b/src/assets/images/user/location.svg new file mode 100644 index 0000000..5f51556 --- /dev/null +++ b/src/assets/images/user/location.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/public/icons/user/name.svg b/src/assets/images/user/name.svg similarity index 93% rename from public/icons/user/name.svg rename to src/assets/images/user/name.svg index bafbb09..6541deb 100644 --- a/public/icons/user/name.svg +++ b/src/assets/images/user/name.svg @@ -2,5 +2,5 @@ p-id="32166" width="200" height="200"> + fill="currentColor" p-id="32167"> \ No newline at end of file diff --git a/src/assets/images/user/phone.svg b/src/assets/images/user/phone.svg new file mode 100644 index 0000000..d1579d5 --- /dev/null +++ b/src/assets/images/user/phone.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/assets/images/user/role.svg b/src/assets/images/user/role.svg new file mode 100644 index 0000000..b2984ad --- /dev/null +++ b/src/assets/images/user/role.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/user/root.svg b/src/assets/images/user/root.svg new file mode 100644 index 0000000..b018c9a --- /dev/null +++ b/src/assets/images/user/root.svg @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/src/assets/images/user/updated-at.svg b/src/assets/images/user/updated-at.svg new file mode 100644 index 0000000..49edaf4 --- /dev/null +++ b/src/assets/images/user/updated-at.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/public/icons/user/user-edit.svg b/src/assets/images/user/user-edit.svg similarity index 100% rename from public/icons/user/user-edit.svg rename to src/assets/images/user/user-edit.svg diff --git a/src/components/404/index.module.css b/src/components/404/index.module.css index f353ce2..5d2af60 100644 --- a/src/components/404/index.module.css +++ b/src/components/404/index.module.css @@ -1,7 +1,7 @@ .container { height: 100vh; overflow: hidden; - background: url(../../../public/images/404/background-image.png); + background: url(../../assets/images/404/background-image.png); } .content { @@ -18,3 +18,13 @@ display: flex; align-items: center; } + +.logoIcon { + width: 1.8rem; + height: 1.8rem; + margin-right: 0.4rem; +} + +.notFound { + width: 38rem; +} diff --git a/src/components/404/index.tsx b/src/components/404/index.tsx index 1c5822a..46a0e52 100644 --- a/src/components/404/index.tsx +++ b/src/components/404/index.tsx @@ -1,6 +1,8 @@ -import { Box, Typography, Grid, CssBaseline, Button } from '@mui/material'; +import { Box, Typography, Grid, Button } from '@mui/material'; import styles from './index.module.css'; import { useNavigate } from 'react-router-dom'; +import { ReactComponent as Logo } from '../../assets/images/404/logo.svg'; +import { ReactComponent as NotFoundImg } from '../../assets/images/404/404.svg'; export default function NotFound() { const navigate = useNavigate(); @@ -8,18 +10,17 @@ export default function NotFound() { return ( - - + + Dragonfly - - - + + Something gone wrong! - + The page you were looking for doesn't exist. - - - - - - - - - - - Cluster - - - - - - - {isLoading ? ( - - ) : ( - clusterCount?.length || 0 - )} - - number of clusters - - - - - - {isLoading ? ( - - ) : ( - numberOfDefaultClusters || 0 - )} - - default - - - - - + + + + + + - - - - - - - - - - - - - Scheduler + + Cluster + + + + + + + {isLoading ? : clusterCount?.length || 0} + number of clusters - - - - - {isLoading ? : scheduler?.length || 0} - - number of schedulers - - - - - - {isLoading ? ( - - ) : ( - numberOfActiveSchedulers || 0 - )} - - active - - + + + + + {isLoading ? ( + + ) : ( + numberOfDefaultClusters || 0 + )} + + + default + - - + - - - - - - - - - - - - - Seed Peer + + + + + + + + + + + + Scheduler + + + + + + + {isLoading ? : scheduler?.length || 0} + number of schedulers - - - - - {isLoading ? : seedPeer.length || 0} - - number of seed peers - - - - - - {isLoading ? ( - - ) : ( - numberOfActiveSeedPeers || 0 - )} - - active - - + + + + + {isLoading ? ( + + ) : ( + numberOfActiveSchedulers || 0 + )} + + + active + - - + - - + + + + + + + + + + + + Seed Peer + + + + + + + {isLoading ? : seedPeer.length || 0} + + number of seed peers + + + + + + {isLoading ? ( + + ) : ( + numberOfActiveSeedPeers || 0 + )} + + + active + + + + + + + @@ -426,19 +391,19 @@ export default function Clusters() { {isLoading ? ( - + - - - + + + - + ) : Array.isArray(clusterCount) && clusterCount.length === 0 ? ( - + You have no clusters. ) : Array.isArray(allClusters) && allClusters.length === 0 ? ( - + No results for  @@ -483,15 +448,11 @@ export default function Clusters() { - - - {clusterIsLoading ? ( - - ) : ( - - {item.id} - - )} + + + + {item.id} + - {clusterIsLoading ? : item.name} + {item.name} @@ -508,11 +469,7 @@ export default function Clusters() { variant="caption" className={styles.descriptionText} > - {clusterIsLoading ? ( - - ) : ( - item.bio || '-' - )} + {item.bio || '-'} @@ -520,61 +477,44 @@ export default function Clusters() { - {clusterIsLoading ? ( - - ) : ( - - {`${ - item.is_default ? 'Default' : 'Non-Default' - }`} - - )} + + {`${item.is_default ? 'Default' : 'Non-Default'}`} + } - label={ - clusterIsLoading ? ( - - ) : ( - getDatetime(item.created_at) - ) - } + label={getDatetime(item.created_at)} variant="outlined" size="small" /> { navigate(`/clusters/${item.id}`); }} > - + @@ -585,7 +525,7 @@ export default function Clusters() { )} {totalPages > 1 ? ( - + )} - + ); } diff --git a/src/components/clusters/information.module.css b/src/components/clusters/information.module.css index 5aa556a..ec61691 100644 --- a/src/components/clusters/information.module.css +++ b/src/components/clusters/information.module.css @@ -31,6 +31,12 @@ width: 2.8rem; height: 2.8rem; margin-right: 1rem; + color: var(--palette-button-color); +} + +.copyIcon { + width: 1rem; + height: 1rem; } .updateClusterIcon { @@ -44,7 +50,7 @@ height: 6rem; } -.className { +.name { padding-bottom: 0.4rem; font-family: 'mabry-bold' !important; } @@ -79,20 +85,21 @@ .descriptionIcon { width: 0.9rem !important ; height: 0.9rem !important; + color: var(--palette-grey-300Channel); } .descriptionIcon:hover { - color: var(--description-color); + color: var(--palette-description-color); } .configLable { - color: var(--table-title-text-color); + color: var(--palette-table-title-text-color); margin-right: 0.2rem !important; font-family: 'mabry-bold' !important; } .scopesLable { - color: var(--button-color) !important; + color: var(--palette-button-color) !important; font-family: 'mabry-bold' !important; margin-right: 0.2rem !important; padding-left: 0.4rem; @@ -116,13 +123,14 @@ .scopesIcon { width: 1.6rem; height: 1.6rem; + color: var(--palette-button-color); } .totalContainer { display: flex; align-items: center; padding: 0.3rem; - background-color: var(--menu-background-color) !important; + background-color: var(--palette-grey-background-color) !important; } .totalIcon { @@ -220,7 +228,7 @@ align-items: center; justify-content: center; margin-top: 0.4rem; - padding: 0.5rem; + padding: 0.2rem 0.4rem; } .cidrsText { @@ -266,42 +274,36 @@ } .idcDialogContainer { - display: flex; - flex-wrap: wrap; - justify-content: space-between; + gap: calc(1.5rem); + display: grid; + grid-template-columns: repeat(2, 1fr); } .cidrsDialogContent { - width: 45%; background-color: #f7f7f8 !important; display: flex; align-items: center; justify-content: flex-start; padding: 0.4rem 0.8rem; - margin-bottom: 0.8rem; - margin-right: 0.5rem; - margin-left: 0.5rem; } .cidrsDialogContainer { - display: flex; - flex-wrap: wrap; + gap: calc(1.5rem); + display: grid; + grid-template-columns: repeat(2, 1fr); } .idcDialogContent { - width: 45%; background-color: #f7f7f8 !important; display: flex; align-items: center; justify-content: flex-start; padding: 0.4rem 0.8rem; - margin-bottom: 0.8rem; - margin-right: 0.5rem; - margin-left: 0.5rem; } .cidrsIcon { width: 1.6rem; height: 1.6rem; margin-right: 0.3rem; + color: var(--palette-save-color); } diff --git a/src/components/clusters/information.tsx b/src/components/clusters/information.tsx index 94c3a9b..d47bbc0 100644 --- a/src/components/clusters/information.tsx +++ b/src/components/clusters/information.tsx @@ -15,11 +15,21 @@ import Dialog from '@mui/material/Dialog'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import styles from './information.module.css'; import Card from '../card'; -import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined'; +import HelpIcon from '@mui/icons-material/Help'; import { useContext, useState } from 'react'; import { useCopyToClipboard } from 'react-use'; import { MyContext } from './show'; -import { CancelLoadingButton, DeleteLoadingButton, SavelLoadingButton } from '../loading-button'; +import { ReactComponent as InformationCluster } from '../../assets/images/cluster/information-cluster.svg'; +import { ReactComponent as Done } from '../../assets/images/tokens/done.svg'; +import { ReactComponent as Copy } from '../../assets/images/tokens/copy.svg'; +import { ReactComponent as Edit } from '../../assets/images/user/edit.svg'; +import { ReactComponent as Location } from '../../assets/images/cluster/location.svg'; +import { ReactComponent as IDC } from '../../assets/images/cluster/idc.svg'; +import { ReactComponent as Total } from '../../assets/images/cluster/total.svg'; +import { ReactComponent as CIDRs } from '../../assets/images/cluster/cidrs.svg'; +import { ReactComponent as Hostnames } from '../../assets/images/cluster/hostnames.svg'; +import { ReactComponent as Delete } from '../../assets/images/cluster/delete.svg'; +import { CancelLoadingButton, DeleteLoadingButton } from '../loading-button'; import { Chart as ChartJS, ArcElement, @@ -109,6 +119,7 @@ export default function Information() { } } }; + return ( - + Update + + + + + + + + + Total + + {isLoading ? ( + + ) : ( + + {peer.length} - } - placement="top" - > - - + )} + + + + + + number of peers + + + + + - - - + + + + + + Git Version + + {isLoading ? ( + + ) : ( + + {gitVersionCount} + + )} + + + + + + number of git versions + + + + + - - - - - + + + + + + Git Commit + + {isLoading ? ( + + ) : ( + + {gitCommitCount} + + )} + + + + + + number of git commits + + + + + + + + + + + + + - - Total + + Peer Statistics  - {isLoading ? ( - - ) : ( - - {peer.length} - - )} - - number of peers + + by Git Version - - + + + + + + - - - - - - - Git Version - - {isLoading ? ( - - ) : ( - - {gitVersionCount} - - )} - - number of git versions - - - - - - - - - - - - - Git Commit - - {isLoading ? ( - - ) : ( - - {gitCommitCount} - - )} - - number of git commits - - - - - - - - - - - - - + + + - + Peer Statistics  by Git Version - + - - + + + - - - - - - - Peer Statistics  - - - by Git Version - - - - - - - - - + + + + + + + Active + + + {isLoading ? : gitVersionActive ? `${gitVersionActive}%` : '0'} + + - - - - - - Active - - - {isLoading ? : gitVersionActive ? `${gitVersionActive}%` : '0'} - - - - - - + + + + + + + + + Git Version + + - - - - - Git Version - - + + + + + + Peer Statistics  + + + by Git Commit + + + + + - - - - + + + + + + + - + Peer Statistics  by Git Commit - + - - + + + - - - - - - - Peer Statistics  - - - by Git Commit - - - - - - - - - + + + + + + + Active + + + {isLoading ? : gitCommitActive ? `${gitCommitActive}%` : '0'} + + - - - - - - Active - - - {isLoading ? : gitCommitActive ? `${gitCommitActive}%` : '0'} - - - - - - - + + - - + + + + + + + Export + + + { + setOpenExport(false); + setExportSelectedVersion('All'); + setExportSelectedCommit('All'); + }} + sx={{ + color: (theme) => theme.palette.grey[500], + p: 0, + }} + > + + + + + + + + + Export Your Data With Fun + + + - - - Export - + + Git Version + + + + Git Commit + + - + + { setOpenExport(false); setExportSelectedVersion('All'); setExportSelectedCommit('All'); }} - sx={{ - color: (theme) => theme.palette.grey[500], - p: 0, - }} - > - - + /> + } + id="save" + text="Save" + onClick={ExportCSV} + /> - - - - - - Export Your Data With Fun + + + + + + + + Refresh + + + { + setOpenRefresh(false); + }} + sx={{ + color: (theme) => theme.palette.grey[500], + p: 0, + }} + > + + + + + + + + + + WARNING:  + + + This action CANNOT be undone. - - - - Git Version - - - - Git Commit - - - - - - { - setOpenExport(false); - setExportSelectedVersion('All'); - setExportSelectedCommit('All'); - }} - /> - } - id="save" - text="Save" - onClick={ExportCSV} - /> - - - - - - - - - Refresh - - - + The peer data will be forced to refresh. + + { setOpenRefresh(false); }} - sx={{ - color: (theme) => theme.palette.grey[500], - p: 0, - }} - > - - + /> + } + id="save" + text="Save" + onClick={handleRefresh} + /> - - - - - - - - WARNING:  - - - This action CANNOT be undone. - - - - The peer data will be forced to refresh. - - { - setOpenRefresh(false); - }} - /> - } - id="save" - text="Save" - onClick={handleRefresh} - /> - - - - - + + + ); } diff --git a/src/components/clusters/schedulers/index.module.css b/src/components/clusters/schedulers/index.module.css index 49fb364..8372f72 100644 --- a/src/components/clusters/schedulers/index.module.css +++ b/src/components/clusters/schedulers/index.module.css @@ -12,7 +12,7 @@ .schedulerInactiveListTitle { display: flex; padding: 0.8rem 1rem; - background-color: var(--table-title-color) !important; + background-color: var(--palette-table-title-color) !important; } .noInactiveScheduler { @@ -26,12 +26,12 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .logHeaderWrapper { display: flex; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .deleteInactiveWrapper { @@ -70,11 +70,16 @@ justify-content: center; } +.featuresHeaderIcon { + width: 1.6rem; + height: 1.6rem; +} + .featuresIconWrapper { width: 2.4rem; height: 2.4rem; border-radius: 50%; - background-color: #fbfbfb; + background-color: var(--palette-white-500Channel); display: flex; align-items: center; justify-content: center; @@ -85,7 +90,7 @@ width: 1.8rem; height: 1.8rem; border-radius: 50%; - background-color: #efefef; + background-color: var(--palette-white-400Channel); display: flex; align-items: center; justify-content: center; @@ -94,24 +99,28 @@ .featuresIcon { width: 1rem; height: 1rem; + color: var(--palette-save-color); } .menu { - background-image: url(../../../../public/images/menu/cyan-blur.png), url(../../../../public/images/menu/red-blur.png); - background-color: rgba(255, 255, 255, 0.9); + background-color: var(--palette-background-menu); + background-image: url(../../../assets/images/menu/cyan-blur.png), url(../../../assets/images/menu/red-blur.png); background-repeat: no-repeat, no-repeat; padding: 0.2rem; background-size: 50%, 50%; - box-shadow: rgba(145, 158, 171, 0.24) 0px 0px 2px 0px, rgba(145, 158, 171, 0.24) -20px 20px 40px -4px; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); outline: 0px; background-position: right top, left bottom; - border-radius: 10px; + border-radius: var(--menu-border-radius); overflow: inherit; } +.menuItem { + border-radius: var(--menu-border-radius) !important; +} + .menuItemIcon { - color: var(--button-color); + color: var(--palette-button-color); } .menuText { @@ -135,7 +144,7 @@ display: flex; justify-content: space-between; align-items: center; - padding-bottom: 2rem; + padding-bottom: 1.5rem; } .idIcon { @@ -148,6 +157,7 @@ width: 1rem; height: 1rem; margin-right: 0.4rem; + color: var(--palette-text-color); } .circularProgressWrapper { @@ -195,6 +205,11 @@ align-items: center; } +.schedulerInactiveTable { + font-family: 'mabry-bold'; + color: var(--palette-table-title-text-color); +} + .schedulerInactiveHeaderIP { width: 25%; white-space: nowrap; @@ -232,7 +247,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 0.5rem; + padding: 0.6rem; } .featuresEdit:nth-last-child(2n) { @@ -308,8 +323,8 @@ align-items: center; justify-content: center; flex-direction: column; - background-color: rgba(var(--no-data-color) / 0.04) !important; - border: dashed 1px rgba(var(--no-data-color) / 0.08); + background-color: rgba(var(--palette-no-data-color) / 0.04) !important; + border: dashed 1px rgba(var(--palette-no-data-color) / 0.08); } .nodataIcon { @@ -322,6 +337,13 @@ font-family: 'mabry-bold'; } +.navigationContainer { + display: grid; + gap: calc(1.5rem); + grid-template-columns: repeat(3, 1fr); + margin-bottom: 1.5rem; +} + .navigationWrapper { padding: 1.2rem; position: relative; @@ -330,33 +352,55 @@ } .navigation { - top: -3rem; + top: -2rem; width: 160px; z-index: -1; height: 160px; - right: -5rem; + right: -6rem; opacity: 0.12; border-radius: 24px; position: absolute; transform: rotate(34deg); - background: linear-gradient(to right, #22c55e 0%, rgba(34 197 94 / 0) 100%); + background: linear-gradient(to right, var(--palette-text-color) 0%, transparent 100%); +} + +.navigationCount { + display: flex; + align-items: center; +} + +.navigationCountIcon { + background-color: rgba(31, 125, 83, 0.2); + color: var(--palette-text-color); + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5rem; } .navigationIcon { - width: 2.4rem; - height: 2.4rem; - top: 1.4rem; - right: 1.8rem; + width: 2rem; + height: 2rem; + top: 2.4rem; + right: 1.2rem; position: absolute; -} - -.tableHeader { - color: var(--table-title-text-color); - font-family: 'mabry-bold' !important; + color: var(--palette-text-color); } .tableRow { - border-bottom: dashed var(--palette-divider); + border-bottom: dashed var(--palette-palette-divider); +} + +.tableHeader { + border-color: var(--palette-palette-divider) !important; +} + +.tableHeaderText { + color: var(--palette-table-title-text-color); + font-family: 'mabry-bold' !important; } .tableRow:last-child { @@ -365,7 +409,43 @@ .divider { border-width: 0px 0px thin; - border-color: var(--palette-divider); + border-color: var(--palette-palette-divider); border-style: dashed; margin: 0; } + +.deleteInactiveInstances { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; +} + +.deleteInactiveHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.deleteIcon { + width: 1.8rem; + height: 1.8rem; + margin-right: 0.4rem; +} + +.inactiveTotalIcon { + width: 0.6rem; + height: 0.6rem; +} + +.deleteWarning { + width: 1.4rem; + height: 1.4rem; + padding-right: 0.2rem; +} + +.failureIcon { + width: 1.2rem; + height: 1.2rem; + margin-right: 0.4rem; +} diff --git a/src/components/clusters/schedulers/index.tsx b/src/components/clusters/schedulers/index.tsx index b15bc2e..72e4728 100644 --- a/src/components/clusters/schedulers/index.tsx +++ b/src/components/clusters/schedulers/index.tsx @@ -1,5 +1,4 @@ import Paper from '@mui/material/Paper'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; import { Alert, Box, @@ -86,20 +85,26 @@ import RemoveRedEyeIcon from '@mui/icons-material/RemoveRedEye'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import Card from '../../card'; - -const theme = createTheme({ - palette: { - primary: { - main: '#1C293A', - }, - secondary: { - main: '#2E8F79', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import { ReactComponent as Total } from '../../../assets/images/cluster/peer/total.svg'; +import { ReactComponent as Active } from '../../../assets/images/cluster/scheduler/active.svg'; +import { ReactComponent as Inactive } from '../../../assets/images/cluster/scheduler/inactive.svg'; +import { ReactComponent as SelectCard } from '../../../assets/images/cluster/scheduler/card.svg'; +import { ReactComponent as SelectTable } from '../../../assets/images/cluster/scheduler/table.svg'; +import { ReactComponent as ID } from '../../../assets/images/cluster/id.svg'; +import { ReactComponent as Status } from '../../../assets/images/cluster/status.svg'; +import { ReactComponent as IP } from '../../../assets/images/cluster/ip.svg'; +import { ReactComponent as CardFeatures } from '../../../assets/images/cluster/card-features.svg'; +import { ReactComponent as Delete } from '../../../assets/images/cluster/delete.svg'; +import { ReactComponent as IcContent } from '../../../assets/images/cluster/scheduler/ic-content.svg'; +import { ReactComponent as InactiveTotal } from '../../../assets/images/cluster/inactive-total.svg'; +import { ReactComponent as DeleteWarning } from '../../../assets/images/cluster/delete-warning.svg'; +import { ReactComponent as DeleteInactive } from '../../../assets/images/cluster/delete-inactive.svg'; +import { ReactComponent as DeleteInactiveError } from '../../../assets/images/cluster/delete-inactive-error.svg'; +import { ReactComponent as Failure } from '../../../assets/images/job/preheat/failure.svg'; +import { ReactComponent as Features } from '../../../assets/images/cluster/features.svg'; +import { ReactComponent as Preheat } from '../../../assets/images/cluster/preheat.svg'; +import { ReactComponent as Schedule } from '../../../assets/images/cluster/scheduler.svg'; +import { ReactComponent as Count } from '../../../assets/images/cluster/scheduler/number.svg'; ChartJS.register(ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title); Chart.defaults.font.family = 'mabry-light'; @@ -123,6 +128,7 @@ function CircularProgressWithLabel(props: LinearProgressProps & { value: number ); } + const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ [`& .${toggleButtonGroupClasses.grouped}`]: { margin: theme.spacing(0.5), @@ -566,7 +572,7 @@ export default function ShowCluster() { }; return ( - + @@ -612,82 +617,89 @@ export default function ShowCluster() { - - - - - - - Total + + + + + Total + + {isLoading ? ( + + + + ) : ( + + {schedulerCount.length} + + )} + + + + + + number of schedulers + + + + + + + + + + + Active + + {isLoading ? ( + + + + ) : ( + + {numberOfActiveSchedulers} - {isLoading ? ( - - - - ) : ( - - {schedulerCount.length} - - )} - - number of schedulers + )} + + + + + + number of active schedulers{' '} - - - - - - - - - - Active + + + + + + + + + Inactive + + {isLoading ? ( + + + + ) : ( + + {numberOfInactiveSchedulers} - {isLoading ? ( - - - - ) : ( - - {numberOfActiveSchedulers} - - )} - - number of active schedulers - - - - - - - - - - - - - Inactive - - {isLoading ? ( - - - - ) : ( - - {numberOfInactiveSchedulers} - - )} - + )} + + + + + number of inactive schedulers - - - - + + + + @@ -740,7 +752,7 @@ export default function ShowCluster() { /> - + {statusList.map((item, index) => ( - handleMenuItemClick(item)}> + handleMenuItemClick(item)} + > {item.lable} ))} @@ -792,6 +813,7 @@ export default function ShowCluster() { border: `1px solid var(--palette-action-hover)`, flexWrap: 'wrap', ml: '1rem', + backgroundColor: 'var(--palette-background-paper)', })} > - + - + @@ -816,9 +838,9 @@ export default function ShowCluster() { {isLoading ? ( - + - + @@ -831,12 +853,11 @@ export default function ShowCluster() { - + @@ -847,7 +868,7 @@ export default function ShowCluster() { {Array.isArray(allSchedulers) && allSchedulers.map((item: any) => ( - + { @@ -861,7 +882,7 @@ export default function ShowCluster() { aria-expanded={Boolean(schedulerAnchorElement) ? 'true' : undefined} className={styles.moreVertIcon} > - + { navigate(`/clusters/${params.id}/schedulers/${schedulerSelectedRow?.id}`); @@ -905,6 +926,7 @@ export default function ShowCluster() { {schedulerFeatures && schedulerFeatures.length > 0 ? ( { setOpenSchedulerEditFeatures(true); @@ -922,6 +944,7 @@ export default function ShowCluster() { '' )} { openHandleScheduler(schedulerSelectedRow); @@ -929,16 +952,20 @@ export default function ShowCluster() { }} > - + - + Delete - + {isLoading ? ( ) : ( @@ -964,7 +991,7 @@ export default function ShowCluster() { - + - + - + - + {Array.isArray(item.features) && item.features.map((features: string, id: any) => ( @@ -1016,7 +1045,7 @@ export default function ShowCluster() { sx={{ borderRadius: '0.2rem', background: 'var(--palette-background-inactive)', - color: 'var(--table-title-text-color)', + color: 'var(--palette-table-title-text-color)', mr: '0.4rem', border: '0', fontFamily: 'mabry-bold', @@ -1032,7 +1061,7 @@ export default function ShowCluster() { ) : ( - + You don't have scheduler cluster. @@ -1041,270 +1070,274 @@ export default function ShowCluster() { ) : ( - - - - +
+ + + + + ID + + + + + Hostname + + + + + IP + + + + + Port + + + + + Status + + + + + Features + + + + + Operation + + + + + + {isLoading ? ( + - - ID - + - - Hostname - + + + - - IP - + + + - - Port - + + + - - Status - + + + - - Features - + + + - - Operation - + + + - - - {isLoading ? ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) : allSchedulers.length === 0 ? ( - - You don't have scheduler cluster. - - ) : ( - <> - {Array.isArray(allSchedulers) && - allSchedulers.map((item: any) => { - return ( - - - {item?.id} - - - - {item?.host_name} - - - - {item?.ip} - - - {item?.port} - - - - - - - {Array.isArray(item.features) && - item.features.map((features: string, id: any) => ( - - ))} - - - - { - setSchedulerAnchorElement(event.currentTarget); - setSchedulerSelectedRow(item); - setSchedulerSelectedID(item.id); - }} - size="small" - aria-controls={Boolean(schedulerAnchorElement) ? item?.host_name : undefined} - aria-haspopup="true" - aria-expanded={Boolean(schedulerAnchorElement) ? 'true' : undefined} - > - - - - - { - navigate(`/clusters/${params.id}/schedulers/${schedulerSelectedRow?.id}`); - setSchedulerAnchorElement(null); + ) : allSchedulers.length === 0 ? ( + + You don't have scheduler cluster. + + ) : ( + <> + {Array.isArray(allSchedulers) && + allSchedulers.map((item: any) => { + return ( + + + {item?.id} + + + + {item?.host_name} + + + + {item?.ip} + + + {item?.port} + + + + + + + {Array.isArray(item.features) && + item.features.map((features: string, id: any) => ( + - - - - - View - - - {schedulerFeatures && schedulerFeatures.length > 0 ? ( - { - setOpenSchedulerEditFeatures(true); - setSchedulerAnchorElement(null); - }} - > - - - - - Edit Features - - - ) : ( - '' - )} + /> + ))} + + + + { + setSchedulerAnchorElement(event.currentTarget); + setSchedulerSelectedRow(item); + setSchedulerSelectedID(item.id); + }} + size="small" + aria-controls={Boolean(schedulerAnchorElement) ? item?.host_name : undefined} + aria-haspopup="true" + aria-expanded={Boolean(schedulerAnchorElement) ? 'true' : undefined} + > + + + + + { + navigate(`/clusters/${params.id}/schedulers/${schedulerSelectedRow?.id}`); + setSchedulerAnchorElement(null); + }} + > + + + + + View + + + {schedulerFeatures && schedulerFeatures.length > 0 ? ( { - openHandleScheduler(schedulerSelectedRow); + setOpenSchedulerEditFeatures(true); setSchedulerAnchorElement(null); }} > - + - - Delete + + Edit Features - - - - - ); - })} - - )} - -
-
+ ) : ( + '' + )} + { + openHandleScheduler(schedulerSelectedRow); + setSchedulerAnchorElement(null); + }} + > + + + + + Delete + + +
+ + + + ); + })} + + )} + +
)} {schedulerTotalPages > 1 ? ( - + - - - - + + + + Delete inactive instances @@ -1414,15 +1431,15 @@ export default function ShowCluster() { {deleteInactiveSchedulerSuccessful || '0'} - + number of deleted schedulers - +
@@ -1438,11 +1455,7 @@ export default function ShowCluster() { number of delete error - +
@@ -1478,11 +1491,7 @@ export default function ShowCluster() { id="inactive-header" > - + Error log @@ -1522,10 +1531,9 @@ export default function ShowCluster() { size="small" onClick={handleReset} sx={{ - '&.MuiButton-root': { - backgroundColor: 'var(--button-color)', - color: '#fff', - }, + background: 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }} > Cancel @@ -1553,11 +1561,7 @@ export default function ShowCluster() { alignItems: 'center', }} > - + - + ID - + Hostname - + IP @@ -1696,21 +1685,21 @@ export default function ShowCluster() { ) : activeStep === 1 ? ( - + WARNING:  - + This action CANNOT be undone. @@ -1750,8 +1739,9 @@ export default function ShowCluster() { onClick={handleBack} sx={{ '&.MuiButton-root': { - backgroundColor: activeStep === 0 ? '' : 'var(--button-color)', - color: '#fff', + backgroundColor: activeStep === 0 ? '' : 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }, }} > @@ -1767,9 +1757,9 @@ export default function ShowCluster() { id="save-delete" sx={{ '&.MuiLoadingButton-root': { - backgroundColor: 'var(--delete-button-color)', - color: '#fff', - borderColor: 'var(--save-color)', + backgroundColor: 'var(--palette-delete-button-color)', + color: 'var(--palette-button-text-color)', + borderColor: 'var(--palette-save-color)', }, }} > @@ -1788,8 +1778,9 @@ export default function ShowCluster() { backgroundColor: Array.isArray(schedulerInactive) && schedulerInactive.length === 0 ? '' - : 'var(--button-color)', - color: '#fff', + : 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }, }} > @@ -1810,7 +1801,7 @@ export default function ShowCluster() { > - + Are you sure you want to delet this scheduler? @@ -1847,7 +1838,7 @@ export default function ShowCluster() { > - + Featrues @@ -1873,14 +1864,14 @@ export default function ShowCluster() { - + Schedule - + If schedule feature is enabled, the scheduler can schedule download tasks. @@ -1899,14 +1890,14 @@ export default function ShowCluster() { - + Preheat - + If preheat feature is enabled, the scheduler can execute preheating job. @@ -1940,6 +1931,6 @@ export default function ShowCluster() { -
+
); } diff --git a/src/components/clusters/schedulers/show.module.css b/src/components/clusters/schedulers/show.module.css index e27b401..a00d833 100644 --- a/src/components/clusters/schedulers/show.module.css +++ b/src/components/clusters/schedulers/show.module.css @@ -23,6 +23,7 @@ .headerIcon { width: 1.4rem; height: 1.4rem; + color: var(var(--palette-detail-lable-color)); } .hostname { @@ -36,9 +37,10 @@ display: flex; align-items: center; justify-content: center; - background-color: var(--button-color); - color: var(--save-size-color); + background-color: var(--palette-text-color); + color: var(--palette-button-text-color); border-radius: 0.3rem; min-width: 1.6rem; padding: 0.2rem 0.4rem; } + diff --git a/src/components/clusters/schedulers/show.tsx b/src/components/clusters/schedulers/show.tsx index 1c100b7..3625485 100644 --- a/src/components/clusters/schedulers/show.tsx +++ b/src/components/clusters/schedulers/show.tsx @@ -17,6 +17,15 @@ import styles from './show.module.css'; import _ from 'lodash'; import { useParams, Link, useLocation } from 'react-router-dom'; import Card from '../../card'; +import { ReactComponent as ID } from '../../../assets/images/cluster/scheduler/scheduler-id.svg'; +import { ReactComponent as Hostname } from '../../../assets/images/cluster/scheduler/hostname.svg'; +import { ReactComponent as ClusterID } from '../../../assets/images/cluster/scheduler/cluster-id.svg'; +import { ReactComponent as IP } from '../../../assets/images/cluster/scheduler/scheduler-ip.svg'; +import { ReactComponent as Status } from '../../../assets/images/cluster/scheduler/status.svg'; +import { ReactComponent as Features } from '../../../assets/images/cluster/scheduler/features.svg'; +import { ReactComponent as Port } from '../../../assets/images/cluster/scheduler/port.svg'; +import { ReactComponent as CreatedAt } from '../../../assets/images/cluster/scheduler/created-at.svg'; +import { ReactComponent as UpdatedAt } from '../../../assets/images/cluster/scheduler/updated-at.svg'; export default function Schedulers() { const [isLoading, setIsLoading] = useState(false); @@ -29,29 +38,6 @@ export default function Schedulers() { const pathSegments = location.pathname.split('/'); const clusterID = pathSegments[pathSegments.length - 3]; - const schedulerLabel = [ - { - label: 'Port', - name: 'port', - }, - { - label: 'State', - name: 'state', - }, - { - label: 'Features', - name: 'features', - }, - { - label: 'Created At', - name: 'created_at', - }, - { - label: 'Updated At', - name: 'updated_at', - }, - ]; - useEffect(() => { (async function () { try { @@ -93,6 +79,7 @@ export default function Schedulers() { {errorMessageText} + Scheduler } aria-label="breadcrumb" - sx={{ mb: '1rem' }} + sx={{ mb: '2rem', mt: '1rem' }} > clusters @@ -113,13 +100,10 @@ export default function Schedulers() { {scheduler?.host_name || '-'} - - Scheduler - - + ID @@ -130,7 +114,7 @@ export default function Schedulers() { - + Hostname @@ -143,13 +127,13 @@ export default function Schedulers() { - + Cluster ID - + {isLoading ? ( ) : ( @@ -160,7 +144,7 @@ export default function Schedulers() { - + IP @@ -171,7 +155,7 @@ export default function Schedulers() { - + Status @@ -187,9 +171,11 @@ export default function Schedulers() { id="status" sx={{ borderRadius: '0.25rem', - backgroundColor: scheduler?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + backgroundColor: + scheduler?.state === 'active' ? 'var(--palette-text-color)' : 'var(--palette-dark-300Channel)', color: scheduler?.state === 'active' ? '#FFFFFF' : '#FFFFFF', - borderColor: scheduler?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + borderColor: + scheduler?.state === 'active' ? 'var(--palette-text-color)' : 'var(--palette-dark-300Channel)', fontWeight: 'bold', }} /> @@ -202,7 +188,7 @@ export default function Schedulers() { - + Features @@ -219,10 +205,10 @@ export default function Schedulers() { variant="outlined" sx={{ borderRadius: '0.25rem', - background: 'var(--button-color)', + background: 'var(--palette-dark-300Channel)', color: '#FFFFFF', mr: '0.4rem', - borderColor: 'var(--button-color)', + borderColor: 'var(--palette-dark-300Channel)', fontWeight: 'bold', }} /> @@ -234,7 +220,7 @@ export default function Schedulers() { - + Port @@ -245,7 +231,7 @@ export default function Schedulers() { - + Created At @@ -267,7 +253,7 @@ export default function Schedulers() { - + Updated At diff --git a/src/components/clusters/seed-peers/index.module.css b/src/components/clusters/seed-peers/index.module.css index ec2a4cf..fda8b03 100644 --- a/src/components/clusters/seed-peers/index.module.css +++ b/src/components/clusters/seed-peers/index.module.css @@ -1,18 +1,18 @@ -.schedulerInactiveCountWrapper { +.seedPeerInactiveCountWrapper { display: flex; align-items: center; padding: 0.8rem 1rem; } -.schedulerInactiveList { +.seedPeerInactiveList { display: flex; padding: 0.8rem 1rem !important; } -.schedulerInactiveListTitle { +.seedPeerInactiveListTitle { display: flex; padding: 0.8rem 1rem; - background-color: var(--table-title-color) !important; + background-color: var(--palette-table-title-color) !important; } .noInactiveScheduler { @@ -26,7 +26,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .logHeaderWrapper { @@ -56,6 +56,7 @@ width: 3rem; height: 3rem; margin-right: '0.5rem'; + color: var(var(--palette-detail-lable-color)); } .headerErrorIcon { @@ -69,27 +70,25 @@ margin-bottom: 0.4rem; } -.menuItem:hover { - background-color: var(--button-color) !important; - color: var(--save-size-color); -} - .menu { - background-image: url(../../../../public/images/menu/cyan-blur.png), url(../../../../public/images/menu/red-blur.png); - background-color: rgba(255, 255, 255, 0.9); + background-image: url(../../../assets/images/menu/cyan-blur.png), url(../../../assets/images/menu/red-blur.png); + background-color: var(--palette-background-menu); background-repeat: no-repeat, no-repeat; padding: 0.2rem; background-size: 50%, 50%; - box-shadow: rgba(145, 158, 171, 0.24) 0px 0px 2px 0px, rgba(145, 158, 171, 0.24) -20px 20px 40px -4px; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); outline: 0px; background-position: right top, left bottom; - border-radius: 10px; + border-radius: var(--menu-border-radius); overflow: inherit; } +.menuItem { + border-radius: var(--menu-border-radius) !important; +} + .menuItemIcon { - color: var(--button-color); + color: var(--palette-button-color); } .menuText { @@ -105,7 +104,7 @@ display: flex; justify-content: space-between; align-items: center; - padding-bottom: 2rem; + padding-bottom: 1.5rem; } .idIcon { @@ -128,7 +127,7 @@ margin-bottom: 2rem; } -.schedulerInactiveHeaderID { +.seedPeerInactiveHeaderID { width: 20%; white-space: nowrap; overflow: hidden; @@ -137,7 +136,7 @@ align-items: center; } -.schedulerInactiveHeaderHostname { +.seedPeerInactiveHeaderHostname { width: 50%; white-space: nowrap; overflow: hidden; @@ -146,7 +145,12 @@ align-items: center; } -.schedulerInactiveHeaderIP { +.seedPeerInactiveTableHeaderText { + font-family: 'mabry-bold'; + color: var(var(--palette-table-title-text-color)); +} + +.seedPeerInactiveHeaderIP { width: 25%; white-space: nowrap; overflow: hidden; @@ -155,7 +159,7 @@ align-items: center; } -.schedulerInactiveID { +.seedPeerInactiveID { display: flex; align-items: center; width: 20%; @@ -164,7 +168,7 @@ text-overflow: ellipsis; } -.schedulerInactiveHostname { +.seedPeerInactiveHostname { width: 50%; white-space: nowrap; overflow: hidden; @@ -172,29 +176,17 @@ padding-right: 1rem; } -.schedulerInactiveIP { +.seedPeerInactiveIP { width: 25%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.seedPeerHeader { - width: 33.3333%; - padding: 1.2rem; - display: flex; - justify-content: space-between; - position: relative; - z-index: 0; - overflow: hidden; -} - .activeHeader { - width: 33.3333%; padding: 1.2rem; display: flex; justify-content: space-between; - margin-left: 1.6rem; position: relative; z-index: 0; overflow: hidden; @@ -204,6 +196,7 @@ width: 1rem; height: 1rem; margin-right: 0.4rem; + color: var(--palette-text-color); } .hostnameCardText { @@ -292,8 +285,8 @@ align-items: center; justify-content: center; flex-direction: column; - background-color: rgba(var(--no-data-color) / 0.04) !important; - border: dashed 1px rgba(var(--no-data-color) / 0.08); + background-color: rgba(var(--palette-no-data-color) / 0.04) !important; + border: dashed 1px rgba(var(--palette-no-data-color) / 0.08); } .nodataIcon { @@ -306,34 +299,63 @@ font-family: 'mabry-bold'; } +.navigationContainer { + display: grid; + gap: calc(1.5rem); + grid-template-columns: repeat(3, 1fr); + margin-bottom: 1.5rem; +} + +.navigationCount { + display: flex; + align-items: center; +} + +.navigationCountIcon { + background-color: rgba(34 197 94 / 0.18); + color: var(--palette-description-color); + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5rem; +} + .navigation { - top: -3rem; + top: -2rem; width: 160px; z-index: -1; height: 160px; - right: -5rem; + right: -6rem; opacity: 0.12; border-radius: 24px; position: absolute; transform: rotate(34deg); - background: linear-gradient(to right, #22c55e 0%, rgba(34 197 94 / 0) 100%); + background: linear-gradient(to right, var(--palette-text-color) 0%, transparent 100%); } .navigationIcon { - width: 2.4rem; - height: 2.4rem; - top: 1.4rem; - right: 1.8rem; + width: 2rem; + height: 2rem; + top: 2.4rem; + right: 1.2rem; position: absolute; + color: var(--palette-text-color); } .tableHeader { - color: var(--table-title-text-color); + border-color: var(--palette-palette-divider) !important; +} + +.tableHeaderText { + color: var(--palette-table-title-text-color); font-family: 'mabry-bold' !important; } .tableRow { - border-bottom: dashed var(--palette-divider); + border-bottom: dashed var(--palette-palette-divider); } .tableRow:last-child { @@ -342,7 +364,43 @@ .divider { border-width: 0px 0px thin; - border-color: var(--palette-divider); + border-color: var(--palette-palette-divider); border-style: dashed; margin: 0; } + +.deleteInactiveInstances { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; +} + +.deleteInactiveHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.deleteIcon { + width: 1.8rem; + height: 1.8rem; + margin-right: 0.4rem; +} + +.inactiveTotalIcon { + width: 0.6rem; + height: 0.6rem; +} + +.deleteWarning { + width: 1.4rem; + height: 1.4rem; + padding-right: 0.2rem; +} + +.failureIcon { + width: 1.2rem; + height: 1.2rem; + margin-right: 0.4rem; +} diff --git a/src/components/clusters/seed-peers/index.tsx b/src/components/clusters/seed-peers/index.tsx index f8d43d3..a5d6c40 100644 --- a/src/components/clusters/seed-peers/index.tsx +++ b/src/components/clusters/seed-peers/index.tsx @@ -1,5 +1,4 @@ import Paper from '@mui/material/Paper'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; import { Alert, Box, @@ -66,20 +65,25 @@ import Card from '../../card'; import RemoveRedEyeIcon from '@mui/icons-material/RemoveRedEye'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; - -const theme = createTheme({ - palette: { - primary: { - main: '#1C293A', - }, - secondary: { - main: '#2E8F79', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import { ReactComponent as Total } from '../../../assets/images/cluster/peer/total.svg'; +import { ReactComponent as Active } from '../../../assets/images/cluster/scheduler/active.svg'; +import { ReactComponent as Inactive } from '../../../assets/images/cluster/scheduler/inactive.svg'; +import { ReactComponent as SelectCard } from '../../../assets/images/cluster/scheduler/card.svg'; +import { ReactComponent as SelectTable } from '../../../assets/images/cluster/scheduler/table.svg'; +import { ReactComponent as ID } from '../../../assets/images/cluster/id.svg'; +import { ReactComponent as Status } from '../../../assets/images/cluster/status.svg'; +import { ReactComponent as IP } from '../../../assets/images/cluster/ip.svg'; +import { ReactComponent as Type } from '../../../assets/images/cluster/seed-peer/type.svg'; +import { ReactComponent as Port } from '../../../assets/images/cluster/seed-peer/port.svg'; +import { ReactComponent as DownloadPort } from '../../../assets/images/cluster/seed-peer/download-port.svg'; +import { ReactComponent as Delete } from '../../../assets/images/cluster/delete.svg'; +import { ReactComponent as IcContent } from '../../../assets/images/cluster/scheduler/ic-content.svg'; +import { ReactComponent as InactiveTotal } from '../../../assets/images/cluster/inactive-total.svg'; +import { ReactComponent as DeleteWarning } from '../../../assets/images/cluster/delete-warning.svg'; +import { ReactComponent as DeleteInactive } from '../../../assets/images/cluster/delete-inactive.svg'; +import { ReactComponent as DeleteInactiveError } from '../../../assets/images/cluster/delete-inactive-error.svg'; +import { ReactComponent as Failure } from '../../../assets/images/job/preheat/failure.svg'; +import { ReactComponent as Count } from '../../../assets/images/cluster/scheduler/number.svg'; function CircularProgressWithLabel(props: LinearProgressProps & { value: number }) { return ( @@ -505,7 +509,7 @@ export default function ShowCluster() { }; return ( - + @@ -563,26 +566,10 @@ export default function ShowCluster() { position: 'absolute', }} > - - - - + + + + Delete inactive instances @@ -634,11 +621,7 @@ export default function ShowCluster() { number of deleted seed peers - + @@ -654,11 +637,7 @@ export default function ShowCluster() { number of delete error - + @@ -694,11 +673,7 @@ export default function ShowCluster() { id="inactive-header" > - + Error log @@ -738,10 +713,9 @@ export default function ShowCluster() { size="small" onClick={handleReset} sx={{ - '&.MuiButton-root': { - backgroundColor: 'var(--button-color)', - color: '#fff', - }, + background: 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }} > Cancel @@ -755,7 +729,7 @@ export default function ShowCluster() { {activeStep === 0 ? ( - + Seed peers @@ -769,11 +743,7 @@ export default function ShowCluster() { alignItems: 'center', }} > - +
- - - + + + ID - - + + Hostname - - + + IP @@ -810,7 +780,7 @@ export default function ShowCluster() { { return index !== seedPeerInactive.length - 1 ? ( - + - + {item.id} @@ -835,17 +801,13 @@ export default function ShowCluster() { {item.host_name} - + {item.ip} @@ -854,13 +816,9 @@ export default function ShowCluster() { ) : ( - + - + {item.id} @@ -868,17 +826,13 @@ export default function ShowCluster() { {item.host_name} - + {item.ip} @@ -897,21 +851,21 @@ export default function ShowCluster() { ) : activeStep === 1 ? ( - + WARNING:  - + This action CANNOT be undone. @@ -951,8 +905,9 @@ export default function ShowCluster() { onClick={handleBack} sx={{ '&.MuiButton-root': { - backgroundColor: activeStep === 0 ? '' : 'var(--button-color)', - color: '#fff', + backgroundColor: activeStep === 0 ? '' : 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }, }} > @@ -968,9 +923,9 @@ export default function ShowCluster() { id="save-delete" sx={{ '&.MuiLoadingButton-root': { - backgroundColor: 'var(--delete-button-color)', + backgroundColor: 'var(--palette-delete-button-color)', color: '#fff', - borderColor: 'var(--save-color)', + borderColor: 'var(--palette-save-color)', }, }} > @@ -989,8 +944,9 @@ export default function ShowCluster() { backgroundColor: Array.isArray(seedPeerInactive) && seedPeerInactive.length === 0 ? '' - : 'var(--button-color)', - color: '#fff', + : 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }, }} > @@ -1003,11 +959,11 @@ export default function ShowCluster() { - - + + - + Total @@ -1020,20 +976,24 @@ export default function ShowCluster() { {seedPeerCount?.length || 0} )} - - - number of seed peers - + + + + + + number of seed peers + + - + - + Active @@ -1046,19 +1006,24 @@ export default function ShowCluster() { {numberOfActiveSeedPeers} )} - - number of active seed peers - + + + + + + number of active seed peers + + - +
- + Inactive @@ -1071,14 +1036,20 @@ export default function ShowCluster() { {numberOfInactiveSeedPeers} )} - - number of inactive seed peers - + + + + + + + number of inactive seed peers + +
- +
@@ -1132,7 +1103,7 @@ export default function ShowCluster() { /> - + {statusList.map((item, index) => ( - handleMenuItemClick(item)}> + handleMenuItemClick(item)} + > {item.lable} ))} @@ -1184,6 +1164,7 @@ export default function ShowCluster() { border: `1px solid var(--palette-action-hover)`, flexWrap: 'wrap', ml: '1rem', + backgroundColor: 'var(--palette-background-paper)', })} > - + - + @@ -1208,9 +1189,9 @@ export default function ShowCluster() { {isLoading ? ( - + - + @@ -1223,12 +1204,11 @@ export default function ShowCluster() { - + @@ -1239,7 +1219,7 @@ export default function ShowCluster() { {Array.isArray(allseedPeers) && allseedPeers.map((item: any) => ( - + { setSeedPeerAnchorElement(event.currentTarget); @@ -1253,7 +1233,7 @@ export default function ShowCluster() { aria-expanded={Boolean(seedPeerAnchorElement) ? 'true' : undefined} className={styles.moreVertIcon} > - + { navigate(`/clusters/${params.id}/seed-peers/${seedPeerSelectedRow?.id}`); @@ -1295,6 +1276,7 @@ export default function ShowCluster() { { openHandleSeedPeer(seedPeerSelectedRow); @@ -1302,12 +1284,12 @@ export default function ShowCluster() { }} > - + Delete @@ -1315,7 +1297,7 @@ export default function ShowCluster() { - + {' '} {isLoading ? ( ) : ( @@ -1341,7 +1323,7 @@ export default function ShowCluster() { - + - + - + {item.ip} - + {_.upperFirst(item?.type) || ''} - + {_.upperFirst(item?.port) || ''} - + {_.upperFirst(item?.download_port) || ''} @@ -1405,7 +1385,7 @@ export default function ShowCluster() { ) : ( - + You don't have seed peer cluster. @@ -1416,50 +1396,50 @@ export default function ShowCluster() { - + - - + + ID - - + + Hostname - - + + IP - - + + Port - - + + Download Port - - + + Object Storage Port - - + + Type - - + + Status - - + + Operation @@ -1540,7 +1520,7 @@ export default function ShowCluster() { component={Link} to={`/clusters/${params.id}/seed-peers/${item?.id}`} underline="hover" - sx={{ color: 'var(--description-color)' }} + color="var(--palette-description-color)" > {item?.host_name} @@ -1568,51 +1548,60 @@ export default function ShowCluster() { sx={{ borderRadius: '0.2rem', backgroundColor: - item?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + item?.state === 'active' + ? 'var(--palette-description-color)' + : 'var(--palette-dark-300Channel)', color: item?.state === 'active' ? '#FFFFFF' : '#FFFFFF', borderColor: - item?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + item?.state === 'active' + ? 'var(--palette-description-color)' + : 'var(--palette-dark-300Channel)', fontWeight: 'bold', }} /> { setSeedPeerAnchorElement(event.currentTarget); setSeedPeerSelectedRow(item); setSeedPeerSelectedID(item.id); }} size="small" - id={`operation-${item?.id}`} aria-controls={Boolean(seedPeerAnchorElement) ? item?.host_name : undefined} aria-haspopup="true" aria-expanded={Boolean(seedPeerAnchorElement) ? 'true' : undefined} - sx={{ position: 'relative' }} > - + { navigate(`/clusters/${params.id}/seed-peers/${seedPeerSelectedRow?.id}`); @@ -1627,6 +1616,7 @@ export default function ShowCluster() { { openHandleSeedPeer(seedPeerSelectedRow); @@ -1634,12 +1624,15 @@ export default function ShowCluster() { }} > - + Delete @@ -1665,7 +1658,7 @@ export default function ShowCluster() { > - + Are you sure you want to delet this seed peer? @@ -1690,7 +1683,7 @@ export default function ShowCluster() { {seedPeerTotalPages > 1 ? ( - + )} - + ); } diff --git a/src/components/clusters/seed-peers/show.module.css b/src/components/clusters/seed-peers/show.module.css index 96dfcc1..7ed79de 100644 --- a/src/components/clusters/seed-peers/show.module.css +++ b/src/components/clusters/seed-peers/show.module.css @@ -35,8 +35,8 @@ display: flex; align-items: center; justify-content: center; - background-color: var(--button-color); - color: var(--save-size-color); + background-color: var(--palette-button-color); + color: var(--palette-button-text-color); border-radius: 0.3rem; min-width: 1.6rem; padding: 0.2rem 0.4rem; diff --git a/src/components/clusters/seed-peers/show.tsx b/src/components/clusters/seed-peers/show.tsx index 099d15a..4caad62 100644 --- a/src/components/clusters/seed-peers/show.tsx +++ b/src/components/clusters/seed-peers/show.tsx @@ -1,4 +1,3 @@ -import Paper from '@mui/material/Paper'; import { Alert, Box, Breadcrumbs, Chip, Link as RouterLink, Skeleton, Snackbar, Typography } from '@mui/material'; import { useEffect, useState } from 'react'; import { getSeedPeer, getSeedPeerResponse } from '../../../lib/api'; @@ -8,6 +7,17 @@ import styles from './show.module.css'; import _ from 'lodash'; import { useParams, Link, useLocation } from 'react-router-dom'; import Card from '../../card'; +import { ReactComponent as ID } from '../../../assets/images/cluster/scheduler/scheduler-id.svg'; +import { ReactComponent as Hostname } from '../../../assets/images/cluster/scheduler/hostname.svg'; +import { ReactComponent as IP } from '../../../assets/images/cluster/scheduler/scheduler-ip.svg'; +import { ReactComponent as ClusterID } from '../../../assets/images/cluster/scheduler/cluster-id.svg'; +import { ReactComponent as Type } from '../../../assets/images/cluster/seed-peer/seed-peer-type.svg'; +import { ReactComponent as Status } from '../../../assets/images/cluster/scheduler/status.svg'; +import { ReactComponent as Port } from '../../../assets/images/cluster/scheduler/port.svg'; +import { ReactComponent as DownloadPort } from '../../../assets/images/cluster/seed-peer/seed-peer-download-port.svg'; +import { ReactComponent as ObjectStoragePort } from '../../../assets/images/cluster/seed-peer/object-storage-port.svg'; +import { ReactComponent as CreatedAt } from '../../../assets/images/cluster/scheduler/created-at.svg'; +import { ReactComponent as UpdatedAt } from '../../../assets/images/cluster/scheduler/updated-at.svg'; export default function SeedPeer() { const [isLoading, setIsLoading] = useState(false); @@ -61,6 +71,7 @@ export default function SeedPeer() { {errorMessageText} + Seed-Peer } aria-label="breadcrumb" - sx={{ mb: '1rem' }} + sx={{ mb: '2rem', mt: '1rem' }} > clusters @@ -81,13 +92,10 @@ export default function SeedPeer() { {seedPeer?.host_name || '-'} - - Seed-Peer - - + ID @@ -98,7 +106,7 @@ export default function SeedPeer() { - + Hostname @@ -109,7 +117,7 @@ export default function SeedPeer() { - + IP @@ -120,7 +128,7 @@ export default function SeedPeer() { - + Cluster ID @@ -137,7 +145,7 @@ export default function SeedPeer() { - + Type @@ -152,7 +160,7 @@ export default function SeedPeer() { - + Status @@ -168,20 +176,38 @@ export default function SeedPeer() { variant="outlined" sx={{ borderRadius: '0.25rem', - backgroundColor: seedPeer?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + backgroundColor: + seedPeer?.state === 'active' + ? 'var(--palette-description-color)' + : 'var(--palette-dark-300Channel)', color: seedPeer?.state === 'active' ? '#FFFFFF' : '#FFFFFF', - borderColor: seedPeer?.state === 'active' ? 'var(--description-color)' : 'var(--button-color)', + borderColor: + seedPeer?.state === 'active' + ? 'var(--palette-description-color)' + : 'var(--palette-dark-300Channel)', fontWeight: 'bold', }} /> ) : ( - '-' + )} - + Port @@ -192,11 +218,7 @@ export default function SeedPeer() { - + Download Port @@ -207,7 +229,7 @@ export default function SeedPeer() { - + Object Storage Port @@ -222,7 +244,7 @@ export default function SeedPeer() { - + Created At @@ -244,7 +266,7 @@ export default function SeedPeer() { - + Updated At diff --git a/src/components/clusters/show.module.css b/src/components/clusters/show.module.css index dfb3879..4b6aa3f 100644 --- a/src/components/clusters/show.module.css +++ b/src/components/clusters/show.module.css @@ -5,231 +5,7 @@ align-items: center; } -.updateClusterIcon { +.tableIcon { width: 1.4rem; height: 1.4rem; - margin-right: 0.4rem; -} - -.deleteClusterIcon { - width: 6rem; - height: 6rem; -} - -.ipContainer { - display: flex; - justify-content: center; - align-items: flex-end; -} - -.ipIcon { - width: 1.4rem; - height: 1.4rem; - margin-right: 0.4rem; -} - -.searchContainer { - display: flex; - justify-content: flex-start; - margin-right: 1rem; - margin-top: 0.6rem; - margin-bottom: 0.6rem; -} - -.openDeleteInactiveDialog { - display: flex; - justify-content: space-between; - align-items: center; - margin: 1rem 0; -} - -.successfulLog { - padding: 0rem !important; -} - -.deleteInactiveWrapper { - padding: 1rem; -} - -.schedulerInactiveCountWrapper { - display: flex; - align-items: center; - padding: 0.8rem 1rem; -} - -.schedulerInactiveList { - display: flex; - padding: 0.8rem 1rem !important; -} - -.schedulerInactiveListTitle { - display: flex; - padding: 0.6rem 1rem; - background-color: #f7f7f7 !important; -} - -.noInactiveScheduler { - padding: 1rem 0; - display: flex; - justify-content: center; - margin: auto; -} - -.schedulerInactiveHeaderID { - width: 20%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: flex; - align-items: center; -} - -.schedulerInactiveHeaderHostname { - width: 50%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: flex; - align-items: center; -} - -.schedulerInactiveHeaderIP { - width: 25%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: flex; - align-items: center; -} - -.schedulerInactiveID { - display: flex; - align-items: center; - width: 20%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.schedulerInactiveHostname { - width: 50%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding-right: 1rem; -} - -.schedulerInactiveIP { - width: 25%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.circularProgressWrapper { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin: 1rem auto; -} - -.logHeaderWrapper { - display: flex; - margin-bottom: 2rem; -} - -.headerContainer { - width: 33.333%; - margin-right: 0.6rem; - padding: 0.8rem; - display: flex; - align-items: flex-end; - justify-content: space-between; -} - -.headerContainer:last-child { - margin-right: 0; - position: relative; -} - -.successHeaderContainer { - width: 50%; - margin-right: 1rem; - padding: 1rem; - display: flex; - align-items: flex-end; - justify-content: space-between; -} - -.successHeaderContainer:last-child { - margin-right: 0; -} - -.headerIcon { - width: 3rem; - height: 3rem; - margin-right: '0.5rem'; -} - -.headerErrorIcon { - width: 2.2rem; - height: 2.2rem; -} - -.headerContent { - display: flex; - align-items: center; - margin-bottom: 0.4rem; -} - -.doughnut { - width: 6rem; - height: 6rem; -} - -.hostnameText { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} - -.featuresIconWrapper { - width: 2.4rem; - height: 2.4rem; - border-radius: 50%; - background-color: #fbfbfb; - display: flex; - align-items: center; - justify-content: center; - margin-right: 0.6rem; -} - -.featuresIconContainer { - width: 1.8rem; - height: 1.8rem; - border-radius: 50%; - background-color: #efefef; - display: flex; - align-items: center; - justify-content: center; -} - -.featuresIcon { - width: 1.2rem; - height: 1.2rem; -} - -.menuItem:hover { - background-color: var(--button-color) !important; - color: var(--save-size-color); -} - -.menuItemIcon { - color: var(--button-color); -} - -.menuItem:hover .menuItemIcon { - color: var(--save-size-color); } diff --git a/src/components/clusters/show.tsx b/src/components/clusters/show.tsx index 3909876..47952cf 100644 --- a/src/components/clusters/show.tsx +++ b/src/components/clusters/show.tsx @@ -1,13 +1,4 @@ -import { - Breadcrumbs, - createTheme, - styled, - ThemeProvider, - Typography, - Link as RouterLink, - Snackbar, - Alert, -} from '@mui/material'; +import { Breadcrumbs, styled, Typography, Link as RouterLink, Snackbar, Alert } from '@mui/material'; import { Link, Outlet, useLocation, useParams } from 'react-router-dom'; import * as React from 'react'; import Box from '@mui/material/Box'; @@ -15,17 +6,11 @@ import Tabs from '@mui/material/Tabs'; import Tab, { TabProps } from '@mui/material/Tab'; import { createContext, useEffect, useState } from 'react'; import { getCluster, getClusterResponse } from '../../lib/api'; - -const theme = createTheme({ - palette: { - primary: { - main: '#2e8f79', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import { ReactComponent as TabCluster } from '../../assets/images/cluster/tab-cluster.svg'; +import { ReactComponent as TabScheduler } from '../../assets/images/cluster/scheduler/tab-scheduler.svg'; +import { ReactComponent as TabSeedPeer } from '../../assets/images/cluster/seed-peer/tab-seed-peer.svg'; +import { ReactComponent as TabPeer } from '../../assets/images/cluster/peer/tab-peer.svg'; +import styles from './show.module.css'; interface MyContextType { cluster: getClusterResponse; @@ -139,60 +124,62 @@ export default function NavTabs() { setValue(newValue); }; - const AntTab = styled((props: StyledTabProps) => )( - ({ theme }) => ({ - textTransform: 'none', + const AntTab = styled((props: StyledTabProps) => )(({ theme }) => ({ + textTransform: 'none', + minWidth: 0, + [theme.breakpoints.up('sm')]: { minWidth: 0, - [theme.breakpoints.up('sm')]: { - minWidth: 0, - }, - fontWeight: theme.typography.fontWeightRegular, - marginRight: theme.spacing(1), - color: 'rgba(0, 0, 0, 0.85)', - fontSize: '0.9rem', - '&:hover': { - color: 'primary', - opacity: 1, - }, - '&.Mui-selected': { - color: '#000', - fontFamily: 'mabry-bold', - }, - }), - ); + }, + minHeight: '3rem', + fontWeight: theme.typography.fontWeightRegular, + color: 'var(--palette-grey-tab)', + padding: '0', + marginRight: '2rem', + fontFamily: 'mabry-bold', + fontSize: '0.9rem', + '&:hover': { + color: 'primary', + opacity: 1, + }, + '&.Mui-selected': { + color: 'var(--palette-text-color)', + fontFamily: 'mabry-bold', + }, + })); const AntTabs = styled(Tabs)({ - borderBottom: '1px solid #e8e8e8', + borderBottom: '1px solid var(--palette-tab-border-color)', '& .MuiTabs-indicator': { - backgroundColor: 'primary', + backgroundColor: 'var(--palette-text-color)', + borderRadius: '1rem', }, }); const tabList = [ { id: 'tab-cluster', - icon: , + icon: , label: 'Cluster', component: Link, to: `/clusters/${params.id}`, }, { id: 'tab-cluster', - icon: , + icon: , label: 'Schedulers', component: Link, to: `/clusters/${params.id}/schedulers`, }, { id: 'tab-cluster', - icon: , + icon: , label: 'Seed Peers', component: Link, to: `/clusters/${params.id}/seed-peers`, }, { id: 'tab-cluster', - icon: , + icon: , label: 'Peers', component: Link, to: `/clusters/${params.id}/peers`, @@ -209,49 +196,65 @@ export default function NavTabs() { return ( - - - - {errorMessageText} - - - - } - aria-label="breadcrumb" - sx={{ mb: '1rem' }} - > - - clusters - - + + + {errorMessageText} + + + + } + aria-label="breadcrumb" + sx={{ mb: '1rem' }} + > + + Cluster + + {location.pathname.split('/')[3] ? ( + {cluster?.name || '-'} - {location.pathname.split('/')[3] && ( - {location.pathname.split('/')[3]} - )} - - - {tabList.map((item) => { - return ; - })} - - - + ) : ( + + {cluster?.name || '-'} + + )} + {location.pathname.split('/')[3] && ( + + {location.pathname.split('/')[3] === 'schedulers' + ? 'Schedulers' + : location.pathname.split('/')[3] === 'seed-peers' + ? 'Seed Peers' + : 'Peers'} + + )} + + + {tabList.map((item) => { + return ; + })} + + ); } diff --git a/src/components/dark-layout.module.css b/src/components/dark-layout.module.css new file mode 100644 index 0000000..1baef12 --- /dev/null +++ b/src/components/dark-layout.module.css @@ -0,0 +1,15 @@ +.github { + width: 2rem; + height: 2rem; + color: var(--palette-secondary-dark); +} + +.icon { + width: 1.2rem; + height: 1.2rem; +} + +.darkModeIcon { + width: 1.2rem; + height: 1.2rem; +} diff --git a/src/components/dark-layout.tsx b/src/components/dark-layout.tsx new file mode 100644 index 0000000..52e6adb --- /dev/null +++ b/src/components/dark-layout.tsx @@ -0,0 +1,191 @@ +import { + Box, + FormControl, + FormControlLabel, + IconButton, + Link, + Paper, + styled, + Switch, + ToggleButton, + ToggleButtonGroup, + toggleButtonGroupClasses, + Typography, + useTheme, +} from '@mui/material'; +import { ReactComponent as Dark } from '../assets/images/menu/dark.svg'; +import { ReactComponent as Light } from '../assets/images/menu/light.svg'; +import { useContext, useState } from 'react'; +import { ColorModeContext } from '../App'; +import styles from './dark-layout.module.css'; + +interface layoutProps { + className?: string; +} + +const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + [`& .${toggleButtonGroupClasses.grouped}`]: { + margin: theme.spacing(0.5), + border: 0, + borderRadius: theme.shape.borderRadius, + [`&.${toggleButtonGroupClasses.disabled}`]: { + border: 0, + }, + }, + [`& .${toggleButtonGroupClasses.middleButton},& .${toggleButtonGroupClasses.lastButton}`]: { + marginLeft: -1, + }, + width: '100%', +})); + +const VerticalToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + [`& .${toggleButtonGroupClasses.grouped}`]: { + margin: theme.spacing(0.5), + border: 0, + borderRadius: theme.shape.borderRadius, + [`&.${toggleButtonGroupClasses.disabled}`]: { + border: 0, + }, + }, + [`& .${toggleButtonGroupClasses.middleButton},& .${toggleButtonGroupClasses.lastButton}`]: { + marginTop: -1, + }, +})); + +export const HeaderLayout: React.FC = ({ className }) => { + const theme = useTheme(); + const colorMode = useContext(ColorModeContext); + + return ( + + ({ + display: 'inline-flex', + flexWrap: 'wrap', + bgcolor: 'var(--palette-grey-600Channel)', + borderRadius: '1.2rem', + width: '100%', + })} + > + + + + + Light + + + + + + Dark + + + + + + ); +}; + +export const ShrinkDarkMode: React.FC = ({ className }) => { + const theme = useTheme(); + const colorMode = useContext(ColorModeContext); + + return ( + + ({ + display: 'inline-flex', + flexWrap: 'wrap', + backgroundColor: 'var(--palette-grey-600Channel)', + borderRadius: '1.2rem', + })} + > + + + + + + + + + + + ); +}; diff --git a/src/components/developer/tokens/edit.module.css b/src/components/developer/tokens/edit.module.css index c669bb1..9b1c3af 100644 --- a/src/components/developer/tokens/edit.module.css +++ b/src/components/developer/tokens/edit.module.css @@ -1,6 +1,13 @@ .header { - display: inline-flex; - align-items: center; - padding: 1rem; - margin-bottom: 1rem; - } \ No newline at end of file + display: inline-flex; + align-items: center; + padding: 1rem; + margin-bottom: 1rem; +} + +.tokenIcon { + width: 2.6rem; + height: 2.6rem; + margin-right: 1rem; + color: var(--palette-button-color); +} diff --git a/src/components/developer/tokens/edit.tsx b/src/components/developer/tokens/edit.tsx index 9d7bea0..517b783 100644 --- a/src/components/developer/tokens/edit.tsx +++ b/src/components/developer/tokens/edit.tsx @@ -9,37 +9,26 @@ import { SelectChangeEvent, FormControlLabel, Checkbox, - createTheme, - ThemeProvider, Snackbar, Alert, Tooltip, InputLabel, FormGroup, FormHelperText, - Paper, Skeleton, + Breadcrumbs, + Link as RouterLink, } from '@mui/material'; import { useEffect, useState } from 'react'; import { formatDate, getExpiredTime } from '../../../lib/utils'; -import { useNavigate, useParams } from 'react-router-dom'; +import { Link, useNavigate, useParams } from 'react-router-dom'; import { getToken, updateTokens } from '../../../lib/api'; import HelpIcon from '@mui/icons-material/Help'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { CancelLoadingButton, SavelLoadingButton } from '../../loading-button'; import Card from '../../card'; import styles from './edit.module.css'; - -const theme = createTheme({ - palette: { - secondary: { - main: '#1c293a', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import { ReactComponent as Token } from '../../../assets/images/tokens/key.svg'; export default function UpdateTokens() { const [successMessage, setSuccessMessage] = useState(false); @@ -74,8 +63,12 @@ export default function UpdateTokens() { endAdornment: ( ), @@ -214,187 +207,211 @@ export default function UpdateTokens() { {errorMessageText} - - Update personal access token - - - - - - - ID:   + Update personal access token + + } + aria-label="breadcrumb" + sx={{ m: '1rem 0' }} + > + Developer + + Personal access tokens + + {tokens.name || ''} + + + + + + + + ID:   + + {isLoading ? ( + + ) : ( + + {tokens.id} - {isLoading ? ( - - ) : ( - - {tokens.id} - - )} - - - - Name:   - - {isLoading ? ( - - ) : ( - - {tokens.name} - - )} - + )} - - - - - - Information + + + Name:   + + {isLoading ? ( + + ) : ( + + {tokens.name} - - - - - {formList.map((item) => ( - - ))} - - - Expiration - - - - - - - - Expiration - - - - - {expiredTimeError && Please select an option.} - - - The token expires on {formatDate(expiredTime) || ''}. - - - - - - Select scopes - - - - - - - - - - { - setPreheat(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of preheating, it's used for preheating of harbor. - - - - - { - setJob(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of job. If you need to call preheat job through open API, it is recommended to use - preheat job. - - - - - { - setCluster(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of cluster. - - - - - - - { - navigate('/developer/personal-access-tokens'); + )} + + + + + + + + Information + + + - } id="save" text="Save" /> + + + {formList.map((item) => ( + + ))} + + + Expiration + + + + + + + + Expiration + + + + + {expiredTimeError && Please select an option.} + + + The token expires on {formatDate(expiredTime) || ''}. + - - + + + Select scopes + + + + + + + + + + { + setPreheat(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of preheating, it's used for preheating of harbor. + + + + + { + setJob(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of job. If you need to call preheat job through open API, it is recommended to use + preheat job. + + + + + { + setCluster(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of cluster. + + + + + + + { + navigate('/developer/personal-access-tokens'); + }} + /> + } id="save" text="Save" /> + + + ); } diff --git a/src/components/developer/tokens/index.module.css b/src/components/developer/tokens/index.module.css index 56f9234..b2c39a2 100644 --- a/src/components/developer/tokens/index.module.css +++ b/src/components/developer/tokens/index.module.css @@ -5,8 +5,8 @@ align-items: center; justify-content: center; flex-direction: column; - background-color: rgba(var(--no-data-color) / 0.04) !important; - border: dashed 1px rgba(var(--no-data-color) / 0.08); + background-color: rgba(var(--palette-no-data-color) / 0.04) !important; + border: dashed 1px rgba(var(--palette-no-data-color) / 0.08); } .nodataIcon { @@ -23,6 +23,16 @@ display: flex; align-items: center; padding: 0.8rem; - background-color: var(--menu-background-color) !important; + background-color: var(--palette-grey-background-color) !important; margin-bottom: 1rem; } + +.copyIcon { + width: 1.4rem; + height: 1.4rem; +} + +.deleteIcon { + width: 6rem; + height: 6rem; +} diff --git a/src/components/developer/tokens/index.tsx b/src/components/developer/tokens/index.tsx index d384a50..0f7335c 100644 --- a/src/components/developer/tokens/index.tsx +++ b/src/components/developer/tokens/index.tsx @@ -25,6 +25,10 @@ import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../../../lib/constants'; import { CancelLoadingButton, DeleteLoadingButton } from '../../loading-button'; import Card from '../../card'; import styles from './index.module.css'; +import { ReactComponent as Done } from '../../../assets/images/tokens/done.svg'; +import { ReactComponent as Copy } from '../../../assets/images/tokens/copy.svg'; +import { ReactComponent as IcContent } from '../../../assets/images/cluster/scheduler/ic-content.svg'; +import { ReactComponent as Delete } from '../../../assets/images/cluster/delete.svg'; export default function PersonalAccessTokens() { const [successMessage, setSuccessMessage] = useState(false); @@ -165,27 +169,17 @@ export default function PersonalAccessTokens() { {errorMessageText} - - } - aria-label="breadcrumb" - sx={{ mb: '1rem' }} - > - developer - personal access tokens - - - Personal access tokens + + + Personal access tokens + - + + } + aria-label="breadcrumb" + sx={{ mb: '1rem' }} + > + Developer + Personal access tokens + + Tokens you have generated that can be used to access the Dragonfly API. {showCopyColumn ? ( @@ -226,15 +232,10 @@ export default function PersonalAccessTokens() { disableTouchListener title="copied!" > - + ) : ( - + )} @@ -261,8 +262,8 @@ export default function PersonalAccessTokens() { ) : allTokens.length === 0 ? ( - - + + You don't have any tokens. @@ -282,7 +283,7 @@ export default function PersonalAccessTokens() { component={Link} to={`/developer/personal-access-tokens/${item?.id}`} underline="hover" - sx={{ color: 'var(--description-color)' }} + sx={{ color: 'var(--palette-description-color)' }} > {item.name} @@ -304,10 +305,9 @@ export default function PersonalAccessTokens() { size="small" id={`delete-token-${item?.id}`} sx={{ - '&.MuiButton-root': { - backgroundColor: 'var(--button-color)', - color: '#fff', - }, + background: 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }} variant="contained" onClick={() => { @@ -321,7 +321,7 @@ export default function PersonalAccessTokens() { @@ -338,7 +338,7 @@ export default function PersonalAccessTokens() { component={Link} to={`/developer/personal-access-tokens/${item?.id}`} underline="hover" - sx={{ color: 'var(--description-color)' }} + sx={{ color: 'var(--palette-description-color)' }} > {item.name} @@ -360,10 +360,9 @@ export default function PersonalAccessTokens() { size="small" id={`delete-token-${item?.id}`} sx={{ - '&.MuiButton-root': { - backgroundColor: 'var(--button-color)', - color: '#fff', - }, + background: 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + ':hover': { backgroundColor: 'var(--palette-hover-button-text-color)' }, }} variant="contained" onClick={() => { @@ -389,12 +388,7 @@ export default function PersonalAccessTokens() { setTokensPage(newPage); navigate(`/developer/personal-access-tokens${newPage > 1 ? `?page=${newPage}` : ''}`); }} - sx={{ - '& .Mui-selected': { - backgroundColor: 'var(--button-color)!important', - color: '#FFF', - }, - }} + color="primary" size="small" /> @@ -411,7 +405,7 @@ export default function PersonalAccessTokens() { > - + Are you sure you want to delete this token? diff --git a/src/components/developer/tokens/new.tsx b/src/components/developer/tokens/new.tsx index 7a91389..38746d0 100644 --- a/src/components/developer/tokens/new.tsx +++ b/src/components/developer/tokens/new.tsx @@ -16,28 +16,18 @@ import { Tooltip, FormGroup, InputLabel, + Breadcrumbs, + Link as RouterLink, } from '@mui/material'; import { useContext, useEffect, useState } from 'react'; import { formatDate, getExpiredTime } from '../../../lib/utils'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { createTokens } from '../../../lib/api'; import HelpIcon from '@mui/icons-material/Help'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { MyContext } from '../../menu'; import { CancelLoadingButton, SavelLoadingButton } from '../../loading-button'; -const theme = createTheme({ - palette: { - secondary: { - contrastText: '#fff', - main: '#1c293a', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); - export default function CreateTokens() { const [successMessage, setSuccessMessage] = useState(false); const [errorMessage, setErrorMessage] = useState(false); @@ -68,8 +58,12 @@ export default function CreateTokens() { endAdornment: ( ), @@ -102,8 +96,12 @@ export default function CreateTokens() { endAdornment: ( ), @@ -220,166 +218,192 @@ export default function CreateTokens() { {errorMessageText} - - Create personal access token - - - - - - Information - - - - - - {formList.map((item) => { - return item?.formProps?.name === 'bio' ? ( - - ) : ( - - ); - })} - - - Expiration - - - - - - - - Expiration - - - - - The token expires on {formatDate(expiredTime) || ''}. - - - - - - Select scopes - - - - - - - - - - { - setPreheat(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of preheating, it's used for preheating of harbor. - - - - - { - setJob(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of job. If you need to call preheat job through open API, it is recommended to use - preheat job. - - - - - { - setCluster(event.target.checked); - }} - sx={{ color: 'var(--button-color)!important' }} - /> - } - /> - - - Full control of cluster. - - - - - - - { - navigate('/developer/personal-access-tokens'); + Create personal access token + + } + aria-label="breadcrumb" + sx={{ m: '1rem 0' }} + > + Developer + + Personal access tokens + + New token + + + + + + + Information + + + - } id="save" text="Save" /> + + + {formList.map((item) => { + return item?.formProps?.name === 'bio' ? ( + + ) : ( + + ); + })} + + + Expiration + + + + + + + + Expiration + + + + + The token expires on {formatDate(expiredTime) || ''}. + - - + + + Select scopes + + + + + + + + + + { + setPreheat(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of preheating, it's used for preheating of harbor. + + + + + { + setJob(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of job. If you need to call preheat job through open API, it is recommended to use + preheat job. + + + + + { + setCluster(event.target.checked); + }} + sx={{ color: 'var(--palette-button-color)!important' }} + /> + } + /> + + + Full control of cluster. + + + + + + + { + navigate('/developer/personal-access-tokens'); + }} + /> + } id="save" text="Save" /> + + + ); } diff --git a/src/components/job/preheats/index.module.css b/src/components/job/preheats/index.module.css index 40e76fb..2ea682c 100644 --- a/src/components/job/preheats/index.module.css +++ b/src/components/job/preheats/index.module.css @@ -16,6 +16,18 @@ font-family: 'mabry-bold'; } +.statusIcon { + width: 1.3rem; + height: 1.3rem; + margin: 0.1rem; +} + +.pendingIcon { + width: 1.3rem; + height: 1.3rem; + color: #dbab0a; +} + .description { width: 90%; overflow: hidden; @@ -37,3 +49,22 @@ justify-content: space-between; margin-left: 0.6rem; } + +.detailIcon { + width: 2rem; + height: 2rem; + color: var(--palette-button-color); +} + +.menu { + background-color: var(--palette-background-menu); + background-image: url(../../../assets/images/menu/cyan-blur.png), url(../../../assets/images/menu/red-blur.png); + background-repeat: no-repeat, no-repeat; + padding: 0.2rem; + background-size: 50%, 50%; + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); + outline: 0px; + background-position: right top, left bottom; + border-radius: var(--menu-border-radius); + overflow: inherit; +} diff --git a/src/components/job/preheats/index.tsx b/src/components/job/preheats/index.tsx index 44e6e82..04762c4 100644 --- a/src/components/job/preheats/index.tsx +++ b/src/components/job/preheats/index.tsx @@ -27,6 +27,11 @@ import { DEFAULT_PAGE_SIZE } from '../../../lib/constants'; import { getBJTDatetime, useQuery } from '../../../lib/utils'; import styles from './index.module.css'; import Card from '../../card'; +import { ReactComponent as IcContent } from '../../../assets/images/cluster/scheduler/ic-content.svg'; +import { ReactComponent as Success } from '../../../assets/images/job/preheat/success.svg'; +import { ReactComponent as Failure } from '../../../assets/images/job/preheat/failure.svg'; +import { ReactComponent as Pending } from '../../../assets/images/job/preheat/pending.svg'; +import { ReactComponent as Detail } from '../../../assets/images/job/preheat/detail.svg'; export default function Preheats() { const [errorMessage, setErrorMessage] = useState(false); @@ -43,17 +48,6 @@ export default function Preheats() { const query = useQuery(); const page = query.get('page') ? parseInt(query.get('page') as string, 10) || 1 : 1; - const theme = createTheme({ - palette: { - primary: { - main: '#1C293A', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, - }); - useEffect(() => { (async function () { try { @@ -70,7 +64,7 @@ export default function Preheats() { setPreheatTotalPages(jobs.total_page || 1); const states = jobs.data.filter( - (obj) => obj.result.state !== 'SUCCESS' && obj.result.state !== 'FAILURE', + (obj) => obj?.result?.state !== 'SUCCESS' && obj?.result?.state !== 'FAILURE', ).length; states === 0 ? setShouldPoll(false) : setShouldPoll(true); @@ -101,7 +95,7 @@ export default function Preheats() { setPreheatTotalPages(jobs.total_page || 1); const states = jobs.data.filter( - (obj) => obj.result.state !== 'SUCCESS' && obj.result.state !== 'FAILURE', + (obj) => obj?.result?.state !== 'SUCCESS' && obj?.result?.state !== 'FAILURE', ).length; states === 0 ? setShouldPoll(false) : setShouldPoll(true); @@ -144,7 +138,7 @@ export default function Preheats() { }; return ( - + - - } - aria-label="breadcrumb" - sx={{ mb: '1rem' }} - > - jobs - preheats - - + Preheats + + } + aria-label="breadcrumb" + sx={{ mb: '1.5rem' }} + > + Job + Preheat + ) : allPreheats.length === 0 ? ( - + You don't have any preheat tasks. @@ -261,27 +255,12 @@ export default function Preheats() { - {item.result.state === 'SUCCESS' ? ( - - ) : item.result.state === 'FAILURE' ? ( - + {item?.result?.state === 'SUCCESS' ? ( + + ) : item?.result?.state === 'FAILURE' ? ( + ) : ( - + )} @@ -313,20 +292,16 @@ export default function Preheats() { id={`preheat-${item?.id}`} to={`/jobs/preheats/${item?.id}`} underline="hover" - sx={{ color: 'var(--description-color)' }} + sx={{ color: 'var(--palette-description-color)' }} > - + @@ -334,27 +309,12 @@ export default function Preheats() { ) : ( - {item.result.state === 'SUCCESS' ? ( - - ) : item.result.state === 'FAILURE' ? ( - + {item?.result?.state === 'SUCCESS' ? ( + + ) : item?.result?.state === 'FAILURE' ? ( + ) : ( - + )} @@ -386,13 +346,9 @@ export default function Preheats() { id={`preheat-${item?.id}`} to={`/jobs/preheats/${item?.id}`} underline="hover" - sx={{ color: 'var(--description-color)' }} + sx={{ color: 'var(--palette-description-color)' }} > - + @@ -402,7 +358,7 @@ export default function Preheats() { )} {preheatTotalPages > 1 ? ( - + )} - + ); } diff --git a/src/components/job/preheats/new.module.css b/src/components/job/preheats/new.module.css index 7abfe5a..11fb65c 100644 --- a/src/components/job/preheats/new.module.css +++ b/src/components/job/preheats/new.module.css @@ -27,3 +27,18 @@ .argsWrapper:nth-child(n + 3) { width: 100%; } + +.argsWrapper { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: calc(1.5rem); + padding: 1.5rem; +} + +.headersWrapper { + padding: 1rem; + width: 37rem; + margin-top: 1rem; + border-radius: 0.25rem !important; + background-color: var(--palette-background-paper) !important; +} diff --git a/src/components/job/preheats/new.tsx b/src/components/job/preheats/new.tsx index e8ad85b..6c1b85f 100644 --- a/src/components/job/preheats/new.tsx +++ b/src/components/job/preheats/new.tsx @@ -13,8 +13,7 @@ import { MenuItem, Checkbox, ListItemText, - createTheme, - ThemeProvider, + Link as RouterLink, FormHelperText, Grid, Chip, @@ -23,17 +22,19 @@ import { Alert, Paper, SelectChangeEvent, + Breadcrumbs, } from '@mui/material'; import { useEffect, useState } from 'react'; import HelpIcon from '@mui/icons-material/Help'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import ClearIcon from '@mui/icons-material/Clear'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { createJob, getClusters } from '../../../lib/api'; import { MAX_PAGE_SIZE } from '../../../lib/constants'; import styles from './new.module.css'; import AddIcon from '@mui/icons-material/Add'; import { CancelLoadingButton, SavelLoadingButton } from '../../loading-button'; +import Card from '../../card'; export default function NewPreheat() { const [successMessage, setSuccessMessage] = useState(false); @@ -55,17 +56,6 @@ export default function NewPreheat() { const navigate = useNavigate(); - const theme = createTheme({ - palette: { - secondary: { - main: '#1c293a', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, - }); - useEffect(() => { (async function () { try { @@ -96,8 +86,12 @@ export default function NewPreheat() { endAdornment: ( ), @@ -137,8 +131,12 @@ export default function NewPreheat() { placement="top" > ), @@ -169,8 +167,12 @@ export default function NewPreheat() { endAdornment: ( ), @@ -261,7 +263,7 @@ export default function NewPreheat() { }; const headersValueValidate = (value: any) => { - const regex = /^.{1,1000}$/; + const regex = /^.{1,10000}$/; return regex.test(value); }; @@ -374,7 +376,7 @@ export default function NewPreheat() { }; return ( - + Create Preheat + + } + aria-label="breadcrumb" + sx={{ mt: '1rem' }} + > + Job + + Preheat + + + New preheat + + @@ -408,8 +427,12 @@ export default function NewPreheat() { @@ -431,19 +454,16 @@ export default function NewPreheat() { - + Clusters {argsForm.map((item) => { - return ( - <> - {item.id === 'filteredQueryParams' ? ( - ( - - {params.InputProps.endAdornment} - - - - - ), - }} - color="success" - {...item.formProps} - /> - )} - /> - ) : item.formProps.id === 'tag' ? ( - - ) : ( - - )} - + return item.id === 'filteredQueryParams' ? ( + + ( + + {params.InputProps.endAdornment} + + + + + ), + }} + color="success" + {...item.formProps} + /> + )} + /> + + ) : item.formProps.id === 'tag' ? ( + + ) : ( + + + ); })} {headers.length > 0 ? ( - + Headers @@ -618,7 +647,7 @@ export default function NewPreheat() { multiline maxRows={3} error={!headersValueValidate(item.value)} - helperText={!headersValueValidate(item.value) && 'Fill in the characters, the length is 1-1000.'} + helperText={!headersValueValidate(item.value) && 'Fill in the characters, the length is 1-10000.'} className={styles.headersValueInput} value={item.value} onChange={(event) => { @@ -639,15 +668,15 @@ export default function NewPreheat() { setheaders(newheaders); }} > - + ))}
- + @@ -539,7 +527,7 @@ export default function ShowExecutions() { - + @@ -585,11 +573,7 @@ export default function ShowExecutions() { - + {item?.ip} @@ -603,10 +587,14 @@ export default function ShowExecutions() { sx={{ borderRadius: '0.2rem', backgroundColor: - item?.host_type === 'super' ? 'var( --description-color)' : 'var(--button-color)', + item?.host_type === 'super' + ? 'var( --palette-description-color)' + : 'var(--palette-button-color)', color: item?.host_type === 'super' ? '#FFFFFF' : '#FFFFFF', borderColor: - item?.host_type === 'super' ? 'var( --description-color)' : 'var(--button-color)', + item?.host_type === 'super' + ? 'var( --palette-description-color)' + : 'var(--palette-button-color)', fontWeight: 'bold', }} /> @@ -618,12 +606,7 @@ export default function ShowExecutions() { setErrorLog(true); }} > - + @@ -635,7 +618,7 @@ export default function ShowExecutions() {
{totalPages > 1 ? ( - + )} - + ); } diff --git a/src/components/job/task/index.module.css b/src/components/job/task/index.module.css new file mode 100644 index 0000000..6af2499 --- /dev/null +++ b/src/components/job/task/index.module.css @@ -0,0 +1,4 @@ +.tabIcon { + width: 1.5rem; + height: 1.5rem; +} diff --git a/src/components/job/task/index.tsx b/src/components/job/task/index.tsx index 7a98ad6..fa7b4d7 100644 --- a/src/components/job/task/index.tsx +++ b/src/components/job/task/index.tsx @@ -1,21 +1,13 @@ -import { Breadcrumbs, createTheme, styled, ThemeProvider, Typography, Link as RouterLink } from '@mui/material'; +import { Breadcrumbs, styled, Typography, Link as RouterLink } from '@mui/material'; import { Link, Outlet, useLocation, useParams } from 'react-router-dom'; import * as React from 'react'; import Box from '@mui/material/Box'; import Tabs from '@mui/material/Tabs'; import Tab, { TabProps } from '@mui/material/Tab'; import { useEffect } from 'react'; - -const theme = createTheme({ - palette: { - primary: { - main: '#2e8f79', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import styles from './index.module.css'; +import { ReactComponent as Clear } from '../../../assets/images/job/task/clear-cache.svg'; +import { ReactComponent as Executions } from '../../../assets/images/job/task/executions.svg'; export default function NavTabs() { const [value, setValue] = React.useState(1); @@ -47,29 +39,33 @@ export default function NavTabs() { [theme.breakpoints.up('sm')]: { minWidth: 0, }, + minHeight: '3rem', fontWeight: theme.typography.fontWeightRegular, - marginRight: theme.spacing(1), - color: 'rgba(0, 0, 0, 0.85)', + color: 'var(--palette-grey-tab)', + padding: '0', + marginRight: '2rem', fontSize: '0.9rem', + fontFamily: 'mabry-bold', '&:hover': { color: 'primary', opacity: 1, }, '&.Mui-selected': { - color: '#000', + color: 'var(--palette-description-color)', fontFamily: 'mabry-bold', }, })); const AntTabs = styled(Tabs)({ - borderBottom: '1px solid #e8e8e8', + borderBottom: '1px solid var(--palette-tab-border-color)', '& .MuiTabs-indicator': { - backgroundColor: 'primary', + backgroundColor: 'var(--palette-description-color)', + borderRadius: '1rem', }, }); return ( - + - jobs - task + Job + Task {location.pathname.split('/')[3] === 'executions' ? ( - executions + Executions ) : ( - {location.pathname.split('/')[3]} + Clear )} - {params?.id ? {params?.id} : ''} + {params?.id ? {params?.id || '-'} : ''} } + icon={} iconPosition="start" label="Clear" component={Link} @@ -112,7 +108,7 @@ export default function NavTabs() { id="tab-clear" /> } + icon={} iconPosition="start" label="Executions" component={Link} @@ -121,6 +117,6 @@ export default function NavTabs() { /> - + ); } diff --git a/src/components/loading-backdrop.tsx b/src/components/loading-backdrop.tsx deleted file mode 100644 index 80a43ad..0000000 --- a/src/components/loading-backdrop.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Backdrop, Box } from '@mui/material'; - -interface LoadingBackdropProps { - open: boolean; -} - -const LoadingBackdrop: React.FC = ({ open }) => { - return ( - theme.zIndex.drawer + 1, - backgroundColor: 'rgba(0,0,0,0.3)', - }} - > - - - ); -}; - -export default LoadingBackdrop; diff --git a/src/components/loading-button.tsx b/src/components/loading-button.tsx index 559ff89..9f4477b 100644 --- a/src/components/loading-button.tsx +++ b/src/components/loading-button.tsx @@ -12,7 +12,7 @@ interface cancelLoadingButtonProps extends ButtonProps { export const CancelLoadingButton: React.FC = ({ loading, onClick, id, ...rest }) => ( } + endIcon={} size="small" variant="outlined" type="reset" @@ -20,17 +20,17 @@ export const CancelLoadingButton: React.FC = ({ loadin id={id} sx={{ '&.MuiLoadingButton-root': { - color: 'var(--calcel-size-color)', - borderColor: 'var(--calcel-color)', + color: 'var(--palette-secondary-dark)', + borderColor: 'var(--palette-secondary-dark)', + backgroundColor: 'var(--palette-calcel-background-color)', }, ':hover': { - backgroundColor: 'var(--calcel-hover-corlor)', - borderColor: 'var(--calcel-hover-corlor)', + backgroundColor: 'var(--palette-calcel-hover-corlor)', + borderColor: 'var(--palette-secondary-dark)', }, '&.MuiLoadingButton-loading': { - backgroundColor: 'var(--button-loading-color)', - color: 'var(--button-loading-size-color)', - borderColor: 'var(--button-loading-color)', + backgroundColor: 'var(--palette-button-loading-color)', + color: 'var(--palette-button-loading-size-color)', }, mr: '1rem', width: '7rem', @@ -75,18 +75,18 @@ export const SavelLoadingButton: React.FC = ({ id={id} sx={{ '&.MuiLoadingButton-root': { - backgroundColor: 'var(--save-color)', - color: 'var(--save-size-color)', - borderColor: 'var(--save-color)', + background: 'var(--palette-button-color)', + color: 'var(--palette-button-text-color)', + borderColor: 'var(--palette-button-color)', }, ':hover': { - backgroundColor: 'var(--save-hover-corlor)', - borderColor: 'var(--save-hover-corlor)', + backgroundColor: 'var(--palette-hover-button-text-color)', + borderColor: 'var(--palette-hover-button-text-color)', }, '&.MuiLoadingButton-loading': { - backgroundColor: 'var(--button-loading-color)', - color: 'var(--button-loading-size-color)', - borderColor: 'var(--button-loading-color)', + backgroundColor: 'var(--palette-button-loading-color)', + color: 'var(--palette-button-loading-size-color)', + borderColor: 'var(--palette-button-loading-color)', }, width: '7rem', }} @@ -123,18 +123,18 @@ export const DeleteLoadingButton: React.FC = ({ id={id} sx={{ '&.MuiLoadingButton-root': { - backgroundColor: 'var(--delete-button-color)', - color: 'var(--save-size-color)', - borderColor: 'var(--delete-button-color)', + backgroundColor: 'var(--palette-delete-button-color)', + color: 'var(--palette-common-white)', + borderColor: 'var(--palette-delete-button-color)', }, ':hover': { - backgroundColor: 'var(--delete-button-hover-color)', - borderColor: 'var(--delete-button-hover-color)', + backgroundColor: 'var(--palette-delete-button-hover-color)', + borderColor: 'var(--palette-delete-button-hover-color)', }, '&.MuiLoadingButton-loading': { - backgroundColor: 'var(--delete-button-color)', + backgroundColor: 'var(--palette-delete-button-color)', - borderColor: 'var(--delete-button-color)', + borderColor: 'var(--palette-delete-button-color)', }, width: '7rem', }} diff --git a/src/components/menu/index.module.css b/src/components/menu/index.module.css index de958c3..4f9d356 100644 --- a/src/components/menu/index.module.css +++ b/src/components/menu/index.module.css @@ -1,85 +1,261 @@ .container { display: flex; - flex-direction: row; + flex: 1 1 auto; + flex-direction: column; + background-color: var(--palette-sidebar-background-color); + height: 100vh; +} + +.layout { + display: flex; + flex: 1 1 auto; + flex-direction: column; + background-color: var(--palette-sidebar-background-color); +} + +.main { + display: flex; + flex: 1 1 auto; + flex-direction: column; + padding: 1.5rem 2rem; + margin: 0.8rem 0.8rem 0.8rem 1.2rem; + overflow-y: auto !important; + background-color: var(--palette-main-background-paper); + box-shadow: var(--palette-card-box-shadow); + border-radius: 0.6rem; } .navigationBarContainer { background-size: cover; background-position: center; - height: 100vh; - max-width: 16rem; + height: 100%; + position: fixed; display: flex; flex-direction: column; + z-index: 1101; + background-color: var(--palette-sidebar-background-color); +} + +.sidebarTitle { + padding: 1.5rem 0 1rem 0.3rem; + display: flex; + align-items: center; justify-content: space-between; } -.title { +.expandIcon { + width: 1rem; + height: 1rem; + color: var(--palette-sidebar-menu-color); +} + +.liMenu { display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.shrinkLiMenu { + display: flex; + flex-direction: column; + gap: 0.6rem; + align-items: center; +} + +.sidebarExpand { + width: 0.6rem; + height: 0.6rem; +} + +.list { + margin-left: 1.25rem !important; + padding-left: 0.75rem !important; + padding-top: 0.8rem !important; + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.list:before { + top: 0px; + left: 0px; + width: 2px; + content: ''; + position: absolute; + background-color: var(--paletteexpandable-sidebar-navigation); + bottom: 1rem; + bottom: calc(36px - 2px - 12px / 2); +} + +.expandable:before { + left: 0px; + content: ''; + position: absolute; + width: 12px; + height: 12px; + background-color: var(--paletteexpandable-sidebar-navigation); + mask: url(../../assets/images/menu/layout.svg) 50% 50% / 100% no-repeat; + transform: translate(calc(12px * -1), calc(12px * -0.4)); +} + +.shrinkMenu { + background-color: var(--palette-background-menu); + background-image: url(../../assets/images/menu/cyan-blur.png), url(../../assets/images/menu/red-blur.png); + background-repeat: no-repeat, no-repeat; + padding: 0.2rem; + background-size: 50%, 50%; + box-shadow: var(--custom-sidebar-shadows-dropdown); + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); + outline: 0px; + background-position: right top, left bottom; + border-radius: var(--menu-border-radius); + overflow: inherit; + min-width: 10rem; +} + +.title { + display: inline-flex; align-items: center; - margin-left: 0.4rem; - margin-top: 2rem; } .logo { width: 1.8rem; height: 1.8rem; - margin-right: 0.8rem; + color: var(--palette-description-color); } .menuIcon { - width: 1.6rem; - height: 1.6rem; -} - -.avatar { - width: 2.6rem !important; - height: 2.6rem !important; - margin-right: 0.6rem; - background: var(--button-color) !important; -} - -.menu { - padding-top: 0.4rem; - padding-bottom: 0.4rem; - width: 14rem; -} - -.expandMenu { - padding-top: 0.4rem; - padding-bottom: 0.4rem; - width: 14rem; - float: right; + width: 1rem; + height: 1rem; } .menuText { font-family: 'mabry-bold' !important; + margin-left: 0.5rem !important; +} + +.avatarMenu { + font-family: 'mabry-bold' !important; + color: var(--palette-color); +} + +.avatarButton { + position: relative; + transition: transform 0.6s easel; + margin-left: 0.3rem; + padding: 0.3rem; +} + +.avatarButton:hover { + transform: scale(1.06); +} + +.avatarWrapper { + background: conic-gradient(#73bafb, #ffd666, #73bafb); + width: 2.7rem; + height: 2.7rem; + animation: rotate 4s linear infinite; + position: absolute; + border-radius: 50%; +} + +@keyframes rotate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.avatar { + width: 2.35rem !important; + height: 2.35rem !important; + background: var(--palette-secondary-dark) !important; } .profileMenu { - background-image: url(../../../public/images/menu/cyan-blur.png), url(../../../public/images/menu/red-blur.png); - background-color: rgba(255, 255, 255, 0.9); + background-color: var(--palette-background-menu); + background-image: url(../../assets/images/menu/cyan-blur.png), url(../../assets/images/menu/red-blur.png); background-repeat: no-repeat, no-repeat; padding: 0.2rem; background-size: 50%, 50%; - box-shadow: rgba(145, 158, 171, 0.24) 0px 0px 2px 0px, rgba(145, 158, 171, 0.24) -20px 20px 40px -4px; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); outline: 0px; background-position: right top, left bottom; - border-radius: 10px; + border-radius: var(--menu-border-radius); overflow: inherit; + min-width: 10rem; +} + +.avatarContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0.8rem; +} + +.menuAvatar { + width: 3rem !important; + height: 3rem !important; + background: var(--palette-secondary-dark) !important; +} + +.menuUserName { + font-family: 'mabry-bold' !important; + padding-top: 0.6rem; + color: var(--palette-color); +} + +.menuUserEmail { + width: 100%; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding-top: 0.6rem; + color: var(--palette-text-palette-text-secondary); } .menuItemIcon { - color: var(--button-color); + color: var(--palette-button-color); } .pageContainer { - position: relative; - width: 100%; + overflow-y: auto; + height: 100vh; } .pageloading { width: 100%; position: absolute; top: 0; + z-index: 1200; +} + +.sidebarUser { + padding: 0.4rem 0.6rem 0.4rem 0.7rem; + margin-bottom: 0.8rem; + display: flex; + align-items: center; + position: relative; + background-color: var(--palette-sidebar-user-background-paper) !important; +} + +.shrinkSidebarUser { + padding-bottom: 0.8rem; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.shrinkDarkMode { + margin-bottom: 1rem; +} + +.darkMode { + margin-bottom: 1rem; + width: 100%; } diff --git a/src/components/menu/index.tsx b/src/components/menu/index.tsx index fcda29e..9bf2df1 100644 --- a/src/components/menu/index.tsx +++ b/src/components/menu/index.tsx @@ -2,7 +2,6 @@ import styles from './index.module.css'; import { Alert, Avatar, - Backdrop, Collapse, Divider, Grid, @@ -12,8 +11,9 @@ import { Menu, MenuItem, Snackbar, - Tooltip, Typography, + Link as RouterLink, + Tooltip, } from '@mui/material'; import { styled } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; @@ -21,14 +21,23 @@ import Box from '@mui/material/Box'; import List from '@mui/material/List'; import { ListItemButton, ListItemIcon } from '@mui/material'; import { createContext, useEffect, useState } from 'react'; -import { ExpandLess, ExpandMore, Logout, PersonAdd } from '@mui/icons-material'; -import ChevronRightOutlinedIcon from '@mui/icons-material/ChevronRightOutlined'; +import { Logout, PersonAdd } from '@mui/icons-material'; import { getUserRoles, getUser, signOut, getUserResponse } from '../../lib/api'; -import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import { getJwtPayload, setPageTitle } from '../../lib/utils'; import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom'; import { ROLE_ROOT, ROLE_GUEST } from '../../lib/constants'; -import LoadingBackdrop from '../loading-backdrop'; +import { ReactComponent as Cluster } from '../../assets/images/menu/cluster.svg'; +import { ReactComponent as Developer } from '../../assets/images/menu/developer.svg'; +import { ReactComponent as Job } from '../../assets/images/menu/job.svg'; +import { ReactComponent as User } from '../../assets/images/menu/user.svg'; +import { ReactComponent as Logo } from '../../assets/images/menu/logo.svg'; +import { ReactComponent as Expand } from '../../assets/images/menu/expand.svg'; +import { ReactComponent as Closure } from '../../assets/images/menu/closure.svg'; +import { ReactComponent as SidebarExpand } from '../../assets/images/menu/sidebar-expand.svg'; +import { ReactComponent as SidebarClosure } from '../../assets/images/menu/sidebar-closure.svg'; +import { HeaderLayout, ShrinkDarkMode } from '../dark-layout'; +import Card from '../card'; +import _ from 'lodash'; interface MyContextType { user: getUserResponse; @@ -56,28 +65,15 @@ export const MyContext = createContext({ }, }); -const Main = styled('div')(({ theme }) => ({ - flexGrow: 1, - overflow: 'auto', - minHeight: '100%', - height: '100vh', - paddingBottom: theme.spacing(8), - [theme.breakpoints.up('lg')]: { - paddingTop: '1.5rem', - paddingLeft: theme.spacing(4), - paddingRight: theme.spacing(4), - }, - fontFamily: 'mabry-light,sans-serif', -})); - const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ borderRadius: 5, + height: '3px', [`&.${linearProgressClasses.colorPrimary}`]: { backgroundColor: 'rgba(0, 167, 111, 0.4)', }, [`& .${linearProgressClasses.bar}`]: { borderRadius: 3, - backgroundColor: 'var(--description-color)', + backgroundColor: 'var(--palette-description-color)', }, })); @@ -104,6 +100,11 @@ export default function Layout(props: any) { const [expandDeveloper, setExpandDeveloper] = useState(false); const [expandJob, setExpandJob] = useState(false); const [progress, setProgress] = useState(0); + const [expandedMenu, setExpandedMenu] = useState(null); + const [compactLayout, setCompactLayout] = useState(() => { + const storedValue = localStorage.getItem('compactLayout'); + return storedValue === 'true'; + }); const openProfile = Boolean(anchorElement); const location = useLocation(); @@ -164,6 +165,14 @@ export default function Layout(props: any) { } }, [location.pathname, navigate, location.state?.firstLogin]); + const handleMouseLeave = () => { + setExpandedMenu(null); + }; + + useEffect(() => { + localStorage.setItem('compactLayout', compactLayout ? 'true' : 'false'); + }, [compactLayout]); + const handleUserUpdate = (newUser: getUserResponse) => { setUser(newUser); }; @@ -173,15 +182,13 @@ export default function Layout(props: any) { label: 'clusters', href: '/clusters', text: 'Cluster', - icon: , - selectedIcon: , + icon: , }, { label: 'developer', href: '/tokens', text: 'Developer', - icon: , - selectedIcon: , + icon: , expand: expandDeveloper, setExpand: setExpandDeveloper, menuProps: [ @@ -196,8 +203,7 @@ export default function Layout(props: any) { label: 'jobs', href: '/jobs', text: 'Job', - icon: , - selectedIcon: , + icon: , expand: expandJob, setExpand: setExpandJob, menuProps: [ @@ -220,8 +226,7 @@ export default function Layout(props: any) { label: 'users', href: '/users', text: 'User', - icon: , - selectedIcon: , + icon: , }); } @@ -244,6 +249,7 @@ export default function Layout(props: any) { if (reason === 'clickaway') { return; } + setFirstLogin(false); setErrorMessage(false); }; @@ -282,216 +288,472 @@ export default function Layout(props: any) { {location.pathname === '/signin' || location.pathname === '/signup' ? (
{props.children}
) : ( - + - - - - - - - - Dragonfly - - - - {menu.map((items) => - items?.menuProps ? ( - - + + {compactLayout ? ( + + + + { - items?.setExpand(!items?.expand); - }} - sx={{ - '&.Mui-selected': { - backgroundColor: 'var(--menu-background-color)', - color: 'var(--description-color)', - }, - '&.Mui-selected:hover': { - backgroundColor: 'var(--hover-menu-background-color)', - color: 'var(--description-color)', - }, - height: '2.6rem', - borderRadius: '0.2rem', + setCompactLayout((e: any) => !e); }} > - {(location.pathname.split('/')[1] || '') === items.label ? items.selectedIcon : items.icon} - - {items.text} - - {items.expand ? : } - - - - {items.menuProps?.map((item) => { - return ( - - - {item.text} - - - ); - })} - - + + - ) : ( - + {menu.map((items, index) => { + return items?.menuProps ? ( + { + setExpandedMenu(items.label); + }} + sx={{ + '&.Mui-selected': { + backgroundColor: 'var(--palette-menu-background-color)', + color: 'var(--palette-secondary-dark)', + }, + '&.Mui-selected:hover': { + backgroundColor: 'var(--palette-hover-menu-background-color)', + color: 'var(--palette-secondary-dark)', + }, + borderRadius: '0.6rem', + color: 'var(--palette-sidebar-menu-color)', + p: '0.5rem', + justifyContent: 'center', + position: 'relative !important', + width: '2.4rem', + height: '2.4rem', + }} + > + {items.icon} + {items?.menuProps && expandedMenu === items.label && ( + + {items.menuProps.map((subItem) => ( + { + setExpandedMenu(null); + }} + component={Link} + to={subItem.href} + > + + {subItem.text} + + + ))} + + )} + + ) : ( + + {items.icon} + + ); + })} + + + + + { + setAnchorElement(event.currentTarget); + }} + sx={{ p: '0.3rem' }} + > + + + + { + setAnchorElement(null); + }} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} sx={{ - '&.Mui-selected': { - backgroundColor: 'var(--menu-background-color)', - color: 'var(--description-color)', + '& .MuiMenu-paper': { + boxShadow: 'var(--custom-shadows-dropdown)', + borderRadius: 'var(--menu-border-radius)', }, - '&.Mui-selected:hover': { - backgroundColor: 'var(--hover-menu-background-color)', - color: 'var(--description-color)', + '& .MuiMenu-list': { + width: '14rem', + p: '0', }, - height: '2.6rem', - borderRadius: '0.2rem', - mb: '0.4rem', - mt: '0.4rem', }} > - {(location.pathname.split('/')[1] || '') === items.label ? items.selectedIcon : items.icon} - - {items.text} - - - ), - )} - - - - - - - - - {user?.name || '-'} - - - + + + + {user?.name} + + + + {user?.email || '-'} + + + + + { + setAnchorElement(null); + navigate('/profile'); + }} + sx={{ borderRadius: 'var(--menu-border-radius)' }} + > + + + + + Profile + + + + + + + + Logout + + + + + +
+ ) : ( + + + + + + + Dragonfly + + + { + setCompactLayout((e: any) => !e); }} > - {user?.email || '-'} - - + + + + + {menu.map((items) => + items?.menuProps ? ( + + { + items?.setExpand(!items?.expand); + }} + className={ + (location.pathname.split('/')[1] || '') === items.label ? styles.listButton : '' + } + sx={{ + '&.Mui-selected': { + backgroundColor: 'var(--palette-menu-background-color)', + color: 'var(--palette-secondary-dark)', + }, + '&.Mui-selected:hover': { + color: 'var(--palette-secondary-dark)', + }, + borderRadius: '0.6rem', + color: 'var(--palette-sidebar-menu-color)', + p: '0.5rem 0.7rem', + justifyContent: 'space-between', + }} + > + + {items.icon} + + {items.text} + + + {items.expand ? ( + + ) : ( + + )} + + + + {items.menuProps?.map((item) => { + return ( + + + {item.text} + + + ); + })} + + + + ) : ( + + {items.icon} + + {items.text} + + + ), + )} + - - { - setAnchorElement(event.currentTarget); - }} - size="small" - id="unfold-more" - aria-controls={openProfile ? 'account-menu' : undefined} - aria-haspopup="true" - aria-expanded={openProfile ? 'true' : undefined} - sx={{ position: 'relative', padding: '0' }} - > - - - - { - setAnchorElement(null); - }} - sx={{ - position: 'absolute', - top: '-6rem', - left: '-5.2rem', - '& .MuiMenu-paper': { - boxShadow: '0 0.075rem 0.2rem -0.0625rem #32325d40, 0 0.0625rem 0.0145rem -0.0625rem #0000004d;', - }, - '& .MuiMenu-list': { - p: 0, - }, - }} - > - - { - setAnchorElement(null); - - navigate('/profile'); - }} - > - - - - - Profile - - - - - - - - Logout - - - - - + + + + { + setAnchorElement(event.currentTarget); + }} + id="unfold-more" + className={styles.avatarButton} + sx={{ p: '0.3rem' }} + > + + + + + + {user?.name || '-'} + + + + {user?.email || '-'} + + + + { + setAnchorElement(null); + }} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + sx={{ + '& .MuiMenu-paper': { + boxShadow: 'var(--custom-shadows-dropdown)', + borderRadius: 'var(--menu-border-radius);', + }, + '& .MuiMenu-list': { + width: '14rem', + p: '0', + }, + }} + > + + + + + {user?.name} + + + + {user?.email || '-'} + + + + + { + setAnchorElement(null); + navigate('/profile'); + }} + sx={{ borderRadius: 'var(--menu-border-radius)' }} + > + + + + + Profile + + + + + + + + Logout + + + + + + + + )} + + +
+ +
+
- -
- -
)} diff --git a/src/components/profile/index.module.css b/src/components/profile/index.module.css index 85827b9..b05a642 100644 --- a/src/components/profile/index.module.css +++ b/src/components/profile/index.module.css @@ -1,30 +1,78 @@ .userIcon { - width: 1.6rem; - height: 1.6rem; + width: 1.4rem; + height: 1.4rem; + color: var(--palette-detail-lable-color); } .profileContainer { margin-bottom: 2rem; - padding: 2rem; + position: relative; + height: 14rem; + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; + border-radius: 16px; + z-index: 0; +} + +.profileImage { + background-color: #155939; + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + height: 100%; +} + +.profileContent { + left: 24px; + bottom: 24px; + z-index: 10; + position: absolute; + display: inline-flex; +} + +.tabs { + position: absolute; + width: 100%; + bottom: 0px; + z-index: 9; + background-color: var(--palette-profile-background-paper); + display: flex; + justify-content: flex-end; } .textField { - width: 22rem; + width: 24rem; max-width: 28rem; margin-top: 0.8rem !important; margin-bottom: 0.8rem !important; } .avatarContainer { - display: flex; - justify-content: space-between; - align-items: flex-start; + position: relative; + transition: transform 0.6s easel; + margin-left: 0.3rem; + padding: 0.3rem; + display: inline-flex; + justify-content: center; + align-items: center; + border-radius: 50%; + color: #000; +} + +.avatarWrapper { + background-color: #fff; + width: 7.25rem; + height: 7.25rem; + animation: rotate 4s linear infinite; + position: absolute; + border-radius: 50%; } .avatarContent { - width: 5rem !important; - height: 5rem !important; - background-color: var(--button-color) !important; + box-shadow: var(--palette-main-box-shadow) !important; + width: 7rem !important; + height: 7rem !important; + background: var(--palette-secondary-dark) !important; } .informationHeader { @@ -44,10 +92,6 @@ padding-bottom: 1rem; } -.editIcon { - color: var(--palette-text-disabled); -} - .informationLable { width: 30%; display: flex; @@ -59,3 +103,38 @@ color: var(--palette-detail-lable-color); font-family: 'mabry-bold' !important; } + +.textFieldIcon { + width: 1.4rem; + height: 1.4rem; + margin-right: 0.4rem; + color: var(--palette-grey-tab); +} + +.editIcon { + width: 1.4rem; + height: 1.4rem; + margin-right: 0.4rem; + color: var(--palette-scopes-icon-color); +} + +.passwordWrapper { + padding: 2rem; +} + +.passwordTextField { + width: 50rem; + max-width: 28rem; + margin-top: 0.8rem !important; + margin-bottom: 0.8rem !important; +} + +.tabIcon { + width: 1.5rem; + height: 1.5rem; +} + +.profileForm { + display: flex; + flex-direction: column; +} diff --git a/src/components/profile/index.tsx b/src/components/profile/index.tsx index c6b3cc4..9897bcb 100644 --- a/src/components/profile/index.tsx +++ b/src/components/profile/index.tsx @@ -1,6 +1,7 @@ import { Alert, Box, + Breadcrumbs, Button, Chip, Divider, @@ -9,6 +10,10 @@ import { InputAdornment, Paper, Snackbar, + styled, + Tab, + TabProps, + Tabs, TextField, Typography, } from '@mui/material'; @@ -16,7 +21,6 @@ import Avatar from '@mui/material/Avatar'; import Stack from '@mui/material/Stack'; import { useState, useEffect, useContext } from 'react'; import { updateUser, getUser, updatePassword, signOut, getUserResponse } from '../../lib/api'; -import LocalPhoneIcon from '@mui/icons-material/LocalPhone'; import EmailIcon from '@mui/icons-material/Email'; import LocationOnIcon from '@mui/icons-material/LocationOn'; import DescriptionIcon from '@mui/icons-material/Description'; @@ -31,6 +35,51 @@ import { MyContext } from '../menu/index'; import { CancelLoadingButton, SavelLoadingButton } from '../loading-button'; import Card from '../card'; import MoreTimeIcon from '@mui/icons-material/MoreTime'; +import { ReactComponent as Name } from '../../assets/images/profile/name.svg'; +import { ReactComponent as ID } from '../../assets/images/profile/id.svg'; +import { ReactComponent as Email } from '../../assets/images/profile/email.svg'; +import { ReactComponent as Location } from '../../assets/images/profile/location.svg'; +import { ReactComponent as Phone } from '../../assets/images/profile/phone.svg'; +import { ReactComponent as CreatedAt } from '../../assets/images/profile/created-at.svg'; +import { ReactComponent as Edit } from '../../assets/images/user/edit.svg'; +import { ReactComponent as UserID } from '../../assets/images/user/id.svg'; +import { ReactComponent as DetailRole } from '../../assets/images/user/detail-role.svg'; +import TabPanel from '@mui/lab/TabPanel'; +import { TabContext } from '@mui/lab'; +import _ from 'lodash'; +import { matchIsValidTel, MuiTelInput } from 'mui-tel-input'; + +type StyledTabProps = Omit & {}; + +const AntTab = styled((props: StyledTabProps) => )(({ theme }) => ({ + textTransform: 'none', + minWidth: 0, + [theme.breakpoints.up('sm')]: { + minWidth: 0, + }, + minHeight: '3rem', + fontWeight: theme.typography.fontWeightRegular, + color: 'var(--palette-grey-tab)', + padding: '0', + marginRight: '2rem', + fontSize: '0.9rem', + fontFamily: 'mabry-bold', + '&:hover': { + color: 'primary', + opacity: 1, + }, + '&.Mui-selected': { + color: 'var(--palette-description-color)', + fontFamily: 'mabry-bold', + }, +})); + +const AntTabs = styled(Tabs)({ + '& .MuiTabs-indicator': { + backgroundColor: 'var(--palette-description-color)', + borderRadius: '1rem', + }, +}); export default function Profile() { const [successMessage, setSuccessMessage] = useState(false); @@ -49,9 +98,9 @@ export default function Profile() { const [showNewPassword, setShowNewPassword] = useState(false); const [passwordLoadingButton, setPasswordLoadingButton] = useState(false); const [personalLoadingButton, setPersonalLoadingButton] = useState(false); - const [showMyProfile, setShowMyProfile] = useState(true); const [showPersonalInformation, setShowPersonalInformation] = useState(true); - const [location, setLocation] = useState(''); + const [phone, setPhone] = useState(''); + const [value, setValue] = useState('1'); const [users, setUsers] = useState({ id: 0, @@ -71,47 +120,59 @@ export default function Profile() { new_password: '', }); - const { user, handleUserUpdate } = useContext(MyContext); + const { user, handleUserUpdate, role } = useContext(MyContext); const navigate = useNavigate(); useEffect(() => { setUsers(user); - setLocation(user?.location); + setPhone(user.phone); }, [user]); const { old_password, new_password } = password; + const handleChangePhone = (newValue: string, data: any, cb?: () => void) => { + setPhone(newValue); + const { setError } = data; + setError( + !matchIsValidTel(newValue, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }), + ); + + cb && cb(); + }; + const userLable = [ { name: 'id', label: 'ID', - icon: , + icon: , }, { name: 'name', label: 'Name', - icon: , + icon: , }, { name: 'email', label: 'Email', - icon: , + icon: , }, { name: 'location', label: 'Location', - icon: , + icon: , }, { name: 'phone', label: 'Phone', - icon: , + icon: , }, { name: 'created_at', label: 'Created At', - icon: , + icon: , }, ]; @@ -137,7 +198,7 @@ export default function Profile() { InputProps: { startAdornment: ( - : + : ), }, @@ -157,31 +218,17 @@ export default function Profile() { label: 'Phone', name: 'phone', autoComplete: 'family-name', - value: users.phone, + value: phone, placeholder: 'Enter your Phone', helperText: phoneError ? 'Invalid phone number.' : '', error: phoneError, onChange: (e: any) => { - setUsers({ ...users, phone: e.target.value }); - changeValidate(e.target.value, profileForm[1]); - }, - - InputProps: { - startAdornment: ( - - : - - ), + handleChangePhone(e, profileForm[1]); }, }, syncError: false, setError: setPhoneError, - - validate: (value: string) => { - const reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$|^$/; - return reg.test(value); - }, }, { formProps: { @@ -202,7 +249,7 @@ export default function Profile() { InputProps: { startAdornment: ( - : + : ), }, @@ -235,7 +282,7 @@ export default function Profile() { InputProps: { startAdornment: ( - : + : ), }, @@ -271,7 +318,7 @@ export default function Profile() { InputProps: { startAdornment: ( - + ), @@ -284,9 +331,9 @@ export default function Profile() { edge="end" > {showOldPassword ? ( - + ) : ( - + )} ), @@ -322,7 +369,7 @@ export default function Profile() { InputProps: { startAdornment: ( - + ), endAdornment: ( @@ -334,9 +381,9 @@ export default function Profile() { edge="end" > {showNewPassword ? ( - + ) : ( - + )} ), @@ -367,7 +414,7 @@ export default function Profile() { InputProps: { startAdornment: ( - + ), @@ -380,9 +427,9 @@ export default function Profile() { edge="end" > {showConfirmPassword ? ( - + ) : ( - + )} ), @@ -422,8 +469,20 @@ export default function Profile() { profileForm.forEach((item) => { const value = data.get(item.formProps.name); - item.setError(!item.validate(value as string)); - item.syncError = !item.validate(value as string); + if (item.validate) { + item.setError(!item.validate(value as string)); + item.syncError = !item.validate(value as string); + } else { + item.setError( + !matchIsValidTel(phone, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }), + ); + + item.syncError = !matchIsValidTel(phone, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }); + } }); const canSubmit = Boolean(!profileForm.filter((item) => item.syncError).length); @@ -432,7 +491,7 @@ export default function Profile() { bio: users.bio, email: users.email, location: users.location, - phone: users.phone, + phone: phone, }; if (canSubmit) { @@ -461,7 +520,6 @@ export default function Profile() { const cancelHandlePersonalInformation = async () => { setUsers(user); - setLocation(user?.location); setShowPersonalInformation(true); }; @@ -507,7 +565,6 @@ export default function Profile() { }; const cancelChangePassword = () => { - setShowMyProfile(true); setPassword({ ...password, old_password: '', new_password: '' }); }; @@ -520,6 +577,10 @@ export default function Profile() { setErrorMessage(false); }; + const handleChange = (event: React.SyntheticEvent, newValue: string) => { + setValue(newValue); + }; + return ( - + My Profile - - {showMyProfile ? ( - - - - - - - + + + + + + + + + + {users?.name || '-'} - - - - {location || '-'} - - + + {_.upperFirst(user?.state) || ''} + + {/* + {_.upperFirst(user?.state) || ''} + */} - - - - ) : ( - - - Change Password - - {passwordForm.map((item) => ( - - - - ))} - - + + } + key="1" + iconPosition="start" + label="Profile" + sx={{ textTransform: 'none' }} + id="tab-profile" + value="1" onClick={cancelChangePassword} /> - } - id="change-password" - text="Save" + } + key="2" + iconPosition="start" + label="Security" + id="tab-password" + value="2" /> - - - )} - - - {showPersonalInformation ? ( - - - - About me - - - - - - - {user?.bio || '-'} - - - Personal Details - - - - {userLable.map((item) => { - return ( - - - - {item.icon} - - {item.label} - - - - {item.name === 'created_at' ? ( - users?.[item.name] ? ( - } - label={getDatetime(users?.[item.name]) || '-'} - variant="outlined" - size="small" - /> - ) : ( - - - - - ) - ) : ( - - {users?.[item?.name as keyof typeof users] || '-'} - - )} - - - - ); - })} - + - ) : ( - - - Update Personal Information - - - {profileForm.map((item) => ( - - + + + {showPersonalInformation ? ( + + + + About me + + + + + + + {user?.bio || '-'} + + + + {userLable.map((item) => { + return ( + + + + {item.icon} + + {item.label} + + + + {item.name === 'created_at' ? ( + users?.[item.name] ? ( + } + label={getDatetime(users?.[item.name]) || '-'} + variant="outlined" + size="small" + /> + ) : ( + + - + + ) + ) : item.label === 'Name' || item?.label === 'ID' ? ( + + {users?.[item?.name as keyof typeof users] || '-'} + + ) : ( + + {users?.[item?.name as keyof typeof users] || '-'} + + )} + + + + ); + })} + + + ) : ( + + + Update Personal Information + + + {profileForm.map((item) => + item.formProps.id === 'phone' ? ( + + ) : ( + + ), + )} + + + } + id="save" + text="Save" + /> + + + + )} + + + + + + + Change Password + + {passwordForm.map((item) => ( + + ))} } - id="save" + id="change-password" text="Save" /> - - - )} - + + + + ); } diff --git a/src/components/rotation.tsx b/src/components/rotation.tsx index 40d61d0..0a43e51 100644 --- a/src/components/rotation.tsx +++ b/src/components/rotation.tsx @@ -1,90 +1,80 @@ -import { Box, Grid, MobileStepper, ThemeProvider, createTheme } from '@mui/material'; +import { Grid, MobileStepper, useTheme } from '@mui/material'; import { useState } from 'react'; import { autoPlay } from 'react-swipeable-views-utils'; import SwipeableViews from 'react-swipeable-views'; +import { ReactComponent as Dragonfly } from '../assets/images/login/dragonfly.svg'; +import { ReactComponent as DarkDragonfly } from '../assets/images/login/dark-dragonfly.svg'; +import { ReactComponent as Features } from '../assets/images/login/features.svg'; +import { ReactComponent as DarkFeatures } from '../assets/images/login/dark-features.svg'; +import { ReactComponent as Milestones } from '../assets/images/login/milestones.svg'; +import { ReactComponent as DarkMilestones } from '../assets/images/login/dark-milestones.svg'; const AutoPlaySwipeableViews = autoPlay(SwipeableViews); -const rotationChart = [ - { - label: 'Dragonfly', - imageURL: '/images/login/dragonfly.svg', - }, - { - label: 'Features', - imageURL: '/images/login/features.svg', - }, - { - label: 'Milestones', - imageURL: '/images/login/milestones.svg', - }, -]; - -const theme = createTheme({ - components: { - MuiMobileStepper: { - styleOverrides: { - dotActive: { - backgroundColor: 'var(--description-color)', - }, - }, - }, - }, -}); - export default function Rotation() { - const [imageList] = useState(rotationChart); const [imageIndex, setImageIndex] = useState(0); + const theme = useTheme(); + + const rotationChart = [ + { + label: 'Dragonfly', + imageURL: , + }, + { + label: 'Milestones', + imageURL: , + }, + ]; + + const darkRotationChart = [ + { + label: 'Dragonfly', + imageURL: , + }, + { + label: 'Milestones', + imageURL: , + }, + ]; const handleStepChange = (step: number) => { setImageIndex(step); }; return ( - - - - {imageList.map((step, index) => { - return ( - -
- {Math.abs(imageIndex - index) <= 2 ? ( - - ) : null} -
-
- ); - })} -
- { - - } -
-
+ + + {theme.palette.mode === 'light' + ? rotationChart.map((step, index) => { + return ( + +
{Math.abs(imageIndex - index) <= 2 ? step.imageURL : null}
+
+ ); + }) + : darkRotationChart.map((step, index) => { + return ( + +
{Math.abs(imageIndex - index) <= 2 ? step.imageURL : null}
+
+ ); + })} +
+ +
); } diff --git a/src/components/signin/index.module.css b/src/components/signin/index.module.css index bb8f9f1..15bbd87 100644 --- a/src/components/signin/index.module.css +++ b/src/components/signin/index.module.css @@ -1,6 +1,12 @@ .page { height: 100vh; overflow: hidden; + position: relative; +} + +.pageLoading { + width: 4rem; + height: 4rem; } .container { @@ -8,17 +14,35 @@ align-items: center; justify-content: center; margin-bottom: 7rem; + position: relative; +} + +.header { + position: absolute; + top: 1rem; + right: 1rem; + width: 11rem; +} + +.githubContainer { + z-index: 9; + right: 1.6rem; + bottom: 1.6rem; + width: 2rem; + height: 2rem; + position: absolute; } .logo { width: 3rem; height: 3rem; margin-bottom: 1rem; + color: var(--palette-description-color); } .separationLineContainer { - margin-top: 1.2rem; - margin-bottom: 0.8rem; + margin-top: 1rem; + margin-bottom: 0.6rem; height: 2rem; display: flex; justify-content: space-evenly; @@ -35,3 +59,9 @@ margin: 0, 15; vertical-align: middle; } + +.github { + width: 2rem; + height: 2rem; + color: var(--palette-secondary-dark); +} diff --git a/src/components/signin/index.tsx b/src/components/signin/index.tsx index 39aa4fa..37ee3ac 100644 --- a/src/components/signin/index.tsx +++ b/src/components/signin/index.tsx @@ -1,10 +1,8 @@ -import CssBaseline from '@mui/material/CssBaseline'; import TextField from '@mui/material/TextField'; import Grid from '@mui/material/Grid'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; import IconButton from '@mui/material/IconButton'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import Visibility from '@mui/icons-material/Visibility'; @@ -15,6 +13,9 @@ import styles from './index.module.css'; import { Alert, Backdrop, Snackbar, Link as RouterLink, Button } from '@mui/material'; import { Link, useNavigate, useLocation } from 'react-router-dom'; import { setPageTitle } from '../../lib/utils'; +import { ReactComponent as Login } from '../../assets/images/login/login.svg'; +import { ReactComponent as PageLoading } from '../../assets/images/login/page-loading.svg'; +import { HeaderLayout } from '../dark-layout'; export default function SignIn() { const [errorMessage, setErrorMessage] = useState(false); @@ -93,15 +94,6 @@ export default function SignIn() { setPageTitle(location.pathname); }, [location]); - const theme = createTheme({ - palette: { - secondary: { - contrastText: '#fff', - main: '#2E8F79', - }, - }, - }); - const changeValidate = (value: string, data: any) => { const { setError, validate } = data; setError(!validate(value)); @@ -175,87 +167,96 @@ export default function SignIn() { backgroundColor: 'rgba(0,0,0,0.3)', }} > - + - - - - - - - Welcome back! - - - Through which you can easily configure clusters and view cluster information. - - - - - {formList.map((item) => ( - - ))} - - - - - or + + + + + + Welcome back! + + + Through which you can easily configure clusters and view cluster information. + + + + + {formList.map((item) => ( + + ))} + + + + + or + + + + + + + New to Dragnfly? - - - - - - New to Dragnfly? - - { - setPageLoding(true); - }} - sx={{ color: '#2E8F79', ml: '0.4rem' }} - > - Create an account. - - - + { + setPageLoding(true); + }} + sx={{ color: '#2E8F79', ml: '0.4rem' }} + > + Create an account. + +
- - +
+ ); diff --git a/src/components/signup/index.module.css b/src/components/signup/index.module.css index bb8f9f1..532fb7e 100644 --- a/src/components/signup/index.module.css +++ b/src/components/signup/index.module.css @@ -1,6 +1,12 @@ .page { height: 100vh; overflow: hidden; + position: relative; +} + +.pageLoading { + width: 4rem; + height: 4rem; } .container { @@ -8,12 +14,36 @@ align-items: center; justify-content: center; margin-bottom: 7rem; + position: relative; +} + +.header { + position: absolute; + top: 1rem; + right: 1rem; + width: 11rem; +} + +.githubContainer { + z-index: 9; + right: 1.6rem; + bottom: 1.6rem; + width: 2rem; + height: 2rem; + position: absolute; +} + +.headerContent { + width: 2rem; + height: 2rem; + color: var(--palette-secondary-dark); } .logo { width: 3rem; height: 3rem; margin-bottom: 1rem; + color: var(--palette-description-color); } .separationLineContainer { diff --git a/src/components/signup/index.tsx b/src/components/signup/index.tsx index 870ba88..e3d3a6d 100644 --- a/src/components/signup/index.tsx +++ b/src/components/signup/index.tsx @@ -1,11 +1,8 @@ -import * as React from 'react'; import Button from '@mui/material/Button'; -import CssBaseline from '@mui/material/CssBaseline'; import TextField from '@mui/material/TextField'; import { useEffect, useState } from 'react'; import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; import IconButton from '@mui/material/IconButton'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import Visibility from '@mui/icons-material/Visibility'; @@ -15,6 +12,9 @@ import { signUp } from '../../lib/api'; import styles from './index.module.css'; import { Link, useNavigate, useLocation } from 'react-router-dom'; import { setPageTitle } from '../../lib/utils'; +import { ReactComponent as Login } from '../../assets/images/login/login.svg'; +import { ReactComponent as PageLoading } from '../../assets/images/login/page-loading.svg'; +import { HeaderLayout } from '../dark-layout'; export default function SignUp() { const [errorMessage, setErrorMessage] = useState(false); @@ -160,15 +160,6 @@ export default function SignUp() { setPageTitle(location.pathname); }, [location]); - const theme = createTheme({ - palette: { - secondary: { - contrastText: '#fff', - main: '#2E8F79', - }, - }, - }); - const handlePassword = (type: 'password' | 'confirmPassword') => { if (type === 'password') { setShowPassword((show) => !show); @@ -244,78 +235,87 @@ export default function SignUp() { backgroundColor: 'rgba(0,0,0,0.3)', }} > - + - - - - - - - Registered Account - - - - - - {formList.map((item) => ( - - - - ))} - - - - - - or - - - - - - Already have an account? - { - setPageLoding(true); - }} - sx={{ color: '#2E8F79', ml: '0.4rem' }} - > - Sign in - + + + + + + Registered Account + + + + + + {formList.map((item) => ( + + - + ))} + + + + + + or + + + + + + Already have an account? + { + setPageLoding(true); + }} + sx={{ color: '#2E8F79', ml: '0.4rem' }} + > + Sign in + + - - + + ); diff --git a/src/components/users/index.module.css b/src/components/users/index.module.css index 636a269..45d4e44 100644 --- a/src/components/users/index.module.css +++ b/src/components/users/index.module.css @@ -1,7 +1,6 @@ .changeRoleContainer { display: flex; flex-direction: column; - align-items: center; } .roleIcon { @@ -19,6 +18,7 @@ display: flex; align-items: center; justify-content: space-between; + padding: 0.2rem 0rem 0.7rem 1rem; } .detailContentWrap { @@ -41,10 +41,11 @@ .detailIcon { width: 1.4rem; height: 1.4rem; + color: var(--palette-table-title-text-color); } .menuItemIcon { - color: var(--button-color); + color: var(--palette-button-color); width: 2rem; } @@ -53,22 +54,21 @@ } .menu { - background-image: url(../../../public/images/menu/cyan-blur.png), url(../../../public/images/menu/red-blur.png); - background-color: rgba(255, 255, 255, 0.9); + background-image: url(../../assets/images/menu/cyan-blur.png), url(../../assets/images/menu/red-blur.png); + background-color: var(--palette-background-menu); background-repeat: no-repeat, no-repeat; padding: 0.2rem; background-size: 50%, 50%; - box-shadow: rgba(145, 158, 171, 0.24) 0px 0px 2px 0px, rgba(145, 158, 171, 0.24) -20px 20px 40px -4px; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); outline: 0px; background-position: right top, left bottom; - border-radius: 10px; + border-radius: var(--menu-border-radius); overflow: inherit; } .detailTitle { padding-left: 0.8rem; - color: var(--text---palette-text-secondary); + color: var(--palette-table-title-text-color); } .detailContent { @@ -76,14 +76,72 @@ } .tableHeader { - color: var(--table-title-text-color); + color: var(--palette-table-title-text-color); font-family: 'mabry-bold' !important; } .tableRow { - border-bottom: dashed var(--palette-divider); + border-bottom: dashed var(--palette-palette-divider); } .tableRow:last-child { border-bottom: none; } + +.tableHeaderText { + border-color: var(--palette-palette-divider) !important; +} + +.searchWrapper { + display: flex; + margin-bottom: 2rem; +} + +.roleContainer { + display: flex; + align-items: center; +} + +.editRoleHeader { + display: flex; + justify-content: space-between; + padding: 0.8rem 1rem; +} + +.editRoleHeaderIcon { + width: 2rem; + height: 2rem; + color: var(--palette-button-color); +} + +.roleEdit { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.6rem; + background-color: var(--palette-background-paper); + box-shadow: var(--palette-card-box-shadow); + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1); + border-radius: 0.6rem; + border: solid; + color: var(--palette-color); + overflow: hidden; + border-width: 1px; +} + +.roleEdit:nth-last-child(2n) { + margin-bottom: 1rem; +} + +.roleIcon { + width: 1.5rem; + height: 1.5rem; + color: var(--palette-secondary-dark); +} + +.roleText { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--palette-text-palette-text-secondary); +} diff --git a/src/components/users/index.tsx b/src/components/users/index.tsx index 92bb61c..14252e3 100644 --- a/src/components/users/index.tsx +++ b/src/components/users/index.tsx @@ -4,19 +4,15 @@ import { Box, Alert, Avatar, - Breadcrumbs, Chip, Dialog, DialogContent, Drawer, FormControl, - FormControlLabel, FormLabel, IconButton, ListItemAvatar, - ListSubheader, Radio, - RadioGroup, Skeleton, Snackbar, Tooltip, @@ -29,64 +25,42 @@ import { ListItem, List, Pagination, - ThemeProvider, - createTheme, MenuItem, Menu, ListItemIcon, + Button, + Stack, + Autocomplete, + TextField, } from '@mui/material'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import CloseIcon from '@mui/icons-material/Close'; +import SearchIcon from '@mui/icons-material/Search'; import { getUserRoles, getUsers, getUser, deleteUserRole, putUserRole, getUsersResponse } from '../../lib/api'; -import { makeStyles } from '@mui/styles'; -import { getDatetime, getPaginatedList, useQuery } from '../../lib/utils'; +import { fuzzySearch, getDatetime, getPaginatedList, useQuery } from '../../lib/utils'; +import AddIcon from '@mui/icons-material/Add'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import styles from './index.module.css'; -import _ from 'lodash'; +import _, { debounce } from 'lodash'; import { ROLE_ROOT, ROLE_GUEST, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../../lib/constants'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { CancelLoadingButton, SavelLoadingButton } from '../loading-button'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import ModeEditIcon from '@mui/icons-material/ModeEdit'; import PersonIcon from '@mui/icons-material/Person'; import Card from '../card'; - -const useStyles = makeStyles((theme: any) => ({ - tableRow: { - '&$selected': { - backgroundColor: 'var(--button-color)', - }, - }, - hover: { - backgroundColor: theme.palette.action.hover, - }, - selected: {}, - tableCell: { - color: theme.palette.text.primary, - }, - - selectedTableCell: { - color: '#fff', - }, - selectedTableAvatar: { - color: 'var(--button-color)!important', - backgroundColor: '#fff!important', - }, - selectedButton: { - color: 'var(--button-color)!important', - backgroundColor: '#fff!important', - }, -})); - -const theme = createTheme({ - palette: { - primary: { - main: '#1C293A', - }, - }, - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); +import { ReactComponent as Role } from '../../assets/images/user/role.svg'; +import { ReactComponent as UserID } from '../../assets/images/user/id.svg'; +import { ReactComponent as Name } from '../../assets/images/user/name.svg'; +import { ReactComponent as DetailRole } from '../../assets/images/user/detail-role.svg'; +import { ReactComponent as Email } from '../../assets/images/user/email.svg'; +import { ReactComponent as Phone } from '../../assets/images/user/phone.svg'; +import { ReactComponent as Location } from '../../assets/images/user/location.svg'; +import { ReactComponent as CreatedAt } from '../../assets/images/user/created-at.svg'; +import { ReactComponent as UpdatedAt } from '../../assets/images/user/updated-at.svg'; +import { ReactComponent as Root } from '../../assets/images/user/root.svg'; +import { ReactComponent as Guest } from '../../assets/images/user/guest.svg'; +import SearchCircularProgress from '../circular-progress'; export default function Users() { const [isLoading, setIsLoading] = useState(true); @@ -103,6 +77,7 @@ export default function Users() { const [userTotalPages, setUserTotalPages] = useState(1); const [users, setUsers] = useState([]); const [allUsers, setAllUsers] = useState([]); + const [userCount, setUserCount] = useState([]); const [user, setUser] = useState({ id: 0, email: '', @@ -116,11 +91,14 @@ export default function Users() { const [detailRole, setDetailRole] = useState(''); const [updateRole, setUpdatelRole] = useState(''); const [anchorElement, setAnchorElement] = useState(null); + const [searchUser, setSearchUser] = useState(''); + const [searchIconISLodaing, setSearchIconISLodaing] = useState(false); - const classes = useStyles(); const navigate = useNavigate(); const query = useQuery(); const page = query.get('page') ? parseInt(query.get('page') as string, 10) || 1 : 1; + const search = query.get('search') ? (query.get('search') as string) : ''; + const location = useLocation(); useEffect(() => { (async function () { @@ -131,6 +109,7 @@ export default function Users() { const user = await getUsers({ page: 1, per_page: MAX_PAGE_SIZE }); setUsers(user); + setUserCount(user); setIsLoading(false); } catch (error) { if (error instanceof Error) { @@ -143,11 +122,16 @@ export default function Users() { }, [userPage, page]); useEffect(() => { - const totalPage = Math.ceil(users.length / DEFAULT_PAGE_SIZE); - const currentPageData = getPaginatedList(users, userPage, DEFAULT_PAGE_SIZE); + if (Array.isArray(users) && users.length > 0) { + const totalPage = Math.ceil(users.length / DEFAULT_PAGE_SIZE); + const currentPageData = getPaginatedList(users, userPage, DEFAULT_PAGE_SIZE); - setUserTotalPages(totalPage || 1); - setAllUsers(currentPageData); + setUserTotalPages(totalPage || 1); + setAllUsers(currentPageData); + } else if (users === null || users) { + setUserTotalPages(1); + setAllUsers([]); + } }, [users, userPage]); const handleChange = async (row: any) => { @@ -248,8 +232,43 @@ export default function Users() { setErrorMessage(false); }; + const debounced = useMemo( + () => + debounce(async (currentSearch) => { + if (currentSearch && userCount.length > 0) { + const user = fuzzySearch(currentSearch, userCount); + + setUsers(user); + setSearchIconISLodaing(false); + } else if (currentSearch === '' && userCount.length > 0) { + setUsers(userCount); + setSearchIconISLodaing(false); + } + }, 500), + [userCount], + ); + + const handleInputChange = useCallback( + (newSearch: any) => { + setSearchUser(newSearch); + setSearchIconISLodaing(true); + debounced(newSearch); + + const queryString = newSearch ? `?search=${newSearch}` : ''; + navigate(`${location.pathname}${queryString}`); + }, + [debounced, location.pathname, navigate], + ); + + useEffect(() => { + if (search) { + setSearchUser(search); + debounced(search); + } + }, [search, debounced]); + return ( - + - - } - sx={{ mb: '2rem' }} - > - + + User - + + + + + { + handleInputChange(newInputValue); + }} + options={(Array.isArray(allUsers) && allUsers.map((option) => option?.name)) || ['']} + renderInput={(params) => ( + + + + ) : ( + + + + ), + }} + /> + )} + /> + +
- + - - + + Name - + Email - + Location - + State - + Operation @@ -367,8 +447,7 @@ export default function Users() { alt="Remy Sharp" sx={{ '&.MuiAvatar-root': { - background: 'var(--button-color)', - color: '#fff', + background: 'var(--palette-secondary-dark)', }, }} src={item?.avatar} @@ -376,7 +455,7 @@ export default function Users() { - + {item?.name || '-'} @@ -389,9 +468,15 @@ export default function Users() { variant="outlined" sx={{ borderRadius: '0.2rem', - backgroundColor: item?.state === 'enable' ? 'var( --description-color)' : 'var(--button-color)', + backgroundColor: + item?.state === 'enable' + ? 'var( --palette-description-color)' + : 'var(--palette-dark-300Channel)', color: item?.state === 'enable' ? '#FFFFFF' : '#FFFFFF', - borderColor: item?.state === 'enable' ? 'var( --description-color)' : 'var(--button-color)', + borderColor: + item?.state === 'enable' + ? 'var( --palette-description-color)' + : 'var(--palette-dark-300Channel)', fontWeight: 'bold', }} /> @@ -404,21 +489,26 @@ export default function Users() { }} id={`action-${item?.name}`} aria-haspopup="true" - sx={{ position: 'relative' }} > - + { handleChange(selectedRow); @@ -444,6 +535,7 @@ export default function Users() { <> ) : ( { openSwitchUser(selectedRow); @@ -487,54 +579,115 @@ export default function Users() { + + + + + Role + + + theme.palette.grey[500], + p: '0.2rem', + }} + > + + + + - - - { - setUpdatelRole(e.target.value); + - + + + + Root + + + + The root user is the super user of the system and has the highest authority. + + + + + - } - label="root" + id="role-root" + name="radio-buttons" + checked={updateRole === 'root'} + sx={{ + '&.Mui-checked': { + color: 'var(--palette-description-color)', + }, + }} + onChange={(e: any) => { + setUpdatelRole(e.target.value); + }} /> - + + + + + + Guest + + + + The guest user has limited permissions and is intended for general user access. + + + + + - } - label="guest" + id="role-guest" + name="radio-buttons" + checked={updateRole === 'guest'} + sx={{ + '&.Mui-checked': { + color: 'var(--palette-description-color)', + }, + }} + onChange={(e: any) => { + setUpdatelRole(e.target.value); + }} /> - + @@ -552,11 +705,12 @@ export default function Users() { - + User Detail { setUserDetail(false); setSwitchUser(false); @@ -564,19 +718,19 @@ export default function Users() { setDetailIsLoading(true); }} > - + - + - + ID @@ -588,13 +742,13 @@ export default function Users() { - + Name @@ -610,13 +764,13 @@ export default function Users() { - + Role @@ -631,10 +785,10 @@ export default function Users() { variant="outlined" sx={{ borderRadius: '0.2rem', - background: 'var(--button-color)', + background: 'var(--palette-button-color)', color: '#FFFFFF', mr: '0.4rem', - borderColor: 'var(--button-color)', + borderColor: 'var(--palette-button-color)', fontWeight: 'bold', }} /> @@ -648,13 +802,13 @@ export default function Users() { - + Email @@ -672,13 +826,13 @@ export default function Users() { - + Phone @@ -694,13 +848,13 @@ export default function Users() { - + Location @@ -720,13 +874,13 @@ export default function Users() { - + Created At @@ -750,13 +904,13 @@ export default function Users() { - + Updated At @@ -780,6 +934,6 @@ export default function Users() { - + ); } diff --git a/src/components/users/new.module.css b/src/components/users/new.module.css new file mode 100644 index 0000000..4b7fbc6 --- /dev/null +++ b/src/components/users/new.module.css @@ -0,0 +1,57 @@ +.container { + display: flex; + gap: calc(3 * 0.5rem) calc(3 * 0.5rem); + align-items: flex-start; +} + +.textField:nth-child(3) { + grid-column: span 2; +} + +.formData { + width: 65%; + padding: 2rem 1.5rem; +} + +.textFieldContainer { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: calc(1.5rem); +} + +.uploadWrapper { + width: 35%; + display: flex; + justify-content: center; + align-items: center; + padding: 4rem 2rem; + flex-direction: column; +} + +.uploadText { + text-align: center; + padding-top: 2rem; +} + +.submitWrapper { + padding-top: 2rem; + display: flex; + justify-content: flex-end; +} + +.form { + display: flex; + flex-wrap: wrap; + width: 70%; +} + +.input { + width: 20rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; + margin-right: 1rem !important; +} + +.input:nth-child(3) { + width: 41rem !important; +} diff --git a/src/components/users/new.tsx b/src/components/users/new.tsx new file mode 100644 index 0000000..9586742 --- /dev/null +++ b/src/components/users/new.tsx @@ -0,0 +1,406 @@ +import TextField from '@mui/material/TextField'; +import { useState } from 'react'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import Visibility from '@mui/icons-material/Visibility'; +import { + Alert, + Backdrop, + Box, + Grid, + InputAdornment, + Snackbar, + Link as RouterLink, + Divider, + styled, + Breadcrumbs, +} from '@mui/material'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { matchIsValidTel, MuiTelInput } from 'mui-tel-input'; +import styles from './new.module.css'; +import { CancelLoadingButton, SavelLoadingButton } from '../loading-button'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import { signUp } from '../../lib/api'; + +export default function NewUser() { + const [errorMessage, setErrorMessage] = useState(false); + const [errorMessageText, setErrorMessageText] = useState(''); + const [accountError, setAccountError] = useState(false); + const [emailError, setEmailError] = useState(false); + const [phoneError, setPhoneError] = useState(false); + const [locationError, setLocationError] = useState(false); + const [bioError, setBioError] = useState(false); + const [passwordError, setPasswordError] = useState(false); + const [confirmPassworError, setConfirmPassworError] = useState(false); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [loadingButton, setLoadingButton] = useState(false); + const [successMessage, setSuccessMessage] = useState(false); + const [phone, setPhone] = useState(''); + + const navigate = useNavigate(); + + const changeValidate = (value: string, data: any, cb?: () => void) => { + const { setError, validate } = data; + setError(!validate(value)); + cb && cb(); + }; + + const handlePassword = (type: 'password' | 'confirmPassword') => { + if (type === 'password') { + setShowPassword((show) => !show); + } else { + setShowConfirmPassword((show) => !show); + } + }; + + const handleChange = (newValue: string, data: any, cb?: () => void) => { + setPhone(newValue); + const { setError } = data; + setError( + !matchIsValidTel(newValue, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }), + ); + + cb && cb(); + }; + + const formList = [ + { + formProps: { + name: 'username', + label: 'Account', + autoComplete: 'family-name', + id: 'account', + required: true, + placeholder: 'Enter your account', + error: accountError, + helperText: accountError ? 'Fill in the characters, the length is 3-10.' : '', + + onChange: (e: any) => { + changeValidate(e.target.value, formList[0]); + }, + }, + syncError: false, + setError: setAccountError, + + validate: (value: string) => { + const reg = /^(?=.*[A-Za-z0-9@$!%*?&._-])[A-Za-z0-9@$!%*?&._-]{3,10}$/; + return reg.test(value); + }, + }, + { + formProps: { + name: 'email', + label: 'Email', + autoComplete: 'email', + id: 'email', + required: true, + placeholder: 'Enter your email', + error: emailError, + helperText: emailError ? 'Email is invalid or already taken.' : '', + + onChange: (e: any) => { + changeValidate(e.target.value, formList[1]); + }, + }, + syncError: false, + setError: setEmailError, + + validate: (value: string) => { + const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; + return reg.test(value); + }, + }, + { + formProps: { + name: 'bio', + label: 'Description', + autoComplete: 'bio', + id: 'bio', + placeholder: 'Enter your description', + error: bioError, + helperText: bioError ? 'Fill in the characters, the length is 0-1000.' : '', + + onChange: (e: any) => { + changeValidate(e.target.value, formList[2]); + }, + }, + syncError: false, + setError: setBioError, + + validate: (value: string) => { + const reg = /^.{0,1000}$/; + return reg.test(value); + }, + }, + { + formProps: { + name: 'phone', + label: 'Phone', + autoComplete: 'phone', + id: 'phone', + placeholder: 'Enter your phone', + error: phoneError, + helperText: phoneError ? 'Enter a valid phone number.' : '', + value: phone, + onChange: (newValue: any) => { + handleChange(newValue, formList[3]); + }, + }, + syncError: false, + setError: setPhoneError, + }, + { + formProps: { + name: 'location', + label: 'Location', + autoComplete: 'location', + id: 'location', + placeholder: 'Enter your location', + error: locationError, + helperText: locationError ? 'Fill in the characters, the length is 0-100.' : '', + + onChange: (e: any) => { + changeValidate(e.target.value, formList[4]); + }, + }, + syncError: false, + setError: setLocationError, + + validate: (value: string) => { + const reg = /^[A-Za-z0-9]{0,100}$/; + return reg.test(value); + }, + }, + { + formProps: { + name: 'password', + label: 'Password', + id: 'password', + required: true, + placeholder: 'Enter your password', + autoComplete: 'new-password', + helperText: passwordError ? `At least 8-16 characters, with at least 1 lowercase letter and 1 number.` : '', + type: showPassword ? 'text' : 'password', + error: passwordError, + InputProps: { + endAdornment: ( + { + handlePassword('password'); + }} + edge="end" + > + {showPassword ? : } + + ), + }, + + onChange: (e: any) => { + changeValidate(e.target.value, formList[5], () => { + setPassword(e.target.value); + }); + }, + }, + syncError: false, + setError: setPasswordError, + + validate: (value: string) => { + const reg = /^(?=.*[a-z])(?=.*\d)[^]{8,16}$/; + return reg.test(value); + }, + }, + { + formProps: { + name: 'confirmPassword', + label: 'ConfirmPassword', + id: 'confirmPassword', + required: true, + type: showConfirmPassword ? 'text' : 'password', + autoComplete: 'new-password', + placeholder: 'Repeat your new password', + error: confirmPassworError, + helperText: confirmPassworError ? 'Please enter the same password.' : '', + + InputProps: { + endAdornment: ( + + { + handlePassword('confirmPassword'); + }} + edge="end" + > + {showConfirmPassword ? : } + + + ), + }, + + onChange: (e: any) => { + changeValidate(e.target.value, formList[6]); + }, + }, + + syncError: false, + setError: setConfirmPassworError, + + validate: (value: string) => { + const reg = /^(?=.*[a-z])(?=.*\d)[^]{8,16}$/; + return value === password && reg.test(value); + }, + }, + ]; + + const handleClose = (_event: any, reason?: string) => { + if (reason === 'clickaway') { + return; + } + + setSuccessMessage(false); + setErrorMessage(false); + }; + + const handleSubmit = async (event: any) => { + setLoadingButton(true); + event.preventDefault(); + const account = event.currentTarget.elements.account.value; + const password = event.currentTarget.elements.password.value; + const email = event.currentTarget.elements.email.value; + const phone = event.currentTarget.elements.phone.value; + const location = event.currentTarget.elements.location.value; + const bio = event.currentTarget.elements.bio.value; + + const data = new FormData(event.currentTarget); + + formList.forEach((item) => { + const value = data.get(item.formProps.name); + + if (item.validate) { + item.setError(!item.validate(value as string)); + item.syncError = !item.validate(value as string); + } else { + item.setError( + !matchIsValidTel(phone, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }), + ); + + item.syncError = !matchIsValidTel(phone, { + onlyCountries: ['FR', 'BE', 'CN', 'US', 'CA', 'JP', 'KR', 'TW', 'HK', 'MO', 'SG', 'MY', 'TH', 'VN'], + }); + } + }); + + const canSubmit = Boolean(!formList.filter((item) => item.syncError).length); + + if (canSubmit) { + try { + await signUp({ + name: account, + password: password, + email: email, + bio: bio, + phone: phone, + location: location, + }); + setLoadingButton(false); + navigate(`/users`); + } catch (error) { + if (error instanceof Error) { + setLoadingButton(false); + setErrorMessage(true); + setErrorMessageText(error.message); + } + } + } else { + setLoadingButton(false); + } + }; + + return ( + + + + Submission successful! + + + + + {errorMessageText} + + + Create a new user + + } + aria-label="breadcrumb" + > + + User + + + New user + + + + + + {formList.map((item) => + item.formProps.id === 'phone' ? ( + + ) : ( + + ), + )} + + + + { + navigate(`/users`); + }} + /> + } id="save" text="Save" /> + + + + ); +} diff --git a/src/index.css b/src/index.css index 70a79c4..4bcc102 100644 --- a/src/index.css +++ b/src/index.css @@ -8,40 +8,145 @@ src: url('../public/fonts/MabryPro-Bold.ttf') format('woff'); } -:root { - --button-color: #1c293a; - --description-color: #2e8f79; - --save-color: #1c293a; - --save-size-color: #ffffff; - --save-hover-corlor: #555555; - --calcel-color: #979797; - --calcel-size-color: #000000; - --calcel-hover-corlor: #f4f3f6; - --button-loading-color: #dedede; - --button-loading-size-color: #000000; - --scopes-icon-color: #edeff2; - --table-title-color: #f4f6f8; - --title-light-color: #7a7a7a; - --menu-background-color: rgba(0, 167, 111, 0.1); - --hover-menu-background-color: rgba(0, 167, 111, 0.2); - --menu-color: #00a76f; - --table-title-text-color: #637381; - --palette-background-paper: #ffffff; - --no-data-color: 145 158 171; +body { + color: #18230f !important; + background-color: rgba(255, 255, 255, 0.9) !important; + --palette-description-color: #1f7d53; + --palette-text-color: #1f7d53; + --palette-color: #18230f; + --palette-button-text-color: #fff; + --palette-button-color: #18230f; + --palette-secondary-dark: #18230f; + --palette-sign-hover-button-text-color: rgb(32, 100, 84); + --palette-hover-button-text-color: #1d3048; + --palette-delete-button-color: #d42536; + --palette-delete-button-hover-color: #d42537b8; + --palette-button-loading-color: #dedede; + --palette-button-loading-size-color: #000000; + --palette-save-color: #18230f; + --palette-calcel-hover-corlor: #f7f7f7; + --palette-calcel-background-color: rgb(255, 255, 255); + --palette-scopes-icon-color: #edeff2; + --palette-table-title-color: #f4f6f8; + --palette-table-title-text-color: #637381; + --palette-main-background-paper: rgba(255, 255, 255, 0.9); + --palette-menu-color: #00a76f; + --palette-sidebar-menu-background-color: #18230f; + --palette-menu-background-color: rgba(145 158 171 / 0.16); + --palette-grey-background-color: #00a76f1a; + --palette-sidebar-expand-background-color: rgba(255, 255, 255, 0.9); + --palette-hover-menu-background-color: #00a76f26; + --palette-background-menu-paper: #fff; + --palette-header-background-color: #f9fafb; + --palette-sidebar-background-color: #f4f6f8; + --palette-sidebar-menu-color: #637381; + --paletteexpandable-sidebar-navigation: #e2e5e9; + --menu-border-radius: 0.4rem; + --palette-background-menu: rgba(255, 255, 255); + --palette-common-white: #fff; + --palette-background-paper: rgba(255, 255, 255, 0.9); + --palette-profile-background-paper: rgba(255, 255, 255, 1); + --palette-sidebar-user-background-paper: #fff; + --palette-no-data-color: 145 158 171; --palette-text-disabled: #919eab; - --text---palette-text-secondary: #637381; - --palette-divider: rgba(145 158 171 / 0.4); - --palette-background-neutral: #f4f6f8; + --palette-text-palette-text-secondary: #637381; + --palette-palette-divider: rgba(145 158 171 / 0.4); --palette-background-inactive: rgb(145 158 171 / 0.16); --palette-card-hover-background-color: #f4f6f8; --palette-detail-lable-color: #515151; --palette-action-hover: rgba(145 158 171 / 0.08); - --delete-button-color: #d42536; - --delete-button-hover-color: #d42537b8; + --palette-background-rotation: #f6f7f9; + --palette-background--hover-rotation: #e7e8ea; + --palette-tab-border-color: #e8e8e8; + --palette-text-secondary: #18230f; + --palette-grey-600Channel: rgba(145 158 171 / 0.16); + --palette-grey-500Channel: 145 158 171; + --palette-grey-400Channel: #e4e5e5; + --palette-grey-300Channel: rgba(0, 0, 0, 0.198); + --palette-grey-200Channel: #18230f; + --palette-grey-100Channel: rgb(232, 232, 232); + --palette-grey-tab: #919eab; + --palette-white-500Channel: #f7f7f7; + --palette-white-400Channel: #efefef; + --palette-dark-500Channel: 134 134 134; + --palette-dark-400Channel: rgba(0, 0, 0, 0.7); + --palette-dark-300Channel: #18230f; + --palette-dark-200Channel: #e9e9e9; + --palette-green-500Channel: rgb(237, 247, 237); + --palette-color-neutral-200: #e8e8e8; + --palette-main-box-shadow: 0 0.125rem 0.2rem -0.0625rem #32325d40, 0 0.0625rem 0.245rem -1.0625rem #0000004d; + --palette-card-box-shadow: 0 0 2px 0 rgba(145 158 171 / 0.2), 0 12px 24px -4px rgba(145 158 171 / 0.12); + --palette-menu-shadow: 0 0.02rem 0.08rem 0.004rem #32325d6b, 0 0.0625rem 0.0145rem -0.0625rem #0000004d; + --custom-shadows-dropdown: 0 0 2px 0 rgba(145 158 171 / 0.24), 2px 5px 0px -5px rgba(145 158 171 / 0.24); + --custom-sidebar-shadows-dropdown: 0 0 2px 0 rgba(0 0 0 / 0.24), 2px 5px 0px -5px rgba(145 158 171 / 0.24); +} + +[data-theme='dark'] body { + color: rgba(255, 255, 255, 0.8) !important; + background-color: #0d121e !important; + --palette-main-background-paper: #0d121e; + --palette-color: rgba(255, 255, 255, 0.7); + --palette-secondary-dark: #ececec; + --palette-description-color: #008170; + --palette-text-color: #008170; + --palette-background-paper: #1f2430; + --palette-profile-background-paper: #1f2430; + --palette-sidebar-user-background-paper: #161b23; + --palette-background-menu-paper: #161b23; + --palette-menu-shadow: none; + --palette-menu-background-color: rgba(145 158 171 / 0.16); + --palette-grey-background-color: rgba(0, 129, 112, 0.2); + --palette-sidebar-expand-background-color: #2a2f3b; + --palette-hover-menu-background-color: #2d323d; + --palette-background-menu: rgba(28, 37, 46); + --palette-text-secondary: #919eab; + --palette-header-background-color: #161b23; + --palette-sidebar-menu-background-color: rgba(255, 255, 255, 0.9); + --palette-sidebar-background-color: #1f2430; + --palette-sidebar-menu-color: rgba(255, 255, 255, 0.8); + --paletteexpandable-sidebar-navigation: #303540; + --palette-grey-tab: #6b7681; + --palette-button-text-color: rgba(255, 255, 255, 0.8); + --palette-button-color: #008170; + --palette-sign-hover-button-text-color: #039b87; + --palette-hover-button-text-color: #039b87; + --palette-save-color: #161b23; + --palette-calcel-hover-corlor: rgba(145 158 171 / 0.3); + --palette-calcel-background-color: rgba(145 158 171 / 0.2); + --palette-card-hover-background-color: #1a202d; + --palette-table-title-color: rgba(255, 255, 255, 0.1); + --palette-table-title-text-color: #919eab; + --palette-detail-lable-color: #a1a0a2; + --palette-background-rotation: #1f2430; + --palette-background--hover-rotation: #25303c; + --palette-tab-border-color: #343536; + --palette-grey-600Channel: #2a2f3b; + --palette-grey-400Channel: #474747; + --palette-grey-300Channel: #c6c6c6; + --palette-grey-200Channel: rgba(255, 255, 255, 0.7); + --palette-grey-100Channel: #4b4e51; + --palette-white-500Channel: #e8e8e8; + --palette-white-400Channel: #d1d1d1; + --palette-dark-500Channel: 145 158 171; + --palette-dark-400Channel: #7d8893; + --palette-dark-300Channel: #5d5f61; + --palette-dark-200Channel: #28323d; + --palette-green-500Channel: rgb(12, 19, 13); + --palette-color-neutral-200: #1f2430; + --palette-main-box-shadow: rgb(255 255 255 / 23%) 0px 0px 0px 0px, rgb(202 204 206 / 32%) 0px 1px 1px -3px; + --custom-shadows-dropdown: 0 0 1px 0 rgb(93 94 95 / 20%), 1px 2px 0px -2px rgb(103 108 112 / 24%); + --custom-sidebar-shadows-dropdown: 0 0 2px 0 rgba(0 0 0 / 0.34), -20px 20px 40px -4px rgba(0 0 0 / 0.24); + --palette-card-box-shadow: 0 0 2px 0 rgba(0 0 0 / 0.2), 0 12px 24px -4px rgba(0 0 0 / 0.12); } body { font-family: 'mabry-light' !important; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + overflow-y: hidden; } h2 { diff --git a/src/index.tsx b/src/index.tsx index 89022ac..a76b0cc 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,22 +3,13 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import { BrowserRouter as Routers } from 'react-router-dom'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -const theme = createTheme({ - typography: { - fontFamily: 'mabry-light,sans-serif', - }, -}); - root.render( - - - - - + + + , ); diff --git a/src/layouts/main.tsx b/src/layouts/main.tsx index 6555a9d..5da592d 100644 --- a/src/layouts/main.tsx +++ b/src/layouts/main.tsx @@ -15,6 +15,7 @@ import ShowSeedPeer from '../components/clusters/seed-peers/show'; import Peers from '../components/clusters/peers'; import Profile from '../components/profile'; import Users from '../components/users'; +import NewUser from '../components/users/new'; import Tokens from '../components/developer/tokens'; import NewTokens from '../components/developer/tokens/new'; import EditTokens from '../components/developer/tokens/edit'; @@ -53,7 +54,7 @@ function Main() { return ( } /> - } /> + } /> } /> } /> }> @@ -81,6 +82,7 @@ function Main() { } /> {isRoot && } />} + } /> ); diff --git a/src/lib/api.ts b/src/lib/api.ts index 2a223f3..aa3752f 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -178,6 +178,9 @@ interface signUpRequset { name: string; password: string; email: string; + bio?: string; + location?: string; + phone?: string; } interface signUpResponse { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 3520206..093db18 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -125,7 +125,7 @@ export const exportCSVFile = (headers: Header[], items: getPeersResponse[], file } }; -export const fuzzySearch = (keyword: string, data: getClustersResponse[]) => { +export const fuzzySearch = (keyword: string, data: any[]) => { if (!data || !Array.isArray(data)) return []; return data.filter((item) => item.name && typeof item.name === 'string' && item.name.includes(keyword));