diff --git a/frontend/src/component/commandBar/CommandBar.tsx b/frontend/src/component/commandBar/CommandBar.tsx index c8aa7b5ea8..e2819bfcb3 100644 --- a/frontend/src/component/commandBar/CommandBar.tsx +++ b/frontend/src/component/commandBar/CommandBar.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Box, IconButton, @@ -13,7 +13,6 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit import { useKeyboardShortcut } from 'hooks/useKeyboardShortcut'; import { SEARCH_INPUT } from 'utils/testIds'; import { useOnClickOutside } from 'hooks/useOnClickOutside'; -import { useOnBlur } from 'hooks/useOnBlur'; import { CommandResultGroup, type CommandResultGroupItem, @@ -26,6 +25,7 @@ import { CommandFeatures } from './CommandFeatures'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { CommandRecent } from './CommandRecent'; import { CommandPages } from './CommandPages'; +import { CommandBarFeedback } from './CommandBarFeedback'; import { RecentlyVisitedRecorder } from './RecentlyVisitedRecorder'; export const CommandResultsPaper = styled(Paper)(({ theme }) => ({ @@ -35,7 +35,7 @@ export const CommandResultsPaper = styled(Paper)(({ theme }) => ({ top: '39px', zIndex: 4, borderTop: theme.spacing(0), - padding: theme.spacing(4, 0, 1.5), + padding: theme.spacing(1.5, 0, 1.5), borderRadius: 0, borderBottomLeftRadius: theme.spacing(1), borderBottomRightRadius: theme.spacing(1), @@ -109,6 +109,7 @@ export const CommandBar = () => { CommandResultGroupItem[] >([]); const [searchedFlagCount, setSearchedFlagCount] = useState(0); + const [hasNoResults, setHasNoResults] = useState(false); const [value, setValue] = useState(''); const { routes } = useRoutes(); const allRoutes: Record = {}; @@ -166,8 +167,13 @@ export const CommandBar = () => { }, }); } + setHasNoResults(noResultsFound); }, 200); + useEffect(() => { + debouncedSetSearchState(value); + }, [searchedFlagCount]); + const onSearchChange = (value: string) => { debouncedSetSearchState(value); setValue(value); @@ -195,8 +201,6 @@ export const CommandBar = () => { const placeholder = `Command bar (${hotkey})`; useOnClickOutside([searchContainerRef], hideSuggestions); - useOnBlur(searchContainerRef, hideSuggestions); - return ( @@ -261,6 +265,14 @@ export const CommandBar = () => { items={searchedProjects} /> + + } + /> } elseShow={ diff --git a/frontend/src/component/commandBar/CommandBarFeedback.tsx b/frontend/src/component/commandBar/CommandBarFeedback.tsx new file mode 100644 index 0000000000..564c28ef63 --- /dev/null +++ b/frontend/src/component/commandBar/CommandBarFeedback.tsx @@ -0,0 +1,96 @@ +import { Button, styled, TextField } from '@mui/material'; +import type React from 'react'; +import { useState } from 'react'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { useUserFeedbackApi } from 'hooks/api/actions/useUserFeedbackApi/useUserFeedbackApi'; +import useToast from 'hooks/useToast'; +import useUserType from '../feedbackNew/useUserType'; + +const StyledContainer = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + padding: theme.spacing(2), + gap: theme.spacing(1), +})); + +const StyledText = styled('span')(({ theme }) => ({ + fontSize: theme.spacing(1.5), +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + fontSize: theme.spacing(1.5), +})); + +interface ICommandBarFeedbackProps { + onSubmit: () => void; +} + +export const CommandBarFeedback = ({ onSubmit }: ICommandBarFeedbackProps) => { + const userType = useUserType(); + const { addFeedback } = useUserFeedbackApi(); + const { setToastData } = useToast(); + const [suggesting, setSuggesting] = useState(false); + const [feedback, setFeedback] = useState(undefined); + + const changeFeedback = (event: React.ChangeEvent) => { + setFeedback(event.target.value.trim()); + }; + + const sendFeedback = async () => { + await addFeedback({ + areasForImprovement: feedback, + category: 'commandBar', + userType: userType, + }); + onSubmit(); + setToastData({ + title: 'Feedback sent', + type: 'success', + }); + }; + return ( + + + Describe the capability + + + Send to Unleash + + + } + elseShow={ + <> + + We couldn’t find anything matching your search + criteria. If you think this is a missing capability, + feel free to make a suggestion. + + { + e.stopPropagation(); + setSuggesting(true); + }} + > + Suggest capability + + + } + /> + + ); +}; diff --git a/frontend/src/component/commandBar/RecentlyVisited/CommandResultGroup.tsx b/frontend/src/component/commandBar/RecentlyVisited/CommandResultGroup.tsx index 726e01fe64..be1d7459f5 100644 --- a/frontend/src/component/commandBar/RecentlyVisited/CommandResultGroup.tsx +++ b/frontend/src/component/commandBar/RecentlyVisited/CommandResultGroup.tsx @@ -17,6 +17,7 @@ import { import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; +import { Children } from 'react'; export const listItemButtonStyle = (theme: Theme) => ({ border: `1px solid transparent`, @@ -26,10 +27,6 @@ export const listItemButtonStyle = (theme: Theme) => ({ borderLeft: `${theme.spacing(0.5)} solid ${theme.palette.primary.main}`, }, }); -const StyledContainer = styled('div')(({ theme }) => ({ - marginBottom: theme.spacing(3), -})); - export const StyledTypography = styled(Typography)(({ theme }) => ({ fontSize: theme.fontSizes.bodySize, padding: theme.spacing(0, 2.5), @@ -193,7 +190,10 @@ export const CommandResultGroup = ({ children, }: CommandResultGroupProps) => { const { trackEvent } = usePlausibleTracker(); - if (!children && (!items || items.length === 0)) { + if ( + (!children || Children.count(children) === 0) && + (!items || items.length === 0) + ) { return null; } @@ -211,7 +211,7 @@ export const CommandResultGroup = ({ }; return ( - +
{groupName} @@ -249,6 +249,6 @@ export const CommandResultGroup = ({ ))} - +
); }; diff --git a/frontend/src/component/feedbackNew/FeedbackComponent.tsx b/frontend/src/component/feedbackNew/FeedbackComponent.tsx index 3252341bfa..2a5c8d6bd4 100644 --- a/frontend/src/component/feedbackNew/FeedbackComponent.tsx +++ b/frontend/src/component/feedbackNew/FeedbackComponent.tsx @@ -16,12 +16,12 @@ import useToast from 'hooks/useToast'; import type { ProvideFeedbackSchema } from 'openapi'; import { useUserFeedbackApi } from 'hooks/api/actions/useUserFeedbackApi/useUserFeedbackApi'; import { useUserSubmittedFeedback } from 'hooks/useSubmittedFeedback'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import type { IToast } from 'interfaces/toast'; import { useTheme } from '@mui/material/styles'; import type { FeedbackData, FeedbackMode } from './FeedbackContext'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { useUiFlag } from 'hooks/useUiFlag'; +import useUserType from './useUserType'; export const ParentContainer = styled('div')(({ theme }) => ({ position: 'relative', @@ -201,9 +201,10 @@ export const FeedbackComponent = ({ feedbackMode, }: IFeedbackComponent) => { const { setToastData } = useToast(); + const userType = useUserType(); const { trackEvent } = usePlausibleTracker(); const theme = useTheme(); - const { isPro, isOss, isEnterprise } = useUiConfig(); + const { addFeedback } = useUserFeedbackApi(); const { setHasSubmittedFeedback } = useUserSubmittedFeedback( feedbackData.category, @@ -276,22 +277,6 @@ export const FeedbackComponent = ({ setSelectedScore(event.target.value); }; - const getUserType = () => { - if (isPro()) { - return 'pro'; - } - - if (isOss()) { - return 'oss'; - } - - if (isEnterprise()) { - return 'enterprise'; - } - - return 'unknown'; - }; - return ( {feedbackData.title} diff --git a/frontend/src/component/feedbackNew/useUserType.ts b/frontend/src/component/feedbackNew/useUserType.ts new file mode 100644 index 0000000000..a9db7bb233 --- /dev/null +++ b/frontend/src/component/feedbackNew/useUserType.ts @@ -0,0 +1,21 @@ +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; + +const useUserType = () => { + const { isPro, isOss, isEnterprise } = useUiConfig(); + + if (isPro()) { + return 'pro'; + } + + if (isOss()) { + return 'oss'; + } + + if (isEnterprise()) { + return 'enterprise'; + } + + return 'unknown'; +}; + +export default useUserType;