mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: back transition from validate to configure (#2982)
This commit is contained in:
		
							parent
							
								
									85566b1431
								
							
						
					
					
						commit
						e2e7f64b5b
					
				@ -1,11 +1,18 @@
 | 
				
			|||||||
import { styled } from '@mui/material';
 | 
					import { styled } from '@mui/material';
 | 
				
			||||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
 | 
					import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
 | 
				
			||||||
import React, { useState } from 'react';
 | 
					import React, { useEffect, useState } from 'react';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { ImportTimeline } from './ImportTimeline';
 | 
					import { ImportTimeline } from './ImportTimeline';
 | 
				
			||||||
import { ImportStage } from './ImportStage';
 | 
					import { ImportStage } from './ImportStage';
 | 
				
			||||||
import { ConfigurationStage } from './configure/ConfigurationStage';
 | 
					import {
 | 
				
			||||||
 | 
					    Actions,
 | 
				
			||||||
 | 
					    ConfigurationStage,
 | 
				
			||||||
 | 
					    ConfigurationTabs,
 | 
				
			||||||
 | 
					    ImportArea,
 | 
				
			||||||
 | 
					    ImportMode,
 | 
				
			||||||
 | 
					} from './configure/ConfigurationStage';
 | 
				
			||||||
import { ValidationStage } from './validate/ValidationStage';
 | 
					import { ValidationStage } from './validate/ValidationStage';
 | 
				
			||||||
 | 
					import { ImportOptions } from './configure/ImportOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ModalContentContainer = styled('div')(({ theme }) => ({
 | 
					const ModalContentContainer = styled('div')(({ theme }) => ({
 | 
				
			||||||
    minHeight: '100vh',
 | 
					    minHeight: '100vh',
 | 
				
			||||||
@ -27,6 +34,15 @@ const TimelineHeader = styled('div')(({ theme }) => ({
 | 
				
			|||||||
    marginBottom: theme.spacing(4),
 | 
					    marginBottom: theme.spacing(4),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isValidJSON = (json: string) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        JSON.parse(json);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IImportModalProps {
 | 
					interface IImportModalProps {
 | 
				
			||||||
    open: boolean;
 | 
					    open: boolean;
 | 
				
			||||||
    setOpen: (value: boolean) => void;
 | 
					    setOpen: (value: boolean) => void;
 | 
				
			||||||
@ -34,9 +50,10 @@ interface IImportModalProps {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
 | 
					export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
 | 
				
			||||||
    const [importStage, setImportStage] = useState<ImportStage>({
 | 
					    const [importStage, setImportStage] = useState<ImportStage>('configure');
 | 
				
			||||||
        name: 'configure',
 | 
					    const [environment, setEnvironment] = useState('');
 | 
				
			||||||
    });
 | 
					    const [importPayload, setImportPayload] = useState('');
 | 
				
			||||||
 | 
					    const [activeTab, setActiveTab] = useState<ImportMode>('file');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <SidebarModal
 | 
					        <SidebarModal
 | 
				
			||||||
@ -49,29 +66,49 @@ export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
 | 
				
			|||||||
            <ModalContentContainer>
 | 
					            <ModalContentContainer>
 | 
				
			||||||
                <TimelineContainer>
 | 
					                <TimelineContainer>
 | 
				
			||||||
                    <TimelineHeader>Process</TimelineHeader>
 | 
					                    <TimelineHeader>Process</TimelineHeader>
 | 
				
			||||||
                    <ImportTimeline stage={importStage.name} />
 | 
					                    <ImportTimeline stage={importStage} />
 | 
				
			||||||
                </TimelineContainer>
 | 
					                </TimelineContainer>
 | 
				
			||||||
                <ConditionallyRender
 | 
					                <ConditionallyRender
 | 
				
			||||||
                    condition={importStage.name === 'configure'}
 | 
					                    condition={importStage === 'configure'}
 | 
				
			||||||
                    show={
 | 
					                    show={
 | 
				
			||||||
                        <ConfigurationStage
 | 
					                        <ConfigurationStage
 | 
				
			||||||
 | 
					                            tabs={
 | 
				
			||||||
 | 
					                                <ConfigurationTabs
 | 
				
			||||||
 | 
					                                    activeTab={activeTab}
 | 
				
			||||||
 | 
					                                    setActiveTab={setActiveTab}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            importOptions={
 | 
				
			||||||
 | 
					                                <ImportOptions
 | 
				
			||||||
                                    project={project}
 | 
					                                    project={project}
 | 
				
			||||||
 | 
					                                    environment={environment}
 | 
				
			||||||
 | 
					                                    onChange={setEnvironment}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            importArea={
 | 
				
			||||||
 | 
					                                <ImportArea
 | 
				
			||||||
 | 
					                                    activeTab={activeTab}
 | 
				
			||||||
 | 
					                                    setActiveTab={setActiveTab}
 | 
				
			||||||
 | 
					                                    importPayload={importPayload}
 | 
				
			||||||
 | 
					                                    setImportPayload={setImportPayload}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            actions={
 | 
				
			||||||
 | 
					                                <Actions
 | 
				
			||||||
 | 
					                                    disabled={!isValidJSON(importPayload)}
 | 
				
			||||||
 | 
					                                    onSubmit={() => setImportStage('validate')}
 | 
				
			||||||
                                    onClose={() => setOpen(false)}
 | 
					                                    onClose={() => setOpen(false)}
 | 
				
			||||||
                            onSubmit={configuration =>
 | 
					                                />
 | 
				
			||||||
                                setImportStage({
 | 
					 | 
				
			||||||
                                    name: 'validate',
 | 
					 | 
				
			||||||
                                    ...configuration,
 | 
					 | 
				
			||||||
                                })
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                {importStage.name === 'validate' ? (
 | 
					                {importStage === 'validate' ? (
 | 
				
			||||||
                    <ValidationStage
 | 
					                    <ValidationStage
 | 
				
			||||||
                        project={project}
 | 
					                        project={project}
 | 
				
			||||||
                        environment={importStage.environment}
 | 
					                        environment={environment}
 | 
				
			||||||
                        payload={JSON.parse(importStage.payload)}
 | 
					                        payload={JSON.parse(importPayload)}
 | 
				
			||||||
                        onBack={() => setImportStage({ name: 'configure' })}
 | 
					                        onBack={() => setImportStage('configure')}
 | 
				
			||||||
                        onClose={() => setOpen(false)}
 | 
					                        onClose={() => setOpen(false)}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                ) : (
 | 
					                ) : (
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1 @@
 | 
				
			|||||||
export type ImportStage =
 | 
					export type ImportStage = 'configure' | 'validate' | 'import';
 | 
				
			||||||
    | { name: 'configure' }
 | 
					 | 
				
			||||||
    | { name: 'validate'; environment: string; payload: string }
 | 
					 | 
				
			||||||
    | { name: 'import' };
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ const TimelineItemDescription = styled(Box)(({ theme }) => ({
 | 
				
			|||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ImportTimeline: FC<{
 | 
					export const ImportTimeline: FC<{
 | 
				
			||||||
    stage: ImportStage['name'];
 | 
					    stage: ImportStage;
 | 
				
			||||||
}> = ({ stage }) => {
 | 
					}> = ({ stage }) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <StyledTimeline>
 | 
					        <StyledTimeline>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,12 @@ import {
 | 
				
			|||||||
    TextField,
 | 
					    TextField,
 | 
				
			||||||
    Typography,
 | 
					    Typography,
 | 
				
			||||||
} from '@mui/material';
 | 
					} from '@mui/material';
 | 
				
			||||||
import { ImportOptions } from './ImportOptions';
 | 
					 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { StyledFileDropZone } from './StyledFileDropZone';
 | 
					import { StyledFileDropZone } from './StyledFileDropZone';
 | 
				
			||||||
import { PulsingAvatar } from './PulsingAvatar';
 | 
					import { PulsingAvatar } from './PulsingAvatar';
 | 
				
			||||||
import { ArrowUpward } from '@mui/icons-material';
 | 
					import { ArrowUpward } from '@mui/icons-material';
 | 
				
			||||||
import { ImportExplanation } from './ImportExplanation';
 | 
					import { ImportExplanation } from './ImportExplanation';
 | 
				
			||||||
import React, { FC, useState } from 'react';
 | 
					import React, { FC, ReactNode, useState } from 'react';
 | 
				
			||||||
import useToast from 'hooks/useToast';
 | 
					import useToast from 'hooks/useToast';
 | 
				
			||||||
import { ImportLayoutContainer } from '../ImportLayoutContainer';
 | 
					import { ImportLayoutContainer } from '../ImportLayoutContainer';
 | 
				
			||||||
import { ActionsContainer } from '../ActionsContainer';
 | 
					import { ActionsContainer } from '../ActionsContainer';
 | 
				
			||||||
@ -38,35 +37,12 @@ const MaxSizeMessage = styled(Typography)(({ theme }) => ({
 | 
				
			|||||||
    color: theme.palette.text.secondary,
 | 
					    color: theme.palette.text.secondary,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ImportMode = 'file' | 'code';
 | 
					export type ImportMode = 'file' | 'code';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isValidJSON = (json: string) => {
 | 
					export const ConfigurationTabs: FC<{
 | 
				
			||||||
    try {
 | 
					    activeTab: ImportMode;
 | 
				
			||||||
        JSON.parse(json);
 | 
					    setActiveTab: (mode: ImportMode) => void;
 | 
				
			||||||
        return true;
 | 
					}> = ({ activeTab, setActiveTab }) => (
 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface IConfigurationSettings {
 | 
					 | 
				
			||||||
    environment: string;
 | 
					 | 
				
			||||||
    payload: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ConfigurationStage: FC<{
 | 
					 | 
				
			||||||
    project: string;
 | 
					 | 
				
			||||||
    onClose: () => void;
 | 
					 | 
				
			||||||
    onSubmit: (props: IConfigurationSettings) => void;
 | 
					 | 
				
			||||||
}> = ({ project, onClose, onSubmit }) => {
 | 
					 | 
				
			||||||
    const [environment, setEnvironment] = useState('');
 | 
					 | 
				
			||||||
    const [importPayload, setImportPayload] = useState('');
 | 
					 | 
				
			||||||
    const [activeTab, setActiveTab] = useState<ImportMode>('file');
 | 
					 | 
				
			||||||
    const [dragActive, setDragActive] = useState(false);
 | 
					 | 
				
			||||||
    const { setToastData } = useToast();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <ImportLayoutContainer>
 | 
					 | 
				
			||||||
    <Box
 | 
					    <Box
 | 
				
			||||||
        sx={{
 | 
					        sx={{
 | 
				
			||||||
            borderBottom: 1,
 | 
					            borderBottom: 1,
 | 
				
			||||||
@ -86,11 +62,18 @@ export const ConfigurationStage: FC<{
 | 
				
			|||||||
            />
 | 
					            />
 | 
				
			||||||
        </Tabs>
 | 
					        </Tabs>
 | 
				
			||||||
    </Box>
 | 
					    </Box>
 | 
				
			||||||
            <ImportOptions
 | 
					);
 | 
				
			||||||
                project={project}
 | 
					
 | 
				
			||||||
                environment={environment}
 | 
					export const ImportArea: FC<{
 | 
				
			||||||
                onChange={setEnvironment}
 | 
					    activeTab: ImportMode;
 | 
				
			||||||
            />
 | 
					    setActiveTab: (mode: ImportMode) => void;
 | 
				
			||||||
 | 
					    importPayload: string;
 | 
				
			||||||
 | 
					    setImportPayload: (payload: string) => void;
 | 
				
			||||||
 | 
					}> = ({ activeTab, setActiveTab, importPayload, setImportPayload }) => {
 | 
				
			||||||
 | 
					    const [dragActive, setDragActive] = useState(false);
 | 
				
			||||||
 | 
					    const { setToastData } = useToast();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
        <ConditionallyRender
 | 
					        <ConditionallyRender
 | 
				
			||||||
            condition={activeTab === 'file'}
 | 
					            condition={activeTab === 'file'}
 | 
				
			||||||
            show={
 | 
					            show={
 | 
				
			||||||
@ -139,16 +122,21 @@ export const ConfigurationStage: FC<{
 | 
				
			|||||||
                />
 | 
					                />
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
            <ImportExplanation />
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Actions: FC<{
 | 
				
			||||||
 | 
					    onSubmit: () => void;
 | 
				
			||||||
 | 
					    onClose: () => void;
 | 
				
			||||||
 | 
					    disabled: boolean;
 | 
				
			||||||
 | 
					}> = ({ onSubmit, onClose, disabled }) => (
 | 
				
			||||||
    <ActionsContainer>
 | 
					    <ActionsContainer>
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
            sx={{ position: 'static' }}
 | 
					            sx={{ position: 'static' }}
 | 
				
			||||||
            variant="contained"
 | 
					            variant="contained"
 | 
				
			||||||
            type="submit"
 | 
					            type="submit"
 | 
				
			||||||
                    onClick={() =>
 | 
					            onClick={onSubmit}
 | 
				
			||||||
                        onSubmit({ payload: importPayload, environment })
 | 
					            disabled={disabled}
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    disabled={!isValidJSON(importPayload)}
 | 
					 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            Validate
 | 
					            Validate
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
@ -161,6 +149,21 @@ export const ConfigurationStage: FC<{
 | 
				
			|||||||
            Cancel import
 | 
					            Cancel import
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
    </ActionsContainer>
 | 
					    </ActionsContainer>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ConfigurationStage: FC<{
 | 
				
			||||||
 | 
					    tabs: ReactNode;
 | 
				
			||||||
 | 
					    importOptions: ReactNode;
 | 
				
			||||||
 | 
					    importArea: ReactNode;
 | 
				
			||||||
 | 
					    actions: ReactNode;
 | 
				
			||||||
 | 
					}> = ({ tabs, importOptions, importArea, actions }) => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <ImportLayoutContainer>
 | 
				
			||||||
 | 
					            {tabs}
 | 
				
			||||||
 | 
					            {importOptions}
 | 
				
			||||||
 | 
					            {importArea}
 | 
				
			||||||
 | 
					            <ImportExplanation />
 | 
				
			||||||
 | 
					            {actions}
 | 
				
			||||||
        </ImportLayoutContainer>
 | 
					        </ImportLayoutContainer>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -39,10 +39,6 @@ export const ImportOptions: FC<IImportOptionsProps> = ({
 | 
				
			|||||||
            title: environment.name,
 | 
					            title: environment.name,
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
        onChange(environmentOptions[0]?.key);
 | 
					 | 
				
			||||||
    }, [JSON.stringify(environmentOptions)]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ImportOptionsContainer>
 | 
					        <ImportOptionsContainer>
 | 
				
			||||||
            <ImportOptionsHeader>Import options</ImportOptionsHeader>
 | 
					            <ImportOptionsHeader>Import options</ImportOptionsHeader>
 | 
				
			||||||
@ -54,7 +50,7 @@ export const ImportOptions: FC<IImportOptionsProps> = ({
 | 
				
			|||||||
                options={environmentOptions}
 | 
					                options={environmentOptions}
 | 
				
			||||||
                onChange={onChange}
 | 
					                onChange={onChange}
 | 
				
			||||||
                label={'Environment'}
 | 
					                label={'Environment'}
 | 
				
			||||||
                value={environment}
 | 
					                value={environment || environmentOptions[0]?.key}
 | 
				
			||||||
                IconComponent={KeyboardArrowDownOutlined}
 | 
					                IconComponent={KeyboardArrowDownOutlined}
 | 
				
			||||||
                fullWidth
 | 
					                fullWidth
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
				
			|||||||
@ -92,16 +92,18 @@ export const ValidationStage: FC<{
 | 
				
			|||||||
    const [validationResult, setValidationResult] = useState<IValidationSchema>(
 | 
					    const [validationResult, setValidationResult] = useState<IValidationSchema>(
 | 
				
			||||||
        { errors: [], warnings: [] }
 | 
					        { errors: [], warnings: [] }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    const [validJSON, setValidJSON] = useState(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        validateImport({ environment, project, data: payload })
 | 
					        validateImport({ environment, project, data: payload })
 | 
				
			||||||
            .then(setValidationResult)
 | 
					            .then(setValidationResult)
 | 
				
			||||||
            .catch(error =>
 | 
					            .catch(error => {
 | 
				
			||||||
 | 
					                setValidJSON(false);
 | 
				
			||||||
                setToastData({
 | 
					                setToastData({
 | 
				
			||||||
                    type: 'error',
 | 
					                    type: 'error',
 | 
				
			||||||
                    title: formatUnknownError(error),
 | 
					                    title: formatUnknownError(error),
 | 
				
			||||||
                })
 | 
					                });
 | 
				
			||||||
            );
 | 
					            });
 | 
				
			||||||
    }, []);
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
@ -185,7 +187,7 @@ export const ValidationStage: FC<{
 | 
				
			|||||||
                    sx={{ position: 'static' }}
 | 
					                    sx={{ position: 'static' }}
 | 
				
			||||||
                    variant="contained"
 | 
					                    variant="contained"
 | 
				
			||||||
                    type="submit"
 | 
					                    type="submit"
 | 
				
			||||||
                    disabled={validationResult.errors.length > 0}
 | 
					                    disabled={validationResult.errors.length > 0 || !validJSON}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    Import configuration
 | 
					                    Import configuration
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user