mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Project stats feedback (#3131)
## About the changes  https://linear.app/unleash/issue/1-694/widgets-explanation-plausible-buttons
This commit is contained in:
		
							parent
							
								
									eac5fca44c
								
							
						
					
					
						commit
						c0ec6f20b2
					
				| @ -0,0 +1,76 @@ | ||||
| import { useState, VFC } from 'react'; | ||||
| import { Box, Paper, Button, styled } from '@mui/material'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; | ||||
| import { createLocalStorage } from 'utils/createLocalStorage'; | ||||
| 
 | ||||
| interface IFeedbackProps { | ||||
|     id: string; | ||||
| } | ||||
| 
 | ||||
| const StyledBox = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     gap: theme.spacing(1), | ||||
|     marginTop: theme.spacing(0.5), | ||||
| })); | ||||
| 
 | ||||
| export const Feedback: VFC<IFeedbackProps> = ({ id }) => { | ||||
|     const { uiConfig } = useUiConfig(); | ||||
|     const { value: selectedValue, setValue: setSelectedValue } = | ||||
|         createLocalStorage<{ value?: 'yes' | 'no' }>( | ||||
|             `ProjectOverviewFeedback:v1:${id}`, | ||||
|             {} | ||||
|         ); | ||||
|     const [selected, setSelected] = useState<'yes' | 'no' | undefined>( | ||||
|         selectedValue.value | ||||
|     ); | ||||
|     const { trackEvent } = usePlausibleTracker(); | ||||
| 
 | ||||
|     if (!uiConfig?.flags?.T) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     const onTrackFeedback = (value: 'yes' | 'no') => { | ||||
|         setSelected(value); | ||||
|         setSelectedValue({ value }); | ||||
|         trackEvent('project_overview', { | ||||
|             props: { | ||||
|                 eventType: id, | ||||
|                 wasHelpful: value === 'yes', | ||||
|             }, | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <Paper | ||||
|             elevation={0} | ||||
|             sx={{ | ||||
|                 background: theme => theme.palette.neutral.light, | ||||
|                 padding: theme => theme.spacing(1.5, 2), | ||||
|                 marginTop: theme => theme.spacing(1.5), | ||||
|             }} | ||||
|         > | ||||
|             Was this information useful to you? | ||||
|             <StyledBox> | ||||
|                 <Button | ||||
|                     size="small" | ||||
|                     variant={selected === 'yes' ? 'contained' : 'outlined'} | ||||
|                     sx={{ padding: 0 }} | ||||
|                     onClick={() => onTrackFeedback('yes')} | ||||
|                     disabled={Boolean(selected)} | ||||
|                 > | ||||
|                     Yes | ||||
|                 </Button> | ||||
|                 <Button | ||||
|                     size="small" | ||||
|                     variant={selected === 'no' ? 'contained' : 'outlined'} | ||||
|                     sx={{ padding: 0 }} | ||||
|                     onClick={() => onTrackFeedback('no')} | ||||
|                     disabled={Boolean(selected)} | ||||
|                 > | ||||
|                     No | ||||
|                 </Button> | ||||
|             </StyledBox> | ||||
|         </Paper> | ||||
|     ); | ||||
| }; | ||||
| @ -0,0 +1,75 @@ | ||||
| import { FC, useState } from 'react'; | ||||
| import { Close, HelpOutline } from '@mui/icons-material'; | ||||
| import { | ||||
|     Box, | ||||
|     IconButton, | ||||
|     Popper, | ||||
|     Paper, | ||||
|     ClickAwayListener, | ||||
|     styled, | ||||
| } from '@mui/material'; | ||||
| import { Feedback } from './Feedback'; | ||||
| 
 | ||||
| interface IHelpPopperProps { | ||||
|     id: string; | ||||
| } | ||||
| 
 | ||||
| const StyledPaper = styled(Paper)(({ theme }) => ({ | ||||
|     padding: theme.spacing(3, 3), | ||||
|     maxWidth: '350px', | ||||
|     borderRadius: `${theme.shape.borderRadiusMedium}px`, | ||||
|     border: `1px solid ${theme.palette.neutral.border}`, | ||||
|     fontSize: theme.typography.body2.fontSize, | ||||
| })); | ||||
| 
 | ||||
| export const HelpPopper: FC<IHelpPopperProps> = ({ children, id }) => { | ||||
|     const [anchor, setAnchorEl] = useState<null | Element>(null); | ||||
| 
 | ||||
|     const onOpen = (event: React.FormEvent<HTMLButtonElement>) => | ||||
|         setAnchorEl(event.currentTarget); | ||||
| 
 | ||||
|     const onClose = () => setAnchorEl(null); | ||||
| 
 | ||||
|     const open = Boolean(anchor); | ||||
| 
 | ||||
|     return ( | ||||
|         <Box | ||||
|             sx={{ | ||||
|                 position: 'absolute', | ||||
|                 top: theme => theme.spacing(0.5), | ||||
|                 right: theme => theme.spacing(0.5), | ||||
|             }} | ||||
|         > | ||||
|             <IconButton onClick={onOpen} aria-describedby={id} size="small"> | ||||
|                 <HelpOutline | ||||
|                     sx={{ fontSize: theme => theme.typography.body1.fontSize }} | ||||
|                 /> | ||||
|             </IconButton> | ||||
| 
 | ||||
|             <Popper | ||||
|                 id={id} | ||||
|                 open={open} | ||||
|                 anchorEl={anchor} | ||||
|                 sx={theme => ({ zIndex: theme.zIndex.tooltip })} | ||||
|             > | ||||
|                 <ClickAwayListener onClickAway={onClose}> | ||||
|                     <StyledPaper elevation={3}> | ||||
|                         <IconButton | ||||
|                             onClick={onClose} | ||||
|                             sx={{ position: 'absolute', right: 4, top: 4 }} | ||||
|                         > | ||||
|                             <Close | ||||
|                                 sx={{ | ||||
|                                     fontSize: theme => | ||||
|                                         theme.typography.body1.fontSize, | ||||
|                                 }} | ||||
|                             /> | ||||
|                         </IconButton> | ||||
|                         {children} | ||||
|                         <Feedback id={id} /> | ||||
|                     </StyledPaper> | ||||
|                 </ClickAwayListener> | ||||
|             </Popper> | ||||
|         </Box> | ||||
|     ); | ||||
| }; | ||||
| @ -1,5 +1,6 @@ | ||||
| import { Box, styled, Typography } from '@mui/material'; | ||||
| import { ProjectStatsSchema } from 'openapi/models'; | ||||
| import { HelpPopper } from './HelpPopper'; | ||||
| import { StatusBox } from './StatusBox'; | ||||
| 
 | ||||
| const StyledBox = styled(Box)(({ theme }) => ({ | ||||
| @ -20,6 +21,7 @@ const StyledBox = styled(Box)(({ theme }) => ({ | ||||
| })); | ||||
| 
 | ||||
| const StyledWidget = styled(Box)(({ theme }) => ({ | ||||
|     position: 'relative', | ||||
|     padding: theme.spacing(3), | ||||
|     backgroundColor: theme.palette.background.paper, | ||||
|     flex: 1, | ||||
| @ -73,7 +75,12 @@ export const ProjectStats = ({ stats }: IProjectStatsProps) => { | ||||
|                         projectActivityPastWindow - | ||||
|                         20 | ||||
|                     } | ||||
|                 /> | ||||
|                 > | ||||
|                     <HelpPopper id="total-changes"> | ||||
|                         Sum of all configuration and state modifications in the | ||||
|                         project. | ||||
|                     </HelpPopper> | ||||
|                 </StatusBox> | ||||
|             </StyledWidget> | ||||
|             <StyledWidget> | ||||
|                 <StatusBox | ||||
| @ -95,7 +102,13 @@ export const ProjectStats = ({ stats }: IProjectStatsProps) => { | ||||
|                         avgTimeToProdPastWindow | ||||
|                     )} | ||||
|                     percentage | ||||
|                 /> | ||||
|                 > | ||||
|                     <HelpPopper id="avg-time-to-prod"> | ||||
|                         How long did it take on average from a feature toggle | ||||
|                         was created until it was enabled in an environment of | ||||
|                         type production. | ||||
|                     </HelpPopper> | ||||
|                 </StatusBox> | ||||
|             </StyledWidget> | ||||
|             <StyledWidget> | ||||
|                 <StatusBox | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import type { ReactNode } from 'react'; | ||||
| import type { FC, ReactNode } from 'react'; | ||||
| import { CallMade, SouthEast } from '@mui/icons-material'; | ||||
| import { Box, Typography, styled } from '@mui/material'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| @ -53,17 +53,19 @@ const resolveColor = (change: number) => { | ||||
|     return 'warning.dark'; | ||||
| }; | ||||
| 
 | ||||
| export const StatusBox = ({ | ||||
| export const StatusBox: FC<IStatusBoxProps> = ({ | ||||
|     title, | ||||
|     boxText, | ||||
|     change, | ||||
|     percentage, | ||||
| }: IStatusBoxProps) => ( | ||||
|     children, | ||||
| }) => ( | ||||
|     <> | ||||
|         <ConditionallyRender | ||||
|             condition={Boolean(title)} | ||||
|             show={<StyledTypographyHeader>{title}</StyledTypographyHeader>} | ||||
|         /> | ||||
|         {children} | ||||
|         <Box | ||||
|             sx={{ | ||||
|                 ...flexRow, | ||||
|  | ||||
| @ -12,7 +12,7 @@ type UsePersistentGlobalState<T> = () => [ | ||||
|  * The state is also persisted to localStorage and restored on page load. | ||||
|  * The localStorage state is not synced between tabs. | ||||
|  * | ||||
|  * @deprecated `hooks/useLocalStorage` -- we don't need `react-hooks-global-state` | ||||
|  * @deprecated `utils/createLocalStorage` -- we don't need `react-hooks-global-state` | ||||
|  */ | ||||
| export const createPersistentGlobalStateHook = <T extends object>( | ||||
|     key: string, | ||||
|  | ||||
| @ -16,6 +16,7 @@ type CustomEvents = | ||||
|     | 'maintenance' | ||||
|     | 'message_banner' | ||||
|     | 'hidden_environment' | ||||
|     | 'project_overview' | ||||
|     | 'suggest_tags' | ||||
|     | 'unknown_ui_error'; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user