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}
+
window.location.reload()}>Retry
+
+
+ );
+ }
+
+ return (
+
+
+
User Scenarios
+
Narrative descriptions of how different personas interact with the system in specific healthcare contexts.
+
+
+ setShowNewScenarioModal(true)}
+ disabled={saving}
+ >
+ Create New Scenario
+
+
+
+
+
+ {/* 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 ? (
+ <>
+ setIsEditMode(false)}
+ disabled={saving}
+ >
+ Cancel
+
+
+ {saving ? 'Saving...' : 'Save'}
+
+ >
+ ) : (
+ setIsEditMode(true)}
+ >
+ Edit
+
+ )}
+
+
+
+
+ {isEditMode ? (
+ Loading editor...
}>
+
+
+ ) : (
+
Loading preview... }>
+
+
+ )}
+
+ >
+ ) : (
+
+
Select a scenario from the list to view or edit it.
+
+ )}
+
+
+
+ {/* New Scenario Modal */}
+ {showNewScenarioModal && (
+
+
+
+
Create New User Scenario
+ setShowNewScenarioModal(false)}
+ >
+ ×
+
+
+
+
+
+ Scenario Title:
+ setNewScenarioTitle(e.target.value)}
+ placeholder="e.g., Patient Registration Flow"
+ />
+
+
+
+ Scenario ID:
+ 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}
+
+ )}
+
+
+
+ setShowNewScenarioModal(false)}
+ disabled={saving}
+ >
+ Cancel
+
+
+ {saving ? 'Creating...' : 'Create Scenario'}
+
+
+
+
+ )}
+
+ {/* Error display for save/create operations */}
+ {error && selectedScenario && (
+
+
{error}
+
setError(null)}>Dismiss
+
+ )}
+
+
+
+ );
+};
+
+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
+
+ Document user journeys and interaction patterns
+ Capture real-world usage scenarios
+ Bridge the gap between personas and business processes
+ Provide context for system requirements and design decisions
+
+
+ 💡 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
+
+ Overview - Brief description of the scenario
+ Actors - Primary and secondary personas involved
+ Preconditions - Starting state or requirements
+ Flow of Events - Step-by-step interaction sequence
+ Postconditions - Expected outcomes
+ Alternative Flows - Different paths through the scenario
+ Exception Flows - Error handling and recovery
+
+
+ ✨ 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
+
+ Lowercase letters and numbers only
+ Use hyphens (-) instead of spaces
+ 3-50 characters in length
+ Descriptive and meaningful
+
+ Examples
+
+ userscenario-patient-registration.md
+ userscenario-immunization-workflow.md
+ userscenario-emergency-care-triage.md
+
+ `
+ }
+ ]
+ },
+ {
+ 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
+
+ Click the "Create New Scenario" button
+ Enter a descriptive title for your scenario
+ Review the auto-generated ID (or customize if needed)
+ Click "Create Scenario" to generate the file
+ Edit the template content to match your specific scenario
+
+
+ 🔧 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
+
+ Full markdown editor with toolbar
+ Live preview pane for immediate feedback
+ Syntax highlighting for better readability
+ Auto-save functionality to prevent data loss
+
+ 👁️ Preview Mode
+
+ Rendered markdown display
+ Final appearance preview
+ Easy switching between edit and preview
+ Print-friendly formatting
+
+
+ ⌨️ 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
+
+ Be Specific: Use concrete examples and real-world contexts
+ Reference Personas: Link to specific actors from your DAK
+ Include Context: Healthcare setting, timing, and circumstances
+ Cover Variations: Normal, alternative, and exception flows
+ Use Clear Language: Avoid technical jargon when possible
+
+ 🔗 Integration Points
+
+ Reference business processes that implement the scenario
+ Link to decision support logic used in the workflow
+ Identify core data elements captured during the scenario
+ Connect to relevant program indicators for measurement
+
+ `
+ }
+ ]
+ }
+ ],
'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 }) => (
+