doc: add sentry stuff section to md docs
This commit is contained in:
		@ -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.
 | 
					  - 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.
 | 
					  - 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**:
 | 
					- **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.
 | 
					  - Crucially, fixed syntax errors within the `systemInstruction` and `contents` template literals in `evaluateWithGemini`. The original code had issues with unescaped backticks (``` ` ```) and newlines (`
 | 
				
			||||||
  - **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.
 | 
					`) 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**:
 | 
					**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.
 | 
					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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
		Reference in New Issue
	
	Block a user