From 03656260f3a2f6c949eb4daf6b497c5afa77b934 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 22:10:35 +0000 Subject: [PATCH 01/11] Initial plan From 58116dfa90ffde0d816a15862e4f665194763b5b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 22:15:40 +0000 Subject: [PATCH 02/11] Initial analysis: Plan user scenarios functionality implementation Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- docs/SERVICE_TABLE.md | 2 +- services/dak-faq-mcp/schemas/questionId.schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/SERVICE_TABLE.md b/docs/SERVICE_TABLE.md index 8289a3adfe..de5604ff24 100644 --- a/docs/SERVICE_TABLE.md +++ b/docs/SERVICE_TABLE.md @@ -35,5 +35,5 @@ This table is automatically generated from the codebase on every commit. - Localhost URLs are clickable links for local development testing - MCP manifest links included for MCP tooling -*Generated on: 2025-09-13T18:26:55.431Z* +*Generated on: 2025-09-16T22:15:18.790Z* *Generator: scripts/generate-service-table.js* diff --git a/services/dak-faq-mcp/schemas/questionId.schema.json b/services/dak-faq-mcp/schemas/questionId.schema.json index 6c67934a7c..d7ae01842b 100644 --- a/services/dak-faq-mcp/schemas/questionId.schema.json +++ b/services/dak-faq-mcp/schemas/questionId.schema.json @@ -21,7 +21,7 @@ "enum": "Question ID must be one of the available FAQ questions. Use the list_faq_questions endpoint to get current options." }, "_generated": { - "timestamp": "2025-09-13T18:26:55.418Z", + "timestamp": "2025-09-16T22:15:18.783Z", "count": 7, "source": "scripts/generate-service-table.js" } From 11d7c1ad5a8e8b9805ae3d96e30a656db9449471 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 22:22:22 +0000 Subject: [PATCH 03/11] Implement UserScenarios component with markdown editing Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- public/routes-config.json | 4 + src/components/UserScenarios.css | 459 +++++++++++++++++++++++++ src/components/UserScenarios.js | 472 ++++++++++++++++++++++++++ src/services/componentRouteService.js | 3 + src/services/helpContentService.js | 135 ++++++++ 5 files changed, 1073 insertions(+) create mode 100644 src/components/UserScenarios.css create mode 100644 src/components/UserScenarios.js diff --git a/public/routes-config.json b/public/routes-config.json index e40c0385ba..6fd62377df 100644 --- a/public/routes-config.json +++ b/public/routes-config.json @@ -18,6 +18,10 @@ "component": "ActorEditor", "path": "./components/ActorEditor" }, + "user-scenarios": { + "component": "UserScenarios", + "path": "./components/UserScenarios" + }, "business-process-selection": { "component": "BusinessProcessSelection", "path": "./components/BusinessProcessSelection" diff --git a/src/components/UserScenarios.css b/src/components/UserScenarios.css new file mode 100644 index 0000000000..59b95f2132 --- /dev/null +++ b/src/components/UserScenarios.css @@ -0,0 +1,459 @@ +/* UserScenarios.css */ + +.user-scenarios-container { + min-height: 100vh; + background: linear-gradient(135deg, #0078d4 0%, #005a9e 100%); + color: white; + padding: 20px; +} + +.user-scenarios-header { + background: rgb(4, 11, 118); + padding: 30px; + border-radius: 8px; + margin-bottom: 30px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.user-scenarios-header h1 { + color: white; + margin: 0 0 10px 0; + font-size: 2.5rem; + font-weight: 700; +} + +.user-scenarios-header p { + color: rgba(255, 255, 255, 0.9); + font-size: 1.1rem; + margin: 0 0 20px 0; + line-height: 1.5; +} + +.header-actions { + display: flex; + gap: 10px; + margin-top: 20px; +} + +.user-scenarios-content { + display: flex; + gap: 30px; + min-height: 600px; +} + +/* Scenarios Sidebar */ +.scenarios-sidebar { + flex: 0 0 350px; + background: rgba(255, 255, 255, 0.95); + color: #333; + border-radius: 8px; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + overflow-y: auto; + max-height: 700px; +} + +.scenarios-sidebar h3 { + margin: 0 0 20px 0; + font-size: 1.3rem; + color: #333; + border-bottom: 2px solid #e1e1e1; + padding-bottom: 10px; +} + +.scenarios-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.scenario-item { + padding: 15px; + border: 1px solid #e1e1e1; + border-radius: 6px; + cursor: pointer; + transition: all 0.2s ease; + background: white; +} + +.scenario-item:hover { + border-color: #0078d4; + box-shadow: 0 2px 4px rgba(0, 120, 212, 0.2); + transform: translateY(-1px); +} + +.scenario-item.selected { + border-color: #0078d4; + background: #f0f8ff; + box-shadow: 0 2px 8px rgba(0, 120, 212, 0.3); +} + +.scenario-item h4 { + margin: 0 0 5px 0; + font-size: 1.1rem; + color: #333; + font-weight: 600; + text-transform: capitalize; +} + +.scenario-id { + margin: 0; + font-size: 0.9rem; + color: #666; + font-family: 'Courier New', monospace; +} + +.empty-state { + text-align: center; + color: #666; + padding: 40px 20px; +} + +.empty-state p { + margin: 0 0 10px 0; + line-height: 1.6; +} + +/* Scenario Editor */ +.scenario-editor { + flex: 1; + background: rgba(255, 255, 255, 0.95); + color: #333; + border-radius: 8px; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + overflow: hidden; +} + +.editor-header { + display: flex; + justify-content: between; + align-items: center; + margin-bottom: 20px; + padding-bottom: 15px; + border-bottom: 2px solid #e1e1e1; +} + +.editor-header h2 { + margin: 0; + font-size: 1.8rem; + color: #333; + text-transform: capitalize; + flex: 1; +} + +.editor-actions { + display: flex; + gap: 10px; +} + +.editor-content { + height: 600px; + overflow: hidden; +} + +.editor-placeholder { + text-align: center; + color: #666; + padding: 100px 20px; + font-size: 1.1rem; +} + +/* Buttons */ +.btn-primary { + background: #0078d4; + color: white; + border: none; + padding: 12px 24px; + border-radius: 4px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 8px; +} + +.btn-primary:hover:not(:disabled) { + background: #005a9e; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 120, 212, 0.3); +} + +.btn-primary:disabled { + background: #ccc; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.btn-secondary { + background: transparent; + color: #0078d4; + border: 2px solid #0078d4; + padding: 10px 22px; + border-radius: 4px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 8px; +} + +.btn-secondary:hover:not(:disabled) { + background: #0078d4; + color: white; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 120, 212, 0.3); +} + +.btn-secondary:disabled { + border-color: #ccc; + color: #ccc; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +/* Modal Styles */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal { + background: white; + border-radius: 8px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + max-width: 500px; + width: 90%; + max-height: 90vh; + overflow-y: auto; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 30px; + border-bottom: 1px solid #e1e1e1; +} + +.modal-header h3 { + margin: 0; + color: #333; + font-size: 1.5rem; +} + +.modal-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #666; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: all 0.2s ease; +} + +.modal-close:hover { + background: #f5f5f5; + color: #333; +} + +.modal-content { + padding: 30px; +} + +.modal-actions { + padding: 20px 30px; + border-top: 1px solid #e1e1e1; + display: flex; + justify-content: flex-end; + gap: 10px; +} + +/* Form Styles */ +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: #333; +} + +.form-group input { + width: 100%; + padding: 12px; + border: 2px solid #e1e1e1; + border-radius: 4px; + font-size: 1rem; + transition: border-color 0.2s ease; + box-sizing: border-box; +} + +.form-group input:focus { + outline: none; + border-color: #0078d4; + box-shadow: 0 0 0 3px rgba(0, 120, 212, 0.1); +} + +.form-help { + display: block; + margin-top: 5px; + font-size: 0.9rem; + color: #666; + line-height: 1.4; +} + +/* Error and Loading States */ +.error-container { + text-align: center; + padding: 50px 20px; + background: rgba(255, 255, 255, 0.95); + border-radius: 8px; + color: #333; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + margin: 20px; +} + +.error-container h2 { + color: #d32f2f; + margin-bottom: 15px; +} + +.error-container button { + margin-top: 20px; +} + +.error-message { + background: #ffebee; + color: #c62828; + padding: 15px; + border-radius: 4px; + border-left: 4px solid #d32f2f; + margin: 15px 0; +} + +.error-banner { + position: fixed; + top: 20px; + right: 20px; + background: #d32f2f; + color: white; + padding: 15px 20px; + border-radius: 4px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + z-index: 1000; + display: flex; + align-items: center; + gap: 15px; + max-width: 400px; +} + +.error-banner p { + margin: 0; + flex: 1; +} + +.error-banner button { + background: none; + border: 1px solid white; + color: white; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; + font-size: 0.9rem; +} + +.error-banner button:hover { + background: rgba(255, 255, 255, 0.1); +} + +.loading-container { + text-align: center; + padding: 50px 20px; + background: rgba(255, 255, 255, 0.95); + border-radius: 8px; + color: #333; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + margin: 20px; +} + +.loading-spinner { + border: 4px solid #f3f3f3; + border-top: 4px solid #0078d4; + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 1s linear infinite; + margin: 0 auto 20px auto; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .user-scenarios-content { + flex-direction: column; + gap: 20px; + } + + .scenarios-sidebar { + flex: none; + max-height: 300px; + } + + .user-scenarios-header h1 { + font-size: 2rem; + } + + .modal { + width: 95%; + margin: 20px; + } + + .modal-content, + .modal-header, + .modal-actions { + padding: 20px; + } +} + +/* Dark mode support for the markdown editor */ +[data-color-mode="dark"] .w-md-editor { + background-color: #1e1e1e; +} + +/* Ensure markdown editor fits properly */ +.w-md-editor { + background-color: white; +} + +.w-md-editor .w-md-editor-text-container .w-md-editor-text { + font-size: 14px !important; + line-height: 1.6 !important; +} \ No newline at end of file diff --git a/src/components/UserScenarios.js b/src/components/UserScenarios.js new file mode 100644 index 0000000000..81160bd75e --- /dev/null +++ b/src/components/UserScenarios.js @@ -0,0 +1,472 @@ +import React, { useState, useEffect, useCallback, lazy, Suspense } from 'react'; +import githubService from '../services/githubService'; +import { PageLayout, usePage } from './framework'; +import ContextualHelpMascot from './ContextualHelpMascot'; +import './UserScenarios.css'; + +// Lazy load the markdown editor to avoid bundle bloat +const MDEditor = lazy(() => import('@uiw/react-md-editor')); + +const UserScenarios = () => { + return ( + + + + ); +}; + +const UserScenariosContent = () => { + const { profile, repository, branch } = usePage(); + + // Get data from page framework + const user = profile?.login; + const repo = repository?.name; + + const [scenarioFiles, setScenarioFiles] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [selectedScenario, setSelectedScenario] = useState(null); + const [scenarioContent, setScenarioContent] = useState(''); + const [isEditMode, setIsEditMode] = useState(false); + const [saving, setSaving] = useState(false); + const [showNewScenarioModal, setShowNewScenarioModal] = useState(false); + const [newScenarioId, setNewScenarioId] = useState(''); + const [newScenarioTitle, setNewScenarioTitle] = useState(''); + + // Function to validate WHO SMART Guidelines SOP ID requirements + const validateScenarioId = (id) => { + // WHO SMART Guidelines SOP ID requirements: + // - Must be lowercase + // - Must use hyphens instead of spaces + // - Should be descriptive and follow kebab-case format + // - Should not contain special characters except hyphens + const regex = /^[a-z0-9]+(-[a-z0-9]+)*$/; + return regex.test(id) && id.length >= 3 && id.length <= 50; + }; + + // Function to generate a valid scenario ID from title + const generateScenarioId = (title) => { + return title + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') // Remove special characters except spaces and hyphens + .replace(/\s+/g, '-') // Replace spaces with hyphens + .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen + .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens + }; + + // Load user scenario files from input/pagecontent/ + const loadScenarioFiles = useCallback(async () => { + if (!user || !repo || !branch) return; + + try { + setLoading(true); + setError(null); + + // Get files from input/pagecontent directory + const pageContentPath = 'input/pagecontent'; + const files = await githubService.getDirectoryContents(user, repo, pageContentPath, branch); + + // Filter for userscenario-*.md files + const scenarioFiles = files + .filter(file => + file.type === 'file' && + file.name.startsWith('userscenario-') && + file.name.endsWith('.md') + ) + .map(file => ({ + ...file, + id: file.name.replace('userscenario-', '').replace('.md', ''), + title: file.name.replace('userscenario-', '').replace('.md', '').replace(/-/g, ' ') + })); + + setScenarioFiles(scenarioFiles); + } catch (err) { + console.error('Error loading scenario files:', err); + setError(`Failed to load user scenarios: ${err.message}`); + } finally { + setLoading(false); + } + }, [user, repo, branch]); + + // Load content of a specific scenario file + const loadScenarioContent = useCallback(async (scenarioFile) => { + if (!user || !repo || !branch || !scenarioFile) return; + + try { + setLoading(true); + const content = await githubService.getFileContent(user, repo, scenarioFile.path, branch); + setScenarioContent(content); + setSelectedScenario(scenarioFile); + } catch (err) { + console.error('Error loading scenario content:', err); + setError(`Failed to load scenario: ${err.message}`); + } finally { + setLoading(false); + } + }, [user, repo, branch]); + + // Save scenario content + const saveScenarioContent = useCallback(async () => { + if (!user || !repo || !branch || !selectedScenario) return; + + try { + setSaving(true); + + const commitMessage = `Update user scenario: ${selectedScenario.id}`; + + await githubService.updateFile( + user, + repo, + selectedScenario.path, + scenarioContent, + commitMessage, + selectedScenario.sha, + branch + ); + + // Refresh the file list to get updated SHA + await loadScenarioFiles(); + + // Update the selected scenario with new SHA + const updatedFiles = await githubService.getDirectoryContents(user, repo, 'input/pagecontent', branch); + const updatedScenario = updatedFiles.find(f => f.path === selectedScenario.path); + if (updatedScenario) { + setSelectedScenario({ + ...selectedScenario, + sha: updatedScenario.sha + }); + } + + setIsEditMode(false); + } catch (err) { + console.error('Error saving scenario:', err); + setError(`Failed to save scenario: ${err.message}`); + } finally { + setSaving(false); + } + }, [user, repo, branch, selectedScenario, scenarioContent, loadScenarioFiles]); + + // Create new scenario + const createNewScenario = useCallback(async () => { + if (!user || !repo || !branch || !newScenarioId || !newScenarioTitle) return; + + try { + setSaving(true); + + // Validate the ID + if (!validateScenarioId(newScenarioId)) { + setError('Scenario ID must be lowercase, use hyphens instead of spaces, and contain only letters, numbers, and hyphens.'); + setSaving(false); + return; + } + + // Check if file already exists + const fileName = `userscenario-${newScenarioId}.md`; + const filePath = `input/pagecontent/${fileName}`; + + try { + await githubService.getFileContent(user, repo, filePath, branch); + setError('A scenario with this ID already exists. Please choose a different ID.'); + setSaving(false); + return; + } catch (err) { + // File doesn't exist, which is what we want + } + + // Create initial content + const initialContent = `# ${newScenarioTitle} + +## Overview +Brief description of this user scenario. + +## Actors +- Primary actor: [Actor name] +- Secondary actors: [List other actors if applicable] + +## Preconditions +- [List any preconditions] + +## Flow of Events +1. [Step 1] +2. [Step 2] +3. [Continue with the main flow] + +## Postconditions +- [List the expected outcomes] + +## Alternative Flows +### [Alternative Flow Name] +- [Describe alternative scenarios] + +## Exception Flows +### [Exception Flow Name] +- [Describe error handling scenarios] + +## Notes +- [Additional notes or considerations] +`; + + const commitMessage = `Create new user scenario: ${newScenarioTitle}`; + + await githubService.createFile( + user, + repo, + filePath, + initialContent, + commitMessage, + branch + ); + + // Refresh the file list + await loadScenarioFiles(); + + // Clear the modal + setShowNewScenarioModal(false); + setNewScenarioId(''); + setNewScenarioTitle(''); + + } catch (err) { + console.error('Error creating scenario:', err); + setError(`Failed to create scenario: ${err.message}`); + } finally { + setSaving(false); + } + }, [user, repo, branch, newScenarioId, newScenarioTitle, loadScenarioFiles]); + + // Load scenario files when component mounts or context changes + useEffect(() => { + loadScenarioFiles(); + }, [loadScenarioFiles]); + + // Auto-generate scenario ID when title changes + useEffect(() => { + if (newScenarioTitle) { + const generatedId = generateScenarioId(newScenarioTitle); + setNewScenarioId(generatedId); + } + }, [newScenarioTitle]); + + // Render loading state + if (loading && !selectedScenario) { + return ( +
+
+
+

Loading user scenarios...

+
+
+ ); + } + + // Render error state + if (error && !selectedScenario) { + return ( +
+
+

Error Loading User Scenarios

+

{error}

+ +
+
+ ); + } + + return ( +
+
+

User Scenarios

+

Narrative descriptions of how different personas interact with the system in specific healthcare contexts.

+ +
+ +
+
+ +
+ {/* Scenario List */} +
+

Available Scenarios ({scenarioFiles.length})

+ + {scenarioFiles.length === 0 ? ( +
+

No user scenarios found.

+

Click "Create New Scenario" to get started.

+
+ ) : ( +
+ {scenarioFiles.map((file) => ( +
loadScenarioContent(file)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + loadScenarioContent(file); + } + }} + role="button" + tabIndex={0} + aria-label={`Select user scenario: ${file.title}`} + > +

{file.title}

+

ID: {file.id}

+
+ ))} +
+ )} +
+ + {/* Scenario Editor */} +
+ {selectedScenario ? ( + <> +
+

{selectedScenario.title}

+
+ {isEditMode ? ( + <> + + + + ) : ( + + )} +
+
+ +
+ {isEditMode ? ( + Loading editor...
}> + + + ) : ( + Loading preview...
}> + + + )} +
+ + ) : ( +
+

Select a scenario from the list to view or edit it.

+
+ )} +
+ + + {/* New Scenario Modal */} + {showNewScenarioModal && ( +
+
+
+

Create New User Scenario

+ +
+ +
+
+ + setNewScenarioTitle(e.target.value)} + placeholder="e.g., Patient Registration Flow" + /> +
+ +
+ + setNewScenarioId(e.target.value)} + placeholder="e.g., patient-registration-flow" + /> + + ID must be lowercase, use hyphens instead of spaces, and contain only letters, numbers, and hyphens. + +
+ + {error && ( +
+ {error} +
+ )} +
+ +
+ + +
+
+
+ )} + + {/* Error display for save/create operations */} + {error && selectedScenario && ( +
+

{error}

+ +
+ )} + + + + ); +}; + +export default UserScenarios; \ No newline at end of file diff --git a/src/services/componentRouteService.js b/src/services/componentRouteService.js index 0877c44e6e..bc7415faa8 100644 --- a/src/services/componentRouteService.js +++ b/src/services/componentRouteService.js @@ -101,6 +101,9 @@ function createLazyComponent(componentName) { case 'ActorEditor': LazyComponent = React.lazy(() => import('../components/ActorEditor')); break; + case 'UserScenarios': + LazyComponent = React.lazy(() => import('../components/UserScenarios')); + break; case 'BusinessProcessSelection': LazyComponent = React.lazy(() => import('../components/BusinessProcessSelection')); break; diff --git a/src/services/helpContentService.js b/src/services/helpContentService.js index 8dbb61f66d..1efd04bb3f 100644 --- a/src/services/helpContentService.js +++ b/src/services/helpContentService.js @@ -334,6 +334,141 @@ class HelpContentService { ] } ], + 'user-scenarios': [ + { + id: 'user-scenarios-overview', + title: 'Understanding User Scenarios', + badge: '/sgex/cat-paw-icon.svg', + type: 'slideshow', + content: [ + { + title: 'What are User Scenarios?', + content: ` +

User scenarios are narrative descriptions that document how different personas interact with healthcare systems in specific contexts.

+

🎯 Purpose

+ +
+ 💡 L2 vs L3: User scenarios exist at both Level 2 (narrative descriptions) and Level 3 (FHIR scenario test bundles). +
+ ` + }, + { + title: 'Scenario Structure', + content: ` +

Well-structured user scenarios typically include these elements:

+

📋 Essential Components

+ +
+ ✨ Best Practice: Reference specific personas from your DAK's actor definitions. +
+ ` + }, + { + title: 'File Naming Convention', + content: ` +

User scenario files follow WHO SMART Guidelines naming conventions:

+

📁 File Pattern

+ input/pagecontent/userscenario-{id}.md +

🏷️ ID Requirements

+ +

Examples

+ + ` + } + ] + }, + { + id: 'editing-scenarios', + title: 'Creating and Editing Scenarios', + badge: '/sgex/cat-paw-edit-icon.svg', + type: 'slideshow', + content: [ + { + title: 'Creating New Scenarios', + content: ` +

Follow these steps to create a new user scenario:

+

🆕 Creation Process

+
    +
  1. Click the "Create New Scenario" button
  2. +
  3. Enter a descriptive title for your scenario
  4. +
  5. Review the auto-generated ID (or customize if needed)
  6. +
  7. Click "Create Scenario" to generate the file
  8. +
  9. Edit the template content to match your specific scenario
  10. +
+
+ 🔧 Auto-Generation: The system creates a structured template with all standard sections. +
+ ` + }, + { + title: 'Edit and Preview Modes', + content: ` +

The scenario editor provides two modes for working with content:

+

✏️ Edit Mode

+ +

👁️ Preview Mode

+ +
+ ⌨️ Keyboard Shortcuts: Use Ctrl+S to save in edit mode. +
+ ` + }, + { + title: 'Content Guidelines', + content: ` +

Follow these guidelines for high-quality user scenarios:

+

📝 Writing Best Practices

+ +

🔗 Integration Points

+ + ` + } + ] + } + ], 'dak-selection': [ { id: 'choosing-dak-repository', From 70c54a1b18f853c49e70c95f38ca9e08b7ac8936 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 16 Sep 2025 22:52:50 +0000 Subject: [PATCH 04/11] Auto-update service table and question ID schema - Generated service table from current codebase - Updated FAQ question ID enum with questions - Links to schemas and OpenAPI specs updated Generated by: scripts/generate-service-table.js Timestamp: 2025-09-16T22:52:50+00:00 --- docs/SERVICE_TABLE.md | 2 +- services/dak-faq-mcp/schemas/questionId.schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/SERVICE_TABLE.md b/docs/SERVICE_TABLE.md index de5604ff24..39dcb2a951 100644 --- a/docs/SERVICE_TABLE.md +++ b/docs/SERVICE_TABLE.md @@ -35,5 +35,5 @@ This table is automatically generated from the codebase on every commit. - Localhost URLs are clickable links for local development testing - MCP manifest links included for MCP tooling -*Generated on: 2025-09-16T22:15:18.790Z* +*Generated on: 2025-09-16T22:52:50.199Z* *Generator: scripts/generate-service-table.js* diff --git a/services/dak-faq-mcp/schemas/questionId.schema.json b/services/dak-faq-mcp/schemas/questionId.schema.json index d7ae01842b..63d7b6fabc 100644 --- a/services/dak-faq-mcp/schemas/questionId.schema.json +++ b/services/dak-faq-mcp/schemas/questionId.schema.json @@ -21,7 +21,7 @@ "enum": "Question ID must be one of the available FAQ questions. Use the list_faq_questions endpoint to get current options." }, "_generated": { - "timestamp": "2025-09-16T22:15:18.783Z", + "timestamp": "2025-09-16T22:52:50.192Z", "count": 7, "source": "scripts/generate-service-table.js" } From 7c211146611ddb2db8ef85a53e47fcfcb491a4e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 23:59:35 +0000 Subject: [PATCH 05/11] Fix routing fallback and add UserScenarios test Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/services/componentRouteService.js | 5 +++ src/tests/UserScenarios.test.js | 61 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/tests/UserScenarios.test.js diff --git a/src/services/componentRouteService.js b/src/services/componentRouteService.js index bc7415faa8..2b04c75179 100644 --- a/src/services/componentRouteService.js +++ b/src/services/componentRouteService.js @@ -219,6 +219,7 @@ export function generateLazyRoutes() { const DAKDashboard = createLazyComponent('DAKDashboard'); const LandingPage = createLazyComponent('LandingPage'); const DocumentationViewer = createLazyComponent('DocumentationViewer'); + const UserScenarios = createLazyComponent('UserScenarios'); return [ } />, @@ -226,6 +227,10 @@ export function generateLazyRoutes() { } />, } />, } />, + } />, + } />, + } />, + } />, } />, } />, } />, diff --git a/src/tests/UserScenarios.test.js b/src/tests/UserScenarios.test.js new file mode 100644 index 0000000000..73e95f8eb5 --- /dev/null +++ b/src/tests/UserScenarios.test.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import UserScenarios from '../components/UserScenarios'; +import { PageProvider } from '../components/framework/PageProvider'; + +// Mock the services to avoid external dependencies in tests +jest.mock('../services/githubService', () => ({ + getDirectoryContents: jest.fn(), + getFileContent: jest.fn(), + updateFile: jest.fn(), + createFile: jest.fn() +})); + +jest.mock('../services/helpContentService', () => ({ + getHelpTopicsForPage: jest.fn(() => []), + hasHelpTopics: jest.fn(() => true) +})); + +// Mock the lazy-loaded markdown editor +jest.mock('@uiw/react-md-editor', () => { + const React = require('react'); + return { + __esModule: true, + default: ({ value, onChange }) => ( +