1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: pulsing avatar for import (#2961)

This commit is contained in:
Mateusz Kwasniewski 2023-01-23 10:44:25 +01:00 committed by GitHub
parent c41f888810
commit bfb038c725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 14 deletions

View File

@ -1,4 +1,4 @@
import React, { FC, useCallback } from 'react';
import React, { FC, useCallback, useEffect } from 'react';
import { useDropzone } from 'react-dropzone';
import { Box } from '@mui/material';
@ -7,10 +7,11 @@ const formatJSON = (json: string) => JSON.stringify(JSON.parse(json), null, 4);
interface IFileDropZoneProps {
onSuccess: (message: string) => void;
onError: (error: string) => void;
onDragStatusChange: (dragOn: boolean) => void;
}
const onFileDropped =
({ onSuccess, onError }: IFileDropZoneProps) =>
({ onSuccess, onError }: Omit<IFileDropZoneProps, 'onDragStatusChange'>) =>
(e: ProgressEvent<FileReader>) => {
const contents = e?.target?.result;
if (typeof contents === 'string') {
@ -30,6 +31,7 @@ export const FileDropZone: FC<IFileDropZoneProps> = ({
onSuccess,
onError,
children,
onDragStatusChange,
...props
}) => {
const onDrop = useCallback(([file]) => {
@ -37,13 +39,16 @@ export const FileDropZone: FC<IFileDropZoneProps> = ({
reader.onload = onFileDropped({ onSuccess, onError });
reader.readAsText(file);
}, []);
const { getRootProps, getInputProps } = useDropzone({
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'application/json': ['.json'],
},
maxFiles: 1,
});
useEffect(() => {
onDragStatusChange(isDragActive);
}, [isDragActive]);
return (
<Box {...getRootProps()} {...props}>

View File

@ -0,0 +1,47 @@
import React, { FC } from 'react';
import { Box, styled, Typography } from '@mui/material';
const ImportExplanationContainer = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.secondaryContainer,
borderRadius: theme.shape.borderRadiusLarge,
padding: theme.spacing(3),
}));
const ImportExplanationHeader = styled(Typography)(({ theme }) => ({
marginBottom: theme.spacing(0.5),
}));
const ImportExplanationDescription = styled(Typography)(({ theme }) => ({
marginBottom: theme.spacing(3),
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,
}));
export const ImportExplanation: FC = () => (
<ImportExplanationContainer>
<ImportExplanationHeader>
What is being imported?
</ImportExplanationHeader>
<ImportExplanationDescription>
Feature toggles will be imported with full configuration:
<ul>
<li>strategies</li>
<li>context fields</li>
<li>variants</li>
<li>tags</li>
<li>feature toggle status</li>
</ul>
</ImportExplanationDescription>
<ImportExplanationHeader>Exceptions?</ImportExplanationHeader>
<ImportExplanationDescription>
If the feature toggle already exists in the new instance, it will be
overwritten
</ImportExplanationDescription>
<ImportExplanationHeader>What is not imported?</ImportExplanationHeader>
<ImportExplanationDescription sx={{ marginBottom: 0 }}>
If we detect segments or custom strategies in your imported file, we
will stop the import. You need to create them first in the new
instance and run the import again
</ImportExplanationDescription>
</ImportExplanationContainer>
);

View File

@ -6,7 +6,6 @@ import {
TextField,
Box,
Typography,
Avatar,
} from '@mui/material';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { ArrowUpward } from '@mui/icons-material';
@ -16,11 +15,13 @@ import { StyledFileDropZone } from './StyledFileDropZone';
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
import useToast from 'hooks/useToast';
import { ImportOptions } from './ImportOptions';
import { ImportExplanation } from './ImportExplanation';
import { PulsingAvatar } from './PulsingAvatar';
const LayoutContainer = styled('div')(({ theme }) => ({
backgroundColor: '#fff',
height: '100vh',
padding: theme.spacing(4, 8, 4, 8),
minHeight: '100vh',
padding: theme.spacing(3, 8, 3, 8),
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
@ -42,7 +43,7 @@ const SelectFileMessage = styled(Typography)(({ theme }) => ({
}));
const MaxSizeMessage = styled(Typography)(({ theme }) => ({
marginTop: theme.spacing(9),
marginTop: theme.spacing(4),
color: theme.palette.text.secondary,
}));
@ -50,7 +51,7 @@ const ActionsContainer = styled(Box)(({ theme }) => ({
width: '100%',
borderTop: `1px solid ${theme.palette.dividerAlternative}`,
marginTop: 'auto',
paddingTop: theme.spacing(4),
paddingTop: theme.spacing(3),
display: 'flex',
justifyContent: 'flex-end',
}));
@ -70,6 +71,7 @@ export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
const [environment, setEnvironment] = useState('');
const [importPayload, setImportPayload] = useState('');
const [activeTab, setActiveTab] = useState<ImportMode>('file');
const [dragActive, setDragActive] = useState(false);
const onSubmit = async () => {
await createImport({
@ -132,11 +134,16 @@ export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
title: error,
});
}}
onDragStatusChange={setDragActive}
>
<Avatar sx={{ width: 80, height: 80 }}>
<PulsingAvatar active={dragActive}>
<ArrowUpward fontSize="large" />
</Avatar>
<DropMessage>Drop your file here</DropMessage>
</PulsingAvatar>
<DropMessage>
{dragActive
? 'Drop your file to upload'
: 'Drop your file here'}
</DropMessage>
<SelectFileMessage>
or select a file from your device
</SelectFileMessage>
@ -155,11 +162,12 @@ export const ImportModal = ({ open, setOpen, project }: IImportModalProps) => {
}
value={importPayload}
multiline
minRows={17}
maxRows={17}
minRows={13}
maxRows={13}
/>
}
/>
<ImportExplanation />
<ActionsContainer>
<Button
sx={{ position: 'static' }}

View File

@ -0,0 +1,21 @@
import { alpha, Avatar, styled } from '@mui/material';
export const PulsingAvatar = styled(Avatar, {
shouldForwardProp: prop => prop !== 'active',
})<{ active: boolean }>(({ theme, active }) => ({
width: '80px',
height: '80px',
transition: 'background-color 0.5s ease',
backgroundColor: active
? theme.palette.primary.main
: theme.palette.tertiary.main,
'@keyframes pulse': {
'0%': {
boxShadow: `0 0 0 0px ${alpha(theme.palette.primary.main, 0.7)}`,
},
'100%': {
boxShadow: `0 0 0 20px ${alpha(theme.palette.primary.main, 0.0)}`,
},
},
animation: active ? 'pulse 2s infinite' : '',
}));

View File

@ -3,7 +3,7 @@ import { FileDropZone } from './FileDropZone';
import React from 'react';
export const StyledFileDropZone = styled(FileDropZone)(({ theme }) => ({
padding: theme.spacing(10, 2, 2, 2),
padding: theme.spacing(4, 2, 2, 2),
border: `1px dashed ${theme.palette.secondary.border}`,
borderRadius: theme.shape.borderRadiusLarge,
display: 'flex',