mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
feat: make feedback available for OSS (#5748)
This commit is contained in:
parent
1556a51e37
commit
a73d87a943
@ -84,7 +84,7 @@ const FeatureToggleListTableComponent: VFC = () => {
|
|||||||
const [showExportDialog, setShowExportDialog] = useState(false);
|
const [showExportDialog, setShowExportDialog] = useState(false);
|
||||||
|
|
||||||
const { setToastApiError } = useToast();
|
const { setToastApiError } = useToast();
|
||||||
const { uiConfig, isPro, isOss, isEnterprise } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const featureSearchFeedback = useUiFlag('featureSearchFeedback');
|
const featureSearchFeedback = useUiFlag('featureSearchFeedback');
|
||||||
|
|
||||||
const stateConfig = {
|
const stateConfig = {
|
||||||
@ -275,16 +275,8 @@ const FeatureToggleListTableComponent: VFC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createFeedbackContext = () => {
|
const createFeedbackContext = () => {
|
||||||
const userType = isPro()
|
|
||||||
? 'pro'
|
|
||||||
: isOss()
|
|
||||||
? 'oss'
|
|
||||||
: isEnterprise()
|
|
||||||
? 'enterprise'
|
|
||||||
: 'unknown';
|
|
||||||
openFeedback({
|
openFeedback({
|
||||||
category: feedbackCategory,
|
category: feedbackCategory,
|
||||||
userType,
|
|
||||||
title: 'How easy was it to use search and filters?',
|
title: 'How easy was it to use search and filters?',
|
||||||
positiveLabel: 'What do you like most about search and filters?',
|
positiveLabel: 'What do you like most about search and filters?',
|
||||||
areasForImprovementsLabel:
|
areasForImprovementsLabel:
|
||||||
@ -335,7 +327,6 @@ const FeatureToggleListTableComponent: VFC = () => {
|
|||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={
|
||||||
!isOss() &&
|
|
||||||
!hasSubmittedFeedback &&
|
!hasSubmittedFeedback &&
|
||||||
featureSearchFeedback
|
featureSearchFeedback
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@ import useToast from 'hooks/useToast';
|
|||||||
import { ProvideFeedbackSchema } from '../../openapi';
|
import { ProvideFeedbackSchema } from '../../openapi';
|
||||||
import { useUserFeedbackApi } from 'hooks/api/actions/useUserFeedbackApi/useUserFeedbackApi';
|
import { useUserFeedbackApi } from 'hooks/api/actions/useUserFeedbackApi/useUserFeedbackApi';
|
||||||
import { useUserSubmittedFeedback } from 'hooks/useSubmittedFeedback';
|
import { useUserSubmittedFeedback } from 'hooks/useSubmittedFeedback';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import { IToast } from 'interfaces/toast';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import { FeedbackData } from './FeedbackContext';
|
||||||
|
|
||||||
export const ParentContainer = styled('div')(({ theme }) => ({
|
export const ParentContainer = styled('div')(({ theme }) => ({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -155,12 +159,34 @@ const StyledCloseButton = styled(IconButton)(({ theme }) => ({
|
|||||||
color: theme.palette.background.paper,
|
color: theme.palette.background.paper,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FeedbackComponent = () => {
|
export const FeedbackComponentWrapper = () => {
|
||||||
const { feedbackData, showFeedback, closeFeedback } = useFeedback();
|
const { feedbackData, showFeedback, closeFeedback } = useFeedback();
|
||||||
|
|
||||||
if (!feedbackData) return null;
|
if (!feedbackData) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FeedbackComponent
|
||||||
|
feedbackData={feedbackData}
|
||||||
|
showFeedback={showFeedback}
|
||||||
|
closeFeedback={closeFeedback}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IFeedbackComponent {
|
||||||
|
feedbackData: FeedbackData;
|
||||||
|
showFeedback: boolean;
|
||||||
|
closeFeedback: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FeedbackComponent = ({
|
||||||
|
feedbackData,
|
||||||
|
showFeedback,
|
||||||
|
closeFeedback,
|
||||||
|
}: IFeedbackComponent) => {
|
||||||
const { setToastData } = useToast();
|
const { setToastData } = useToast();
|
||||||
|
const theme = useTheme();
|
||||||
|
const { isPro, isOss, isEnterprise } = useUiConfig();
|
||||||
const { addFeedback } = useUserFeedbackApi();
|
const { addFeedback } = useUserFeedbackApi();
|
||||||
const { setHasSubmittedFeedback } = useUserSubmittedFeedback(
|
const { setHasSubmittedFeedback } = useUserSubmittedFeedback(
|
||||||
feedbackData.category,
|
feedbackData.category,
|
||||||
@ -184,19 +210,22 @@ export const FeedbackComponent = () => {
|
|||||||
const formData = new FormData(event.currentTarget);
|
const formData = new FormData(event.currentTarget);
|
||||||
const data = Object.fromEntries(formData);
|
const data = Object.fromEntries(formData);
|
||||||
|
|
||||||
|
let toastType: IToast['type'] = 'error';
|
||||||
|
let toastTitle = 'Feedback not sent';
|
||||||
|
|
||||||
if (isProvideFeedbackSchema(data)) {
|
if (isProvideFeedbackSchema(data)) {
|
||||||
|
try {
|
||||||
await addFeedback(data as ProvideFeedbackSchema);
|
await addFeedback(data as ProvideFeedbackSchema);
|
||||||
setToastData({
|
toastTitle = 'Feedback sent';
|
||||||
title: 'Feedback sent',
|
toastType = 'success';
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
setHasSubmittedFeedback(true);
|
setHasSubmittedFeedback(true);
|
||||||
} else {
|
} catch (e) {}
|
||||||
setToastData({
|
|
||||||
title: 'Feedback not sent',
|
|
||||||
type: 'error',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setToastData({
|
||||||
|
title: toastTitle,
|
||||||
|
type: toastType,
|
||||||
|
});
|
||||||
closeFeedback();
|
closeFeedback();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,6 +235,22 @@ export const FeedbackComponent = () => {
|
|||||||
setSelectedScore(event.target.value);
|
setSelectedScore(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getUserType = () => {
|
||||||
|
if (isPro()) {
|
||||||
|
return 'pro';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOss()) {
|
||||||
|
return 'oss';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnterprise()) {
|
||||||
|
return 'enterprise';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showFeedback}
|
condition={showFeedback}
|
||||||
@ -233,7 +278,7 @@ export const FeedbackComponent = () => {
|
|||||||
<input
|
<input
|
||||||
type='hidden'
|
type='hidden'
|
||||||
name='userType'
|
name='userType'
|
||||||
value={feedbackData.userType}
|
value={getUserType()}
|
||||||
/>
|
/>
|
||||||
<FormTitle>{feedbackData.title}</FormTitle>
|
<FormTitle>{feedbackData.title}</FormTitle>
|
||||||
<StyledScoreContainer>
|
<StyledScoreContainer>
|
||||||
@ -273,7 +318,8 @@ export const FeedbackComponent = () => {
|
|||||||
size='small'
|
size='small'
|
||||||
InputLabelProps={{
|
InputLabelProps={{
|
||||||
style: {
|
style: {
|
||||||
fontSize: '14px',
|
fontSize:
|
||||||
|
theme.fontSizes.smallBody,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -290,7 +336,8 @@ export const FeedbackComponent = () => {
|
|||||||
rows={3}
|
rows={3}
|
||||||
InputLabelProps={{
|
InputLabelProps={{
|
||||||
style: {
|
style: {
|
||||||
fontSize: '14px',
|
fontSize:
|
||||||
|
theme.fontSizes.smallBody,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
|
@ -3,21 +3,22 @@ import { ProvideFeedbackSchema } from '../../openapi';
|
|||||||
import { IFeedbackCategory } from 'hooks/useSubmittedFeedback';
|
import { IFeedbackCategory } from 'hooks/useSubmittedFeedback';
|
||||||
|
|
||||||
interface IFeedbackContext {
|
interface IFeedbackContext {
|
||||||
feedbackData: IFeedbackData | undefined;
|
feedbackData: FeedbackData | undefined;
|
||||||
openFeedback: (data: IFeedbackData) => void;
|
openFeedback: (data: FeedbackData) => void;
|
||||||
closeFeedback: () => void;
|
closeFeedback: () => void;
|
||||||
showFeedback: boolean;
|
showFeedback: boolean;
|
||||||
setShowFeedback: (visible: boolean) => void;
|
setShowFeedback: (visible: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IFeedbackText = {
|
interface IFeedbackText {
|
||||||
title: string;
|
title: string;
|
||||||
positiveLabel: string;
|
positiveLabel: string;
|
||||||
areasForImprovementsLabel: string;
|
areasForImprovementsLabel: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type IFeedbackData = Pick<ProvideFeedbackSchema, 'userType'> &
|
export type FeedbackData = IFeedbackText & {
|
||||||
IFeedbackText & { category: IFeedbackCategory };
|
category: IFeedbackCategory;
|
||||||
|
};
|
||||||
|
|
||||||
export const FeedbackContext = createContext<IFeedbackContext | undefined>(
|
export const FeedbackContext = createContext<IFeedbackContext | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import { FeedbackComponent } from './FeedbackComponent';
|
import {
|
||||||
import { FeedbackContext, IFeedbackData } from './FeedbackContext';
|
FeedbackComponent,
|
||||||
|
FeedbackComponentWrapper,
|
||||||
|
} from './FeedbackComponent';
|
||||||
|
import { FeedbackContext, FeedbackData } from './FeedbackContext';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
|
|
||||||
export const FeedbackProvider: FC = ({ children }) => {
|
export const FeedbackProvider: FC = ({ children }) => {
|
||||||
const [feedbackData, setFeedbackData] = useState<IFeedbackData | undefined>(
|
const [feedbackData, setFeedbackData] = useState<FeedbackData | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showFeedback, setShowFeedback] = useState(false);
|
const [showFeedback, setShowFeedback] = useState(false);
|
||||||
const openFeedback = (data: IFeedbackData) => {
|
const openFeedback = (data: FeedbackData) => {
|
||||||
setFeedbackData(data);
|
setFeedbackData(data);
|
||||||
setShowFeedback(true);
|
setShowFeedback(true);
|
||||||
};
|
};
|
||||||
@ -29,7 +32,7 @@ export const FeedbackProvider: FC = ({ children }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<FeedbackComponent />
|
<FeedbackComponentWrapper />
|
||||||
</FeedbackContext.Provider>
|
</FeedbackContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,41 +2,20 @@ import useAPI from '../useApi/useApi';
|
|||||||
import { ProvideFeedbackSchema } from '../../../../openapi';
|
import { ProvideFeedbackSchema } from '../../../../openapi';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
|
||||||
const ENDPOINT = 'feedback';
|
const ENDPOINT = 'https://app.unleash-hosted.com/hosted/feedback';
|
||||||
|
|
||||||
export const useUserFeedbackApi = () => {
|
export const useUserFeedbackApi = () => {
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
|
||||||
const { loading, makeRequest, createRequest, errors } = useAPI({
|
|
||||||
propagateErrors: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const addFeedback = async (feedbackSchema: ProvideFeedbackSchema) => {
|
const addFeedback = async (feedbackSchema: ProvideFeedbackSchema) => {
|
||||||
if (uiConfig.feedbackUriPath !== undefined) {
|
await fetch(uiConfig.feedbackUriPath || ENDPOINT, {
|
||||||
await fetch(uiConfig.feedbackUriPath, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(feedbackSchema),
|
body: JSON.stringify(feedbackSchema),
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
const requestId = 'addFeedback';
|
|
||||||
const req = createRequest(
|
|
||||||
ENDPOINT,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(feedbackSchema),
|
|
||||||
},
|
|
||||||
requestId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await makeRequest(req.caller, req.id);
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addFeedback,
|
addFeedback,
|
||||||
errors,
|
|
||||||
loading,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -84,7 +84,7 @@ exports[`should create default config 1`] = `
|
|||||||
"embedProxy": true,
|
"embedProxy": true,
|
||||||
"embedProxyFrontend": true,
|
"embedProxyFrontend": true,
|
||||||
"featureSearchAPI": false,
|
"featureSearchAPI": false,
|
||||||
"featureSearchFeedback": false,
|
"featureSearchFeedback": true,
|
||||||
"featureSearchFeedbackPosting": false,
|
"featureSearchFeedbackPosting": false,
|
||||||
"featureSearchFrontend": false,
|
"featureSearchFrontend": false,
|
||||||
"featuresExportImport": true,
|
"featuresExportImport": true,
|
||||||
|
@ -53,6 +53,7 @@ const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => {
|
|||||||
'plausible.getunleash.io',
|
'plausible.getunleash.io',
|
||||||
'gravatar.com',
|
'gravatar.com',
|
||||||
'europe-west3-metrics-304612.cloudfunctions.net',
|
'europe-west3-metrics-304612.cloudfunctions.net',
|
||||||
|
'app.unleash-hosted.com',
|
||||||
...config.additionalCspAllowedDomains.connectSrc,
|
...config.additionalCspAllowedDomains.connectSrc,
|
||||||
],
|
],
|
||||||
mediaSrc: [
|
mediaSrc: [
|
||||||
|
@ -155,7 +155,7 @@ const flags: IFlags = {
|
|||||||
),
|
),
|
||||||
featureSearchFeedback: parseEnvVarBoolean(
|
featureSearchFeedback: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_FEEDBACK,
|
process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_FEEDBACK,
|
||||||
false,
|
true,
|
||||||
),
|
),
|
||||||
featureSearchFeedbackPosting: parseEnvVarBoolean(
|
featureSearchFeedbackPosting: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_FEEDBACK_POSTING,
|
process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_FEEDBACK_POSTING,
|
||||||
|
@ -47,7 +47,6 @@ process.nextTick(async () => {
|
|||||||
stripHeadersOnAPI: true,
|
stripHeadersOnAPI: true,
|
||||||
celebrateUnleash: true,
|
celebrateUnleash: true,
|
||||||
increaseUnleashWidth: true,
|
increaseUnleashWidth: true,
|
||||||
featureSearchFeedback: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
Loading…
Reference in New Issue
Block a user