Skip to content

Commit

Permalink
Feedback modal
Browse files Browse the repository at this point in the history
  • Loading branch information
laushinka committed May 18, 2022
1 parent 38b975c commit 2fd9cbe
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 7 deletions.
10 changes: 8 additions & 2 deletions components/dashboard/src/Analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import Cookies from "js-cookie";
import { v4 } from "uuid";
import { Experiment } from "./experiments";

export type Event = "invite_url_requested" | "organisation_authorised" | "dotfile_repo_changed";
export type Event = "invite_url_requested" | "organisation_authorised" | "dotfile_repo_changed" | "feedback_submitted";
type InternalEvent = Event | "path_changed" | "dashboard_clicked";

export type EventProperties = TrackOrgAuthorised | TrackInviteUrlRequested | TrackDotfileRepo;
export type EventProperties = TrackOrgAuthorised | TrackInviteUrlRequested | TrackDotfileRepo | TrackFeedback;
type InternalEventProperties = TrackUIExperiments & (EventProperties | TrackDashboardClick | TrackPathChanged);

export interface TrackOrgAuthorised {
Expand All @@ -30,6 +30,12 @@ export interface TrackDotfileRepo {
current: string;
}

export interface TrackFeedback {
score: number;
feedback: string;
href: string;
path: string;
}
interface TrackDashboardClick {
dnt?: boolean;
path: string;
Expand Down
21 changes: 17 additions & 4 deletions components/dashboard/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { getTeamSettingsMenu } from "./teams/TeamSettings";
import { getProjectSettingsMenu } from "./projects/ProjectSettings";
import { ProjectContext } from "./projects/project-context";
import { PaymentContext } from "./payment-context";
import FeedbackFormModal from "./feedback-form/FeedbackModal";

interface Entry {
title: string;
Expand All @@ -41,6 +42,7 @@ export default function Menu() {
const { showPaymentUI, setShowPaymentUI, setCurrency, setIsStudent, setIsChargebeeCustomer } =
useContext(PaymentContext);
const { project, setProject } = useContext(ProjectContext);
const [isFeedbackFormVisible, setFeedbackFormVisible] = useState<boolean>(false);

const match = useRouteMatch<{ segment1?: string; segment2?: string; segment3?: string }>(
"/(t/)?:segment1/:segment2?/:segment3?",
Expand Down Expand Up @@ -227,12 +229,15 @@ export default function Menu() {
title: "Docs",
link: "https://www.gitpod.io/docs/",
},
{
title: "Help",
link: "https://www.gitpod.io/support",
},
];

const handleFeedbackFormClick = () => {
setFeedbackFormVisible(true);
};

const onFeedbackFormClose = () => {
setFeedbackFormVisible(false);
};
const renderTeamMenu = () => {
return (
<div className="flex p-1 pl-3 ">
Expand Down Expand Up @@ -371,6 +376,9 @@ export default function Menu() {
/>
</li>
))}
<li className="cursor-pointer">
<PillMenuItem name="Feedback" onClick={handleFeedbackFormClick} />
</li>
</ul>
</nav>
<div
Expand All @@ -387,6 +395,10 @@ export default function Menu() {
{
title: "Settings",
link: "/settings",
},
{
title: "Help",
link: "https://www.gitpod.io/support",
separator: true,
},
{
Expand All @@ -403,6 +415,7 @@ export default function Menu() {
</ContextMenu>
</div>
</div>
{isFeedbackFormVisible && <FeedbackFormModal onClose={onFeedbackFormClose} />}
</div>
{!isMinimalUI && !prebuildId && (
<nav className="flex">
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/src/components/PillMenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Link } from "react-router-dom";

export default function PillMenuItem(p: {
name: string;
selected: boolean;
selected?: boolean;
link?: string;
onClick?: (event: React.MouseEvent) => void;
}) {
Expand Down
160 changes: 160 additions & 0 deletions components/dashboard/src/feedback-form/FeedbackComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/

import { useState } from "react";
import starryEyed from "../images/feedback/starry-eyed-emoji.svg";
import happy from "../images/feedback/happy-emoji.svg";
import meh from "../images/feedback/meh-emoji.svg";
import crying from "../images/feedback/crying-emoji.svg";
import { trackEvent } from "../Analytics";

function FeedbackComponent(props: { onClose: () => void; onSubmit: () => void; isModal: boolean }) {
const [text, setText] = useState<string>("");
const [selectedEmoji, setSelectedEmoji] = useState<number | undefined>();

const emojiScore: any = {
starry: 4,
happy: 3,
meh: 2,
crying: 1,
};

const height = props.isModal ? "300px" : "";

const onSubmit = () => {
if (selectedEmoji) {
const feedbackObj = {
score: selectedEmoji,
feedback: text,
href: window.location.href,
path: window.location.pathname,
};
trackEvent("feedback_submitted", feedbackObj);
}

props.onSubmit();
};

const handleClick = (target: any) => {
const title = target.title;
setSelectedEmoji(emojiScore[title]);
};

const emojiGroup = (width: number) => {
return (
<>
<button
className={
"hover:scale-150 transform bg-transparent bottom-5 right-5 cursor-pointer " +
(selectedEmoji === emojiScore["starry"] ? "" : "grayed")
}
onClick={(e) => handleClick(e.target)}
>
<img src={starryEyed} alt="starry eyed emoji" width={width || "24px"} title="starry" />
</button>
<button
className={
"hover:scale-150 transform bg-transparent bottom-5 right-5 cursor-pointer " +
(selectedEmoji === emojiScore["happy"] ? "" : "grayed")
}
onClick={(e) => handleClick(e.target)}
>
<img
className="bottom-5 right-5 cursor-pointer"
title="happy"
src={happy}
alt="happy emoji"
width={width}
/>
</button>
<button
className={
"hover:scale-150 transform bg-transparent bottom-5 right-5 cursor-pointer " +
(selectedEmoji === emojiScore["meh"] ? "" : "grayed")
}
onClick={(e) => handleClick(e.target)}
>
<img
className="bottom-5 right-5 cursor-pointer"
title="meh"
src={meh}
alt="meh emoji"
width={width}
/>
</button>
<button
className={
"hover:scale-150 transform bg-transparent bottom-5 right-5 cursor-pointer " +
(selectedEmoji === emojiScore["crying"] ? "" : "grayed")
}
onClick={(e) => handleClick(e.target)}
>
<img
className="bottom-5 right-5 cursor-pointer"
title="crying"
src={crying}
alt="crying emoji"
width={width}
/>
</button>
</>
);
};
return (
<>
<h3 className="mb-4">Send Feedback</h3>
{selectedEmoji ? (
<>
<div className="flex flex-col -mx-6 px-6 py-4 border-t border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">
<div className="relative">
<div className="absolute flex bottom-5 right-5 -space-x-3">{emojiGroup(24)}</div>
<textarea
style={{ height: "160px", borderRadius: "6px" }}
autoFocus
className="w-full resize-none text-gray-500 dark:text-gray-400 bg-white focus:ring-0 focus:border-gray-400 dark:focus:border-gray-400 rounded-md border border-gray-300 dark:border-gray-500"
name="name"
value={text}
placeholder="Have more feedback?"
onChange={(e) => setText(e.target.value)}
/>
</div>
<div>
<p className="text-gray-500">
{" "}
By submitting this form you acknowledge that you have read and understood our{" "}
<a className="gp-link" target="gitpod-privacy" href="https://www.gitpod.io/privacy/">
privacy policy
</a>
.
</p>
</div>
</div>
<div className="flex justify-end mt-6">
<button className="secondary" onClick={props.onClose}>
Cancel
</button>
<button className="ml-2" onClick={onSubmit}>
Send Feedback
</button>
</div>
</>
) : (
<div
className="flex flex-col justify-center -mx-6 px-6 py-4 border-t border-gray-200 dark:border-gray-800"
style={{ height: height }}
>
<p className="text-center text-lg mb-8 text-gray-500 dark:text-gray-400">
We'd love to know what you think!
</p>

<div className="flex items-center justify-center w-full space-x-3">{emojiGroup(50)}</div>
</div>
)}
</>
);
}

export default FeedbackComponent;
26 changes: 26 additions & 0 deletions components/dashboard/src/feedback-form/FeedbackModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/

import Modal from "../components/Modal";
import FeedbackComponent from "./FeedbackComponent";

function FeedbackFormModal(props: { onClose: () => void }) {
const onClose = () => {
props.onClose();
};

const onSubmit = () => {
props.onClose();
};

return (
<Modal visible={true} onClose={onClose}>
<FeedbackComponent onClose={onClose} onSubmit={onSubmit} isModal={true} />
</Modal>
);
}

export default FeedbackFormModal;
10 changes: 10 additions & 0 deletions components/dashboard/src/images/feedback/crying-emoji.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions components/dashboard/src/images/feedback/happy-emoji.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions components/dashboard/src/images/feedback/meh-emoji.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2fd9cbe

Please sign in to comment.