Skip to content

Commit

Permalink
Add feedback form (#158)
Browse files Browse the repository at this point in the history
* Add feedback form

* Add comment, remove debug log

* Add optional email + send from API to avoid CORS
  • Loading branch information
nichochar authored Jul 27, 2024
1 parent 24e537c commit 92e4412
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
17 changes: 17 additions & 0 deletions packages/api/server/http.mts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,23 @@ router.delete('/secrets/:name', cors(), async (req, res) => {
return res.json({ result: updated });
});

router.options('/feedback', cors());
router.post('/feedback', cors(), async (req, res) => {
const { feedback, email } = req.body;
// Every time you modify the appscript here, you'll need to update the URL below
// @TODO: once we have an env variable setup, we can use that here.
const url =
'https://script.google.com/macros/s/AKfycbxPrg8z47SkJnHyoZBYqNtkcH8hBe12f-f2UJJ3PcIHmKdbMMuJuPoOemEB1ib8a_IKCg/exec';

const result = await fetch(url, {
method: 'POST',
body: JSON.stringify({ feedback, email }),
headers: { 'Content-Type': 'text/plain;charset=utf-8' },
});

return res.json({ success: result.ok });
});

type NpmSearchResult = {
package: {
name: string;
Expand Down
74 changes: 74 additions & 0 deletions packages/web/src/components/feedback-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useState } from 'react';
import { sendFeedback } from '@/lib/server';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Textarea } from '@/components/ui/textarea';

export default function FeedbackDialog({
open,
onOpenChange,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
}) {
const [feedback, setFeedback] = useState('');
const [email, setEmail] = useState('');

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-xl">
<DialogHeader>
<DialogTitle>Share Feedback</DialogTitle>
<DialogDescription asChild>
<div className="pt-4 flex flex-col gap-2">
<p>
We're always looking to improve Srcbook and your feedback is invaluable.
<br />
You can open a public{' '}
<a
href="https://github.com/srcbookdev/srcbook/issues/new"
className="underline font-medium"
>
GitHub issue
</a>{' '}
or use the form below.
</p>
<Textarea
placeholder="Share anonymous feedback"
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
/>
<Input
type="text"
value={email}
placeholder="Email (optional)"
onChange={(e) => setEmail(e.target.value)}
/>
<Button
disabled={!feedback}
onClick={() => {
sendFeedback({ feedback, email });
setFeedback('');
setEmail('');
toast.info('Thanks for the feedback!');
onOpenChange(false);
}}
className="self-end"
>
Send
</Button>
</div>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
}
15 changes: 13 additions & 2 deletions packages/web/src/components/session-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { useState } from 'react';
import {
Upload,
Trash2,
MessageCircleQuestion,
MessageCircleMore,
Circle,
List,
Settings,
LoaderCircle,
Keyboard,
} from 'lucide-react';
import type { SessionType } from '../types';
import type { CodeCellType, MarkdownCellType, TitleCellType } from '@srcbook/shared';
import KeyboardShortcutsDialog from '@/components/keyboard-shortcuts-dialog';
import FeedbackDialog from '@/components/feedback-dialog';
import DeleteSrcbookModal from '@/components/delete-srcbook-dialog';
import { ExportSrcbookModal } from '@/components/import-export-srcbook-modal';
import {
Expand Down Expand Up @@ -65,6 +67,7 @@ export default function SessionMenu({
openDepsInstallModal,
}: Props) {
const [showShortcuts, setShowShortcuts] = useState(false);
const [showFeedback, setShowFeedback] = useState(false);
const [showDelete, setShowDelete] = useState(false);
const [showSave, setShowSave] = useState(false);

Expand Down Expand Up @@ -140,9 +143,16 @@ export default function SessionMenu({
onClick={() => setShowShortcuts(true)}
className="flex items-center gap-2 hover:text-foreground cursor-pointer"
>
<MessageCircleQuestion size={16} />
<Keyboard size={16} />
Shortcuts
</button>
<button
onClick={() => setShowFeedback(true)}
className="flex items-center gap-2 hover:text-foreground cursor-pointer"
>
<MessageCircleMore size={16} />
Feedback
</button>
</div>
</div>
);
Expand All @@ -151,6 +161,7 @@ export default function SessionMenu({
return (
<>
<KeyboardShortcutsDialog open={showShortcuts} onOpenChange={setShowShortcuts} />
<FeedbackDialog open={showFeedback} onOpenChange={setShowFeedback} />
<DeleteSrcbookModal open={showDelete} onOpenChange={setShowDelete} session={session} />
<ExportSrcbookModal open={showSave} onOpenChange={setShowSave} session={session} />
<SettingsSheet
Expand Down
17 changes: 17 additions & 0 deletions packages/web/src/lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,20 @@ export async function loadSrcbookExamples(): Promise<SrcbookExamplesResponse> {

return response.json();
}

type FeedbackRequestType = {
feedback: string;
email: string;
};

export async function sendFeedback({ feedback, email }: FeedbackRequestType) {
const response = await fetch(API_BASE_URL + '/feedback', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ feedback, email }),
});

if (!response.ok) {
console.error(response);
}
}

0 comments on commit 92e4412

Please sign in to comment.