diff --git a/components/dashboard/src/Analytics.tsx b/components/dashboard/src/Analytics.tsx index b6f127850942b1..9099f07d5b0076 100644 --- a/components/dashboard/src/Analytics.tsx +++ b/components/dashboard/src/Analytics.tsx @@ -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 { @@ -30,6 +30,12 @@ export interface TrackDotfileRepo { current: string; } +export interface TrackFeedback { + selectedEmoji: number; + text: string; + href: string; + path: string; +} interface TrackDashboardClick { dnt?: boolean; path: string; diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx index 1db31a3a659df5..49821b7930661c 100644 --- a/components/dashboard/src/App.tsx +++ b/components/dashboard/src/App.tsx @@ -48,6 +48,7 @@ import { parseProps } from "./start/StartWorkspace"; import SelectIDEModal from "./settings/SelectIDEModal"; import { StartPage, StartPhase } from "./start/StartPage"; import { isGitpodIo } from "./utils"; +import FeedbackFormModal from "./feedback-form/FeedbackModal"; const Setup = React.lazy(() => import(/* webpackPrefetch: true */ "./Setup")); const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ "./workspaces/Workspaces")); @@ -372,6 +373,7 @@ function App() { + diff --git a/components/dashboard/src/Menu.tsx b/components/dashboard/src/Menu.tsx index 225c1b5f6d3414..5c330b0b4f9928 100644 --- a/components/dashboard/src/Menu.tsx +++ b/components/dashboard/src/Menu.tsx @@ -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; @@ -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(false); const match = useRouteMatch<{ segment1?: string; segment2?: string; segment3?: string }>( "/(t/)?:segment1/:segment2?/:segment3?", @@ -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 ( @@ -371,6 +376,9 @@ export default function Menu() { /> ))} + + + + {isFeedbackFormVisible && } {!isMinimalUI && !prebuildId && ( diff --git a/components/dashboard/src/components/PillMenuItem.tsx b/components/dashboard/src/components/PillMenuItem.tsx index 63d6efe130e077..5616b9cd63316f 100644 --- a/components/dashboard/src/components/PillMenuItem.tsx +++ b/components/dashboard/src/components/PillMenuItem.tsx @@ -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; }) { diff --git a/components/dashboard/src/feedback-form/FeedbackComponent.tsx b/components/dashboard/src/feedback-form/FeedbackComponent.tsx new file mode 100644 index 00000000000000..feb72537b3ed3a --- /dev/null +++ b/components/dashboard/src/feedback-form/FeedbackComponent.tsx @@ -0,0 +1,166 @@ +/** + * 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 }) { + const [text, setText] = useState(""); + const [selectedEmoji, setSelectedEmoji] = useState(); + + const emojiScore: any = { + starry: 4, + happy: 3, + meh: 2, + crying: 1, + }; + + const onSubmit = () => { + if (selectedEmoji) { + const feedbackObj = { + selectedEmoji, + 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 ( + <> + handleClick(e.target)} + > + + + handleClick(e.target)} + > + + + handleClick(e.target)} + > + + + handleClick(e.target)} + > + + + > + ); + }; + return ( + <> + Send Feedback + {selectedEmoji ? ( + + + {emojiGroup(24)} + setText(e.target.value)} + /> + + + + Alternatively, consider opening{" "} + + {" "} + a bug report{" "} + + or + + {" "} + a feature request{" "} + {" "} + in our issue tracker. + + + + + Cancel + + + Send Feedback + + + + ) : ( + + We'd love to know what you think! + + {emojiGroup(50)} + + )} + > + ); +} + +export default FeedbackComponent; diff --git a/components/dashboard/src/feedback-form/FeedbackModal.tsx b/components/dashboard/src/feedback-form/FeedbackModal.tsx new file mode 100644 index 00000000000000..102d3cec7a7a72 --- /dev/null +++ b/components/dashboard/src/feedback-form/FeedbackModal.tsx @@ -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 ( + + + + ); +} + +export default FeedbackFormModal; diff --git a/components/dashboard/src/images/feedback/crying-emoji.svg b/components/dashboard/src/images/feedback/crying-emoji.svg new file mode 100644 index 00000000000000..4a182a244378d2 --- /dev/null +++ b/components/dashboard/src/images/feedback/crying-emoji.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/components/dashboard/src/images/feedback/happy-emoji.svg b/components/dashboard/src/images/feedback/happy-emoji.svg new file mode 100644 index 00000000000000..c30bfaa05b147e --- /dev/null +++ b/components/dashboard/src/images/feedback/happy-emoji.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/components/dashboard/src/images/feedback/meh-emoji.svg b/components/dashboard/src/images/feedback/meh-emoji.svg new file mode 100644 index 00000000000000..90ccc0c6e0ffc4 --- /dev/null +++ b/components/dashboard/src/images/feedback/meh-emoji.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/components/dashboard/src/images/feedback/starry-eyed-emoji.svg b/components/dashboard/src/images/feedback/starry-eyed-emoji.svg new file mode 100644 index 00000000000000..897593900c03d0 --- /dev/null +++ b/components/dashboard/src/images/feedback/starry-eyed-emoji.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/components/dashboard/src/index.css b/components/dashboard/src/index.css index a6d1c9712e8fd4..a6f669a7539b43 100644 --- a/components/dashboard/src/index.css +++ b/components/dashboard/src/index.css @@ -43,6 +43,12 @@ .dark .dark\:filter-invert { @apply filter-invert; } + .grayed { + filter: grayscale(100%); + } + .grayed:hover { + filter: none; + } } @layer components {
+ Alternatively, consider opening{" "} + + {" "} + a bug report{" "} + + or + + {" "} + a feature request{" "} + {" "} + in our issue tracker. +