diff --git a/packages/api/server/http.mts b/packages/api/server/http.mts index 9c81f27a..17547912 100644 --- a/packages/api/server/http.mts +++ b/packages/api/server/http.mts @@ -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; diff --git a/packages/web/src/components/feedback-dialog.tsx b/packages/web/src/components/feedback-dialog.tsx new file mode 100644 index 00000000..c2d274d4 --- /dev/null +++ b/packages/web/src/components/feedback-dialog.tsx @@ -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 ( + + + + Share Feedback + + + + We're always looking to improve Srcbook and your feedback is invaluable. + + You can open a public{' '} + + GitHub issue + {' '} + or use the form below. + + setFeedback(e.target.value)} + /> + setEmail(e.target.value)} + /> + { + sendFeedback({ feedback, email }); + setFeedback(''); + setEmail(''); + toast.info('Thanks for the feedback!'); + onOpenChange(false); + }} + className="self-end" + > + Send + + + + + + + ); +} diff --git a/packages/web/src/components/session-menu.tsx b/packages/web/src/components/session-menu.tsx index bc9fbbf0..7bcf6864 100644 --- a/packages/web/src/components/session-menu.tsx +++ b/packages/web/src/components/session-menu.tsx @@ -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 { @@ -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); @@ -140,9 +143,16 @@ export default function SessionMenu({ onClick={() => setShowShortcuts(true)} className="flex items-center gap-2 hover:text-foreground cursor-pointer" > - + Shortcuts + setShowFeedback(true)} + className="flex items-center gap-2 hover:text-foreground cursor-pointer" + > + + Feedback + ); @@ -151,6 +161,7 @@ export default function SessionMenu({ return ( <> + { 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); + } +}
+ We're always looking to improve Srcbook and your feedback is invaluable. + + You can open a public{' '} + + GitHub issue + {' '} + or use the form below. +