Skip to content

Commit 4cf96d8

Browse files
authored
feat: Add feedback popover in header (#4008)
* feat: add feedback popover in header * feat: load links from config * feat: handle null value * feat: remove feedback entry from header menu * feat: add notification dot * feat: store notification state in local storage * fix: issue in backend error logging * feat: update local storage only after survey has been opened * test: add tests * test: fix test issues * fix: remove redundant line
1 parent b25b19e commit 4cf96d8

File tree

12 files changed

+350
-48
lines changed

12 files changed

+350
-48
lines changed

backend/companion/companionRouter.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,13 @@ async function handleChatMessage(req, res) {
162162
} else {
163163
setTimeout(() => {
164164
req.log.warn(error);
165-
res.status(500).json({
166-
error: 'Failed to fetch AI chat data. Request ID: ' + escape(req.id),
167-
});
165+
res.write(
166+
JSON.stringify({
167+
error:
168+
'Failed to fetch AI chat data. Request ID: ' + escape(req.id),
169+
}),
170+
);
171+
res.end();
168172
}, 500);
169173
}
170174
}

public/i18n/en.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,6 @@ kyma-companion:
774774
description: Joule is your personal, contextual assistant, ready to help with all Kyma-related questions. Ask about Kyma features, Kubernetes best practices, step-by-step instructions, and more.
775775
buttons:
776776
try-joule: Try Out Joule
777-
feedback: Give Feedback
778777
name: Joule
779778
feedback:
780779
title: We’d love to hear from you!
@@ -852,6 +851,19 @@ kyma-modules:
852851
modules-update-failed: Modules update failed
853852
community-modules:
854853
title: Community Modules
854+
feedback:
855+
feedback: Feedback
856+
give-feedback: Give Feedback
857+
new: New
858+
intro:
859+
title: Hello,
860+
info: Tell us what's on your mind.
861+
joule:
862+
title: Joule Feedback
863+
info: Complete a 1-minute survey and help us improve your Joule experience! The survey will open in a new tab.
864+
kyma:
865+
title: Kyma Dashboard Feedback
866+
info: Your opinion matters. Share your thoughts with us at any time!
855867
legal:
856868
copyright: Copyright
857869
legal-disclosure: Legal Disclosure
@@ -899,12 +911,10 @@ namespaces:
899911
title: Namespaces
900912
navigation:
901913
all-namespaces: All Namespaces
902-
feedback: Feedback
903914
snow: Let it snow
904915
snow-stop: Stop the snow
905916
menu:
906917
get-help: Get Help
907-
give-feedback: Give Feedback
908918
legal-information: Legal Information
909919
preferences:
910920
title: Preferences

src/components/Extensibility/ExtensibilityList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ const ExtensibilityList = ({ overrideResMetadata, ...props }) => {
196196
>
197197
<DataSourcesContextProvider dataSources={resMetaData?.dataSources || {}}>
198198
<ExtensibilityErrBoundary key={urlPath}>
199-
{isExtensibilityCustomComponentsEnabled && resMetaData.customHtml ? (
199+
{isExtensibilityCustomComponentsEnabled && resMetaData?.customHtml ? (
200200
<>
201201
<div
202202
id="custom-html"

src/components/KymaCompanion/components/AIBanner/AIBanner.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function AIBanner({ feedbackUrl }: { feedbackUrl: string }) {
6363
window.open(feedbackUrl, '_blank');
6464
}}
6565
>
66-
{t('kyma-companion.banner.buttons.feedback')}
66+
{t('feedback.give-feedback')}
6767
</Button>
6868
</>
6969
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.feedbackPopover > * {
2+
width: calc(410px - 2rem);
3+
}
4+
5+
.feedbackPopover {
6+
.info-text {
7+
color: var(--sapContent_LabelColor);
8+
}
9+
10+
ui5-button {
11+
width: 100%;
12+
}
13+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import {
2+
Button,
3+
FlexBox,
4+
ObjectStatus,
5+
Popover,
6+
ShellBarItem,
7+
Text,
8+
Title,
9+
} from '@ui5/webcomponents-react';
10+
import { useFeature } from 'hooks/useFeature';
11+
import { useState, useEffect } from 'react';
12+
import { createPortal } from 'react-dom';
13+
import { useTranslation } from 'react-i18next';
14+
import { configFeaturesNames } from 'state/types';
15+
import './FeedbackPopover.scss';
16+
17+
const FEEDBACK_VIEWED_STORAGE_KEY = 'feedback-new-indicators-viewed';
18+
19+
export default function FeedbackPopover() {
20+
const { isEnabled: isFeedbackEnabled, link: kymaFeedbackLink } = useFeature(
21+
configFeaturesNames.FEEDBACK,
22+
);
23+
const {
24+
isEnabled: isKymaCompanionEnabled,
25+
config: { feedbackLink: companionFeedbackLink } = {},
26+
} = useFeature(configFeaturesNames.KYMA_COMPANION);
27+
28+
const { t } = useTranslation();
29+
const [feedbackOpen, setFeedbackOpen] = useState(false);
30+
const [showNewIndicators, setShowNewIndicators] = useState(false);
31+
32+
useEffect(() => {
33+
const hasViewed = localStorage.getItem(FEEDBACK_VIEWED_STORAGE_KEY);
34+
if (hasViewed !== 'true') {
35+
setShowNewIndicators(true);
36+
}
37+
}, []);
38+
39+
const handleNewFeedbackViewed = () => {
40+
if (showNewIndicators) {
41+
localStorage.setItem(FEEDBACK_VIEWED_STORAGE_KEY, 'true');
42+
setShowNewIndicators(false);
43+
}
44+
};
45+
46+
if (!isFeedbackEnabled) {
47+
return null;
48+
}
49+
50+
return (
51+
<>
52+
<ShellBarItem
53+
id="feedbackOpener"
54+
onClick={() => setFeedbackOpen(true)}
55+
icon="feedback"
56+
text={t('feedback.feedback')}
57+
title={t('feedback.give-feedback')}
58+
count={
59+
isKymaCompanionEnabled && companionFeedbackLink && showNewIndicators
60+
? '1'
61+
: undefined
62+
}
63+
/>
64+
{createPortal(
65+
<Popover
66+
opener="feedbackOpener"
67+
open={feedbackOpen}
68+
onClose={() => setFeedbackOpen(false)}
69+
horizontalAlign="End"
70+
placement="Bottom"
71+
verticalAlign="Center"
72+
className="feedbackPopover"
73+
>
74+
<FlexBox
75+
alignItems="Start"
76+
direction="Column"
77+
justifyContent="Start"
78+
gap={4}
79+
className="sap-margin-bottom-medium"
80+
>
81+
<Title level="H5" size="H5">
82+
{t('feedback.intro.title')}
83+
</Title>
84+
<Text className="info-text">{t('feedback.intro.info')}</Text>
85+
</FlexBox>
86+
{isKymaCompanionEnabled && companionFeedbackLink && (
87+
<FlexBox
88+
alignItems="Start"
89+
direction="Column"
90+
justifyContent="Start"
91+
gap={16}
92+
className="sap-margin-bottom-medium"
93+
>
94+
<FlexBox
95+
direction="Row"
96+
alignItems="Center"
97+
justifyContent="Start"
98+
gap={12}
99+
>
100+
<Title level="H6" size="H6">
101+
{t('feedback.joule.title')}
102+
</Title>
103+
{showNewIndicators && (
104+
<ObjectStatus state="Information" inverted>
105+
{t('feedback.new')}
106+
</ObjectStatus>
107+
)}
108+
</FlexBox>
109+
<Text className="info-text">{t('feedback.joule.info')}</Text>
110+
<Button
111+
design="Emphasized"
112+
endIcon="inspect"
113+
onClick={() => {
114+
handleNewFeedbackViewed();
115+
window.open(companionFeedbackLink, '_blank');
116+
}}
117+
>
118+
{t('feedback.give-feedback')}
119+
</Button>
120+
</FlexBox>
121+
)}
122+
<FlexBox
123+
alignItems="Start"
124+
direction="Column"
125+
justifyContent="Start"
126+
gap={16}
127+
>
128+
<Title level="H6" size="H6">
129+
{t('feedback.kyma.title')}
130+
</Title>
131+
<Text className="info-text">{t('feedback.kyma.info')}</Text>
132+
<Button
133+
endIcon="inspect"
134+
design={
135+
!isKymaCompanionEnabled || !companionFeedbackLink
136+
? 'Emphasized'
137+
: 'Default'
138+
}
139+
onClick={() => window.open(kymaFeedbackLink, '_blank')}
140+
>
141+
{t('feedback.give-feedback')}
142+
</Button>
143+
</FlexBox>
144+
</Popover>,
145+
document.body,
146+
)}
147+
</>
148+
);
149+
}

src/header/Header.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
Avatar,
55
ListItemStandard,
66
ShellBar,
7-
ShellBarItem,
87
ToggleButton,
98
} from '@ui5/webcomponents-react';
109

@@ -26,6 +25,7 @@ import { CommandPaletteSearchBar } from 'command-pallette/CommandPalletteUI/Comm
2625
import { SnowFeature } from './SnowFeature';
2726

2827
import { configFeaturesNames } from 'state/types';
28+
import FeedbackPopover from './Feedback/FeedbackPopover';
2929
import './Header.scss';
3030

3131
export function Header() {
@@ -38,9 +38,6 @@ export function Header() {
3838
const { t } = useTranslation();
3939
const navigate = useNavigate();
4040
const { navigateSafely } = useFormNavigation();
41-
const { isEnabled: isFeedbackEnabled, link: feedbackLink } = useFeature(
42-
configFeaturesNames.FEEDBACK,
43-
);
4441

4542
const cluster = useRecoilValue(clusterState);
4643
const clusters = useRecoilValue(clustersState);
@@ -142,14 +139,7 @@ export function Header() {
142139
ref={shellbarRef}
143140
>
144141
<SnowFeature />
145-
{isFeedbackEnabled && (
146-
<ShellBarItem
147-
onClick={() => window.open(feedbackLink, '_blank')}
148-
icon="feedback"
149-
text={t('navigation.feedback')}
150-
title={t('navigation.feedback')}
151-
/>
152-
)}
142+
<FeedbackPopover />
153143
{isKymaCompanionEnabled &&
154144
isSAPUser &&
155145
window.location.pathname !== '/clusters' && (

src/header/HeaderMenu.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { isPreferencesOpenState } from 'state/preferences/isPreferencesModalOpen
66
import { useGetBusolaVersionDetails } from './SidebarMenu/useGetBusolaVersion';
77
import { useGetLegalLinks } from './SidebarMenu/useGetLegalLinks';
88
import { useGetHelpLinks } from './SidebarMenu/useGetHelpLinks';
9-
import { useFeature } from 'hooks/useFeature';
10-
import { configFeaturesNames } from 'state/types';
119
import { useTranslation } from 'react-i18next';
1210

1311
interface LegalLink {
@@ -31,7 +29,6 @@ export function HeaderMenu({ isMenuOpen, setIsMenuOpen }: HeaderMenuProps) {
3129
const { githubLink, busolaVersion } = useGetBusolaVersionDetails();
3230
const legalLinks = useGetLegalLinks();
3331
const getHelpLinks = useGetHelpLinks();
34-
const { link: feedbackLink } = useFeature(configFeaturesNames.FEEDBACK);
3532

3633
const nonBreakableSpaces = (number: number): string => {
3734
let spaces = '';
@@ -54,8 +51,6 @@ export function HeaderMenu({ isMenuOpen, setIsMenuOpen }: HeaderMenuProps) {
5451

5552
if (e.detail.text === t('navigation.preferences.title')) {
5653
setPreferencesOpen(true);
57-
} else if (e.detail.text === t('navigation.menu.give-feedback')) {
58-
openNewWindow(feedbackLink);
5954
} else if (legalLinkUsed) {
6055
openNewWindow(legalLinkUsed.link);
6156
} else if (
@@ -81,11 +76,6 @@ export function HeaderMenu({ isMenuOpen, setIsMenuOpen }: HeaderMenuProps) {
8176
text={t('navigation.preferences.title')}
8277
icon="wrench"
8378
/>
84-
<MenuItem
85-
key="give-feedback"
86-
text={t('navigation.menu.give-feedback')}
87-
icon="feedback"
88-
/>
8979
<MenuItem
9080
key="get-help"
9181
text={t('navigation.menu.get-help')}

src/shared/components/ServiceAccountTokenStatus.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export const ServiceAccountTokenStatus = ({
4343
<Token
4444
style={{ textTransform: 'capitalize' }}
4545
text={accountTokenValues.status}
46-
readonly
4746
></Token>
4847
</button>
4948
);

tests/integration/cypress.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module.exports = defineConfig({
4242
'tests/cluster/test-edit-cluster.spec.js',
4343
'tests/cluster/test-cluster-overview.spec.js',
4444
'tests/cluster/test-cluster-role-bindings.spec.js',
45+
'tests/cluster/test-feedback.spec.js',
4546
'tests/cluster/test-storage-classes.spec.js',
4647
'tests/cluster/test-persistent-volumes.spec.js',
4748
'tests/cluster/test-dashboard-version.spec.js',

0 commit comments

Comments
 (0)