Skip to content

Commit

Permalink
feat: add Sentry-based user feedback form (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmoesbergen authored May 11, 2024
1 parent a2f6a55 commit dee86d4
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 21 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

Expand Down
4 changes: 4 additions & 0 deletions src/assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"MORE": "More...",
"MORE_ABOUT": "About Easybloqs",
"UPLOAD": "Upload to robot",
"SEND": "Send",
"NAME": "Your name...",
"DRIVERINSTALLATIONINSTRUCTIONS_TITLE": "We are almost ready!",
"DRIVERINSTALLATIONINSTRUCTIONS": "You will get two popups to install the USB driver. Click 'Yes' and then 'Install'.",
"CONNECTIONINSTRUCTIONS_TITLE": "Robot not found",
Expand Down Expand Up @@ -95,6 +97,8 @@
"UNKNOWN": "Unknown",
"DEVICE_INFORMATION": "Device information",
"EMAIL": "Email",
"FEEDBACK": "Suggestion / Feedback",
"FEEDBACK_SENT": "Your feedback has been sent, thank you!",
"FLASH_FIRMWARE": "Flash firmware",
"FLASHING": "Flashing firmware...",
"FLASHED": "Firmware flashed",
Expand Down
4 changes: 4 additions & 0 deletions src/assets/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"MORE": "Meer...",
"MORE_ABOUT": "Over Easybloqs",
"UPLOAD": "Upload naar robot",
"SEND": "Versturen",
"NAME": "Je naam...",
"DRIVERINSTALLATIONINSTRUCTIONS_TITLE": "We zijn bijna klaar!",
"DRIVERINSTALLATIONINSTRUCTIONS": "Je krijgt nu nog twee popups om de USB driver te installeren. Klik op 'Yes' en dan op 'Install'.",
"CONNECTIONINSTRUCTIONS_TITLE": "Robot niet gevonden",
Expand Down Expand Up @@ -95,6 +97,8 @@
"UNKNOWN": "Niet gevonden",
"DEVICE_INFORMATION": "Apparaat informatie",
"EMAIL": "E-mail",
"FEEDBACK": "Suggestie / Feedback",
"FEEDBACK_SENT": "Uw feedback is verstuurd, bedankt!",
"FLASH_FIRMWARE": "Firmware flashen",
"FLASHING": "Flashing de firmware...",
"FLASHED": "Firmware succesvol geflashed",
Expand Down
11 changes: 11 additions & 0 deletions src/lib/components/core/header/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
tempSave,
} from "$state/workspace.svelte";
import {
faComment,
faDownload,
faEnvelope,
faFile,
Expand All @@ -58,6 +59,7 @@ import { get } from "svelte/store";
import MicroPythonIO from "../../../micropython";
import About from "../popups/popups/About.svelte";
import Examples from "../popups/popups/Examples.svelte";
import Feedback from "../popups/popups/Feedback.svelte";
import SaveProject from "../popups/popups/Prompt.svelte";
import UploadLog from "../popups/popups/UploadLog.svelte";
import Uploader from "../popups/popups/Uploader.svelte";
Expand Down Expand Up @@ -190,6 +192,14 @@ function email() {
window.open("mailto:[email protected]", "_blank").focus();
}
function feedback() {
popups.open({
component: Feedback,
data: {},
allowInteraction: true,
});
}
function about() {
popups.open({
component: About,
Expand Down Expand Up @@ -311,6 +321,7 @@ function runPython() {
{open}
/>
<ContextItem icon={faEnvelope} name={$_("EMAIL")} onclick={email} {open} />
<ContextItem icon={faComment} name={$_("FEEDBACK")} onclick={feedback} {open} />
{/snippet}
{#snippet moreContext(open: Writable<boolean>)}
{#snippet languageContext(open: Writable<boolean>)}
Expand Down
105 changes: 105 additions & 0 deletions src/lib/components/core/popups/popups/Feedback.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<script lang="ts">
import Warning from "$components/core/popups/popups/Warning.svelte";
import Button from "$components/ui/Button.svelte";
import TextArea from "$components/ui/TextArea.svelte";
import TextInput from "$components/ui/TextInput.svelte";
import { type PopupState, popups } from "$state/popup.svelte";
import * as Sentry from "@sentry/browser";
import { getContext } from "svelte";
import { _ } from "svelte-i18n";
import type { Writable } from "svelte/store";
let comments = "";
let senderName = "";
let senderEmail = "";
const popupState = getContext<Writable<PopupState>>("state");
function cancel() {
popups.close($popupState.id, false);
}
async function save() {
const eventId = Sentry.captureMessage("User Feedback");
const userFeedback = {
event_id: eventId,
name: senderName,
email: senderEmail,
comments: comments,
};
Sentry.captureUserFeedback(userFeedback);
await popups.open({
component: Warning,
data: {
title: "FEEDBACK",
message: "FEEDBACK_SENT",
showCancel: false,
},
allowInteraction: true,
});
popups.close($popupState.id, comments);
}
function onsubmit(event: SubmitEvent) {
event.preventDefault();
save();
}
</script>

<form class="content" {onsubmit}>
<h2>{$_("FEEDBACK")}</h2>
<div class="input">
<TextInput
placeholder={$_("NAME")}
bind:value={senderEmail}
mode="secondary"
focus={true}
required
rounded={true}>
</TextInput>
</div>

<div class="input">
<TextInput
placeholder={$_("EMAIL")}
bind:value={senderName}
mode="secondary"
type="email"
required
rounded={true}>
</TextInput>
</div>

<TextArea
bind:value={comments}
mode={"secondary"}
rounded={true}
required
rows={10}
/>
<div class="actions">
<Button onclick={cancel} mode={"secondary"} name={$_("CANCEL")}/>
<Button type="submit" mode={"primary"} name={$_("SEND")}/>
</div>
</form>

<style>
.input {
padding-bottom: 10px;
}
.content {
padding: 20px;
display: flex;
flex-direction: column;
min-width: 400px;
text-align: center;
}
.actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
</style>
47 changes: 27 additions & 20 deletions src/lib/components/core/popups/popups/Warning.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,46 @@ import type { Writable } from "svelte/store";
interface Props {
title: string;
message: string;
showCancel: boolean;
}
const { title, message }: Props = $props();
const { title, message, showCancel = true }: Props = $props();
const popupState = getContext<Writable<PopupState>>("state");
function cancel() {
popups.close($popupState.id, false);
}
function ok() {
popups.close($popupState.id, true);
}
</script>

<div class="content">
<h2>{$_(title)}</h2>
<div class="text">{$_(message)}</div>
<div class="actions">
<Button name={$_("CANCEL")} mode={"secondary"} onclick={cancel} />
<Button name={$_("OK")} mode={"primary"} onclick={ok} />
</div>
<h2>{$_(title)}</h2>
<div class="text">{$_(message)}</div>
<div class="actions">
{#if showCancel}
<Button name={$_("CANCEL")} mode={"secondary"} onclick={cancel}/>
{/if}
<div></div>
<Button name={$_("OK")} mode={"primary"} onclick={ok}/>
</div>
</div>

<style>
.content {
padding: 20px;
display: flex;
flex-direction: column;
min-width: 400px;
text-align: center;
}
.actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.content {
padding: 20px;
display: flex;
flex-direction: column;
min-width: 400px;
text-align: center;
}
.actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
</style>
71 changes: 71 additions & 0 deletions src/lib/components/ui/TextArea.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script lang="ts">
import { onMount } from "svelte";
interface Props {
value: string;
mode: "primary" | "secondary";
rounded: boolean;
focus?: boolean;
rows: number;
required?: boolean;
}
let {
value = $bindable(""),
mode,
rounded,
focus,
rows,
required = false,
}: Props = $props();
let input: HTMLTextAreaElement;
onMount(() => {
if (focus) input.focus();
});
</script>

<textarea
bind:this={input}
class="textarea"
bind:value
rows={rows}
{required}
class:primary={mode === "primary"}
class:secondary={mode === "secondary"}
class:rounded
></textarea>

<style>
.textarea {
font-family: inherit;
border: none;
padding: 5px 10px;
margin: 0;
width: 100%;
outline: 0;
font-size: 1em;
}
.primary {
background: var(--primary);
color: var(--on-primary);
}
.secondary {
background: var(--secondary);
color: var(--on-secondary);
}
.rounded {
border-radius: 10px;
}
.primary::placeholder {
color: var(--text-muted);
}
.secondary::placeholder {
color: var(--on-secondary-muted);
}
</style>
4 changes: 3 additions & 1 deletion src/lib/components/ui/TextInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Props {
rounded: boolean;
focus?: boolean;
required?: boolean;
type?: string;
}
let {
Expand All @@ -17,6 +18,7 @@ let {
rounded,
focus,
required,
type = "text",
}: Props = $props();
let input: HTMLInputElement;
Expand All @@ -28,7 +30,7 @@ onMount(() => {
<input
bind:this={input}
class="input"
type="text"
{type}
{placeholder}
bind:value
class:primary={mode === "primary"}
Expand Down

0 comments on commit dee86d4

Please sign in to comment.