mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Merge branch 'main' into fix/context-form-validation
This commit is contained in:
		
						commit
						c0455962ec
					
				
							
								
								
									
										8
									
								
								frontend/.github/workflows/e2e.auth.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								frontend/.github/workflows/e2e.auth.yml
									
									
									
									
										vendored
									
									
								
							| @ -16,10 +16,10 @@ jobs: | |||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Run Cypress |       - name: Run Cypress | ||||||
|         uses: cypress-io/github-action@v2 |         uses: cypress-io/github-action@v2 | ||||||
|         with:  |         with: | ||||||
|           env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }},DEFAULT_ENV="development" |           env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }} | ||||||
|           config: baseUrl=${{ github.event.deployment_status.target_url }} |           config: baseUrl=${{ github.event.deployment_status.target_url }} | ||||||
|           record: true |           record: true | ||||||
|           spec: cypress/integration/auth/auth.spec.js |           spec: cypress/integration/auth/auth.spec.ts | ||||||
|         env: |         env: | ||||||
|           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} |           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								frontend/.github/workflows/e2e.feature.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								frontend/.github/workflows/e2e.feature.yml
									
									
									
									
										vendored
									
									
								
							| @ -16,10 +16,10 @@ jobs: | |||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Run Cypress |       - name: Run Cypress | ||||||
|         uses: cypress-io/github-action@v2 |         uses: cypress-io/github-action@v2 | ||||||
|         with:  |         with: | ||||||
|           env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }},DEFAULT_ENV="development" |           env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }} | ||||||
|           config: baseUrl=${{ github.event.deployment_status.target_url }} |           config: baseUrl=${{ github.event.deployment_status.target_url }} | ||||||
|           record: true |           record: true | ||||||
|           spec: cypress/integration/feature-toggle/feature.spec.js |           spec: cypress/integration/feature/feature.spec.ts | ||||||
|         env: |         env: | ||||||
|           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} |           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| { | { | ||||||
|   "projectId": "tc2qff", |   "projectId": "tc2qff", | ||||||
|   "defaultCommandTimeout": 12000 |   "defaultCommandTimeout": 12000, | ||||||
|  |   "screenshotOnRunFailure": false, | ||||||
|  |   "video": false | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,15 +1,4 @@ | |||||||
| /* eslint-disable jest/no-conditional-expect */ |  | ||||||
| /// <reference types="cypress" />
 | /// <reference types="cypress" />
 | ||||||
| // Welcome to Cypress!
 |  | ||||||
| //
 |  | ||||||
| // This spec file contains a variety of sample tests
 |  | ||||||
| // for a todo list app that are designed to demonstrate
 |  | ||||||
| // the power of writing tests in Cypress.
 |  | ||||||
| //
 |  | ||||||
| // To learn more about how Cypress works and
 |  | ||||||
| // what makes it such an awesome testing tool,
 |  | ||||||
| // please read our getting started guide:
 |  | ||||||
| // https://on.cypress.io/introduction-to-cypress
 |  | ||||||
| 
 | 
 | ||||||
| const username = 'test@test.com'; | const username = 'test@test.com'; | ||||||
| const password = 'qY70$NDcJNXA'; | const password = 'qY70$NDcJNXA'; | ||||||
| @ -1,82 +1,48 @@ | |||||||
| /* eslint-disable jest/no-conditional-expect */ |  | ||||||
| /// <reference types="cypress" />
 | /// <reference types="cypress" />
 | ||||||
| // Welcome to Cypress!
 |  | ||||||
| //
 |  | ||||||
| // This spec file contains a variety of sample tests
 |  | ||||||
| // for a todo list app that are designed to demonstrate
 |  | ||||||
| // the power of writing tests in Cypress.
 |  | ||||||
| //
 |  | ||||||
| // To learn more about how Cypress works and
 |  | ||||||
| // what makes it such an awesome testing tool,
 |  | ||||||
| // please read our getting started guide:
 |  | ||||||
| // https://on.cypress.io/introduction-to-cypress
 |  | ||||||
| 
 | 
 | ||||||
| let featureToggleName = ''; | import { disableFeatureStrategiesProductionGuard } from '../../../src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard'; | ||||||
| let enterprise = false; | 
 | ||||||
|  | const randomId = String(Math.random()).split('.')[1]; | ||||||
|  | const featureToggleName = `unleash-e2e-${randomId}`; | ||||||
|  | const enterprise = Boolean(Cypress.env('ENTERPRISE')); | ||||||
|  | const passwordAuth = Cypress.env('PASSWORD_AUTH'); | ||||||
|  | const authToken = Cypress.env('AUTH_TOKEN'); | ||||||
|  | const baseUrl = Cypress.config().baseUrl; | ||||||
| let strategyId = ''; | let strategyId = ''; | ||||||
| let defaultEnv = 'development'; |  | ||||||
| 
 |  | ||||||
| describe('feature toggle', () => { |  | ||||||
|     before(() => { |  | ||||||
|         featureToggleName = `unleash-e2e-${Math.floor(Math.random() * 100)}`; |  | ||||||
|         enterprise = Boolean(Cypress.env('ENTERPRISE')); |  | ||||||
| 
 |  | ||||||
|         const env = Cypress.env('DEFAULT_ENV'); |  | ||||||
|         if (env) { |  | ||||||
|             defaultEnv = env; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|  | describe('feature', () => { | ||||||
|     after(() => { |     after(() => { | ||||||
|         const authToken = Cypress.env('AUTH_TOKEN'); |  | ||||||
| 
 |  | ||||||
|         cy.request({ |         cy.request({ | ||||||
|             method: 'DELETE', |             method: 'DELETE', | ||||||
|             url: `${ |             url: `${baseUrl}/api/admin/features/${featureToggleName}`, | ||||||
|                 Cypress.config().baseUrl |             headers: { Authorization: authToken }, | ||||||
|             }/api/admin/features/${featureToggleName}`,
 |  | ||||||
|             headers: { |  | ||||||
|                 Authorization: authToken, |  | ||||||
|             }, |  | ||||||
|         }); |         }); | ||||||
| 
 |  | ||||||
|         cy.request({ |         cy.request({ | ||||||
|             method: 'DELETE', |             method: 'DELETE', | ||||||
|             url: `${ |             url: `${baseUrl}/api/admin/archive/${featureToggleName}`, | ||||||
|                 Cypress.config().baseUrl |             headers: { Authorization: authToken }, | ||||||
|             }/api/admin/archive/${featureToggleName}`,
 |  | ||||||
|             headers: { |  | ||||||
|                 Authorization: authToken, |  | ||||||
|             }, |  | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|         // Cypress starts out with a blank slate for each test
 |         disableFeatureStrategiesProductionGuard(); | ||||||
|         // so we must tell it to visit our website with the `cy.visit()` command.
 |  | ||||||
|         // Since we want to visit the same URL at the start of all our tests,
 |  | ||||||
|         // we include it in our beforeEach function so that it runs before each test
 |  | ||||||
|         const passwordAuth = Cypress.env('PASSWORD_AUTH'); |  | ||||||
|         enterprise = Boolean(Cypress.env('ENTERPRISE')); |  | ||||||
| 
 |  | ||||||
|         cy.visit('/'); |         cy.visit('/'); | ||||||
| 
 | 
 | ||||||
|         if (passwordAuth) { |         if (passwordAuth) { | ||||||
|             cy.get('[data-test="LOGIN_EMAIL_ID"]').type('test@test.com'); |             cy.get('[data-test="LOGIN_EMAIL_ID"]').type('test@test.com'); | ||||||
| 
 |  | ||||||
|             cy.get('[data-test="LOGIN_PASSWORD_ID"]').type('qY70$NDcJNXA'); |             cy.get('[data-test="LOGIN_PASSWORD_ID"]').type('qY70$NDcJNXA'); | ||||||
| 
 |  | ||||||
|             cy.get("[data-test='LOGIN_BUTTON']").click(); |             cy.get("[data-test='LOGIN_BUTTON']").click(); | ||||||
|         } else { |         } else { | ||||||
|             cy.get('[data-test=LOGIN_EMAIL_ID]').type('test@unleash-e2e.com'); |             cy.get('[data-test=LOGIN_EMAIL_ID]').type('test@unleash-e2e.com'); | ||||||
|             cy.get('[data-test=LOGIN_BUTTON]').click(); |             cy.get('[data-test=LOGIN_BUTTON]').click(); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // Wait for the login redirects to complete.
 | ||||||
|  |         cy.get('[data-test=HEADER_USER_AVATAR'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Creates a feature toggle', () => { |     it('can create a feature toggle', () => { | ||||||
|         if ( |         if (document.querySelector("[data-test='CLOSE_SPLASH']")) { | ||||||
|             document.querySelectorAll("[data-test='CLOSE_SPLASH']").length > 0 |  | ||||||
|         ) { |  | ||||||
|             cy.get("[data-test='CLOSE_SPLASH']").click(); |             cy.get("[data-test='CLOSE_SPLASH']").click(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -87,14 +53,13 @@ describe('feature toggle', () => { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); |         cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); | ||||||
|         cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); |         cy.get("[data-test='CF_DESC_ID'").type('hello-world'); | ||||||
| 
 |  | ||||||
|         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); |         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); | ||||||
|         cy.wait('@createFeature'); |         cy.wait('@createFeature'); | ||||||
|         cy.url().should('include', featureToggleName); |         cy.url().should('include', featureToggleName); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Gives an error if a toggle exists with the same name', () => { |     it('gives an error if a toggle exists with the same name', () => { | ||||||
|         cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); |         cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); | ||||||
| 
 | 
 | ||||||
|         cy.intercept('POST', '/api/admin/projects/default/features').as( |         cy.intercept('POST', '/api/admin/projects/default/features').as( | ||||||
| @ -102,16 +67,14 @@ describe('feature toggle', () => { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); |         cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); | ||||||
|         cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); |         cy.get("[data-test='CF_DESC_ID'").type('hello-world'); | ||||||
| 
 |  | ||||||
|         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); |         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); | ||||||
| 
 |  | ||||||
|         cy.get("[data-test='INPUT_ERROR_TEXT']").contains( |         cy.get("[data-test='INPUT_ERROR_TEXT']").contains( | ||||||
|             'A feature with this name already exists' |             'A feature with this name already exists' | ||||||
|         ); |         ); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Gives an error if a toggle name is url unsafe', () => { |     it('gives an error if a toggle name is url unsafe', () => { | ||||||
|         cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); |         cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); | ||||||
| 
 | 
 | ||||||
|         cy.intercept('POST', '/api/admin/projects/default/features').as( |         cy.intercept('POST', '/api/admin/projects/default/features').as( | ||||||
| @ -119,17 +82,14 @@ describe('feature toggle', () => { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         cy.get("[data-test='CF_NAME_ID'").type('featureToggleUnsafe####$#//'); |         cy.get("[data-test='CF_NAME_ID'").type('featureToggleUnsafe####$#//'); | ||||||
|         cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); |         cy.get("[data-test='CF_DESC_ID'").type('hello-world'); | ||||||
| 
 |  | ||||||
|         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); |         cy.get("[data-test='CF_CREATE_BTN_ID']").click(); | ||||||
| 
 |  | ||||||
|         cy.get("[data-test='INPUT_ERROR_TEXT']").contains( |         cy.get("[data-test='INPUT_ERROR_TEXT']").contains( | ||||||
|             `"name" must be URL friendly` |             `"name" must be URL friendly` | ||||||
|         ); |         ); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Can add a gradual rollout strategy to the development environment', () => { |     it('can add a gradual rollout strategy to the development environment', () => { | ||||||
|         cy.wait(1000); |  | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); |         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); | ||||||
|         cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); |         cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); | ||||||
|         cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-2').click(); |         cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-2').click(); | ||||||
| @ -147,10 +107,9 @@ describe('feature toggle', () => { | |||||||
| 
 | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'POST', |             'POST', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies`, |             `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`, | ||||||
|             req => { |             req => { | ||||||
|                 expect(req.body.name).to.equal('flexibleRollout'); |                 expect(req.body.name).to.equal('flexibleRollout'); | ||||||
| 
 |  | ||||||
|                 expect(req.body.parameters.groupId).to.equal(featureToggleName); |                 expect(req.body.parameters.groupId).to.equal(featureToggleName); | ||||||
|                 expect(req.body.parameters.stickiness).to.equal('default'); |                 expect(req.body.parameters.stickiness).to.equal('default'); | ||||||
|                 expect(req.body.parameters.rollout).to.equal(30); |                 expect(req.body.parameters.rollout).to.equal(30); | ||||||
| @ -172,7 +131,6 @@ describe('feature toggle', () => { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('can update a strategy in the development environment', () => { |     it('can update a strategy in the development environment', () => { | ||||||
|         cy.wait(1000); |  | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); |         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); | ||||||
|         cy.get('[data-test=STRATEGY_ACCORDION_ID-flexibleRollout').click(); |         cy.get('[data-test=STRATEGY_ACCORDION_ID-flexibleRollout').click(); | ||||||
| 
 | 
 | ||||||
| @ -188,7 +146,6 @@ describe('feature toggle', () => { | |||||||
|             .first() |             .first() | ||||||
|             .click(); |             .click(); | ||||||
| 
 | 
 | ||||||
|         let newGroupId = 'new-group-id'; |  | ||||||
|         cy.get('[data-test=FLEXIBLE_STRATEGY_GROUP_ID]') |         cy.get('[data-test=FLEXIBLE_STRATEGY_GROUP_ID]') | ||||||
|             .first() |             .first() | ||||||
|             .clear() |             .clear() | ||||||
| @ -196,9 +153,9 @@ describe('feature toggle', () => { | |||||||
| 
 | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'PUT', |             'PUT', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies/${strategyId}`, |             `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies/${strategyId}`, | ||||||
|             req => { |             req => { | ||||||
|                 expect(req.body.parameters.groupId).to.equal(newGroupId); |                 expect(req.body.parameters.groupId).to.equal('new-group-id'); | ||||||
|                 expect(req.body.parameters.stickiness).to.equal('sessionId'); |                 expect(req.body.parameters.stickiness).to.equal('sessionId'); | ||||||
|                 expect(req.body.parameters.rollout).to.equal(60); |                 expect(req.body.parameters.rollout).to.equal(60); | ||||||
| 
 | 
 | ||||||
| @ -219,12 +176,11 @@ describe('feature toggle', () => { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('can delete a strategy in the development environment', () => { |     it('can delete a strategy in the development environment', () => { | ||||||
|         cy.wait(1000); |  | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); |         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); | ||||||
| 
 | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'DELETE', |             'DELETE', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies/${strategyId}`, |             `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies/${strategyId}`, | ||||||
|             req => { |             req => { | ||||||
|                 req.continue(res => { |                 req.continue(res => { | ||||||
|                     expect(res.statusCode).to.equal(200); |                     expect(res.statusCode).to.equal(200); | ||||||
| @ -237,8 +193,7 @@ describe('feature toggle', () => { | |||||||
|         cy.wait('@deleteStrategy'); |         cy.wait('@deleteStrategy'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Can add a userid  strategy to the development environment', () => { |     it('can add a userid strategy to the development environment', () => { | ||||||
|         cy.wait(1000); |  | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); |         cy.visit(`/projects/default/features/${featureToggleName}/strategies`); | ||||||
|         cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); |         cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); | ||||||
|         cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-3').click(); |         cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-3').click(); | ||||||
| @ -260,7 +215,7 @@ describe('feature toggle', () => { | |||||||
| 
 | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'POST', |             'POST', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies`, |             `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`, | ||||||
|             req => { |             req => { | ||||||
|                 expect(req.body.name).to.equal('userWithId'); |                 expect(req.body.name).to.equal('userWithId'); | ||||||
| 
 | 
 | ||||||
| @ -282,11 +237,12 @@ describe('feature toggle', () => { | |||||||
|         cy.wait('@addStrategyToFeature'); |         cy.wait('@addStrategyToFeature'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('Can add two variant to the feature', () => { |     it('can add two variant to the feature', () => { | ||||||
|         const variantName = 'my-new-variant'; |         const variantName = 'my-new-variant'; | ||||||
|         const secondVariantName = 'my-second-variant'; |         const secondVariantName = 'my-second-variant'; | ||||||
|         cy.wait(1000); | 
 | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/variants`); |         cy.visit(`/projects/default/features/${featureToggleName}/variants`); | ||||||
|  | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'PATCH', |             'PATCH', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/variants`, |             `/api/admin/projects/default/features/${featureToggleName}/variants`, | ||||||
| @ -304,20 +260,23 @@ describe('feature toggle', () => { | |||||||
|                     expect(req.body[1].value.name).to.equal(secondVariantName); |                     expect(req.body[1].value.name).to.equal(secondVariantName); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         ).as('variantcreation'); |         ).as('variantCreation'); | ||||||
|         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); |  | ||||||
|         cy.get('[data-test=VARIANT_NAME_INPUT]').type(variantName); |  | ||||||
|         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); |  | ||||||
|         cy.wait('@variantcreation'); |  | ||||||
|         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); |  | ||||||
|         cy.get('[data-test=VARIANT_NAME_INPUT]').type(secondVariantName); |  | ||||||
|         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); |  | ||||||
|         cy.wait('@variantcreation'); |  | ||||||
|     }); |  | ||||||
|     it('Can set weight to fixed value for one of the variants', () => { |  | ||||||
|         cy.wait(1000); |  | ||||||
| 
 | 
 | ||||||
|  |         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); | ||||||
|  |         cy.get('[data-test=VARIANT_NAME_INPUT]').click().type(variantName); | ||||||
|  |         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); | ||||||
|  |         cy.wait('@variantCreation'); | ||||||
|  |         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); | ||||||
|  |         cy.get('[data-test=VARIANT_NAME_INPUT]') | ||||||
|  |             .click() | ||||||
|  |             .type(secondVariantName); | ||||||
|  |         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); | ||||||
|  |         cy.wait('@variantCreation'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('can set weight to fixed value for one of the variants', () => { | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/variants`); |         cy.visit(`/projects/default/features/${featureToggleName}/variants`); | ||||||
|  | 
 | ||||||
|         cy.get('[data-test=VARIANT_EDIT_BUTTON]').first().click(); |         cy.get('[data-test=VARIANT_EDIT_BUTTON]').first().click(); | ||||||
|         cy.get('[data-test=VARIANT_NAME_INPUT]') |         cy.get('[data-test=VARIANT_NAME_INPUT]') | ||||||
|             .children() |             .children() | ||||||
| @ -328,6 +287,7 @@ describe('feature toggle', () => { | |||||||
|             .find('input') |             .find('input') | ||||||
|             .check(); |             .check(); | ||||||
|         cy.get('[data-test=VARIANT_WEIGHT_INPUT]').clear().type('15'); |         cy.get('[data-test=VARIANT_WEIGHT_INPUT]').clear().type('15'); | ||||||
|  | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'PATCH', |             'PATCH', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/variants`, |             `/api/admin/projects/default/features/${featureToggleName}/variants`, | ||||||
| @ -342,21 +302,23 @@ describe('feature toggle', () => { | |||||||
|                 expect(req.body[2].path).to.match(/weight/); |                 expect(req.body[2].path).to.match(/weight/); | ||||||
|                 expect(req.body[2].value).to.equal(150); |                 expect(req.body[2].value).to.equal(150); | ||||||
|             } |             } | ||||||
|         ).as('variantupdate'); |         ).as('variantUpdate'); | ||||||
|  | 
 | ||||||
|         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); |         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); | ||||||
|         cy.wait('@variantupdate'); |         cy.wait('@variantUpdate'); | ||||||
|         cy.get('[data-test=VARIANT_WEIGHT]') |         cy.get('[data-test=VARIANT_WEIGHT]') | ||||||
|             .first() |             .first() | ||||||
|             .should('have.text', '15 %'); |             .should('have.text', '15 %'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it(`can delete variant`, () => { |     it('can delete variant', () => { | ||||||
|         const variantName = 'to-be-deleted'; |         const variantName = 'to-be-deleted'; | ||||||
|         cy.wait(1000); | 
 | ||||||
|         cy.visit(`/projects/default/features/${featureToggleName}/variants`); |         cy.visit(`/projects/default/features/${featureToggleName}/variants`); | ||||||
|         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); |         cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); | ||||||
|         cy.get('[data-test=VARIANT_NAME_INPUT]').type(variantName); |         cy.get('[data-test=VARIANT_NAME_INPUT]').click().type(variantName); | ||||||
|         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); |         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); | ||||||
|  | 
 | ||||||
|         cy.intercept( |         cy.intercept( | ||||||
|             'PATCH', |             'PATCH', | ||||||
|             `/api/admin/projects/default/features/${featureToggleName}/variants`, |             `/api/admin/projects/default/features/${featureToggleName}/variants`, | ||||||
| @ -365,6 +327,7 @@ describe('feature toggle', () => { | |||||||
|                 expect(e.path).to.match(/\//); |                 expect(e.path).to.match(/\//); | ||||||
|             } |             } | ||||||
|         ).as('delete'); |         ).as('delete'); | ||||||
|  | 
 | ||||||
|         cy.get(`[data-test=VARIANT_DELETE_BUTTON_${variantName}]`).click(); |         cy.get(`[data-test=VARIANT_DELETE_BUTTON_${variantName}]`).click(); | ||||||
|         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); |         cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); | ||||||
|         cy.wait('@delete'); |         cy.wait('@delete'); | ||||||
| @ -42,6 +42,7 @@ | |||||||
|     "@material-ui/core": "4.12.3", |     "@material-ui/core": "4.12.3", | ||||||
|     "@material-ui/icons": "4.11.2", |     "@material-ui/icons": "4.11.2", | ||||||
|     "@material-ui/lab": "4.0.0-alpha.60", |     "@material-ui/lab": "4.0.0-alpha.60", | ||||||
|  |     "@testing-library/dom": "8.11.3", | ||||||
|     "@testing-library/jest-dom": "5.16.2", |     "@testing-library/jest-dom": "5.16.2", | ||||||
|     "@testing-library/react": "12.1.3", |     "@testing-library/react": "12.1.3", | ||||||
|     "@testing-library/user-event": "13.5.0", |     "@testing-library/user-event": "13.5.0", | ||||||
| @ -49,7 +50,7 @@ | |||||||
|     "@types/deep-diff": "1.0.1", |     "@types/deep-diff": "1.0.1", | ||||||
|     "@types/jest": "27.4.1", |     "@types/jest": "27.4.1", | ||||||
|     "@types/lodash.clonedeep": "4.5.6", |     "@types/lodash.clonedeep": "4.5.6", | ||||||
|     "@types/node": "14.18.12", |     "@types/node": "17.0.18", | ||||||
|     "@types/react": "17.0.39", |     "@types/react": "17.0.39", | ||||||
|     "@types/react-dom": "17.0.11", |     "@types/react-dom": "17.0.11", | ||||||
|     "@types/react-outside-click-handler": "1.3.1", |     "@types/react-outside-click-handler": "1.3.1", | ||||||
| @ -63,7 +64,7 @@ | |||||||
|     "copy-to-clipboard": "3.3.1", |     "copy-to-clipboard": "3.3.1", | ||||||
|     "craco": "0.0.3", |     "craco": "0.0.3", | ||||||
|     "css-loader": "6.6.0", |     "css-loader": "6.6.0", | ||||||
|     "cypress": "8.7.0", |     "cypress": "9.5.0", | ||||||
|     "date-fns": "2.28.0", |     "date-fns": "2.28.0", | ||||||
|     "debounce": "1.2.1", |     "debounce": "1.2.1", | ||||||
|     "deep-diff": "1.0.2", |     "deep-diff": "1.0.2", | ||||||
| @ -82,7 +83,7 @@ | |||||||
|     "react-outside-click-handler": "1.3.0", |     "react-outside-click-handler": "1.3.0", | ||||||
|     "react-router-dom": "5.3.0", |     "react-router-dom": "5.3.0", | ||||||
|     "react-scripts": "4.0.3", |     "react-scripts": "4.0.3", | ||||||
|     "react-test-renderer": "16.14.0", |     "react-test-renderer": "17.0.2", | ||||||
|     "react-timeago": "6.2.1", |     "react-timeago": "6.2.1", | ||||||
|     "sass": "1.49.8", |     "sass": "1.49.8", | ||||||
|     "swr": "1.2.2", |     "swr": "1.2.2", | ||||||
| @ -117,6 +118,9 @@ | |||||||
|       "no-restricted-globals": "off", |       "no-restricted-globals": "off", | ||||||
|       "no-useless-computed-key": "off", |       "no-useless-computed-key": "off", | ||||||
|       "import/no-anonymous-default-export": "off" |       "import/no-anonymous-default-export": "off" | ||||||
|     } |     }, | ||||||
|  |     "ignorePatterns": [ | ||||||
|  |       "cypress" | ||||||
|  |     ] | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,7 +7,9 @@ import { IAddonProvider } from '../../../../interfaces/addons'; | |||||||
| interface IAddonProps { | interface IAddonProps { | ||||||
|     provider: IAddonProvider; |     provider: IAddonProvider; | ||||||
|     checkedEvents: string[]; |     checkedEvents: string[]; | ||||||
|     setEventValue: (name: string) => void; |     setEventValue: ( | ||||||
|  |         name: string | ||||||
|  |     ) => (event: React.ChangeEvent<HTMLInputElement>) => void; | ||||||
|     error: Record<string, string>; |     error: Record<string, string>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { ReactElement } from 'react'; | import React, { ReactElement } from 'react'; | ||||||
| import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons'; | import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons'; | ||||||
| import { AvailableAddons } from './AvailableAddons/AvailableAddons'; | import { AvailableAddons } from './AvailableAddons/AvailableAddons'; | ||||||
| import { Avatar } from '@material-ui/core'; | import { Avatar } from '@material-ui/core'; | ||||||
| @ -12,7 +12,7 @@ import dataDogIcon from '../../../assets/icons/datadog.svg'; | |||||||
| import { formatAssetPath } from '../../../utils/format-path'; | import { formatAssetPath } from '../../../utils/format-path'; | ||||||
| import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; | import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; | ||||||
| 
 | 
 | ||||||
| const style = { | const style: React.CSSProperties = { | ||||||
|     width: '40px', |     width: '40px', | ||||||
|     height: '40px', |     height: '40px', | ||||||
|     marginRight: '16px', |     marginRight: '16px', | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ import AccessContext from '../../../../contexts/AccessContext'; | |||||||
| import { IAddon } from '../../../../interfaces/addons'; | import { IAddon } from '../../../../interfaces/addons'; | ||||||
| import PermissionIconButton from '../../../common/PermissionIconButton/PermissionIconButton'; | import PermissionIconButton from '../../../common/PermissionIconButton/PermissionIconButton'; | ||||||
| import Dialogue from '../../../common/Dialogue'; | import Dialogue from '../../../common/Dialogue'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| interface IConfigureAddonsProps { | interface IConfigureAddonsProps { | ||||||
|     getAddonIcon: (name: string) => ReactElement; |     getAddonIcon: (name: string) => ReactElement; | ||||||
| @ -59,8 +60,8 @@ export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => { | |||||||
|                 title: 'Success', |                 title: 'Success', | ||||||
|                 text: 'Addon state switched successfully', |                 text: 'Addon state switched successfully', | ||||||
|             }); |             }); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -24,7 +24,6 @@ import { | |||||||
|     DELETE_API_TOKEN, |     DELETE_API_TOKEN, | ||||||
| } from '../../../providers/AccessProvider/permissions'; | } from '../../../providers/AccessProvider/permissions'; | ||||||
| import { useStyles } from './ApiTokenList.styles'; | import { useStyles } from './ApiTokenList.styles'; | ||||||
| import { formatDateWithLocale } from '../../../common/util'; |  | ||||||
| import Secret from './secret'; | import Secret from './secret'; | ||||||
| import { Delete, FileCopy } from '@material-ui/icons'; | import { Delete, FileCopy } from '@material-ui/icons'; | ||||||
| import Dialogue from '../../../common/Dialogue'; | import Dialogue from '../../../common/Dialogue'; | ||||||
| @ -32,6 +31,7 @@ import { CREATE_API_TOKEN_BUTTON } from '../../../../testIds'; | |||||||
| import { Alert } from '@material-ui/lab'; | import { Alert } from '@material-ui/lab'; | ||||||
| import copy from 'copy-to-clipboard'; | import copy from 'copy-to-clipboard'; | ||||||
| import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | ||||||
|  | import { formatDateYMD } from '../../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| interface IApiToken { | interface IApiToken { | ||||||
|     createdAt: Date; |     createdAt: Date; | ||||||
| @ -146,7 +146,7 @@ export const ApiTokenList = () => { | |||||||
|                                     align="left" |                                     align="left" | ||||||
|                                     className={styles.hideSM} |                                     className={styles.hideSM} | ||||||
|                                 > |                                 > | ||||||
|                                     {formatDateWithLocale( |                                     {formatDateYMD( | ||||||
|                                         item.createdAt, |                                         item.createdAt, | ||||||
|                                         locationSettings.locale |                                         locationSettings.locale | ||||||
|                                     )} |                                     )} | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import { ADMIN } from '../../../providers/AccessProvider/permissions'; | |||||||
| import { ConfirmToken } from '../ConfirmToken/ConfirmToken'; | import { ConfirmToken } from '../ConfirmToken/ConfirmToken'; | ||||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||||
| import { scrollToTop } from '../../../common/util'; | import { scrollToTop } from '../../../common/util'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| export const CreateApiToken = () => { | export const CreateApiToken = () => { | ||||||
|     const { setToastApiError } = useToast(); |     const { setToastApiError } = useToast(); | ||||||
| @ -49,8 +50,8 @@ export const CreateApiToken = () => { | |||||||
|                     setToken(api.secret); |                     setToken(api.secret); | ||||||
|                     setShowConfirm(true); |                     setShowConfirm(true); | ||||||
|                 }); |                 }); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -68,8 +68,8 @@ export const GoogleAuth = () => { | |||||||
|                 title: 'Settings stored', |                 title: 'Settings stored', | ||||||
|                 type: 'success', |                 type: 'success', | ||||||
|             }); |             }); | ||||||
|         } catch (err) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(formatUnknownError(err)); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -79,8 +79,8 @@ export const OidcAuth = () => { | |||||||
|                 title: 'Settings stored', |                 title: 'Settings stored', | ||||||
|                 type: 'success', |                 type: 'success', | ||||||
|             }); |             }); | ||||||
|         } catch (err) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(formatUnknownError(err)); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -51,8 +51,8 @@ export const PasswordAuth = () => { | |||||||
|                 type: 'success', |                 type: 'success', | ||||||
|                 show: true, |                 show: true, | ||||||
|             }); |             }); | ||||||
|         } catch (err) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(formatUnknownError(err)); |             setToastApiError(formatUnknownError(error)); | ||||||
|             setDisablePasswordAuth(config.disabled); |             setDisablePasswordAuth(config.disabled); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -75,8 +75,8 @@ export const SamlAuth = () => { | |||||||
|                 title: 'Settings stored', |                 title: 'Settings stored', | ||||||
|                 type: 'success', |                 type: 'success', | ||||||
|             }); |             }); | ||||||
|         } catch (err) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(formatUnknownError(err)); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ import { | |||||||
|     Button, |     Button, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import OpenInNew from '@material-ui/icons/OpenInNew'; | import OpenInNew from '@material-ui/icons/OpenInNew'; | ||||||
| import { formatDateWithLocale } from '../../common/util'; |  | ||||||
| import PageContent from '../../common/PageContent'; | import PageContent from '../../common/PageContent'; | ||||||
| import HeaderTitle from '../../common/HeaderTitle'; | import HeaderTitle from '../../common/HeaderTitle'; | ||||||
| import ConditionallyRender from '../../common/ConditionallyRender'; | import ConditionallyRender from '../../common/ConditionallyRender'; | ||||||
| @ -16,6 +15,7 @@ import { formatApiPath } from '../../../utils/format-path'; | |||||||
| import useInvoices from '../../../hooks/api/getters/useInvoices/useInvoices'; | import useInvoices from '../../../hooks/api/getters/useInvoices/useInvoices'; | ||||||
| import { IInvoice } from '../../../interfaces/invoice'; | import { IInvoice } from '../../../interfaces/invoice'; | ||||||
| import { useLocationSettings } from '../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../hooks/useLocationSettings'; | ||||||
|  | import { formatDateYMD } from '../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| const PORTAL_URL = formatApiPath('api/admin/invoices/portal'); | const PORTAL_URL = formatApiPath('api/admin/invoices/portal'); | ||||||
| 
 | 
 | ||||||
| @ -87,7 +87,7 @@ const InvoiceList = () => { | |||||||
|                                             style={{ textAlign: 'left' }} |                                             style={{ textAlign: 'left' }} | ||||||
|                                         > |                                         > | ||||||
|                                             {item.dueDate && |                                             {item.dueDate && | ||||||
|                                                 formatDateWithLocale( |                                                 formatDateYMD( | ||||||
|                                                     item.dueDate, |                                                     item.dueDate, | ||||||
|                                                     locationSettings.locale |                                                     locationSettings.locale | ||||||
|                                                 )} |                                                 )} | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; | |||||||
| import useToast from '../../../../hooks/useToast'; | import useToast from '../../../../hooks/useToast'; | ||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { ADMIN } from '../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../providers/AccessProvider/permissions'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const CreateProjectRole = () => { | const CreateProjectRole = () => { | ||||||
|     const { setToastData, setToastApiError } = useToast(); |     const { setToastData, setToastApiError } = useToast(); | ||||||
| @ -49,8 +50,8 @@ const CreateProjectRole = () => { | |||||||
|                     confetti: true, |                     confetti: true, | ||||||
|                     type: 'success', |                     type: 'success', | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; | |||||||
| import useToast from '../../../../hooks/useToast'; | import useToast from '../../../../hooks/useToast'; | ||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { ADMIN } from '../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../providers/AccessProvider/permissions'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EditProjectRole = () => { | const EditProjectRole = () => { | ||||||
|     const { uiConfig } = useUiConfig(); |     const { uiConfig } = useUiConfig(); | ||||||
| @ -88,8 +89,8 @@ const EditProjectRole = () => { | |||||||
|                     text: 'Your role changes will automatically be applied to the users with this role.', |                     text: 'Your role changes will automatically be applied to the users with this role.', | ||||||
|                     confetti: true, |                     confetti: true, | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -1,16 +1,16 @@ | |||||||
| import Input from '../../../common/Input/Input'; | import Input from '../../../common/Input/Input'; | ||||||
| import EnvironmentPermissionAccordion from './EnvironmentPermissionAccordion/EnvironmentPermissionAccordion'; | import EnvironmentPermissionAccordion from './EnvironmentPermissionAccordion/EnvironmentPermissionAccordion'; | ||||||
| import { | import { | ||||||
|  |     Button, | ||||||
|     Checkbox, |     Checkbox, | ||||||
|     FormControlLabel, |     FormControlLabel, | ||||||
|     TextField, |     TextField, | ||||||
|     Button, |  | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import useProjectRolePermissions from '../../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | import useProjectRolePermissions from '../../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | ||||||
| 
 | 
 | ||||||
| import { useStyles } from './ProjectRoleForm.styles'; | import { useStyles } from './ProjectRoleForm.styles'; | ||||||
| import ConditionallyRender from '../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../common/ConditionallyRender'; | ||||||
| import React from 'react'; | import React, { ReactNode } from 'react'; | ||||||
| import { IPermission } from '../../../../interfaces/project'; | import { IPermission } from '../../../../interfaces/project'; | ||||||
| import { | import { | ||||||
|     ICheckedPermission, |     ICheckedPermission, | ||||||
| @ -33,6 +33,7 @@ interface IProjectRoleForm { | |||||||
|     clearErrors: () => void; |     clearErrors: () => void; | ||||||
|     validateNameUniqueness?: () => void; |     validateNameUniqueness?: () => void; | ||||||
|     getRoleKey: (permission: { id: number; environment?: string }) => string; |     getRoleKey: (permission: { id: number; environment?: string }) => string; | ||||||
|  |     children: ReactNode; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ProjectRoleForm: React.FC<IProjectRoleForm> = ({ | const ProjectRoleForm: React.FC<IProjectRoleForm> = ({ | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { makeStyles } from '@material-ui/styles'; | import { makeStyles } from '@material-ui/core/styles'; | ||||||
| 
 | 
 | ||||||
| export const useStyles = makeStyles(theme => ({ | export const useStyles = makeStyles(theme => ({ | ||||||
|     deleteParagraph: { |     deleteParagraph: { | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import IRole, { IProjectRole } from '../../../../../interfaces/role'; | |||||||
| import useProjectRolesApi from '../../../../../hooks/api/actions/useProjectRolesApi/useProjectRolesApi'; | import useProjectRolesApi from '../../../../../hooks/api/actions/useProjectRolesApi/useProjectRolesApi'; | ||||||
| import useToast from '../../../../../hooks/useToast'; | import useToast from '../../../../../hooks/useToast'; | ||||||
| import ProjectRoleDeleteConfirm from '../ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm'; | import ProjectRoleDeleteConfirm from '../ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm'; | ||||||
|  | import { formatUnknownError } from '../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const ROOTROLE = 'root'; | const ROOTROLE = 'root'; | ||||||
| 
 | 
 | ||||||
| @ -44,8 +45,8 @@ const ProjectRoleList = () => { | |||||||
|                 title: 'Successfully deleted role', |                 title: 'Successfully deleted role', | ||||||
|                 text: 'Your role is now deleted', |                 text: 'Your role is now deleted', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|         setDelDialog(false); |         setDelDialog(false); | ||||||
|         setConfirmName(''); |         setConfirmName(''); | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| import { useStyles } from './ProjectRoleListItem.styles'; | import { useStyles } from './ProjectRoleListItem.styles'; | ||||||
| import { TableRow, TableCell, Typography } from '@material-ui/core'; | import { TableCell, TableRow, Typography } from '@material-ui/core'; | ||||||
| import { Edit, Delete } from '@material-ui/icons'; | import { Delete, Edit } from '@material-ui/icons'; | ||||||
| import { ADMIN } from '../../../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../../../providers/AccessProvider/permissions'; | ||||||
| import SupervisedUserCircleIcon from '@material-ui/icons/SupervisedUserCircle'; | import SupervisedUserCircleIcon from '@material-ui/icons/SupervisedUserCircle'; | ||||||
| import PermissionIconButton from '../../../../../common/PermissionIconButton/PermissionIconButton'; | import PermissionIconButton from '../../../../../common/PermissionIconButton/PermissionIconButton'; | ||||||
| import { IProjectRole } from '../../../../../../interfaces/role'; | import { IProjectRole } from '../../../../../../interfaces/role'; | ||||||
| import { useHistory } from 'react-router-dom'; | import { useHistory } from 'react-router-dom'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IRoleListItemProps { | interface IRoleListItemProps { | ||||||
|     id: number; |     id: number; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { makeStyles } from '@material-ui/styles'; | import { makeStyles } from '@material-ui/core/styles'; | ||||||
| 
 | 
 | ||||||
| export const useStyles = makeStyles(theme => ({ | export const useStyles = makeStyles(theme => ({ | ||||||
|     rolesListBody: { |     rolesListBody: { | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { makeStyles } from '@material-ui/styles'; | import { makeStyles } from '@material-ui/core/styles'; | ||||||
| 
 | 
 | ||||||
| export const useStyles = makeStyles({ | export const useStyles = makeStyles({ | ||||||
|     iconContainer: { |     iconContainer: { | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import { useState } from 'react'; | |||||||
| import { scrollToTop } from '../../../common/util'; | import { scrollToTop } from '../../../common/util'; | ||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { ADMIN } from '../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../providers/AccessProvider/permissions'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const CreateUser = () => { | const CreateUser = () => { | ||||||
|     const { setToastApiError } = useToast(); |     const { setToastApiError } = useToast(); | ||||||
| @ -51,8 +52,8 @@ const CreateUser = () => { | |||||||
|                         setInviteLink(user.inviteLink); |                         setInviteLink(user.inviteLink); | ||||||
|                         setShowConfirm(true); |                         setShowConfirm(true); | ||||||
|                     }); |                     }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ import { useEffect } from 'react'; | |||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { ADMIN } from '../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../providers/AccessProvider/permissions'; | ||||||
| import { EDIT } from '../../../../constants/misc'; | import { EDIT } from '../../../../constants/misc'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EditUser = () => { | const EditUser = () => { | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
| @ -60,8 +61,8 @@ const EditUser = () => { | |||||||
|                     title: 'User information updated', |                     title: 'User information updated', | ||||||
|                     type: 'success', |                     type: 'success', | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { makeStyles } from '@material-ui/styles'; | import { makeStyles } from '@material-ui/core/styles'; | ||||||
| 
 | 
 | ||||||
| export const useStyles = makeStyles(theme => ({ | export const useStyles = makeStyles(theme => ({ | ||||||
|     userListBody: { |     userListBody: { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||||
| import classnames from 'classnames'; | import classnames from 'classnames'; | ||||||
| import { TextField, Typography, Avatar } from '@material-ui/core'; | import { Avatar, TextField, Typography } from '@material-ui/core'; | ||||||
| import { trim } from '../../../../common/util'; | import { trim } from '../../../../common/util'; | ||||||
| import { modalStyles } from '../../util'; | import { modalStyles } from '../../util'; | ||||||
| import Dialogue from '../../../../common/Dialogue/Dialogue'; | import Dialogue from '../../../../common/Dialogue/Dialogue'; | ||||||
| @ -12,10 +12,10 @@ import { Alert } from '@material-ui/lab'; | |||||||
| import { IUser } from '../../../../../interfaces/user'; | import { IUser } from '../../../../../interfaces/user'; | ||||||
| 
 | 
 | ||||||
| interface IChangePasswordProps { | interface IChangePasswordProps { | ||||||
|     showDialog: () => void; |     showDialog: boolean; | ||||||
|     closeDialog: () => void; |     closeDialog: () => void; | ||||||
|     changePassword: () => void; |     changePassword: (user: IUser, password: string) => Promise<Response>; | ||||||
|     user: IUser; |     user: Partial<IUser>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ChangePassword = ({ | const ChangePassword = ({ | ||||||
| @ -25,7 +25,7 @@ const ChangePassword = ({ | |||||||
|     user = {}, |     user = {}, | ||||||
| }: IChangePasswordProps) => { | }: IChangePasswordProps) => { | ||||||
|     const [data, setData] = useState({}); |     const [data, setData] = useState({}); | ||||||
|     const [error, setError] = useState({}); |     const [error, setError] = useState<Record<string, string>>({}); | ||||||
|     const [validPassword, setValidPassword] = useState(false); |     const [validPassword, setValidPassword] = useState(false); | ||||||
|     const commonStyles = useCommonStyles(); |     const commonStyles = useCommonStyles(); | ||||||
| 
 | 
 | ||||||
| @ -88,7 +88,7 @@ const ChangePassword = ({ | |||||||
|                 )} |                 )} | ||||||
|             > |             > | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={error.general} |                     condition={Boolean(error.general)} | ||||||
|                     show={<Alert severity="error">{error.general}</Alert>} |                     show={<Alert severity="error">{error.general}</Alert>} | ||||||
|                 /> |                 /> | ||||||
|                 <Typography variant="subtitle1"> |                 <Typography variant="subtitle1"> | ||||||
|  | |||||||
| @ -9,12 +9,12 @@ import { useCommonStyles } from '../../../../../common.styles'; | |||||||
| import { IUser } from '../../../../../interfaces/user'; | import { IUser } from '../../../../../interfaces/user'; | ||||||
| 
 | 
 | ||||||
| interface IDeleteUserProps { | interface IDeleteUserProps { | ||||||
|     showDialog: () => void; |     showDialog: boolean; | ||||||
|     closeDialog: () => void; |     closeDialog: () => void; | ||||||
|     user: IUser; |     user: IUser; | ||||||
|     userLoading: boolean; |     userLoading: boolean; | ||||||
|     removeUser: () => void; |     removeUser: () => void; | ||||||
|     userApiErrors: Object; |     userApiErrors: Record<string, string>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const DeleteUser = ({ | const DeleteUser = ({ | ||||||
| @ -33,13 +33,13 @@ const DeleteUser = ({ | |||||||
|             open={showDialog} |             open={showDialog} | ||||||
|             title="Really delete user?" |             title="Really delete user?" | ||||||
|             onClose={closeDialog} |             onClose={closeDialog} | ||||||
|             onClick={() => removeUser(user)} |             onClick={removeUser} | ||||||
|             primaryButtonText="Delete user" |             primaryButtonText="Delete user" | ||||||
|             secondaryButtonText="Cancel" |             secondaryButtonText="Cancel" | ||||||
|         > |         > | ||||||
|             <div ref={ref}> |             <div ref={ref}> | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={userApiErrors[REMOVE_USER_ERROR]} |                     condition={Boolean(userApiErrors[REMOVE_USER_ERROR])} | ||||||
|                     show={ |                     show={ | ||||||
|                         <Alert |                         <Alert | ||||||
|                             data-loading |                             data-loading | ||||||
|  | |||||||
| @ -1,25 +1,24 @@ | |||||||
| import { | import { | ||||||
|     TableRow, |  | ||||||
|     TableCell, |  | ||||||
|     Avatar, |     Avatar, | ||||||
|     IconButton, |     IconButton, | ||||||
|  |     TableCell, | ||||||
|  |     TableRow, | ||||||
|     Typography, |     Typography, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { Edit, Lock, Delete } from '@material-ui/icons'; | import { Delete, Edit, Lock } from '@material-ui/icons'; | ||||||
| import { SyntheticEvent, useContext } from 'react'; | import { SyntheticEvent, useContext } from 'react'; | ||||||
| import { ADMIN } from '../../../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../../../providers/AccessProvider/permissions'; | ||||||
| import ConditionallyRender from '../../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../../common/ConditionallyRender'; | ||||||
| import { formatDateWithLocale } from '../../../../common/util'; |  | ||||||
| import AccessContext from '../../../../../contexts/AccessContext'; | import AccessContext from '../../../../../contexts/AccessContext'; | ||||||
| import { IUser } from '../../../../../interfaces/user'; | import { IUser } from '../../../../../interfaces/user'; | ||||||
| import { useStyles } from './UserListItem.styles'; | import { useStyles } from './UserListItem.styles'; | ||||||
| import { useHistory } from 'react-router-dom'; | import { useHistory } from 'react-router-dom'; | ||||||
| import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; | import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; | ||||||
|  | import { formatDateYMD } from '../../../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| interface IUserListItemProps { | interface IUserListItemProps { | ||||||
|     user: IUser; |     user: IUser; | ||||||
|     renderRole: (roleId: number) => string; |     renderRole: (roleId: number) => string; | ||||||
|     openUpdateDialog: (user: IUser) => (e: SyntheticEvent) => void; |  | ||||||
|     openPwDialog: (user: IUser) => (e: SyntheticEvent) => void; |     openPwDialog: (user: IUser) => (e: SyntheticEvent) => void; | ||||||
|     openDelDialog: (user: IUser) => (e: SyntheticEvent) => void; |     openDelDialog: (user: IUser) => (e: SyntheticEvent) => void; | ||||||
|     locationSettings: ILocationSettings; |     locationSettings: ILocationSettings; | ||||||
| @ -30,7 +29,6 @@ const UserListItem = ({ | |||||||
|     renderRole, |     renderRole, | ||||||
|     openDelDialog, |     openDelDialog, | ||||||
|     openPwDialog, |     openPwDialog, | ||||||
|     openUpdateDialog, |  | ||||||
|     locationSettings, |     locationSettings, | ||||||
| }: IUserListItemProps) => { | }: IUserListItemProps) => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
| @ -51,10 +49,7 @@ const UserListItem = ({ | |||||||
|             </TableCell> |             </TableCell> | ||||||
|             <TableCell> |             <TableCell> | ||||||
|                 <span data-loading> |                 <span data-loading> | ||||||
|                     {formatDateWithLocale( |                     {formatDateYMD(user.createdAt, locationSettings.locale)} | ||||||
|                         user.createdAt, |  | ||||||
|                         locationSettings.locale |  | ||||||
|                     )} |  | ||||||
|                 </span> |                 </span> | ||||||
|             </TableCell> |             </TableCell> | ||||||
|             <TableCell className={styles.leftTableCell}> |             <TableCell className={styles.leftTableCell}> | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ import { IUser } from '../../../../interfaces/user'; | |||||||
| import IRole from '../../../../interfaces/role'; | import IRole from '../../../../interfaces/role'; | ||||||
| import useToast from '../../../../hooks/useToast'; | import useToast from '../../../../hooks/useToast'; | ||||||
| import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const UsersList = () => { | const UsersList = () => { | ||||||
|     const { users, roles, refetch, loading } = useUsers(); |     const { users, roles, refetch, loading } = useUsers(); | ||||||
| @ -79,8 +80,8 @@ const UsersList = () => { | |||||||
|             }); |             }); | ||||||
|             refetch(); |             refetch(); | ||||||
|             closeDelDialog(); |             closeDelDialog(); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -172,7 +173,7 @@ const UsersList = () => { | |||||||
|                     <DeleteUser |                     <DeleteUser | ||||||
|                         showDialog={delDialog} |                         showDialog={delDialog} | ||||||
|                         closeDialog={closeDelDialog} |                         closeDialog={closeDelDialog} | ||||||
|                         user={delUser} |                         user={delUser!} | ||||||
|                         removeUser={onDeleteUser} |                         removeUser={onDeleteUser} | ||||||
|                         userLoading={userLoading} |                         userLoading={userLoading} | ||||||
|                         userApiErrors={userApiErrors} |                         userApiErrors={userApiErrors} | ||||||
|  | |||||||
| @ -1,16 +1,15 @@ | |||||||
| /* eslint react/no-multi-comp:off */ | /* eslint react/no-multi-comp:off */ | ||||||
| import { useContext, useState } from 'react'; | import React, { useContext, useState } from 'react'; | ||||||
| import { | import { | ||||||
|     Avatar, |     Avatar, | ||||||
|     Link, |  | ||||||
|     Icon, |     Icon, | ||||||
|     IconButton, |     IconButton, | ||||||
|     LinearProgress, |     LinearProgress, | ||||||
|  |     Link, | ||||||
|     Typography, |     Typography, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { Link as LinkIcon } from '@material-ui/icons'; | import { Link as LinkIcon } from '@material-ui/icons'; | ||||||
| import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; | import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; | ||||||
| import { formatDateWithLocale } from '../../common/util'; |  | ||||||
| import { UPDATE_APPLICATION } from '../../providers/AccessProvider/permissions'; | import { UPDATE_APPLICATION } from '../../providers/AccessProvider/permissions'; | ||||||
| import { ApplicationView } from '../ApplicationView/ApplicationView'; | import { ApplicationView } from '../ApplicationView/ApplicationView'; | ||||||
| import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate'; | import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate'; | ||||||
| @ -25,6 +24,8 @@ import { useHistory, useParams } from 'react-router-dom'; | |||||||
| import { useLocationSettings } from '../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../hooks/useLocationSettings'; | ||||||
| import useToast from '../../../hooks/useToast'; | import useToast from '../../../hooks/useToast'; | ||||||
| import PermissionButton from '../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../common/PermissionButton/PermissionButton'; | ||||||
|  | import { formatDateYMD } from '../../../utils/format-date'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| export const ApplicationEdit = () => { | export const ApplicationEdit = () => { | ||||||
|     const history = useHistory(); |     const history = useHistory(); | ||||||
| @ -42,10 +43,9 @@ export const ApplicationEdit = () => { | |||||||
|         setShowDialog(!showDialog); |         setShowDialog(!showDialog); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const formatDate = (v: string) => |     const formatDate = (v: string) => formatDateYMD(v, locationSettings.locale); | ||||||
|         formatDateWithLocale(v, locationSettings.locale); |  | ||||||
| 
 | 
 | ||||||
|     const onDeleteApplication = async (evt: Event) => { |     const onDeleteApplication = async (evt: React.SyntheticEvent) => { | ||||||
|         evt.preventDefault(); |         evt.preventDefault(); | ||||||
|         try { |         try { | ||||||
|             await deleteApplication(appName); |             await deleteApplication(appName); | ||||||
| @ -55,8 +55,8 @@ export const ApplicationEdit = () => { | |||||||
|                 type: 'success', |                 type: 'success', | ||||||
|             }); |             }); | ||||||
|             history.push('/applications'); |             history.push('/applications'); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { ChangeEvent, useState } from 'react'; | import { ChangeEvent, useState } from 'react'; | ||||||
| import { TextField, Grid } from '@material-ui/core'; | import { Grid, TextField } from '@material-ui/core'; | ||||||
| import { useCommonStyles } from '../../../common.styles'; | import { useCommonStyles } from '../../../common.styles'; | ||||||
| import icons from '../icon-names'; | import icons from '../icon-names'; | ||||||
| import GeneralSelect from '../../common/GeneralSelect/GeneralSelect'; | import GeneralSelect from '../../common/GeneralSelect/GeneralSelect'; | ||||||
| @ -7,6 +7,7 @@ import useApplicationsApi from '../../../hooks/api/actions/useApplicationsApi/us | |||||||
| import useToast from '../../../hooks/useToast'; | import useToast from '../../../hooks/useToast'; | ||||||
| import { IApplication } from '../../../interfaces/application'; | import { IApplication } from '../../../interfaces/application'; | ||||||
| import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; | import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| interface IApplicationUpdateProps { | interface IApplicationUpdateProps { | ||||||
|     application: IApplication; |     application: IApplication; | ||||||
| @ -35,8 +36,8 @@ export const ApplicationUpdate = ({ application }: IApplicationUpdateProps) => { | |||||||
|                 title: 'Updated Successfully', |                 title: 'Updated Successfully', | ||||||
|                 text: `${field} successfully updated`, |                 text: `${field} successfully updated`, | ||||||
|             }); |             }); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,16 +4,16 @@ import { | |||||||
|     Grid, |     Grid, | ||||||
|     List, |     List, | ||||||
|     ListItem, |     ListItem, | ||||||
|     ListItemText, |  | ||||||
|     ListItemAvatar, |     ListItemAvatar, | ||||||
|  |     ListItemText, | ||||||
|     Typography, |     Typography, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { | import { | ||||||
|     Report, |  | ||||||
|     Extension, |     Extension, | ||||||
|     Timeline, |  | ||||||
|     FlagRounded, |     FlagRounded, | ||||||
|  |     Report, | ||||||
|     SvgIconComponent, |     SvgIconComponent, | ||||||
|  |     Timeline, | ||||||
| } from '@material-ui/icons'; | } from '@material-ui/icons'; | ||||||
| import { | import { | ||||||
|     CREATE_FEATURE, |     CREATE_FEATURE, | ||||||
| @ -23,13 +23,16 @@ import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyR | |||||||
| import { getTogglePath } from '../../../utils/route-path-helpers'; | import { getTogglePath } from '../../../utils/route-path-helpers'; | ||||||
| import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; | import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; | ||||||
| import AccessContext from '../../../contexts/AccessContext'; | import AccessContext from '../../../contexts/AccessContext'; | ||||||
| import { formatFullDateTimeWithLocale } from '../../common/util'; | import { formatDateYMDHMS } from '../../../utils/format-date'; | ||||||
|  | import { useLocationSettings } from '../../../hooks/useLocationSettings'; | ||||||
| 
 | 
 | ||||||
| export const ApplicationView = () => { | export const ApplicationView = () => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
|     const { name } = useParams<{ name: string }>(); |     const { name } = useParams<{ name: string }>(); | ||||||
|     const { application } = useApplication(name); |     const { application } = useApplication(name); | ||||||
|  |     const { locationSettings } = useLocationSettings(); | ||||||
|     const { instances, strategies, seenToggles } = application; |     const { instances, strategies, seenToggles } = application; | ||||||
|  | 
 | ||||||
|     const notFoundListItem = ({ |     const notFoundListItem = ({ | ||||||
|         createUrl, |         createUrl, | ||||||
|         name, |         name, | ||||||
| @ -114,10 +117,9 @@ export const ApplicationView = () => { | |||||||
|                                     createUrl: `/projects/default/create-toggle?name=${name}`, |                                     createUrl: `/projects/default/create-toggle?name=${name}`, | ||||||
|                                     name, |                                     name, | ||||||
|                                     permission: CREATE_FEATURE, |                                     permission: CREATE_FEATURE, | ||||||
|                                     i, |  | ||||||
|                                 })} |                                 })} | ||||||
|                                 elseShow={foundListItem({ |                                 elseShow={foundListItem({ | ||||||
|                                     viewUrl: getTogglePath(project, name, true), |                                     viewUrl: getTogglePath(project, name), | ||||||
|                                     name, |                                     name, | ||||||
|                                     description, |                                     description, | ||||||
|                                     Icon: FlagRounded, |                                     Icon: FlagRounded, | ||||||
| @ -195,8 +197,9 @@ export const ApplicationView = () => { | |||||||
|                                         <span> |                                         <span> | ||||||
|                                             {clientIp} last seen at{' '} |                                             {clientIp} last seen at{' '} | ||||||
|                                             <small> |                                             <small> | ||||||
|                                                 {formatFullDateTimeWithLocale( |                                                 {formatDateYMDHMS( | ||||||
|                                                     lastSeen |                                                     lastSeen, | ||||||
|  |                                                     locationSettings.locale | ||||||
|                                                 )} |                                                 )} | ||||||
|                                             </small> |                                             </small> | ||||||
|                                         </span> |                                         </span> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { useEffect, useState, useRef, FC } from 'react'; | import React, { useEffect, useState, useRef, FC } from 'react'; | ||||||
| import ConditionallyRender from '../ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender'; | ||||||
| 
 | 
 | ||||||
| interface IAnimateOnMountProps { | interface IAnimateOnMountProps { | ||||||
| @ -7,7 +7,7 @@ interface IAnimateOnMountProps { | |||||||
|     start: string; |     start: string; | ||||||
|     leave: string; |     leave: string; | ||||||
|     container?: string; |     container?: string; | ||||||
|     style?: Object; |     style?: React.CSSProperties; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const AnimateOnMount: FC<IAnimateOnMountProps> = ({ | const AnimateOnMount: FC<IAnimateOnMountProps> = ({ | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { Button } from '@material-ui/core'; | import { Button } from '@material-ui/core'; | ||||||
| import { Alert } from '@material-ui/lab'; | import { Alert } from '@material-ui/lab'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IApiErrorProps { | interface IApiErrorProps { | ||||||
|     className?: string; |     className?: string; | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| interface IConditionallyRenderProps { | interface IConditionallyRenderProps { | ||||||
|     condition: boolean; |     condition: boolean; | ||||||
|     show: JSX.Element | RenderFunc; |     show: TargetElement; | ||||||
|     elseShow?: JSX.Element | RenderFunc; |     elseShow?: TargetElement; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type TargetElement = JSX.Element | JSX.Element[] | RenderFunc | null; | ||||||
| type RenderFunc = () => JSX.Element; | type RenderFunc = () => JSX.Element; | ||||||
| 
 | 
 | ||||||
| const ConditionallyRender = ({ | const ConditionallyRender = ({ | ||||||
| @ -23,8 +24,9 @@ const ConditionallyRender = ({ | |||||||
|         return result; |         return result; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const isFunc = (param: JSX.Element | RenderFunc) => |     const isFunc = (param: TargetElement): boolean => { | ||||||
|         typeof param === 'function'; |         return typeof param === 'function'; | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     if (condition) { |     if (condition) { | ||||||
|         if (isFunc(show)) { |         if (isFunc(show)) { | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { | import { | ||||||
|  |     Button, | ||||||
|     Dialog, |     Dialog, | ||||||
|     DialogTitle, |  | ||||||
|     DialogActions, |     DialogActions, | ||||||
|     DialogContent, |     DialogContent, | ||||||
|     Button, |     DialogTitle, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| 
 | 
 | ||||||
| import ConditionallyRender from '../ConditionallyRender/ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender/ConditionallyRender'; | ||||||
| @ -15,15 +15,15 @@ interface IDialogue { | |||||||
|     primaryButtonText?: string; |     primaryButtonText?: string; | ||||||
|     secondaryButtonText?: string; |     secondaryButtonText?: string; | ||||||
|     open: boolean; |     open: boolean; | ||||||
|     onClick: (e: any) => void; |     onClick: (e: React.SyntheticEvent) => void; | ||||||
|     onClose: () => void; |     onClose?: (e: React.SyntheticEvent) => void; | ||||||
|     style?: object; |     style?: object; | ||||||
|     title: string; |     title: string; | ||||||
|     fullWidth?: boolean; |     fullWidth?: boolean; | ||||||
|     maxWidth?: 'lg' | 'sm' | 'xs' | 'md' | 'xl'; |     maxWidth?: 'lg' | 'sm' | 'xs' | 'md' | 'xl'; | ||||||
|     disabledPrimaryButton?: boolean; |     disabledPrimaryButton?: boolean; | ||||||
|     formId?: string; |     formId?: string; | ||||||
|     permissionButton?: React.ReactNode; |     permissionButton?: JSX.Element; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Dialogue: React.FC<IDialogue> = ({ | const Dialogue: React.FC<IDialogue> = ({ | ||||||
| @ -69,7 +69,7 @@ const Dialogue: React.FC<IDialogue> = ({ | |||||||
|             <DialogActions> |             <DialogActions> | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={Boolean(permissionButton)} |                     condition={Boolean(permissionButton)} | ||||||
|                     show={permissionButton} |                     show={permissionButton!} | ||||||
|                     elseShow={ |                     elseShow={ | ||||||
|                         <ConditionallyRender |                         <ConditionallyRender | ||||||
|                             condition={Boolean(onClick)} |                             condition={Boolean(onClick)} | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | 
 | ||||||
| interface EnvironmentSplashPageProps { | interface EnvironmentSplashPageProps { | ||||||
|     title: React.ReactNode; |     title: React.ReactNode; | ||||||
|     topDescription: React.ReactNode; |     topDescription: React.ReactNode; | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import ConditionallyRender from '../ConditionallyRender'; | |||||||
| import Loader from '../Loader/Loader'; | import Loader from '../Loader/Loader'; | ||||||
| import copy from 'copy-to-clipboard'; | import copy from 'copy-to-clipboard'; | ||||||
| import useToast from '../../../hooks/useToast'; | import useToast from '../../../hooks/useToast'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface ICreateProps { | interface ICreateProps { | ||||||
|     title: string; |     title: string; | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | 
 | ||||||
| interface IGradientProps { | interface IGradientProps { | ||||||
|     from: string; |     from: string; | ||||||
|     to: string; |     to: string; | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { TextField } from '@material-ui/core'; | import { TextField } from '@material-ui/core'; | ||||||
| import { INPUT_ERROR_TEXT } from '../../../testIds'; | import { INPUT_ERROR_TEXT } from '../../../testIds'; | ||||||
| import { useStyles } from './Input.styles.ts'; | import { useStyles } from './Input.styles'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> { | interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> { | ||||||
|     label: string; |     label: string; | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { ReactComponent as NoItemsIcon } from '../../../assets/icons/addfiles.svg'; | import { ReactComponent as NoItemsIcon } from '../../../assets/icons/addfiles.svg'; | ||||||
| import { useStyles } from './NoItems.styles'; | import { useStyles } from './NoItems.styles'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| const NoItems: React.FC = ({ children }) => { | const NoItems: React.FC = ({ children }) => { | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { useState, useEffect } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||||
| import ConditionallyRender from '../ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender'; | ||||||
| import classnames from 'classnames'; | import classnames from 'classnames'; | ||||||
| import { useStyles } from './PaginationUI.styles'; | import { useStyles } from './PaginationUI.styles'; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { IconButton, InputAdornment, TextField } from '@material-ui/core'; | import { IconButton, InputAdornment, TextField } from '@material-ui/core'; | ||||||
| import { Visibility, VisibilityOff } from '@material-ui/icons'; | import { Visibility, VisibilityOff } from '@material-ui/icons'; | ||||||
| import { useState } from 'react'; | import React, { useState } from 'react'; | ||||||
| 
 | 
 | ||||||
| const PasswordField = ({ ...rest }) => { | const PasswordField = ({ ...rest }) => { | ||||||
|     const [showPassword, setShowPassword] = useState(false); |     const [showPassword, setShowPassword] = useState(false); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { Button, Tooltip } from '@material-ui/core'; | import { Button, Tooltip } from '@material-ui/core'; | ||||||
| import { Lock } from '@material-ui/icons'; | import { Lock } from '@material-ui/icons'; | ||||||
| import { useContext } from 'react'; | import React, { useContext } from 'react'; | ||||||
| import AccessContext from '../../../contexts/AccessContext'; | import AccessContext from '../../../contexts/AccessContext'; | ||||||
| import ConditionallyRender from '../ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { IconButton, Tooltip } from '@material-ui/core'; | import { IconButton, Tooltip } from '@material-ui/core'; | ||||||
| import { useContext } from 'react'; | import React, { useContext } from 'react'; | ||||||
| import AccessContext from '../../../contexts/AccessContext'; | import AccessContext from '../../../contexts/AccessContext'; | ||||||
| 
 | 
 | ||||||
| interface IPermissionIconButtonProps | interface IPermissionIconButtonProps | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ import { useMediaQuery } from '@material-ui/core'; | |||||||
| import ConditionallyRender from '../ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender'; | ||||||
| import PermissionButton from '../PermissionButton/PermissionButton'; | import PermissionButton from '../PermissionButton/PermissionButton'; | ||||||
| import PermissionIconButton from '../PermissionIconButton/PermissionIconButton'; | import PermissionIconButton from '../PermissionIconButton/PermissionIconButton'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IResponsiveButtonProps { | interface IResponsiveButtonProps { | ||||||
|     Icon: React.ElementType; |     Icon: React.ElementType; | ||||||
|  | |||||||
| @ -1,12 +1,11 @@ | |||||||
| import { Fragment } from 'react'; | import React, { Fragment, useState } from 'react'; | ||||||
| import { Button, IconButton } from '@material-ui/core'; | import { Button, IconButton } from '@material-ui/core'; | ||||||
| import { useStyles } from './Splash.styles'; | import { useStyles } from './Splash.styles'; | ||||||
| import { | import { | ||||||
|  |     CloseOutlined, | ||||||
|     FiberManualRecord, |     FiberManualRecord, | ||||||
|     FiberManualRecordOutlined, |     FiberManualRecordOutlined, | ||||||
|     CloseOutlined, |  | ||||||
| } from '@material-ui/icons'; | } from '@material-ui/icons'; | ||||||
| import { useState } from 'react'; |  | ||||||
| import ConditionallyRender from '../ConditionallyRender'; | import ConditionallyRender from '../ConditionallyRender'; | ||||||
| import { CLOSE_SPLASH } from '../../../testIds'; | import { CLOSE_SPLASH } from '../../../testIds'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ interface ITagSelect extends React.SelectHTMLAttributes<HTMLSelectElement> { | |||||||
|     onChange: (val: any) => void; |     onChange: (val: any) => void; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const TagSelect = ({ value, types, onChange, ...rest }: ITagSelect) => { | const TagSelect = ({ value, onChange, ...rest }: ITagSelect) => { | ||||||
|     const { tagTypes } = useTagTypes(); |     const { tagTypes } = useTagTypes(); | ||||||
| 
 | 
 | ||||||
|     const options = tagTypes.map(tagType => ({ |     const options = tagTypes.map(tagType => ({ | ||||||
|  | |||||||
| @ -3,12 +3,12 @@ import classnames from 'classnames'; | |||||||
| import { useContext } from 'react'; | import { useContext } from 'react'; | ||||||
| import { IconButton } from '@material-ui/core'; | import { IconButton } from '@material-ui/core'; | ||||||
| import CheckMarkBadge from '../../CheckmarkBadge/CheckMarkBadge'; | import CheckMarkBadge from '../../CheckmarkBadge/CheckMarkBadge'; | ||||||
| import UIContext, { IToastData } from '../../../../contexts/UIContext'; | import UIContext from '../../../../contexts/UIContext'; | ||||||
| import ConditionallyRender from '../../ConditionallyRender'; | import ConditionallyRender from '../../ConditionallyRender'; | ||||||
| import Close from '@material-ui/icons/Close'; | import Close from '@material-ui/icons/Close'; | ||||||
|  | import { IToast } from '../../../../interfaces/toast'; | ||||||
| 
 | 
 | ||||||
| const Toast = ({ title, text, type, confetti }: IToastData) => { | const Toast = ({ title, text, type, confetti }: IToast) => { | ||||||
|     // @ts-expect-error
 |  | ||||||
|     const { setToast } = useContext(UIContext); |     const { setToast } = useContext(UIContext); | ||||||
| 
 | 
 | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
| @ -51,7 +51,7 @@ const Toast = ({ title, text, type, confetti }: IToastData) => { | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const hide = () => { |     const hide = () => { | ||||||
|         setToast((prev: IToastData) => ({ ...prev, show: false })); |         setToast((prev: IToast) => ({ ...prev, show: false })); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|  | |||||||
| @ -1,19 +1,19 @@ | |||||||
| import { Portal } from '@material-ui/core'; | import { Portal } from '@material-ui/core'; | ||||||
| import { useContext, useEffect } from 'react'; | import { useContext, useEffect } from 'react'; | ||||||
| import { useCommonStyles } from '../../../common.styles'; | import { useCommonStyles } from '../../../common.styles'; | ||||||
| import UIContext, { IToastData } from '../../../contexts/UIContext'; | import UIContext from '../../../contexts/UIContext'; | ||||||
| import { useStyles } from './ToastRenderer.styles'; | import { useStyles } from './ToastRenderer.styles'; | ||||||
| import AnimateOnMount from '../AnimateOnMount/AnimateOnMount'; | import AnimateOnMount from '../AnimateOnMount/AnimateOnMount'; | ||||||
| import Toast from './Toast/Toast'; | import Toast from './Toast/Toast'; | ||||||
|  | import { IToast } from '../../../interfaces/toast'; | ||||||
| 
 | 
 | ||||||
| const ToastRenderer = () => { | const ToastRenderer = () => { | ||||||
|     // @ts-expect-error
 |  | ||||||
|     const { toastData, setToast } = useContext(UIContext); |     const { toastData, setToast } = useContext(UIContext); | ||||||
|     const commonStyles = useCommonStyles(); |     const commonStyles = useCommonStyles(); | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
| 
 | 
 | ||||||
|     const hide = () => { |     const hide = () => { | ||||||
|         setToast((prev: IToastData) => ({ ...prev, show: false })); |         setToast((prev: IToast) => ({ ...prev, show: false })); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
| @ -31,7 +31,7 @@ const ToastRenderer = () => { | |||||||
|     return ( |     return ( | ||||||
|         <Portal> |         <Portal> | ||||||
|             <AnimateOnMount |             <AnimateOnMount | ||||||
|                 mounted={toastData?.show} |                 mounted={Boolean(toastData?.show)} | ||||||
|                 start={commonStyles.fadeInBottomStartWithoutFixed} |                 start={commonStyles.fadeInBottomStartWithoutFixed} | ||||||
|                 enter={commonStyles.fadeInBottomEnter} |                 enter={commonStyles.fadeInBottomEnter} | ||||||
|                 leave={commonStyles.fadeInBottomLeave} |                 leave={commonStyles.fadeInBottomLeave} | ||||||
|  | |||||||
| @ -1,35 +0,0 @@ | |||||||
| import { formatFullDateTimeWithLocale } from '../util'; |  | ||||||
| 
 |  | ||||||
| test.skip('formats dates correctly', () => { |  | ||||||
|     expect(formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'UTC')).toEqual( |  | ||||||
|         '2017-02-23 14:56:49' |  | ||||||
|     ); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/Paris') |  | ||||||
|     ).toEqual('2017-02-23 15:56:49'); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/Oslo') |  | ||||||
|     ).toEqual('2017-02-23 15:56:49'); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/London') |  | ||||||
|     ).toEqual('2017-02-23 14:56:49'); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/Paris') |  | ||||||
|     ).toEqual('02/23/2017, 3:56:49 PM'); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/Oslo') |  | ||||||
|     ).toEqual('02/23/2017, 3:56:49 PM'); |  | ||||||
|     expect( |  | ||||||
|         formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/London') |  | ||||||
|     ).toEqual('02/23/2017, 2:56:49 PM'); |  | ||||||
| 
 |  | ||||||
|     expect(formatFullDateTimeWithLocale(1487861809466, 'nb-NO')).toEqual( |  | ||||||
|         expect.stringMatching(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) |  | ||||||
|     ); |  | ||||||
|     expect(formatFullDateTimeWithLocale(1487861809466, 'en-GB')).toEqual( |  | ||||||
|         expect.stringContaining('02/23/2017') |  | ||||||
|     ); |  | ||||||
|     expect(formatFullDateTimeWithLocale(1487861809466, 'en-US')).toEqual( |  | ||||||
|         expect.stringContaining('02/23/2017') |  | ||||||
|     ); |  | ||||||
| }); |  | ||||||
| @ -1,26 +1,6 @@ | |||||||
| import { weightTypes } from '../feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/enums'; | import { weightTypes } from '../feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/enums'; | ||||||
| import differenceInDays from 'date-fns/differenceInDays'; | import differenceInDays from 'date-fns/differenceInDays'; | ||||||
| 
 | 
 | ||||||
| const dateTimeOptions = { |  | ||||||
|     day: '2-digit', |  | ||||||
|     month: '2-digit', |  | ||||||
|     year: 'numeric', |  | ||||||
|     hour: '2-digit', |  | ||||||
|     minute: '2-digit', |  | ||||||
|     second: '2-digit', |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const dateOptions = { |  | ||||||
|     day: '2-digit', |  | ||||||
|     month: '2-digit', |  | ||||||
|     year: 'numeric', |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const timeOptions = { |  | ||||||
|     hour: '2-digit', |  | ||||||
|     minute: '2-digit', |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const filterByFlags = flags => r => { | export const filterByFlags = flags => r => { | ||||||
|     if (r.flag && !flags[r.flag]) { |     if (r.flag && !flags[r.flag]) { | ||||||
|         return false; |         return false; | ||||||
| @ -32,27 +12,6 @@ export const scrollToTop = () => { | |||||||
|     window.scrollTo(0, 0); |     window.scrollTo(0, 0); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const formatFullDateTimeWithLocale = (v, locale, tz) => { |  | ||||||
|     if (tz) { |  | ||||||
|         dateTimeOptions.timeZone = tz; |  | ||||||
|     } |  | ||||||
|     return new Date(v).toLocaleString(locale, dateTimeOptions); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const formatDateWithLocale = (v, locale, tz) => { |  | ||||||
|     if (tz) { |  | ||||||
|         dateTimeOptions.timeZone = tz; |  | ||||||
|     } |  | ||||||
|     return new Date(v).toLocaleString(locale, dateOptions); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const formatTimeWithLocale = (v, locale, tz) => { |  | ||||||
|     if (tz) { |  | ||||||
|         dateTimeOptions.timeZone = tz; |  | ||||||
|     } |  | ||||||
|     return new Date(v).toLocaleString(locale, timeOptions); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const trim = value => { | export const trim = value => { | ||||||
|     if (value && value.trim) { |     if (value && value.trim) { | ||||||
|         return value.trim(); |         return value.trim(); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import { | |||||||
|     UPDATE_CONTEXT_FIELD, |     UPDATE_CONTEXT_FIELD, | ||||||
| } from '../../providers/AccessProvider/permissions'; | } from '../../providers/AccessProvider/permissions'; | ||||||
| import { | import { | ||||||
|  |     Button, | ||||||
|     IconButton, |     IconButton, | ||||||
|     List, |     List, | ||||||
|     ListItem, |     ListItem, | ||||||
| @ -14,7 +15,6 @@ import { | |||||||
|     ListItemText, |     ListItemText, | ||||||
|     Tooltip, |     Tooltip, | ||||||
|     useMediaQuery, |     useMediaQuery, | ||||||
|     Button, |  | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { Add, Album, Delete, Edit } from '@material-ui/icons'; | import { Add, Album, Delete, Edit } from '@material-ui/icons'; | ||||||
| import { useContext, useState } from 'react'; | import { useContext, useState } from 'react'; | ||||||
| @ -25,6 +25,7 @@ import AccessContext from '../../../contexts/AccessContext'; | |||||||
| import useUnleashContext from '../../../hooks/api/getters/useUnleashContext/useUnleashContext'; | import useUnleashContext from '../../../hooks/api/getters/useUnleashContext/useUnleashContext'; | ||||||
| import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi'; | import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi'; | ||||||
| import useToast from '../../../hooks/useToast'; | import useToast from '../../../hooks/useToast'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const ContextList = () => { | const ContextList = () => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
| @ -46,8 +47,8 @@ const ContextList = () => { | |||||||
|                 title: 'Successfully deleted context', |                 title: 'Successfully deleted context', | ||||||
|                 text: 'Your context is now deleted', |                 text: 'Your context is now deleted', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|         setName(undefined); |         setName(undefined); | ||||||
|         setShowDelDialogue(false); |         setShowDelDialogue(false); | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ export const EditContext = () => { | |||||||
|     const handleSubmit = async (e: Event) => { |     const handleSubmit = async (e: Event) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         const payload = getContextPayload(); |         const payload = getContextPayload(); | ||||||
| 
 |        | ||||||
|         try { |         try { | ||||||
|             await updateContext(payload); |             await updateContext(payload); | ||||||
|             refetch(); |             refetch(); | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ import HeaderTitle from '../../common/HeaderTitle'; | |||||||
| import PermissionButton from '../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../common/PermissionButton/PermissionButton'; | ||||||
| import { ADMIN } from '../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../providers/AccessProvider/permissions'; | ||||||
| import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const CreateEnvironment = () => { | const CreateEnvironment = () => { | ||||||
|     const { setToastApiError, setToastData } = useToast(); |     const { setToastApiError, setToastData } = useToast(); | ||||||
| @ -49,8 +50,8 @@ const CreateEnvironment = () => { | |||||||
|                     confetti: true, |                     confetti: true, | ||||||
|                 }); |                 }); | ||||||
|                 history.push('/environments'); |                 history.push('/environments'); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; | |||||||
| import { ADMIN } from '../../providers/AccessProvider/permissions'; | import { ADMIN } from '../../providers/AccessProvider/permissions'; | ||||||
| import EnvironmentForm from '../EnvironmentForm/EnvironmentForm'; | import EnvironmentForm from '../EnvironmentForm/EnvironmentForm'; | ||||||
| import useEnvironmentForm from '../hooks/useEnvironmentForm'; | import useEnvironmentForm from '../hooks/useEnvironmentForm'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EditEnvironment = () => { | const EditEnvironment = () => { | ||||||
|     const { uiConfig } = useUiConfig(); |     const { uiConfig } = useUiConfig(); | ||||||
| @ -49,8 +50,8 @@ const EditEnvironment = () => { | |||||||
|                 type: 'success', |                 type: 'success', | ||||||
|                 title: 'Successfully updated environment.', |                 title: 'Successfully updated environment.', | ||||||
|             }); |             }); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -61,7 +62,7 @@ const EditEnvironment = () => { | |||||||
|     return ( |     return ( | ||||||
|         <FormTemplate |         <FormTemplate | ||||||
|             title="Edit environment" |             title="Edit environment" | ||||||
|             description="Environments allow you to manage your  |             description="Environments allow you to manage your | ||||||
|             product lifecycle from local development |             product lifecycle from local development | ||||||
|             through production. Your projects and |             through production. Your projects and | ||||||
|             feature toggles are accessible in all your |             feature toggles are accessible in all your | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| import { | import { | ||||||
|     FormControl, |     FormControl, | ||||||
|     FormControlLabel, |     FormControlLabel, | ||||||
|     RadioGroup, |  | ||||||
|     Radio, |     Radio, | ||||||
|  |     RadioGroup, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { useStyles } from './EnvironmentTypeSelector.styles'; | import { useStyles } from './EnvironmentTypeSelector.styles'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IEnvironmentTypeSelectorProps { | interface IEnvironmentTypeSelectorProps { | ||||||
|     onChange: (event: React.FormEvent<HTMLInputElement>) => void; |     onChange: (event: React.FormEvent<HTMLInputElement>) => void; | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ interface IEnviromentDeleteConfirmProps { | |||||||
|     open: boolean; |     open: boolean; | ||||||
|     setSelectedEnv: React.Dispatch<React.SetStateAction<IEnvironment>>; |     setSelectedEnv: React.Dispatch<React.SetStateAction<IEnvironment>>; | ||||||
|     setDeldialogue: React.Dispatch<React.SetStateAction<boolean>>; |     setDeldialogue: React.Dispatch<React.SetStateAction<boolean>>; | ||||||
|     handleDeleteEnvironment: (name: string) => Promise<void>; |     handleDeleteEnvironment: () => Promise<void>; | ||||||
|     confirmName: string; |     confirmName: string; | ||||||
|     setConfirmName: React.Dispatch<React.SetStateAction<string>>; |     setConfirmName: React.Dispatch<React.SetStateAction<string>>; | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ import EnvironmentToggleConfirm from './EnvironmentToggleConfirm/EnvironmentTogg | |||||||
| import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; | ||||||
| import { ADMIN } from 'component/providers/AccessProvider/permissions'; | import { ADMIN } from 'component/providers/AccessProvider/permissions'; | ||||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EnvironmentList = () => { | const EnvironmentList = () => { | ||||||
|     const defaultEnv = { |     const defaultEnv = { | ||||||
| @ -75,16 +76,16 @@ const EnvironmentList = () => { | |||||||
|         try { |         try { | ||||||
|             await sortOrderAPICall(sortOrder); |             await sortOrderAPICall(sortOrder); | ||||||
|             refetch(); |             refetch(); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const sortOrderAPICall = async (sortOrder: ISortOrderPayload) => { |     const sortOrderAPICall = async (sortOrder: ISortOrderPayload) => { | ||||||
|         try { |         try { | ||||||
|             await changeSortOrder(sortOrder); |             await changeSortOrder(sortOrder); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -97,8 +98,8 @@ const EnvironmentList = () => { | |||||||
|                 title: 'Project environment deleted', |                 title: 'Project environment deleted', | ||||||
|                 text: 'You have successfully deleted the project environment.', |                 text: 'You have successfully deleted the project environment.', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } finally { |         } finally { | ||||||
|             setDeldialogue(false); |             setDeldialogue(false); | ||||||
|             setSelectedEnv(defaultEnv); |             setSelectedEnv(defaultEnv); | ||||||
| @ -124,8 +125,8 @@ const EnvironmentList = () => { | |||||||
|                 title: 'Project environment enabled', |                 title: 'Project environment enabled', | ||||||
|                 text: 'Your environment is enabled', |                 text: 'Your environment is enabled', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } finally { |         } finally { | ||||||
|             refetch(); |             refetch(); | ||||||
|         } |         } | ||||||
| @ -140,8 +141,8 @@ const EnvironmentList = () => { | |||||||
|                 title: 'Project environment disabled', |                 title: 'Project environment disabled', | ||||||
|                 text: 'Your environment is disabled.', |                 text: 'Your environment is disabled.', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } finally { |         } finally { | ||||||
|             refetch(); |             refetch(); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; | |||||||
| import { CF_CREATE_BTN_ID } from '../../../testIds'; | import { CF_CREATE_BTN_ID } from '../../../testIds'; | ||||||
| import { useContext } from 'react'; | import { useContext } from 'react'; | ||||||
| import UIContext from '../../../contexts/UIContext'; | import UIContext from '../../../contexts/UIContext'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const CreateFeature = () => { | const CreateFeature = () => { | ||||||
|     const { setToastData, setToastApiError } = useToast(); |     const { setToastData, setToastApiError } = useToast(); | ||||||
| @ -53,8 +54,8 @@ const CreateFeature = () => { | |||||||
|                     type: 'success', |                     type: 'success', | ||||||
|                 }); |                 }); | ||||||
|                 setShowFeedback(true); |                 setShowFeedback(true); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import { IFeatureViewParams } from '../../../interfaces/params'; | |||||||
| import * as jsonpatch from 'fast-json-patch'; | import * as jsonpatch from 'fast-json-patch'; | ||||||
| import PermissionButton from '../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../common/PermissionButton/PermissionButton'; | ||||||
| import { UPDATE_FEATURE } from '../../providers/AccessProvider/permissions'; | import { UPDATE_FEATURE } from '../../providers/AccessProvider/permissions'; | ||||||
|  | import { formatUnknownError } from '../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EditFeature = () => { | const EditFeature = () => { | ||||||
|     const { setToastData, setToastApiError } = useToast(); |     const { setToastData, setToastApiError } = useToast(); | ||||||
| @ -57,8 +58,8 @@ const EditFeature = () => { | |||||||
|                 title: 'Toggle updated successfully', |                 title: 'Toggle updated successfully', | ||||||
|                 type: 'success', |                 type: 'success', | ||||||
|             }); |             }); | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| import { Tooltip } from '@material-ui/core'; | import { Tooltip } from '@material-ui/core'; | ||||||
| import { |  | ||||||
|     formatDateWithLocale, |  | ||||||
|     formatFullDateTimeWithLocale, |  | ||||||
| } from '../../../common/util'; |  | ||||||
| import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../../hooks/useLocationSettings'; | ||||||
|  | import { formatDateYMD, formatDateYMDHMS } from '../../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| interface CreatedAtProps { | interface CreatedAtProps { | ||||||
|     time: Date; |     time: Date; | ||||||
| @ -14,12 +11,12 @@ const CreatedAt = ({ time }: CreatedAtProps) => { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <Tooltip |         <Tooltip | ||||||
|             title={`Created at ${formatFullDateTimeWithLocale( |             title={`Created at ${formatDateYMDHMS( | ||||||
|                 time, |                 time, | ||||||
|                 locationSettings.locale |                 locationSettings.locale | ||||||
|             )}`}
 |             )}`}
 | ||||||
|         > |         > | ||||||
|             <span>{formatDateWithLocale(time, locationSettings.locale)}</span> |             <span>{formatDateYMD(time, locationSettings.locale)}</span> | ||||||
|         </Tooltip> |         </Tooltip> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,10 +1,12 @@ | |||||||
| import { useRef, useState } from 'react'; | import { useRef, useState } from 'react'; | ||||||
| import { TableCell, TableRow } from '@material-ui/core'; | import { TableCell, TableRow } from '@material-ui/core'; | ||||||
| import { useHistory } from 'react-router'; | import { useHistory } from 'react-router'; | ||||||
| 
 |  | ||||||
| import { useStyles } from '../FeatureToggleListNew.styles'; | import { useStyles } from '../FeatureToggleListNew.styles'; | ||||||
| import useToggleFeatureByEnv from '../../../../hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv'; | import useToggleFeatureByEnv from '../../../../hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv'; | ||||||
| import { IEnvironments } from '../../../../interfaces/featureToggle'; | import { | ||||||
|  |     IEnvironments, | ||||||
|  |     IFeatureEnvironment, | ||||||
|  | } from '../../../../interfaces/featureToggle'; | ||||||
| import useToast from '../../../../hooks/useToast'; | import useToast from '../../../../hooks/useToast'; | ||||||
| import { getTogglePath } from '../../../../utils/route-path-helpers'; | import { getTogglePath } from '../../../../utils/route-path-helpers'; | ||||||
| import { SyntheticEvent } from 'react-router/node_modules/@types/react'; | import { SyntheticEvent } from 'react-router/node_modules/@types/react'; | ||||||
| @ -25,8 +27,8 @@ interface IFeatureToggleListNewItemProps { | |||||||
|     type: string; |     type: string; | ||||||
|     environments: IFeatureEnvironment[]; |     environments: IFeatureEnvironment[]; | ||||||
|     projectId: string; |     projectId: string; | ||||||
|     lastSeenAt?: Date; |     lastSeenAt?: string; | ||||||
|     createdAt: Date; |     createdAt: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FeatureToggleListNewItem = ({ | const FeatureToggleListNewItem = ({ | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; | import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; | ||||||
| import 'chartjs-adapter-date-fns'; | import 'chartjs-adapter-date-fns'; | ||||||
| import { ChartOptions, defaults } from 'chart.js'; | import { ChartOptions, defaults } from 'chart.js'; | ||||||
| import { formatTimeWithLocale } from '../../../../common/util'; |  | ||||||
| import { IFeatureMetricsRaw } from '../../../../../interfaces/featureToggle'; | import { IFeatureMetricsRaw } from '../../../../../interfaces/featureToggle'; | ||||||
| import theme from '../../../../../themes/main-theme'; | import theme from '../../../../../themes/main-theme'; | ||||||
|  | import { formatDateHM } from '../../../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| export const createChartOptions = ( | export const createChartOptions = ( | ||||||
|     metrics: IFeatureMetricsRaw[], |     metrics: IFeatureMetricsRaw[], | ||||||
| @ -30,7 +30,7 @@ export const createChartOptions = ( | |||||||
|                 usePointStyle: true, |                 usePointStyle: true, | ||||||
|                 callbacks: { |                 callbacks: { | ||||||
|                     title: items => |                     title: items => | ||||||
|                         formatTimeWithLocale( |                         formatDateHM( | ||||||
|                             items[0].parsed.x, |                             items[0].parsed.x, | ||||||
|                             locationSettings.locale |                             locationSettings.locale | ||||||
|                         ), |                         ), | ||||||
| @ -73,10 +73,7 @@ export const createChartOptions = ( | |||||||
|                 grid: { display: false }, |                 grid: { display: false }, | ||||||
|                 ticks: { |                 ticks: { | ||||||
|                     callback: (_, i, data) => |                     callback: (_, i, data) => | ||||||
|                         formatTimeWithLocale( |                         formatDateHM(data[i].value, locationSettings.locale), | ||||||
|                             data[i].value, |  | ||||||
|                             locationSettings.locale |  | ||||||
|                         ), |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -9,8 +9,8 @@ import { | |||||||
|     useTheme, |     useTheme, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { useLocationSettings } from '../../../../../hooks/useLocationSettings'; | import { useLocationSettings } from '../../../../../hooks/useLocationSettings'; | ||||||
| import { formatFullDateTimeWithLocale } from '../../../../common/util'; |  | ||||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||||
|  | import { formatDateYMDHMS } from 'utils/format-date'; | ||||||
| 
 | 
 | ||||||
| export const FEATURE_METRICS_TABLE_ID = 'feature-metrics-table-id'; | export const FEATURE_METRICS_TABLE_ID = 'feature-metrics-table-id'; | ||||||
| 
 | 
 | ||||||
| @ -48,7 +48,7 @@ export const FeatureMetricsTable = ({ metrics }: IFeatureMetricsTableProps) => { | |||||||
|                 {sortedMetrics.map(metric => ( |                 {sortedMetrics.map(metric => ( | ||||||
|                     <TableRow key={metric.timestamp}> |                     <TableRow key={metric.timestamp}> | ||||||
|                         <TableCell> |                         <TableCell> | ||||||
|                             {formatFullDateTimeWithLocale( |                             {formatDateYMDHMS( | ||||||
|                                 metric.timestamp, |                                 metric.timestamp, | ||||||
|                                 locationSettings.locale |                                 locationSettings.locale | ||||||
|                             )} |                             )} | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { DialogContentText } from '@material-ui/core'; | import { DialogContentText } from '@material-ui/core'; | ||||||
| import { useParams } from 'react-router'; | import { useParams } from 'react-router'; | ||||||
| import { useState } from 'react'; | import React, { useState } from 'react'; | ||||||
| import { IFeatureViewParams } from '../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../interfaces/params'; | ||||||
| import Dialogue from '../../../../common/Dialogue'; | import Dialogue from '../../../../common/Dialogue'; | ||||||
| import Input from '../../../../common/Input/Input'; | import Input from '../../../../common/Input/Input'; | ||||||
| @ -11,6 +11,7 @@ import TagSelect from '../../../../common/TagSelect/TagSelect'; | |||||||
| import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | ||||||
| import useTags from '../../../../../hooks/api/getters/useTags/useTags'; | import useTags from '../../../../../hooks/api/getters/useTags/useTags'; | ||||||
| import useToast from '../../../../../hooks/useToast'; | import useToast from '../../../../../hooks/useToast'; | ||||||
|  | import { formatUnknownError } from '../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| interface IAddTagDialogProps { | interface IAddTagDialogProps { | ||||||
|     open: boolean; |     open: boolean; | ||||||
| @ -20,6 +21,7 @@ interface IAddTagDialogProps { | |||||||
| interface IDefaultTag { | interface IDefaultTag { | ||||||
|     type: string; |     type: string; | ||||||
|     value: string; |     value: string; | ||||||
|  | 
 | ||||||
|     [index: string]: string; |     [index: string]: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -62,9 +64,10 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => { | |||||||
|                 text: 'We successfully added a tag to your toggle', |                 text: 'We successfully added a tag to your toggle', | ||||||
|                 confetti: true, |                 confetti: true, | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             const message = formatUnknownError(error); | ||||||
|             setErrors({ tagError: e.message }); |             setToastApiError(message); | ||||||
|  |             setErrors({ tagError: message }); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import { IFeatureViewParams } from '../../../../../../interfaces/params'; | |||||||
| import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; | import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; | ||||||
| import StringTruncator from '../../../../../common/StringTruncator/StringTruncator'; | import StringTruncator from '../../../../../common/StringTruncator/StringTruncator'; | ||||||
| import { UPDATE_FEATURE_ENVIRONMENT } from '../../../../../providers/AccessProvider/permissions'; | import { UPDATE_FEATURE_ENVIRONMENT } from '../../../../../providers/AccessProvider/permissions'; | ||||||
|  | import React from 'react'; | ||||||
|  | import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| interface IFeatureOverviewEnvSwitchProps { | interface IFeatureOverviewEnvSwitchProps { | ||||||
|     env: IFeatureEnvironment; |     env: IFeatureEnvironment; | ||||||
| @ -40,7 +42,7 @@ const FeatureOverviewEnvSwitch = ({ | |||||||
|             if (callback) { |             if (callback) { | ||||||
|                 callback(); |                 callback(); | ||||||
|             } |             } | ||||||
|         } catch (e: any) { |         } catch (e) { | ||||||
|             if (e.message === ENVIRONMENT_STRATEGY_ERROR) { |             if (e.message === ENVIRONMENT_STRATEGY_ERROR) { | ||||||
|                 showInfoBox(true); |                 showInfoBox(true); | ||||||
|             } else { |             } else { | ||||||
| @ -61,8 +63,8 @@ const FeatureOverviewEnvSwitch = ({ | |||||||
|             if (callback) { |             if (callback) { | ||||||
|                 callback(); |                 callback(); | ||||||
|             } |             } | ||||||
|         } catch (e: any) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { Settings } from '@material-ui/icons'; | import { Settings } from '@material-ui/icons'; | ||||||
| import { useTheme } from '@material-ui/styles'; | import { useTheme } from '@material-ui/core/styles'; | ||||||
| import { Link, useParams } from 'react-router-dom'; | import { Link, useParams } from 'react-router-dom'; | ||||||
| import { IFeatureViewParams } from '../../../../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../../../../interfaces/params'; | ||||||
| import { IFeatureStrategy } from '../../../../../../../../interfaces/strategy'; | import { IFeatureStrategy } from '../../../../../../../../interfaces/strategy'; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { useState, useContext } from 'react'; | import React, { useContext, useState } from 'react'; | ||||||
| import { Chip } from '@material-ui/core'; | import { Chip } from '@material-ui/core'; | ||||||
| import { Label, Close } from '@material-ui/icons'; | import { Close, Label } from '@material-ui/icons'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import useTags from '../../../../../../hooks/api/getters/useTags/useTags'; | import useTags from '../../../../../../hooks/api/getters/useTags/useTags'; | ||||||
| import { IFeatureViewParams } from '../../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../../interfaces/params'; | ||||||
| @ -17,6 +17,7 @@ import useToast from '../../../../../../hooks/useToast'; | |||||||
| import { UPDATE_FEATURE } from '../../../../../providers/AccessProvider/permissions'; | import { UPDATE_FEATURE } from '../../../../../providers/AccessProvider/permissions'; | ||||||
| import ConditionallyRender from '../../../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../../../common/ConditionallyRender'; | ||||||
| import AccessContext from '../../../../../../contexts/AccessContext'; | import AccessContext from '../../../../../../contexts/AccessContext'; | ||||||
|  | import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| interface IFeatureOverviewTagsProps extends React.HTMLProps<HTMLButtonElement> { | interface IFeatureOverviewTagsProps extends React.HTMLProps<HTMLButtonElement> { | ||||||
|     projectId: string; |     projectId: string; | ||||||
| @ -53,8 +54,8 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({ | |||||||
|                 title: 'Tag deleted', |                 title: 'Tag deleted', | ||||||
|                 text: 'Successfully deleted tag', |                 text: 'Successfully deleted tag', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { useState, useEffect, useContext } from 'react'; | import { useContext, useEffect, useState } from 'react'; | ||||||
| import * as jsonpatch from 'fast-json-patch'; | import * as jsonpatch from 'fast-json-patch'; | ||||||
| import { TextField } from '@material-ui/core'; | import { TextField } from '@material-ui/core'; | ||||||
| import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; | ||||||
| @ -11,6 +11,7 @@ import { IFeatureViewParams } from '../../../../../interfaces/params'; | |||||||
| import useToast from '../../../../../hooks/useToast'; | import useToast from '../../../../../hooks/useToast'; | ||||||
| import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | ||||||
| import ConditionallyRender from '../../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../../common/ConditionallyRender'; | ||||||
|  | import { formatUnknownError } from '../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const FeatureSettingsMetadata = () => { | const FeatureSettingsMetadata = () => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
| @ -54,8 +55,8 @@ const FeatureSettingsMetadata = () => { | |||||||
|             }); |             }); | ||||||
|             setDirty(false); |             setDirty(false); | ||||||
|             refetch(); |             refetch(); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.toString()); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { useState, useEffect, useContext } from 'react'; | import { useContext, useEffect, useState } from 'react'; | ||||||
| import { useHistory, useParams } from 'react-router'; | import { useHistory, useParams } from 'react-router'; | ||||||
| import AccessContext from '../../../../../contexts/AccessContext'; | import AccessContext from '../../../../../contexts/AccessContext'; | ||||||
| import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; | ||||||
| @ -12,6 +12,7 @@ import FeatureProjectSelect from './FeatureProjectSelect/FeatureProjectSelect'; | |||||||
| import FeatureSettingsProjectConfirm from './FeatureSettingsProjectConfirm/FeatureSettingsProjectConfirm'; | import FeatureSettingsProjectConfirm from './FeatureSettingsProjectConfirm/FeatureSettingsProjectConfirm'; | ||||||
| import { IPermission } from '../../../../../interfaces/user'; | import { IPermission } from '../../../../../interfaces/user'; | ||||||
| import { useAuthPermissions } from '../../../../../hooks/api/getters/useAuth/useAuthPermissions'; | import { useAuthPermissions } from '../../../../../hooks/api/getters/useAuth/useAuthPermissions'; | ||||||
|  | import { formatUnknownError } from '../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const FeatureSettingsProject = () => { | const FeatureSettingsProject = () => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
| @ -61,16 +62,16 @@ const FeatureSettingsProject = () => { | |||||||
|             history.replace( |             history.replace( | ||||||
|                 `/projects/${newProject}/features/${featureId}/settings` |                 `/projects/${newProject}/features/${featureId}/settings` | ||||||
|             ); |             ); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const createMoveTargets = () => { |     const createMoveTargets = () => { | ||||||
|         return permissions.reduce( |         return permissions.reduce( | ||||||
|             (acc: { [key: string]: boolean }, permission: IPermission) => { |             (acc: { [key: string]: boolean }, p: IPermission) => { | ||||||
|                 if (permission.permission === MOVE_FEATURE_TOGGLE) { |                 if (p.project && p.permission === MOVE_FEATURE_TOGGLE) { | ||||||
|                     acc[permission.project] = true; |                     acc[p.project] = true; | ||||||
|                 } |                 } | ||||||
|                 return acc; |                 return acc; | ||||||
|             }, |             }, | ||||||
|  | |||||||
| @ -1,7 +1,8 @@ | |||||||
| import { useStyles } from './FeatureStatus.styles'; | import { useStyles } from './FeatureStatus.styles'; | ||||||
| import TimeAgo from 'react-timeago'; | import TimeAgo from 'react-timeago'; | ||||||
| import ConditionallyRender from '../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../common/ConditionallyRender'; | ||||||
| import { Tooltip } from '@material-ui/core'; | import { Tooltip, TooltipProps } from '@material-ui/core'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| function generateUnit(unit?: string): string { | function generateUnit(unit?: string): string { | ||||||
|     switch (unit) { |     switch (unit) { | ||||||
| @ -46,8 +47,8 @@ function getColor(unit?: string): string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface FeatureStatusProps { | interface FeatureStatusProps { | ||||||
|     lastSeenAt?: Date; |     lastSeenAt?: string; | ||||||
|     tooltipPlacement?: string; |     tooltipPlacement?: TooltipProps['placement']; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FeatureStatus = ({ | const FeatureStatus = ({ | ||||||
| @ -76,7 +77,7 @@ const FeatureStatus = ({ | |||||||
|             condition={!!lastSeenAt} |             condition={!!lastSeenAt} | ||||||
|             show={ |             show={ | ||||||
|                 <TimeAgo |                 <TimeAgo | ||||||
|                     date={lastSeenAt} |                     date={lastSeenAt!} | ||||||
|                     title="" |                     title="" | ||||||
|                     live={false} |                     live={false} | ||||||
|                     formatter={( |                     formatter={( | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ import { ADD_NEW_STRATEGY_SAVE_ID } from '../../../../../../testIds'; | |||||||
| import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; | import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; | ||||||
| import { scrollToTop } from '../../../../../common/util'; | import { scrollToTop } from '../../../../../common/util'; | ||||||
| import useToast from '../../../../../../hooks/useToast'; | import useToast from '../../../../../../hooks/useToast'; | ||||||
|  | import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const FeatureStrategiesConfigure = () => { | const FeatureStrategiesConfigure = () => { | ||||||
|     const history = useHistory(); |     const history = useHistory(); | ||||||
| @ -99,8 +100,8 @@ const FeatureStrategiesConfigure = () => { | |||||||
|             history.replace(history.location.pathname); |             history.replace(history.location.pathname); | ||||||
|             refetch(); |             refetch(); | ||||||
|             scrollToTop(); |             scrollToTop(); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,7 +39,6 @@ const FeatureStrategiesEnvironmentList = ({ | |||||||
| 
 | 
 | ||||||
|     const { |     const { | ||||||
|         activeEnvironmentsRef, |         activeEnvironmentsRef, | ||||||
|         setToastData, |  | ||||||
|         deleteStrategy, |         deleteStrategy, | ||||||
|         updateStrategy, |         updateStrategy, | ||||||
|         delDialog, |         delDialog, | ||||||
| @ -162,7 +161,6 @@ const FeatureStrategiesEnvironmentList = ({ | |||||||
|                                                 : 'Toggle is disabled and no strategies are executing' |                                                 : 'Toggle is disabled and no strategies are executing' | ||||||
|                                         } |                                         } | ||||||
|                                         env={activeEnvironment} |                                         env={activeEnvironment} | ||||||
|                                         setToastData={setToastData} |  | ||||||
|                                         callback={updateFeatureEnvironmentCache} |                                         callback={updateFeatureEnvironmentCache} | ||||||
|                                     /> |                                     /> | ||||||
|                                 </div> |                                 </div> | ||||||
|  | |||||||
| @ -4,9 +4,13 @@ import FeatureStrategiesUIContext from '../../../../../../contexts/FeatureStrate | |||||||
| import useFeatureStrategyApi from '../../../../../../hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; | import useFeatureStrategyApi from '../../../../../../hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; | ||||||
| import useToast from '../../../../../../hooks/useToast'; | import useToast from '../../../../../../hooks/useToast'; | ||||||
| import { IFeatureViewParams } from '../../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../../interfaces/params'; | ||||||
| import { IFeatureStrategy } from '../../../../../../interfaces/strategy'; | import { | ||||||
|  |     IFeatureStrategy, | ||||||
|  |     IStrategyPayload, | ||||||
|  | } from '../../../../../../interfaces/strategy'; | ||||||
| import cloneDeep from 'lodash.clonedeep'; | import cloneDeep from 'lodash.clonedeep'; | ||||||
| import { IFeatureEnvironment } from '../../../../../../interfaces/featureToggle'; | import { IFeatureEnvironment } from '../../../../../../interfaces/featureToggle'; | ||||||
|  | import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const useFeatureStrategiesEnvironmentList = () => { | const useFeatureStrategiesEnvironmentList = () => { | ||||||
|     const { projectId, featureId } = useParams<IFeatureViewParams>(); |     const { projectId, featureId } = useParams<IFeatureViewParams>(); | ||||||
| @ -85,8 +89,8 @@ const useFeatureStrategiesEnvironmentList = () => { | |||||||
|             strategy.constraints = updateStrategyPayload.constraints; |             strategy.constraints = updateStrategyPayload.constraints; | ||||||
|             history.replace(history.location.pathname); |             history.replace(history.location.pathname); | ||||||
|             setFeatureCache(feature); |             setFeatureCache(feature); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -118,14 +122,13 @@ const useFeatureStrategiesEnvironmentList = () => { | |||||||
|                 text: `Successfully deleted strategy from ${featureId}`, |                 text: `Successfully deleted strategy from ${featureId}`, | ||||||
|             }); |             }); | ||||||
|             history.replace(history.location.pathname); |             history.replace(history.location.pathname); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|         activeEnvironmentsRef, |         activeEnvironmentsRef, | ||||||
|         setToastData, |  | ||||||
|         deleteStrategy, |         deleteStrategy, | ||||||
|         updateStrategy, |         updateStrategy, | ||||||
|         delDialog, |         delDialog, | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ interface IFeatureStrategiesProductionGuard { | |||||||
|     onClick: () => void; |     onClick: () => void; | ||||||
|     onClose: () => void; |     onClose: () => void; | ||||||
|     primaryButtonText: string; |     primaryButtonText: string; | ||||||
|     loading: boolean; |     loading?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FeatureStrategiesProductionGuard = ({ | const FeatureStrategiesProductionGuard = ({ | ||||||
| @ -61,4 +61,11 @@ const FeatureStrategiesProductionGuard = ({ | |||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const disableFeatureStrategiesProductionGuard = () => { | ||||||
|  |     localStorage.setItem( | ||||||
|  |         FEATURE_STRATEGY_PRODUCTION_GUARD_SETTING, | ||||||
|  |         String(true) | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export default FeatureStrategiesProductionGuard; | export default FeatureStrategiesProductionGuard; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { useContext, useEffect, useState } from 'react'; | import React, { useContext, useEffect, useState } from 'react'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import { mutate } from 'swr'; | import { mutate } from 'swr'; | ||||||
| import FeatureStrategiesUIContext from '../../../../../../contexts/FeatureStrategiesUIContext'; | import FeatureStrategiesUIContext from '../../../../../../contexts/FeatureStrategiesUIContext'; | ||||||
| @ -6,8 +6,8 @@ import useFeatureStrategy from '../../../../../../hooks/api/getters/useFeatureSt | |||||||
| import { IFeatureViewParams } from '../../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../../interfaces/params'; | ||||||
| import { | import { | ||||||
|     IConstraint, |     IConstraint, | ||||||
|     IParameter, |  | ||||||
|     IFeatureStrategy, |     IFeatureStrategy, | ||||||
|  |     IParameter, | ||||||
| } from '../../../../../../interfaces/strategy'; | } from '../../../../../../interfaces/strategy'; | ||||||
| import FeatureStrategyAccordion from '../../FeatureStrategyAccordion/FeatureStrategyAccordion'; | import FeatureStrategyAccordion from '../../FeatureStrategyAccordion/FeatureStrategyAccordion'; | ||||||
| import cloneDeep from 'lodash.clonedeep'; | import cloneDeep from 'lodash.clonedeep'; | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ interface IFeatureStrategyCardProps { | |||||||
|     name: string; |     name: string; | ||||||
|     description: string; |     description: string; | ||||||
|     configureNewStrategy: boolean; |     configureNewStrategy: boolean; | ||||||
|     index?: number; |     index: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const FEATURE_STRATEGIES_DRAG_TYPE = 'FEATURE_STRATEGIES_DRAG_TYPE'; | export const FEATURE_STRATEGIES_DRAG_TYPE = 'FEATURE_STRATEGIES_DRAG_TYPE'; | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ import useStrategies from '../../../../../../hooks/api/getters/useStrategies/use | |||||||
| import GeneralStrategy from '../../common/GeneralStrategy/GeneralStrategy'; | import GeneralStrategy from '../../common/GeneralStrategy/GeneralStrategy'; | ||||||
| import UserWithIdStrategy from '../../common/UserWithIdStrategy/UserWithId'; | import UserWithIdStrategy from '../../common/UserWithIdStrategy/UserWithId'; | ||||||
| import StrategyConstraints from '../../common/StrategyConstraints/StrategyConstraints'; | import StrategyConstraints from '../../common/StrategyConstraints/StrategyConstraints'; | ||||||
| import { useContext, useState } from 'react'; | import React, { useContext, useState } from 'react'; | ||||||
| import ConditionallyRender from '../../../../../common/ConditionallyRender'; | import ConditionallyRender from '../../../../../common/ConditionallyRender'; | ||||||
| import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig'; | import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig'; | ||||||
| import { C } from '../../../../../common/flags'; | import { C } from '../../../../../common/flags'; | ||||||
|  | |||||||
| @ -1,16 +1,16 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { | import { | ||||||
|     Switch, |  | ||||||
|     FormControlLabel, |     FormControlLabel, | ||||||
|     Tooltip, |     Switch, | ||||||
|     TextField, |     TextField, | ||||||
|  |     Tooltip, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| 
 | 
 | ||||||
| import StrategyInputList from '../StrategyInputList/StrategyInputList'; | import StrategyInputList from '../StrategyInputList/StrategyInputList'; | ||||||
| import RolloutSlider from '../RolloutSlider/RolloutSlider'; | import RolloutSlider from '../RolloutSlider/RolloutSlider'; | ||||||
| import { | import { | ||||||
|     IParameter, |  | ||||||
|     IFeatureStrategy, |     IFeatureStrategy, | ||||||
|  |     IParameter, | ||||||
| } from '../../../../../../interfaces/strategy'; | } from '../../../../../../interfaces/strategy'; | ||||||
| import { useStyles } from './GeneralStrategy.styles'; | import { useStyles } from './GeneralStrategy.styles'; | ||||||
| 
 | 
 | ||||||
| @ -77,7 +77,7 @@ const GeneralStrategy = ({ | |||||||
|                         </div> |                         </div> | ||||||
|                     ); |                     ); | ||||||
|                 } else if (type === 'list') { |                 } else if (type === 'list') { | ||||||
|                     let list = []; |                     let list: string[] = []; | ||||||
|                     if (typeof value === 'string') { |                     if (typeof value === 'string') { | ||||||
|                         list = value.trim().split(',').filter(Boolean); |                         list = value.trim().split(',').filter(Boolean); | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { makeStyles, withStyles } from '@material-ui/core/styles'; | import { makeStyles, withStyles } from '@material-ui/core/styles'; | ||||||
| import { Slider, Typography } from '@material-ui/core'; | import { Slider, Typography } from '@material-ui/core'; | ||||||
| import { ROLLOUT_SLIDER_ID } from '../../../../../../testIds'; | import { ROLLOUT_SLIDER_ID } from '../../../../../../testIds'; | ||||||
|  | import React from 'react'; | ||||||
| 
 | 
 | ||||||
| const StyledSlider = withStyles({ | const StyledSlider = withStyles({ | ||||||
|     root: { |     root: { | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiCo | |||||||
| import { C } from '../../../../../common/flags'; | import { C } from '../../../../../common/flags'; | ||||||
| import useUnleashContext from '../../../../../../hooks/api/getters/useUnleashContext/useUnleashContext'; | import useUnleashContext from '../../../../../../hooks/api/getters/useUnleashContext/useUnleashContext'; | ||||||
| import StrategyConstraintInputField from './StrategyConstraintInputField'; | import StrategyConstraintInputField from './StrategyConstraintInputField'; | ||||||
| import { useEffect } from 'react'; | import React, { useEffect } from 'react'; | ||||||
| 
 | 
 | ||||||
| interface IStrategyConstraintProps { | interface IStrategyConstraintProps { | ||||||
|     constraints: IConstraint[]; |     constraints: IConstraint[]; | ||||||
| @ -38,7 +38,7 @@ const StrategyConstraints: React.FC<IStrategyConstraintProps> = ({ | |||||||
|     const enabled = uiConfig.flags[C]; |     const enabled = uiConfig.flags[C]; | ||||||
|     const contextNames = contextFields.map(context => context.name); |     const contextNames = contextFields.map(context => context.name); | ||||||
| 
 | 
 | ||||||
|     const onClick = evt => { |     const onClick = (evt: React.SyntheticEvent) => { | ||||||
|         evt.preventDefault(); |         evt.preventDefault(); | ||||||
|         addConstraint(); |         addConstraint(); | ||||||
|     }; |     }; | ||||||
| @ -57,15 +57,15 @@ const StrategyConstraints: React.FC<IStrategyConstraintProps> = ({ | |||||||
|         }; |         }; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const removeConstraint = index => evt => { |     const removeConstraint = (index: number) => (event: Event) => { | ||||||
|         evt.preventDefault(); |         event.preventDefault(); | ||||||
|         const updatedConstraints = [...constraints]; |         const updatedConstraints = [...constraints]; | ||||||
|         updatedConstraints.splice(index, 1); |         updatedConstraints.splice(index, 1); | ||||||
| 
 | 
 | ||||||
|         updateConstraints(updatedConstraints); |         updateConstraints(updatedConstraints); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const updateConstraint = index => (value, field) => { |     const updateConstraint = (index: number) => (value, field) => { | ||||||
|         const updatedConstraints = [...constraints]; |         const updatedConstraints = [...constraints]; | ||||||
|         const constraint = updatedConstraints[index]; |         const constraint = updatedConstraints[index]; | ||||||
|         constraint[field] = value; |         constraint[field] = value; | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ import { | |||||||
| interface IStrategyInputList { | interface IStrategyInputList { | ||||||
|     name: string; |     name: string; | ||||||
|     list: string[]; |     list: string[]; | ||||||
|     setConfig: () => void; |     setConfig: (field: string, value: string) => void; | ||||||
|     disabled: boolean; |     disabled: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import StrategyInputList from '../StrategyInputList/StrategyInputList'; | |||||||
| 
 | 
 | ||||||
| interface IUserWithIdStrategyProps { | interface IUserWithIdStrategyProps { | ||||||
|     parameters: IParameter; |     parameters: IParameter; | ||||||
|     updateParameter: (field: string, value: any) => void; |     updateParameter: (field: string, value: string) => void; | ||||||
|     editable: boolean; |     editable: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,12 +1,11 @@ | |||||||
| import { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import { | import { | ||||||
|  |     Button, | ||||||
|     FormControl, |     FormControl, | ||||||
|     FormControlLabel, |     FormControlLabel, | ||||||
|     Grid, |     Grid, | ||||||
|     TextField, |  | ||||||
|     InputAdornment, |     InputAdornment, | ||||||
|     Button, |     TextField, | ||||||
|     Tooltip, |     Tooltip, | ||||||
| } from '@material-ui/core'; | } from '@material-ui/core'; | ||||||
| import { Info } from '@material-ui/icons'; | import { Info } from '@material-ui/icons'; | ||||||
| @ -18,13 +17,16 @@ import ConditionallyRender from '../../../../../common/ConditionallyRender'; | |||||||
| import GeneralSelect from '../../../../../common/GeneralSelect/GeneralSelect'; | import GeneralSelect from '../../../../../common/GeneralSelect/GeneralSelect'; | ||||||
| import { useCommonStyles } from '../../../../../../common.styles'; | import { useCommonStyles } from '../../../../../../common.styles'; | ||||||
| import Dialogue from '../../../../../common/Dialogue'; | import Dialogue from '../../../../../common/Dialogue'; | ||||||
| import { trim, modalStyles } from '../../../../../common/util'; | import { modalStyles, trim } from '../../../../../common/util'; | ||||||
| import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; | import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; | ||||||
| import { UPDATE_FEATURE_VARIANTS } from '../../../../../providers/AccessProvider/permissions'; | import { UPDATE_FEATURE_VARIANTS } from '../../../../../providers/AccessProvider/permissions'; | ||||||
| import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; | import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import { IFeatureViewParams } from '../../../../../../interfaces/params'; | import { IFeatureViewParams } from '../../../../../../interfaces/params'; | ||||||
| import { IFeatureVariant } from '../../../../../../interfaces/featureToggle'; | import { | ||||||
|  |     IFeatureVariant, | ||||||
|  |     IOverride, | ||||||
|  | } from '../../../../../../interfaces/featureToggle'; | ||||||
| import cloneDeep from 'lodash.clonedeep'; | import cloneDeep from 'lodash.clonedeep'; | ||||||
| 
 | 
 | ||||||
| const payloadOptions = [ | const payloadOptions = [ | ||||||
| @ -35,6 +37,17 @@ const payloadOptions = [ | |||||||
| 
 | 
 | ||||||
| const EMPTY_PAYLOAD = { type: 'string', value: '' }; | const EMPTY_PAYLOAD = { type: 'string', value: '' }; | ||||||
| 
 | 
 | ||||||
|  | interface IAddVariantProps { | ||||||
|  |     showDialog: boolean; | ||||||
|  |     closeDialog: () => void; | ||||||
|  |     save: (variantToSave: IFeatureVariant) => Promise<void>; | ||||||
|  |     editVariant: IFeatureVariant; | ||||||
|  |     validateName: (value: string) => Record<string, string> | undefined; | ||||||
|  |     validateWeight: (value: string) => Record<string, string> | undefined; | ||||||
|  |     title: string; | ||||||
|  |     editing: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const AddVariant = ({ | const AddVariant = ({ | ||||||
|     showDialog, |     showDialog, | ||||||
|     closeDialog, |     closeDialog, | ||||||
| @ -44,11 +57,11 @@ const AddVariant = ({ | |||||||
|     validateWeight, |     validateWeight, | ||||||
|     title, |     title, | ||||||
|     editing, |     editing, | ||||||
| }) => { | }: IAddVariantProps) => { | ||||||
|     const [data, setData] = useState({}); |     const [data, setData] = useState({}); | ||||||
|     const [payload, setPayload] = useState(EMPTY_PAYLOAD); |     const [payload, setPayload] = useState(EMPTY_PAYLOAD); | ||||||
|     const [overrides, setOverrides] = useState([]); |     const [overrides, setOverrides] = useState<IOverride[]>([]); | ||||||
|     const [error, setError] = useState({}); |     const [error, setError] = useState<Record<string, string>>({}); | ||||||
|     const commonStyles = useCommonStyles(); |     const commonStyles = useCommonStyles(); | ||||||
|     const { projectId, featureId } = useParams<IFeatureViewParams>(); |     const { projectId, featureId } = useParams<IFeatureViewParams>(); | ||||||
|     const { feature } = useFeature(projectId, featureId); |     const { feature } = useFeature(projectId, featureId); | ||||||
| @ -80,7 +93,7 @@ const AddVariant = ({ | |||||||
|         setError({}); |         setError({}); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const setClonedVariants = clonedVariants => |     const setClonedVariants = (clonedVariants: IFeatureVariant[]) => | ||||||
|         setVariants(cloneDeep(clonedVariants)); |         setVariants(cloneDeep(clonedVariants)); | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
| @ -159,7 +172,7 @@ const AddVariant = ({ | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const onPayload = e => { |     const onPayload = (e: React.SyntheticEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         setPayload({ |         setPayload({ | ||||||
|             ...payload, |             ...payload, | ||||||
| @ -167,13 +180,13 @@ const AddVariant = ({ | |||||||
|         }); |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const onCancel = e => { |     const onCancel = (e: React.SyntheticEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         clear(); |         clear(); | ||||||
|         closeDialog(); |         closeDialog(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const updateOverrideType = index => e => { |     const updateOverrideType = (index: number) => (e: React.SyntheticEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         setOverrides( |         setOverrides( | ||||||
|             overrides.map((o, i) => { |             overrides.map((o, i) => { | ||||||
| @ -186,7 +199,7 @@ const AddVariant = ({ | |||||||
|         ); |         ); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const updateOverrideValues = (index, values) => { |     const updateOverrideValues = (index: number, values: string[]) => { | ||||||
|         setOverrides( |         setOverrides( | ||||||
|             overrides.map((o, i) => { |             overrides.map((o, i) => { | ||||||
|                 if (i === index) { |                 if (i === index) { | ||||||
| @ -197,12 +210,12 @@ const AddVariant = ({ | |||||||
|         ); |         ); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const removeOverride = index => e => { |     const removeOverride = (index: number) => (e: React.SyntheticEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         setOverrides(overrides.filter((o, i) => i !== index)); |         setOverrides(overrides.filter((o, i) => i !== index)); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const onAddOverride = e => { |     const onAddOverride = (e: React.SyntheticEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         setOverrides([ |         setOverrides([ | ||||||
|             ...overrides, |             ...overrides, | ||||||
| @ -388,7 +401,6 @@ const AddVariant = ({ | |||||||
|                     removeOverride={removeOverride} |                     removeOverride={removeOverride} | ||||||
|                     updateOverrideType={updateOverrideType} |                     updateOverrideType={updateOverrideType} | ||||||
|                     updateOverrideValues={updateOverrideValues} |                     updateOverrideValues={updateOverrideValues} | ||||||
|                     updateValues={updateOverrideValues} |  | ||||||
|                 /> |                 /> | ||||||
|                 <Button |                 <Button | ||||||
|                     onClick={onAddOverride} |                     onClick={onAddOverride} | ||||||
| @ -402,15 +414,4 @@ const AddVariant = ({ | |||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| AddVariant.propTypes = { |  | ||||||
|     showDialog: PropTypes.bool.isRequired, |  | ||||||
|     closeDialog: PropTypes.func.isRequired, |  | ||||||
|     save: PropTypes.func.isRequired, |  | ||||||
|     validateName: PropTypes.func.isRequired, |  | ||||||
|     validateWeight: PropTypes.func.isRequired, |  | ||||||
|     editVariant: PropTypes.object, |  | ||||||
|     title: PropTypes.string, |  | ||||||
|     uiConfig: PropTypes.object, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default AddVariant; | export default AddVariant; | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ import cloneDeep from 'lodash.clonedeep'; | |||||||
| import useDeleteVariantMarkup from './FeatureVariantsListItem/useDeleteVariantMarkup'; | import useDeleteVariantMarkup from './FeatureVariantsListItem/useDeleteVariantMarkup'; | ||||||
| import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; | ||||||
| import { mutate } from 'swr'; | import { mutate } from 'swr'; | ||||||
|  | import { formatUnknownError } from '../../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const FeatureOverviewVariants = () => { | const FeatureOverviewVariants = () => { | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
| @ -155,8 +156,8 @@ const FeatureOverviewVariants = () => { | |||||||
|                 type: 'success', |                 type: 'success', | ||||||
|                 text: 'Successfully updated variant stickiness', |                 text: 'Successfully updated variant stickiness', | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -167,8 +168,8 @@ const FeatureOverviewVariants = () => { | |||||||
|                 updatedVariants, |                 updatedVariants, | ||||||
|                 'Successfully removed variant' |                 'Successfully removed variant' | ||||||
|             ); |             ); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     const updateVariant = async (variant: IFeatureVariant) => { |     const updateVariant = async (variant: IFeatureVariant) => { | ||||||
| @ -210,8 +211,8 @@ const FeatureOverviewVariants = () => { | |||||||
|                 type: 'success', |                 type: 'success', | ||||||
|                 text: successText, |                 text: successText, | ||||||
|             }); |             }); | ||||||
|         } catch (e) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(e.message); |             setToastApiError(formatUnknownError(error)); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -221,7 +222,7 @@ const FeatureOverviewVariants = () => { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const validateWeight = (weight: number) => { |     const validateWeight = (weight: string) => { | ||||||
|         const weightValue = parseInt(weight); |         const weightValue = parseInt(weight); | ||||||
|         if (weightValue > 100 || weightValue < 0) { |         if (weightValue > 100 || weightValue < 0) { | ||||||
|             return { weight: 'weight must be between 0 and 100' }; |             return { weight: 'weight must be between 0 and 100' }; | ||||||
| @ -230,7 +231,7 @@ const FeatureOverviewVariants = () => { | |||||||
| 
 | 
 | ||||||
|     const delDialogueMarkup = useDeleteVariantMarkup({ |     const delDialogueMarkup = useDeleteVariantMarkup({ | ||||||
|         show: delDialog.show, |         show: delDialog.show, | ||||||
|         onClick: e => { |         onClick: () => { | ||||||
|             removeVariant(delDialog.name); |             removeVariant(delDialog.name); | ||||||
|             setDelDialog({ name: '', show: false }); |             setDelDialog({ name: '', show: false }); | ||||||
|             setToastData({ |             setToastData({ | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { IconButton, Chip, TableCell, TableRow } from '@material-ui/core'; | import { Chip, IconButton, TableCell, TableRow } from '@material-ui/core'; | ||||||
| import { Edit, Delete } from '@material-ui/icons'; | import { Delete, Edit } from '@material-ui/icons'; | ||||||
| 
 | 
 | ||||||
| import styles from '../variants.module.scss'; | import styles from '../variants.module.scss'; | ||||||
| import { IFeatureVariant } from '../../../../../../interfaces/featureToggle'; | import { IFeatureVariant } from '../../../../../../interfaces/featureToggle'; | ||||||
| @ -26,7 +26,7 @@ const FeatureVariantListItem = ({ | |||||||
|             <TableCell data-test={'VARIANT_NAME'}>{variant.name}</TableCell> |             <TableCell data-test={'VARIANT_NAME'}>{variant.name}</TableCell> | ||||||
|             <TableCell className={styles.chipContainer}> |             <TableCell className={styles.chipContainer}> | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|                     condition={variant.payload} |                     condition={Boolean(variant.payload)} | ||||||
|                     show={<Chip label="Payload" />} |                     show={<Chip label="Payload" />} | ||||||
|                 /> |                 /> | ||||||
|                 <ConditionallyRender |                 <ConditionallyRender | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ const FeatureView = () => { | |||||||
|             setShowDelDialog(false); |             setShowDelDialog(false); | ||||||
|             projectRefetch(); |             projectRefetch(); | ||||||
|             history.push(`/projects/${projectId}`); |             history.push(`/projects/${projectId}`); | ||||||
|         } catch (error) { |         } catch (error: unknown) { | ||||||
|             setToastApiError(formatUnknownError(error)); |             setToastApiError(formatUnknownError(error)); | ||||||
|             setShowDelDialog(false); |             setShowDelDialog(false); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -11,9 +11,7 @@ interface IRedirectParams { | |||||||
| const RedirectFeatureView = () => { | const RedirectFeatureView = () => { | ||||||
|     const { name } = useParams<IRedirectParams>(); |     const { name } = useParams<IRedirectParams>(); | ||||||
|     const { features } = useFeatures(); |     const { features } = useFeatures(); | ||||||
|     const [featureToggle, setFeatureToggle] = useState<IFeatureToggle | null>( |     const [featureToggle, setFeatureToggle] = useState<IFeatureToggle>(); | ||||||
|         null |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         const toggle = features.find( |         const toggle = features.find( | ||||||
| @ -23,7 +21,9 @@ const RedirectFeatureView = () => { | |||||||
|         setFeatureToggle(toggle); |         setFeatureToggle(toggle); | ||||||
|     }, [features, name]); |     }, [features, name]); | ||||||
| 
 | 
 | ||||||
|     if (!featureToggle) return null; |     if (!featureToggle) { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <Redirect |         <Redirect | ||||||
|  | |||||||
| @ -1,14 +1,11 @@ | |||||||
| import { List, Switch, FormControlLabel } from '@material-ui/core'; | import { List, Switch, FormControlLabel } from '@material-ui/core'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| 
 |  | ||||||
| import { formatFullDateTimeWithLocale } from '../../common/util'; |  | ||||||
| 
 |  | ||||||
| import EventJson from './EventJson/EventJson'; | import EventJson from './EventJson/EventJson'; | ||||||
| import PageContent from '../../common/PageContent/PageContent'; | import PageContent from '../../common/PageContent/PageContent'; | ||||||
| import HeaderTitle from '../../common/HeaderTitle'; | import HeaderTitle from '../../common/HeaderTitle'; | ||||||
| import EventCard from './EventCard/EventCard'; | import EventCard from './EventCard/EventCard'; | ||||||
| 
 |  | ||||||
| import { useStyles } from './EventLog.styles.js'; | import { useStyles } from './EventLog.styles.js'; | ||||||
|  | import { formatDateYMDHMS } from '../../../utils/format-date'; | ||||||
| 
 | 
 | ||||||
| const EventLog = ({ | const EventLog = ({ | ||||||
|     title, |     title, | ||||||
| @ -23,7 +20,7 @@ const EventLog = ({ | |||||||
|         setEventSettings({ showData: !eventSettings.showData }); |         setEventSettings({ showData: !eventSettings.showData }); | ||||||
|     }; |     }; | ||||||
|     const formatFulldateTime = v => { |     const formatFulldateTime = v => { | ||||||
|         return formatFullDateTimeWithLocale(v, locationSettings.locale); |         return formatDateYMDHMS(v, locationSettings.locale); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (!history || history.length < 0) { |     if (!history || history.length < 0) { | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import useToast from '../../../../hooks/useToast'; | |||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { CREATE_PROJECT } from '../../../providers/AccessProvider/permissions'; | import { CREATE_PROJECT } from '../../../providers/AccessProvider/permissions'; | ||||||
| import { useAuthUser } from '../../../../hooks/api/getters/useAuth/useAuthUser'; | import { useAuthUser } from '../../../../hooks/api/getters/useAuth/useAuthUser'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const CreateProject = () => { | const CreateProject = () => { | ||||||
|     const { setToastData, setToastApiError } = useToast(); |     const { setToastData, setToastApiError } = useToast(); | ||||||
| @ -48,8 +49,8 @@ const CreateProject = () => { | |||||||
|                     confetti: true, |                     confetti: true, | ||||||
|                     type: 'success', |                     type: 'success', | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; | |||||||
| import useToast from '../../../../hooks/useToast'; | import useToast from '../../../../hooks/useToast'; | ||||||
| import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | import PermissionButton from '../../../common/PermissionButton/PermissionButton'; | ||||||
| import { UPDATE_PROJECT } from '../../../providers/AccessProvider/permissions'; | import { UPDATE_PROJECT } from '../../../providers/AccessProvider/permissions'; | ||||||
|  | import { formatUnknownError } from '../../../../utils/format-unknown-error'; | ||||||
| 
 | 
 | ||||||
| const EditProject = () => { | const EditProject = () => { | ||||||
|     const { uiConfig } = useUiConfig(); |     const { uiConfig } = useUiConfig(); | ||||||
| @ -58,8 +59,8 @@ const EditProject = () => { | |||||||
|                     title: 'Project information updated', |                     title: 'Project information updated', | ||||||
|                     type: 'success', |                     type: 'success', | ||||||
|                 }); |                 }); | ||||||
|             } catch (e: any) { |             } catch (error: unknown) { | ||||||
|                 setToastApiError(e.toString()); |                 setToastApiError(formatUnknownError(error)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ const Project = () => { | |||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             title: 'Access', |             title: 'Access', | ||||||
|             component: <ProjectAccess projectId={id} />, |             component: <ProjectAccess />, | ||||||
|             path: `${basePath}/access`, |             path: `${basePath}/access`, | ||||||
|             name: 'access', |             name: 'access', | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ const ProjectFeatureToggles = ({ | |||||||
|     loading, |     loading, | ||||||
| }: IProjectFeatureToggles) => { | }: IProjectFeatureToggles) => { | ||||||
|     const styles = useStyles(); |     const styles = useStyles(); | ||||||
|     const { id } = useParams(); |     const { id } = useParams<{ id: string }>(); | ||||||
|     const history = useHistory(); |     const history = useHistory(); | ||||||
|     const { hasAccess } = useContext(AccessContext); |     const { hasAccess } = useContext(AccessContext); | ||||||
|     const { uiConfig } = useUiConfig(); |     const { uiConfig } = useUiConfig(); | ||||||
|  | |||||||
| @ -38,7 +38,6 @@ export const useStyles = makeStyles(theme => ({ | |||||||
|         position: 'relative', |         position: 'relative', | ||||||
|     }, |     }, | ||||||
|     errorMessage: { |     errorMessage: { | ||||||
|         // @ts-expect-error
 |  | ||||||
|         fontSize: theme.fontSizes.smallBody, |         fontSize: theme.fontSizes.smallBody, | ||||||
|         color: theme.palette.error.main, |         color: theme.palette.error.main, | ||||||
|         position: 'absolute', |         position: 'absolute', | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user