doc: add sentry stuff section to md docs

This commit is contained in:
2025-07-09 15:47:02 +02:00
parent 99cd4098da
commit 8d39486d67

View File

@ -98,8 +98,10 @@ This guide details the comprehensive changes implemented to prepare the SWA Prac
- Modified the `evaluateWithGemini` function to accept the `ai` instance as a parameter, ensuring it uses the dynamically created `GoogleGenAI` instance.
- Added a check within `handleSubmit` in `ExerciseSheet` to verify the `ai` instance and its `models.generateContent` method before making API calls, providing a user-friendly error message if the API key is missing or invalid.
- **Template Literal Escaping Fixes**:
- Crucially, fixed syntax errors within the `systemInstruction` and `contents` template literals in `evaluateWithGemini`. The original code had issues with unescaped backticks (``` ` ```) and newlines (`\n`) when they were part of the string content itself, leading to build failures.
- **Specific Fix**: All literal backticks within the template strings (e.g., for code blocks like ``` ```c ```) were escaped as``` \`\`\` ```. All literal newlines (`\n`) within the template strings were escaped as `\\n`. This ensures they are interpreted as literal characters within the string rather than JavaScript syntax.
- Crucially, fixed syntax errors within the `systemInstruction` and `contents` template literals in `evaluateWithGemini`. The original code had issues with unescaped backticks (``` ` ```) and newlines (`
`) when they were part of the string content itself, leading to build failures.
- **Specific Fix**: All literal backticks within the template strings (e.g., for code blocks like ``` ```c ```) were escaped as``` \`\`\` ```. All literal newlines (`
`) within the template strings were escaped as `\\n`. This ensures they are interpreted as literal characters within the string rather than JavaScript syntax.
**Step-by-step Actions**:
@ -525,3 +527,215 @@ CMD [ "serve", "-s", "dist", "-l", "8000" ]
This enhancement makes the application more suitable for software architecture exercises that require diagram creation and visualization.
---
### 6. Optional: Implementing Sentry User Feedback
**Objective**: To provide a simple and effective way for users to provide feedback, report issues, or suggest new ideas directly from within the application.
**Previous State**: The application had no built-in feedback mechanism.
**Changes Made**:
- **Sentry SDK Update**: Replaced `@sentry/browser` with `@sentry/react` to leverage React-specific features.
- **Sentry Initialization**: Configured the Sentry SDK in `index.tsx` to include the `feedbackIntegration`, enabling the user feedback features.
- **Custom Feedback Dialog**:
- Instead of using the default Sentry feedback widget, a custom feedback dialog was implemented in `App.tsx` for a more integrated user experience.
- A "Feedback" button with a `ChatBubbleLeftRightIcon` was added to the top-right corner of the application.
- Clicking the button opens a `Dialog` (modal) from `@headlessui/react`.
- The modal contains a textarea for the user to enter their feedback, with German placeholder text.
- When the user submits the feedback, `Sentry.captureFeedback` is called to send the feedback to Sentry.
**Step-by-step Actions**:
1. Remove `@sentry/browser` and add `@sentry/react` to `package.json`.
2. Update `index.tsx` to import from `@sentry/react` and add the `feedbackIntegration` to the `Sentry.init` call.
3. In `App.tsx`, import `ChatBubbleLeftRightIcon` from `@heroicons/react/24/outline` and `* as Sentry from "@sentry/react"`.
4. Add state variables for the feedback dialog visibility and the feedback message.
5. Add a "Feedback" button next to the "Settings" button.
6. Implement the custom feedback `Dialog` component with a textarea and submit/cancel buttons.
7. Create a handler function that calls `Sentry.captureFeedback` with the user's message.
8. Write the modified content back to the respective files.
**Code Changes (package.json)**:
```diff
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"dependencies": {
"@google/genai": "latest",
"@headlessui/react": "^2.2.4",
- "@heroicons/react": "^2.2.0",
- "@sentry/browser": "^9.36.0",
+ "@heroicons/react": "^2.2.0",
+ "@sentry/react": "9.36.0",
"diff": "5.2.0",
"mermaid": "^11.8.1",
"prismjs": "1.29.0",
```
**Code Changes (index.tsx)**:
```diff
--- a/index.tsx
+++ b/index.tsx
@@ -1,8 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
-import * as Sentry from "@sentry/browser";
+import * as Sentry from "@sentry/react";
-Sentry.init({ dsn: "https://2851a11b9f1b4715b389979628da322f@glitchtip.yandrik.dev/3" });
+Sentry.init({
+ dsn: "https://2851a11b9f1b4715b389979628da322f@glitchtip.yandrik.dev/3",
+ integrations: [
+ Sentry.feedbackIntegration({
+ colorScheme: "system",
+ }),
+ ],
+});
const rootElement = document.getElementById('root');
if (!rootElement) {
```
**Code Changes (App.tsx)**:
```diff
--- a/App.tsx
+++ b/App.tsx
@@ -1,7 +1,8 @@
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { Dialog } from "@headlessui/react";
-import { Cog6ToothIcon } from "@heroicons/react/24/outline";
+import { Cog6ToothIcon, ChatBubbleLeftRightIcon } from "@heroicons/react/24/outline";
import { Exercise, CheckResult, ExercisePartType } from './types';
+import * as Sentry from "@sentry/react";
import { swaExercises1 } from './data/swa_exercises.1';
import { swaExercises2 } from './data/swa_exercises.2';
import { swaExercises3 } from './data/swa_exercises.3';
@@ -564,6 +565,18 @@
const [exercises, setExercises] = useState<Exercise[]>([]);
const [currentIndex, setCurrentIndex] = useState(0);
+ const [showFeedback, setShowFeedback] = useState(false);
+ const [feedbackMessage, setFeedbackMessage] = useState('');
+
+ const handleFeedbackSubmit = () => {
+ Sentry.captureFeedback({
+ message: feedbackMessage,
+ });
+ setFeedbackMessage('');
+ setShowFeedback(false);
+ };
+
+
// --- API KEY STATE ---
const [apiKey, setApiKey] = useState<string>('');
const [showSettings, setShowSettings] = useState(false);
@@ -604,12 +617,60 @@
return (
<div className="min-h-screen flex flex-col p-4 md:p-8">
+ {/* Feedback Button */}
+ <button
+ className="fixed top-4 right-16 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
+ aria-label="Feedback"
+ onClick={() => setShowFeedback(true)}
+ >
+ <ChatBubbleLeftRightIcon className="w-7 h-7 text-slate-600" />
+ </button>
{/* Settings Button */}
<button
className="fixed top-4 right-4 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
aria-label="API Key Settings"
onClick={() => setShowSettings(true)}
>
<Cog6ToothIcon className="w-7 h-7 text-slate-600" />
</button>
{/* Settings Modal */}
<Dialog open={showSettings} onClose={() => setShowSettings(false)} className="relative z-50">
+ <div className="fixed inset-0 bg-black/30" aria-hidden="true" />
+ <div className="fixed inset-0 flex items-center justify-center p-4">
+ <Dialog.Panel className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
+ <Dialog.Title className="font-bold text-lg mb-4 flex items-center gap-2">
+ <Cog6ToothIcon className="w-6 h-6 text-blue-600" />
+ Gemini API Key
+ </Dialog.Title>
+ <div className="mb-4">
+ <input
+ type="text"
+ className="w-full border rounded px-3 py-2 text-slate-800"
+ value={inputKey}
+ onChange={e => setInputKey(e.target.value)}
+ placeholder="Enter Gemini API Key"
+ />
+ </div>
+ <div className="flex justify-end gap-2">
+ <button
+ className="px-4 py-2 rounded bg-slate-200 text-slate-700 hover:bg-slate-300"
+ onClick={() => setShowSettings(false)}
+ >
+ Cancel
+ </button>
+ <button
+ className="px-4 py-2 rounded bg-blue-600 text-white font-bold hover:bg-blue-700"
+ onClick={handleSaveKey}
+ disabled={!inputKey.trim()}
+ >
+ Save
+ </button>
+ </div>
+ <div className="mt-4 text-xs text-slate-500">
+ Your API key is stored in your browser only.
+ </div>
+ <div className="mt-2 text-xs text-slate-500">
+ Get an API key at <a href="https://aistudio.google.com/" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">https://aistudio.google.com/</a> - this app uses gemini-2.5-flash, which is free for 500 requests a day (as of 9.7.2025)
+ </div>
+ </Dialog.Panel>
+ </div>
+ </Dialog>
+ {/* Feedback Modal */}
+ <Dialog open={showFeedback} onClose={() => setShowFeedback(false)} className="relative z-50">
+ <div className="fixed inset-0 bg-black/30" aria-hidden="true" />
+ <div className="fixed inset-0 flex items-center justify-center p-4">
+ <Dialog.Panel className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
+ <Dialog.Title className="font-bold text-lg mb-4 flex items-center gap-2">
+ <ChatBubbleLeftRightIcon className="w-6 h-6 text-blue-600" />
+ Feedback, Aufgabenideen, oder was auch immer
+ </Dialog.Title>
+ <div className="mb-4">
+ <textarea
+ className="w-full border rounded px-3 py-2 text-slate-800"
+ value={feedbackMessage}
+ onChange={e => setFeedbackMessage(e.target.value)}
+ placeholder="Was kann ich besser machen?"
+ rows={5}
+ />
+ </div>
+ <div className="flex justify-end gap-2">
+ <button
+ className="px-4 py-2 rounded bg-slate-200 text-slate-700 hover:bg-slate-300"
+ onClick={() => setShowFeedback(false)}
+ >
+ Abbrechen
+ </button>
+ <button
+ className="px-4 py-2 rounded bg-blue-600 text-white font-bold hover:bg-blue-700"
+ onClick={handleFeedbackSubmit}
+ disabled={!feedbackMessage.trim()}
+ >
+ Senden
+ </button>
+ </div>
+ </Dialog.Panel>
+ </div>
+ </Dialog>
<header className="mb-6 flex-shrink-0">
<h1 className="text-3xl font-serif font-bold text-slate-800">SWA Trainer</h1>
<p className="text-slate-500">Interaktive Übungen zur Vorbereitung auf die SWA-Klausur</p>
```