Skip to content

Commit 83de620

Browse files
move feedback button
1 parent 6cd6696 commit 83de620

File tree

6 files changed

+189
-116
lines changed

6 files changed

+189
-116
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { PopoverDomRef, Ui5CustomEvent, TextAreaDomRef, Button, ButtonDomRef, Popover, Form, FormGroup, FormItem, Label, Link, RatingIndicator, TextArea } from "@ui5/webcomponents-react";
2+
import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react";
3+
import { useAuthOnboarding } from "../../spaces/onboarding/auth/AuthContextOnboarding";
4+
import { useTranslation } from "react-i18next";
5+
import { ButtonClickEventDetail } from "@ui5/webcomponents/dist/Button.js";
6+
import PopoverPlacement from "@ui5/webcomponents/dist/types/PopoverPlacement.js";
7+
import ButtonDesign from "@ui5/webcomponents/dist/types/ButtonDesign.js";
8+
9+
type UI5RatingIndicatorElement = HTMLElement & { value: number };
10+
11+
12+
export function FeedbackButton() {
13+
const feedbackPopoverRef = useRef<PopoverDomRef>(null);
14+
const [feedbackMessage, setFeedbackMessage] = useState('');
15+
const [feedbackSent, setFeedbackSent] = useState(false);
16+
const [feedbackPopoverOpen, setFeedbackPopoverOpen] = useState(false);
17+
const [rating, setRating] = useState(0);
18+
const { user } = useAuthOnboarding();
19+
20+
const onFeedbackClick = (e: Ui5CustomEvent<ButtonDomRef, ButtonClickEventDetail>) => {
21+
feedbackPopoverRef.current!.opener = e.target;
22+
setFeedbackPopoverOpen(!feedbackPopoverOpen);
23+
};
24+
25+
const onFeedbackMessageChange = (event: Ui5CustomEvent<TextAreaDomRef, { value: string; previousValue: string }>) => {
26+
const newValue = event.target.value;
27+
setFeedbackMessage(newValue);
28+
};
29+
30+
async function onFeedbackSent() {
31+
const payload = {
32+
message: feedbackMessage,
33+
rating: rating.toString(),
34+
user: user?.email,
35+
environment: window.location.hostname,
36+
};
37+
try {
38+
await fetch('/api/feedback', {
39+
method: 'POST',
40+
headers: {
41+
'Content-Type': 'application/json',
42+
},
43+
body: JSON.stringify(payload),
44+
});
45+
} catch (err) {
46+
console.log(err);
47+
} finally {
48+
setFeedbackSent(true);
49+
}
50+
}
51+
52+
return (
53+
<>
54+
<Button
55+
icon="feedback"
56+
tooltip="Feedback"
57+
onClick={onFeedbackClick}
58+
design={ButtonDesign.Transparent}
59+
>
60+
61+
</Button>
62+
<FeedbackPopover
63+
open={feedbackPopoverOpen}
64+
setOpen={setFeedbackPopoverOpen}
65+
popoverRef={feedbackPopoverRef}
66+
setRating={setRating}
67+
rating={rating}
68+
feedbackMessage={feedbackMessage}
69+
feedbackSent={feedbackSent}
70+
onFeedbackSent={onFeedbackSent}
71+
onFeedbackMessageChange={onFeedbackMessageChange}
72+
/>
73+
</>
74+
)
75+
}
76+
77+
const FeedbackPopover = ({
78+
open,
79+
setOpen,
80+
popoverRef,
81+
setRating,
82+
rating,
83+
onFeedbackSent,
84+
feedbackMessage,
85+
onFeedbackMessageChange,
86+
feedbackSent,
87+
}: {
88+
open: boolean;
89+
setOpen: (arg0: boolean) => void;
90+
popoverRef: RefObject<PopoverDomRef | null>;
91+
setRating: Dispatch<SetStateAction<number>>;
92+
rating: number;
93+
onFeedbackSent: () => void;
94+
feedbackMessage: string;
95+
onFeedbackMessageChange: (
96+
event: Ui5CustomEvent<
97+
TextAreaDomRef,
98+
{
99+
value: string;
100+
previousValue: string;
101+
}
102+
>,
103+
) => void;
104+
feedbackSent: boolean;
105+
}) => {
106+
const { t } = useTranslation();
107+
108+
const onRatingChange = (event: Event & { target: UI5RatingIndicatorElement }) => {
109+
setRating(event.target.value);
110+
};
111+
112+
return (
113+
<>
114+
<Popover ref={popoverRef} placement={PopoverPlacement.Bottom} open={open} onClose={() => setOpen(false)}>
115+
<div
116+
style={{
117+
padding: '1rem',
118+
width: '250px',
119+
}}
120+
>
121+
{!feedbackSent ? (
122+
<Form headerText={t('ShellBar.feedbackHeader')}>
123+
<FormGroup>
124+
<FormItem labelContent={<Label style={{ color: 'black' }}>{t('ShellBar.feedbackRatingLabel')}</Label>}>
125+
<RatingIndicator value={rating} max={5} onChange={onRatingChange} />
126+
</FormItem>
127+
<FormItem
128+
className="formAlignLabelStart"
129+
labelContent={<Label style={{ color: 'black' }}>{t('ShellBar.feedbackMessageLabel')}</Label>}
130+
>
131+
<TextArea
132+
value={feedbackMessage}
133+
placeholder={t('ShellBar.feedbackPlaceholder')}
134+
rows={5}
135+
onInput={onFeedbackMessageChange}
136+
/>
137+
</FormItem>
138+
<FormItem>
139+
<Button design="Emphasized" onClick={() => onFeedbackSent()}>
140+
{t('ShellBar.feedbackButton')}
141+
</Button>
142+
</FormItem>
143+
<FormItem>
144+
<Label style={{ color: 'gray' }}>
145+
{t('ShellBar.feedbackNotificationText')}
146+
<Link
147+
href="https://github.com/openmcp-project/ui-frontend/issues/new/choose"
148+
target="_blank"
149+
rel="noreferrer"
150+
>
151+
{t('ShellBar.feedbackNotificationAction')}
152+
</Link>
153+
</Label>
154+
</FormItem>
155+
</FormGroup>
156+
</Form>
157+
) : (
158+
<Label>{t('ShellBar.feedbackThanks')}</Label>
159+
)}
160+
</div>
161+
</Popover>
162+
</>
163+
);
164+
};

src/components/Core/IntelligentBreadcrumbs.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Breadcrumbs, BreadcrumbsDomRef, Ui5CustomEvent } from '@ui5/webcomponents-react';
1+
import { Breadcrumbs, BreadcrumbsDomRef, FlexBox, FlexBoxAlignItems, Ui5CustomEvent } from '@ui5/webcomponents-react';
22
import { BreadcrumbsItem } from '@ui5/webcomponents-react/wrappers';
33
import { NavigateOptions, useParams } from 'react-router-dom';
44
import useLuigiNavigate from '../Shared/useLuigiNavigate.tsx';
55
import LandscapeLabel from './LandscapeLabel.tsx';
66
import { useTranslation } from 'react-i18next';
7+
import { FeedbackButton } from './FeedbackButton.tsx';
78

89
const PREFIX = '/mcp';
910

@@ -58,3 +59,15 @@ export default function IntelligentBreadcrumbs() {
5859
</>
5960
);
6061
}
62+
63+
64+
export function BreadCrumbFeedbackHeader() {
65+
return (
66+
<FlexBox
67+
alignItems={FlexBoxAlignItems.Center}
68+
>
69+
<IntelligentBreadcrumbs />
70+
<FeedbackButton />
71+
</FlexBox>
72+
)
73+
}

src/components/Core/ShellBar.tsx

Lines changed: 2 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,20 @@ import {
22
Avatar,
33
Button,
44
ButtonDomRef,
5-
Form,
6-
FormGroup,
7-
FormItem,
85
Icon,
9-
Label,
10-
Link,
116
List,
127
ListItemStandard,
138
Popover,
149
PopoverDomRef,
15-
RatingIndicator,
1610
ShellBar,
1711
ShellBarDomRef,
1812
ShellBarItem,
1913
ShellBarItemDomRef,
20-
TextArea,
2114
TextAreaDomRef,
2215
Ui5CustomEvent,
2316
} from '@ui5/webcomponents-react';
2417
import { useAuthOnboarding } from '../../spaces/onboarding/auth/AuthContextOnboarding.tsx';
25-
import { Dispatch, RefObject, SetStateAction, useEffect, useRef, useState } from 'react';
18+
import { RefObject, useEffect, useRef, useState } from 'react';
2619
import { ShellBarProfileClickEventDetail } from '@ui5/webcomponents-fiori/dist/ShellBar.js';
2720
import PopoverPlacement from '@ui5/webcomponents/dist/types/PopoverPlacement.js';
2821
import { useTranslation } from 'react-i18next';
@@ -31,7 +24,6 @@ import styles from './ShellBar.module.css';
3124
import { ThemingParameters } from '@ui5/webcomponents-react-base';
3225
import { ShellBarItemClickEventDetail } from '@ui5/webcomponents-fiori/dist/ShellBarItem.js';
3326

34-
type UI5RatingIndicatorElement = HTMLElement & { value: number };
3527

3628
export function ShellBarComponent() {
3729
const auth = useAuthOnboarding();
@@ -129,17 +121,7 @@ export function ShellBarComponent() {
129121

130122
<ProfilePopover open={profilePopoverOpen} setOpen={setProfilePopoverOpen} popoverRef={profilePopoverRef} />
131123
<BetaPopover open={betaPopoverOpen} setOpen={setBetaPopoverOpen} popoverRef={betaPopoverRef} />
132-
<FeedbackPopover
133-
open={feedbackPopoverOpen}
134-
setOpen={setFeedbackPopoverOpen}
135-
popoverRef={feedbackPopoverRef}
136-
setRating={setRating}
137-
rating={rating}
138-
feedbackMessage={feedbackMessage}
139-
feedbackSent={feedbackSent}
140-
onFeedbackSent={onFeedbackSent}
141-
onFeedbackMessageChange={onFeedbackMessageChange}
142-
/>
124+
143125
</>
144126
);
145127
}
@@ -205,91 +187,3 @@ const BetaPopover = ({
205187
);
206188
};
207189

208-
const FeedbackPopover = ({
209-
open,
210-
setOpen,
211-
popoverRef,
212-
setRating,
213-
rating,
214-
onFeedbackSent,
215-
feedbackMessage,
216-
onFeedbackMessageChange,
217-
feedbackSent,
218-
}: {
219-
open: boolean;
220-
setOpen: (arg0: boolean) => void;
221-
popoverRef: RefObject<PopoverDomRef | null>;
222-
setRating: Dispatch<SetStateAction<number>>;
223-
rating: number;
224-
onFeedbackSent: () => void;
225-
feedbackMessage: string;
226-
onFeedbackMessageChange: (
227-
event: Ui5CustomEvent<
228-
TextAreaDomRef,
229-
{
230-
value: string;
231-
previousValue: string;
232-
}
233-
>,
234-
) => void;
235-
feedbackSent: boolean;
236-
}) => {
237-
const { t } = useTranslation();
238-
239-
const onRatingChange = (event: Event & { target: UI5RatingIndicatorElement }) => {
240-
setRating(event.target.value);
241-
};
242-
243-
return (
244-
<>
245-
<Popover ref={popoverRef} placement={PopoverPlacement.Bottom} open={open} onClose={() => setOpen(false)}>
246-
<div
247-
style={{
248-
padding: '1rem',
249-
width: '250px',
250-
}}
251-
>
252-
{!feedbackSent ? (
253-
<Form headerText={t('ShellBar.feedbackHeader')}>
254-
<FormGroup>
255-
<FormItem labelContent={<Label style={{ color: 'black' }}>{t('ShellBar.feedbackRatingLabel')}</Label>}>
256-
<RatingIndicator value={rating} max={5} onChange={onRatingChange} />
257-
</FormItem>
258-
<FormItem
259-
className="formAlignLabelStart"
260-
labelContent={<Label style={{ color: 'black' }}>{t('ShellBar.feedbackMessageLabel')}</Label>}
261-
>
262-
<TextArea
263-
value={feedbackMessage}
264-
placeholder={t('ShellBar.feedbackPlaceholder')}
265-
rows={5}
266-
onInput={onFeedbackMessageChange}
267-
/>
268-
</FormItem>
269-
<FormItem>
270-
<Button design="Emphasized" onClick={() => onFeedbackSent()}>
271-
{t('ShellBar.feedbackButton')}
272-
</Button>
273-
</FormItem>
274-
<FormItem>
275-
<Label style={{ color: 'gray' }}>
276-
{t('ShellBar.feedbackNotificationText')}
277-
<Link
278-
href="https://github.com/openmcp-project/ui-frontend/issues/new/choose"
279-
target="_blank"
280-
rel="noreferrer"
281-
>
282-
{t('ShellBar.feedbackNotificationAction')}
283-
</Link>
284-
</Label>
285-
</FormItem>
286-
</FormGroup>
287-
</Form>
288-
) : (
289-
<Label>{t('ShellBar.feedbackThanks')}</Label>
290-
)}
291-
</div>
292-
</Popover>
293-
</>
294-
);
295-
};

src/spaces/mcp/pages/McpPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import '@ui5/webcomponents-fiori/dist/illustrations/SimpleError';
77
// thorws error sometimes if not imported
88
import '@ui5/webcomponents-fiori/dist/illustrations/BeforeSearch';
99
import IllustratedError from '../../../components/Shared/IllustratedError.tsx';
10-
import IntelligentBreadcrumbs from '../../../components/Core/IntelligentBreadcrumbs.tsx';
10+
import { BreadCrumbFeedbackHeader } from '../../../components/Core/IntelligentBreadcrumbs.tsx';
1111

1212
import FluxList from '../../../components/ControlPlane/FluxList.tsx';
1313
import { ControlPlane as ControlPlaneResource } from '../../../lib/api/types/crate/controlPlanes.ts';
@@ -65,7 +65,7 @@ export default function McpPage() {
6565
titleArea={
6666
<ObjectPageTitle
6767
header={controlPlaneName}
68-
breadcrumbs={<IntelligentBreadcrumbs />}
68+
breadcrumbs={<BreadCrumbFeedbackHeader />}
6969
//TODO: actionBar should use Toolbar and ToolbarButton for consistent design
7070
actionsBar={
7171
<div

src/spaces/onboarding/pages/ProjectPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ObjectPage, ObjectPageTitle, Title } from '@ui5/webcomponents-react';
22
import ProjectChooser from '../../../components/Projects/ProjectChooser.tsx';
33
import { useParams } from 'react-router-dom';
44
import ControlPlaneListAllWorkspaces from '../../../components/ControlPlanes/List/ControlPlaneListAllWorkspaces.tsx';
5-
import IntelligentBreadcrumbs from '../../../components/Core/IntelligentBreadcrumbs.tsx';
5+
import { BreadCrumbFeedbackHeader } from '../../../components/Core/IntelligentBreadcrumbs.tsx';
66
import { ControlPlaneListToolbar } from '../../../components/ControlPlanes/List/ControlPlaneListToolbar.tsx';
77
import { Trans, useTranslation } from 'react-i18next';
88
import { useApiResource } from '../../../lib/api/useApiResource.ts';
@@ -52,11 +52,11 @@ export default function ProjectPage() {
5252
<ProjectChooser currentProjectName={projectName ?? ''} />
5353
</div>
5454
}
55-
breadcrumbs={<IntelligentBreadcrumbs />}
55+
breadcrumbs={<BreadCrumbFeedbackHeader />}
5656
actionsBar={<ControlPlaneListToolbar projectName={projectName ?? ''} />}
5757
/>
5858
}
59-
//TODO: project chooser should be part of the breadcrumb section if possible?
59+
//TODO: project chooser should be part of the breadcrumb section if possible?
6060
>
6161
<ControlPlaneListAllWorkspaces projectName={projectName} workspaces={workspaces} />
6262
</ObjectPage>

0 commit comments

Comments
 (0)