#
tokens: 25872/50000 4/77 files (page 3/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 3. Use http://codebase.md/hatrigt/hana-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .env.example
├── .github
│   └── pull_request_template.md
├── .gitignore
├── .npmignore
├── claude_template.json
├── docs
│   ├── hana_mcp_architecture.svg
│   └── hana_mcp_ui.gif
├── hana-mcp-server.js
├── hana-mcp-ui
│   ├── .gitignore
│   ├── bin
│   │   └── cli.js
│   ├── hana_mcp_ui.gif
│   ├── index.html
│   ├── logo.png
│   ├── package.json
│   ├── postcss.config.js
│   ├── README.md
│   ├── server
│   │   └── index.js
│   ├── src
│   │   ├── App.jsx
│   │   ├── components
│   │   │   ├── ClaudeConfigTile.jsx
│   │   │   ├── ClaudeDesktopView.jsx
│   │   │   ├── ClaudeServerCard.jsx
│   │   │   ├── ConfigurationModal.jsx
│   │   │   ├── ConnectionDetailsModal.jsx
│   │   │   ├── DashboardView.jsx
│   │   │   ├── DatabaseListView.jsx
│   │   │   ├── EnhancedServerCard.jsx
│   │   │   ├── EnvironmentManager.jsx
│   │   │   ├── EnvironmentSelector.jsx
│   │   │   ├── layout
│   │   │   │   ├── index.js
│   │   │   │   └── VerticalSidebar.jsx
│   │   │   ├── MainApp.jsx
│   │   │   ├── PathConfigModal.jsx
│   │   │   ├── PathSetupModal.jsx
│   │   │   ├── SearchAndFilter.jsx
│   │   │   └── ui
│   │   │       ├── DatabaseTypeBadge.jsx
│   │   │       ├── GlassCard.jsx
│   │   │       ├── GlassWindow.jsx
│   │   │       ├── GradientButton.jsx
│   │   │       ├── IconComponent.jsx
│   │   │       ├── index.js
│   │   │       ├── LoadingSpinner.jsx
│   │   │       ├── MetricCard.jsx
│   │   │       ├── StatusBadge.jsx
│   │   │       └── Tabs.jsx
│   │   ├── index.css
│   │   ├── main.jsx
│   │   └── utils
│   │       ├── cn.js
│   │       ├── databaseTypes.js
│   │       └── theme.js
│   ├── start.js
│   ├── tailwind.config.js
│   └── vite.config.js
├── LICENSE
├── manifest.yml
├── package-lock.json
├── package.json
├── README.md
├── setup.sh
├── src
│   ├── constants
│   │   ├── mcp-constants.js
│   │   └── tool-definitions.js
│   ├── database
│   │   ├── connection-manager.js
│   │   ├── hana-client.js
│   │   └── query-executor.js
│   ├── server
│   │   ├── index.js
│   │   ├── lifecycle-manager.js
│   │   └── mcp-handler.js
│   ├── tools
│   │   ├── config-tools.js
│   │   ├── index-tools.js
│   │   ├── index.js
│   │   ├── query-tools.js
│   │   ├── schema-tools.js
│   │   └── table-tools.js
│   └── utils
│       ├── config.js
│       ├── formatters.js
│       ├── logger.js
│       └── validators.js
└── tests
    ├── automated
    │   └── test-mcp-inspector.js
    ├── manual
    │   └── manual-test.js
    ├── mcpInspector
    │   ├── mcp-inspector-config.json
    │   └── mcp-inspector-config.template.json
    ├── mcpTestingGuide.md
    └── README.md
```

# Files

--------------------------------------------------------------------------------
/docs/hana_mcp_architecture.svg:
--------------------------------------------------------------------------------

```
  1 | <svg viewBox="0 0 1400 700" xmlns="http://www.w3.org/2000/svg">
  2 |   <!-- Clean background -->
  3 |   <rect width="1400" height="700" fill="#ffffff"/>
  4 |   
  5 |   <!-- Title -->
  6 |   <text x="700" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="28" font-weight="bold" fill="#1a1a1a">
  7 |     HANA MCP Server Architecture
  8 |   </text>
  9 |   <text x="700" y="65" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" fill="#666666">
 10 |     Enterprise AI-Database Integration Platform
 11 |   </text>
 12 |   
 13 |   <!-- Client Applications Layer -->
 14 |   <rect x="80" y="100" width="220" height="130" rx="8" fill="#f8f9fa" stroke="#dee2e6" stroke-width="2"/>
 15 |   <text x="190" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#212529">
 16 |     MCP Clients
 17 |   </text>
 18 |   <rect x="100" y="145" width="180" height="65" rx="4" fill="#ffffff" stroke="#e9ecef" stroke-width="1"/>
 19 |   <text x="190" y="165" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#495057">
 20 |     • Claude Desktop
 21 |   </text>
 22 |   <text x="190" y="185" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#495057">
 23 |     • VSCode Extensions
 24 |   </text>
 25 |   <text x="190" y="205" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#495057">
 26 |     • Custom AI Applications
 27 |   </text>
 28 | 
 29 |   <!-- MCP Protocol Bridge -->
 30 |   <rect x="380" y="140" width="140" height="50" rx="25" fill="#e9ecef" stroke="#ced4da" stroke-width="1"/>
 31 |   <text x="450" y="160" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#495057">
 32 |     MCP Protocol
 33 |   </text>
 34 |   <text x="450" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">
 35 |     JSON-RPC
 36 |   </text>
 37 | 
 38 |   <!-- HANA MCP Server Core -->
 39 |   <rect x="600" y="80" width="260" height="170" rx="8" fill="#343a40" stroke="#495057" stroke-width="2"/>
 40 |   <text x="730" y="110" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#ffffff">
 41 |     HANA MCP Server
 42 |   </text>
 43 |   <text x="730" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#f8f9fa">
 44 |     Enterprise Database Gateway
 45 |   </text>
 46 |   
 47 |   <!-- Server components -->
 48 |   <rect x="620" y="145" width="100" height="40" rx="4" fill="#495057" stroke="#6c757d" stroke-width="1"/>
 49 |   <text x="670" y="165" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Connection</text>
 50 |   <text x="670" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9ecef">Manager</text>
 51 |   
 52 |   <rect x="740" y="145" width="100" height="40" rx="4" fill="#495057" stroke="#6c757d" stroke-width="1"/>
 53 |   <text x="790" y="165" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Schema</text>
 54 |   <text x="790" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9ecef">Inspector</text>
 55 |   
 56 |   <rect x="620" y="200" width="100" height="40" rx="4" fill="#495057" stroke="#6c757d" stroke-width="1"/>
 57 |   <text x="670" y="220" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Query</text>
 58 |   <text x="670" y="230" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9ecef">Engine</text>
 59 |   
 60 |   <rect x="740" y="200" width="100" height="40" rx="4" fill="#495057" stroke="#6c757d" stroke-width="1"/>
 61 |   <text x="790" y="220" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Tool</text>
 62 |   <text x="790" y="230" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9ecef">Handler</text>
 63 | 
 64 |   <!-- SAP HANA Database -->
 65 |   <rect x="940" y="80" width="220" height="170" rx="8" fill="#212529" stroke="#343a40" stroke-width="2"/>
 66 |   <text x="1050" y="110" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#ffffff">
 67 |     SAP HANA
 68 |   </text>
 69 |   <text x="1050" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#f8f9fa">
 70 |     Enterprise Database
 71 |   </text>
 72 |   
 73 |   <!-- Database features -->
 74 |   <rect x="960" y="145" width="180" height="85" rx="4" fill="#343a40" stroke="#495057" stroke-width="1"/>
 75 |   <text x="1050" y="165" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#ffffff">In-Memory Processing</text>
 76 |   <text x="1050" y="185" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e9ecef">• Columnar Store Engine</text>
 77 |   <text x="1050" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e9ecef">• Real-time Analytics</text>
 78 |   <text x="1050" y="215" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e9ecef">• Enterprise Security</text>
 79 | 
 80 |   <!-- Available MCP Tools Section -->
 81 |   <text x="700" y="310" text-anchor="middle" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#212529">
 82 |     Available MCP Tools
 83 |   </text>
 84 |   
 85 |   <!-- Connection Tools -->
 86 |   <rect x="80" y="340" width="200" height="110" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
 87 |   <rect x="90" y="350" width="180" height="25" rx="3" fill="#f8f9fa"/>
 88 |   <text x="180" y="367" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">Connection Tools</text>
 89 |   <text x="90" y="385" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_test_connection</text>
 90 |   <text x="90" y="400" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_show_config</text>
 91 |   <text x="90" y="415" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_show_env_vars</text>
 92 |   <text x="90" y="435" font-family="Arial, sans-serif" font-size="9" fill="#6c757d">Database connectivity &amp; setup</text>
 93 |   
 94 |   <!-- Schema Tools -->
 95 |   <rect x="300" y="340" width="200" height="110" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
 96 |   <rect x="310" y="350" width="180" height="25" rx="3" fill="#f8f9fa"/>
 97 |   <text x="400" y="367" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">Schema Tools</text>
 98 |   <text x="310" y="385" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_list_schemas</text>
 99 |   <text x="310" y="400" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_list_tables</text>
100 |   <text x="310" y="415" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_describe_table</text>
101 |   <text x="310" y="435" font-family="Arial, sans-serif" font-size="9" fill="#6c757d">Metadata &amp; structure discovery</text>
102 |   
103 |   <!-- Index Tools -->
104 |   <rect x="520" y="340" width="200" height="110" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
105 |   <rect x="530" y="350" width="180" height="25" rx="3" fill="#f8f9fa"/>
106 |   <text x="620" y="367" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">Index Tools</text>
107 |   <text x="530" y="385" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_list_indexes</text>
108 |   <text x="530" y="400" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_describe_index</text>
109 |   <text x="530" y="415" font-family="Arial, sans-serif" font-size="10" fill="#495057">• Performance optimization</text>
110 |   <text x="530" y="435" font-family="Arial, sans-serif" font-size="9" fill="#6c757d">Index management &amp; analysis</text>
111 |   
112 |   <!-- Query Tools -->
113 |   <rect x="740" y="340" width="200" height="110" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
114 |   <rect x="750" y="350" width="180" height="25" rx="3" fill="#f8f9fa"/>
115 |   <text x="840" y="367" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">Query Tools</text>
116 |   <text x="750" y="385" font-family="Arial, sans-serif" font-size="10" fill="#495057">• hana_execute_query</text>
117 |   <text x="750" y="400" font-family="Arial, sans-serif" font-size="10" fill="#495057">• Parameterized queries</text>
118 |   <text x="750" y="415" font-family="Arial, sans-serif" font-size="10" fill="#495057">• Custom SQL execution</text>
119 |   <text x="750" y="435" font-family="Arial, sans-serif" font-size="9" fill="#6c757d">Data retrieval &amp; analysis</text>
120 |   
121 |   <!-- Browser Control Tools -->
122 |   <rect x="960" y="340" width="200" height="110" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
123 |   <rect x="970" y="350" width="180" height="25" rx="3" fill="#f8f9fa"/>
124 |   <text x="1060" y="367" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">Browser Control</text>
125 |   <text x="970" y="385" font-family="Arial, sans-serif" font-size="10" fill="#495057">• open_url</text>
126 |   <text x="970" y="400" font-family="Arial, sans-serif" font-size="10" fill="#495057">• get_page_content</text>
127 |   <text x="970" y="415" font-family="Arial, sans-serif" font-size="10" fill="#495057">• execute_javascript</text>
128 |   <text x="970" y="435" font-family="Arial, sans-serif" font-size="9" fill="#6c757d">Web automation &amp; integration</text>
129 | 
130 |   <!-- Configuration and Benefits Section -->
131 |   <!-- Configuration -->
132 |   <rect x="180" y="500" width="350" height="120" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
133 |   <rect x="190" y="510" width="330" height="25" rx="3" fill="#f8f9fa"/>
134 |   <text x="355" y="527" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="bold" fill="#212529">
135 |     Configuration &amp; Security
136 |   </text>
137 |   <text x="200" y="550" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Environment Variables (HANA_HOST, HANA_USER, etc.)</text>
138 |   <text x="200" y="570" font-family="Arial, sans-serif" font-size="11" fill="#495057">• SSL/TLS Encryption &amp; Certificate Validation</text>
139 |   <text x="200" y="590" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Connection Pooling &amp; Resource Management</text>
140 | 
141 |   <!-- Key Benefits -->
142 |   <rect x="580" y="500" width="350" height="120" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
143 |   <rect x="590" y="510" width="330" height="25" rx="3" fill="#f8f9fa"/>
144 |   <text x="755" y="527" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="bold" fill="#212529">
145 |     Enterprise Benefits
146 |   </text>
147 |   <text x="600" y="550" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Seamless AI-Database Integration</text>
148 |   <text x="600" y="570" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Enterprise-Grade Security &amp; Compliance</text>
149 |   <text x="600" y="590" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Real-time Analytics &amp; Decision Support</text>
150 | 
151 |   <!-- Performance Metrics -->
152 |   <rect x="980" y="500" width="240" height="120" rx="6" fill="#ffffff" stroke="#dee2e6" stroke-width="1"/>
153 |   <rect x="990" y="510" width="220" height="25" rx="3" fill="#f8f9fa"/>
154 |   <text x="1100" y="527" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="bold" fill="#212529">
155 |     Performance
156 |   </text>
157 |   <text x="1000" y="550" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Sub-second Query Response</text>
158 |   <text x="1000" y="570" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Concurrent User Support</text>
159 |   <text x="1000" y="590" font-family="Arial, sans-serif" font-size="11" fill="#495057">• Optimized Memory Usage</text>
160 | 
161 |   <!-- Simple Data Flow Arrows -->
162 |   <polygon points="300,165 380,165 375,160 375,170" fill="#6c757d"/>
163 |   <polygon points="520,165 600,165 595,160 595,170" fill="#6c757d"/>
164 |   <polygon points="860,165 940,165 935,160 935,170" fill="#6c757d"/>
165 | 
166 |   <!-- Flow labels -->
167 |   <text x="340" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">JSON-RPC</text>
168 |   <text x="560" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Tool Calls</text>
169 |   <text x="900" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">SQL Queries</text>
170 | 
171 |   <!-- Footer -->
172 |   <text x="700" y="660" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="#212529">
173 |     Enterprise AI-Database Integration Platform
174 |   </text>
175 |   <text x="700" y="680" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#6c757d">
176 |     Enabling secure, scalable AI-powered database interactions through standardized protocols
177 |   </text>
178 | </svg>
```

--------------------------------------------------------------------------------
/hana-mcp-ui/src/components/MainApp.jsx:
--------------------------------------------------------------------------------

```javascript
  1 | import { useState, useEffect } from 'react';
  2 | import axios from 'axios';
  3 | import toast from 'react-hot-toast';
  4 | import { motion, AnimatePresence } from 'framer-motion';
  5 | 
  6 | // Import components
  7 | import VerticalSidebar from './layout/VerticalSidebar';
  8 | import DashboardView from './DashboardView';
  9 | import DatabaseListView from './DatabaseListView';
 10 | import ClaudeConfigTile from './ClaudeConfigTile'
 11 | import ClaudeDesktopView from './ClaudeDesktopView'
 12 | import ConnectionDetailsModal from './ConnectionDetailsModal';
 13 | 
 14 | // Import existing components
 15 | import ConfigurationModal from './ConfigurationModal';
 16 | import EnvironmentSelector from './EnvironmentSelector';
 17 | import PathSetupModal from './PathSetupModal';
 18 | import { LoadingOverlay, GlassWindow } from './ui';
 19 | 
 20 | const API_BASE = 'http://localhost:3001/api';
 21 | 
 22 | const MainApp = () => {
 23 |   // State management
 24 |   const [activeView, setActiveView] = useState('dashboard');
 25 |   const [hanaServers, setHanaServers] = useState({});
 26 |   const [claudeServers, setClaudeServers] = useState([]);
 27 |   const [claudeConfigPath, setClaudeConfigPath] = useState(null);
 28 |   const [activeEnvironments, setActiveEnvironments] = useState({});
 29 |   
 30 |   // UI State
 31 |   const [isConfigModalOpen, setIsConfigModalOpen] = useState(false);
 32 |   const [isPathSetupOpen, setIsPathSetupOpen] = useState(false)
 33 |   const [isConnectionDetailsOpen, setIsConnectionDetailsOpen] = useState(false)
 34 |   const [selectedConnection, setSelectedConnection] = useState(null);
 35 |   const [isEnvironmentSelectorOpen, setIsEnvironmentSelectorOpen] = useState(false);
 36 |   const [selectedServer, setSelectedServer] = useState(null);
 37 |   const [deploymentTarget, setDeploymentTarget] = useState(null);
 38 |   const [activeTab, setActiveTab] = useState('Production');
 39 |   const [isLoading, setIsLoading] = useState(false);
 40 | 
 41 |   // Form data for multi-environment configuration
 42 |   const [formData, setFormData] = useState({
 43 |     name: '',
 44 |     description: '',
 45 |     environments: {
 46 |       Production: {
 47 |         HANA_HOST: '',
 48 |         HANA_PORT: '443',
 49 |         HANA_USER: '',
 50 |         HANA_PASSWORD: '',
 51 |         HANA_SCHEMA: '',
 52 |         HANA_SSL: 'true',
 53 |         HANA_ENCRYPT: 'true',
 54 |         HANA_VALIDATE_CERT: 'true',
 55 |         LOG_LEVEL: 'info',
 56 |         ENABLE_FILE_LOGGING: 'true',
 57 |         ENABLE_CONSOLE_LOGGING: 'false'
 58 |       },
 59 |       Development: {
 60 |         HANA_HOST: '',
 61 |         HANA_PORT: '443',
 62 |         HANA_USER: '',
 63 |         HANA_PASSWORD: '',
 64 |         HANA_SCHEMA: '',
 65 |         HANA_SSL: 'true',
 66 |         HANA_ENCRYPT: 'true',
 67 |         HANA_VALIDATE_CERT: 'false',
 68 |         LOG_LEVEL: 'debug',
 69 |         ENABLE_FILE_LOGGING: 'true',
 70 |         ENABLE_CONSOLE_LOGGING: 'true'
 71 |       },
 72 |       Staging: {
 73 |         HANA_HOST: '',
 74 |         HANA_PORT: '443',
 75 |         HANA_USER: '',
 76 |         HANA_PASSWORD: '',
 77 |         HANA_SCHEMA: '',
 78 |         HANA_SSL: 'true',
 79 |         HANA_ENCRYPT: 'true',
 80 |         HANA_VALIDATE_CERT: 'true',
 81 |         LOG_LEVEL: 'info',
 82 |         ENABLE_FILE_LOGGING: 'true',
 83 |         ENABLE_CONSOLE_LOGGING: 'false'
 84 |       }
 85 |     }
 86 |   });
 87 | 
 88 |   const [pathInput, setPathInput] = useState('');
 89 | 
 90 |   // Load data on component mount
 91 |   useEffect(() => {
 92 |     loadData();
 93 |   }, []);
 94 | 
 95 |   // Auto-refresh Claude data when Claude tab is opened (only if not initial load)
 96 |   useEffect(() => {
 97 |     if (activeView === 'claude' && claudeServers.length > 0) {
 98 |       // Only refresh if we already have data (prevents race condition on first load)
 99 |       // Silent refresh (no toast notification)
100 |       refreshClaudeData(false);
101 |     }
102 |   }, [activeView]);
103 | 
104 |   const loadData = async () => {
105 |     try {
106 |       setIsLoading(true);
107 |       await Promise.all([
108 |         loadHanaServers(),
109 |         loadClaudeServers(),
110 |         loadClaudeConfigPath(),
111 |         loadActiveEnvironments()
112 |       ]);
113 |     } catch (error) {
114 |       console.error('Error loading data:', error);
115 |       toast.error('Failed to load data');
116 |     } finally {
117 |       setIsLoading(false);
118 |     }
119 |   };
120 | 
121 |   const refreshClaudeData = async (showToast = true) => {
122 |     try {
123 |       await Promise.all([
124 |         loadClaudeServers(),
125 |         loadActiveEnvironments()
126 |       ]);
127 |       if (showToast) {
128 |         toast.success('Configuration refreshed');
129 |       }
130 |     } catch (error) {
131 |       console.error('Error refreshing Claude data:', error);
132 |       if (showToast) {
133 |         toast.error('Failed to refresh configuration');
134 |       }
135 |     }
136 |   };
137 | 
138 |   const loadHanaServers = async () => {
139 |     try {
140 |       const response = await axios.get(`${API_BASE}/hana-servers`);
141 |       setHanaServers(response.data);
142 |     } catch (error) {
143 |       console.error('Error loading HANA servers:', error);
144 |     }
145 |   };
146 | 
147 |   const loadClaudeServers = async () => {
148 |     try {
149 |       const response = await axios.get(`${API_BASE}/claude`);
150 |       setClaudeServers(response.data);
151 |     } catch (error) {
152 |       console.error('Error loading Claude servers:', error);
153 |     }
154 |   };
155 | 
156 |   const loadClaudeConfigPath = async () => {
157 |     try {
158 |       const response = await axios.get(`${API_BASE}/claude/config-path`);
159 |       setClaudeConfigPath(response.data.configPath);
160 |       
161 |       if (!response.data.configPath) {
162 |         setPathInput(response.data.defaultPath || '');
163 |         setIsPathSetupOpen(true);
164 |       }
165 |     } catch (error) {
166 |       console.error('Error loading Claude config path:', error);
167 |     }
168 |   };
169 | 
170 |   const loadActiveEnvironments = async () => {
171 |     try {
172 |       const response = await axios.get(`${API_BASE}/claude/active-environments`);
173 |       setActiveEnvironments(response.data);
174 |     } catch (error) {
175 |       console.error('Error loading active environments:', error);
176 |     }
177 |   };
178 | 
179 |   // Form handlers
180 |   const handleFormChange = (environment, field, value) => {
181 |     setFormData(prev => ({
182 |       ...prev,
183 |       environments: {
184 |         ...prev.environments,
185 |         [environment]: {
186 |           ...(prev.environments[environment] || {}),
187 |           [field]: value
188 |         }
189 |       }
190 |     }));
191 |   };
192 | 
193 |   const handleServerInfoChange = (field, value) => {
194 |     setFormData(prev => ({
195 |       ...prev,
196 |       [field]: value
197 |     }));
198 |   };
199 | 
200 |   // Handle environment-specific updates
201 |   const handleEnvironmentUpdate = (environments) => {
202 |     setFormData(prev => ({
203 |       ...prev,
204 |       environments: environments
205 |     }));
206 |   };
207 | 
208 |   // Navigation handlers
209 |   const handleViewChange = (view) => {
210 |     setActiveView(view);
211 |     
212 |     // Handle special actions
213 |     if (view === 'add-database') {
214 |       openConfigModal();
215 |       return;
216 |     }
217 |   };
218 | 
219 |   const handleQuickAction = (actionId) => {
220 |     switch (actionId) {
221 |       case 'add-database':
222 |         openConfigModal();
223 |         break;
224 |       case 'manage-databases':
225 |         setActiveView('databases');
226 |         break;
227 |       case 'claude-integration':
228 |         setActiveView('claude');
229 |         break;
230 |       default:
231 |         console.warn(`Unknown quick action: ${actionId}`);
232 |     }
233 |   };
234 | 
235 |   const handleBulkAction = (action, selectedItems) => {
236 |     switch (action) {
237 |       case 'deploy':
238 |         toast.success(`Adding ${selectedItems.length} database(s) to Claude`);
239 |         break;
240 |       case 'test':
241 |         toast.success(`Testing connections for ${selectedItems.length} database(s)`);
242 |         break;
243 |       case 'export':
244 |         toast.success(`Exporting ${selectedItems.length} database configuration(s)`);
245 |         break;
246 |       default:
247 |         console.warn(`Unknown bulk action: ${action}`);
248 |     }
249 |   };
250 | 
251 |   const handleConfigPathChange = async (newPath) => {
252 |     try {
253 |       // Update the config path via API
254 |       await axios.post(`${API_BASE}/claude/config-path`, { 
255 |         configPath: newPath 
256 |       });
257 |       
258 |       // Update local state
259 |       setClaudeConfigPath(newPath);
260 |       toast.success('Configuration path updated successfully');
261 |       
262 |       // Refresh Claude data to reflect changes
263 |       await refreshClaudeData(false);
264 |     } catch (error) {
265 |       console.error('Error updating config path:', error);
266 |       toast.error('Failed to update configuration path');
267 |     }
268 |   };
269 | 
270 |   // Modal handlers
271 |   const openConfigModal = (server = null) => {
272 |     if (server) {
273 |       setFormData(server);
274 |       setSelectedServer(server);
275 |       // Set active tab to first available environment when editing
276 |       const envKeys = Object.keys(server.environments || {});
277 |       setActiveTab(envKeys.length > 0 ? envKeys[0] : null);
278 |     } else {
279 |       // Reset form for new server
280 |       setFormData({
281 |         name: '',
282 |         description: '',
283 |         environments: {}
284 |       });
285 |       setSelectedServer(null);
286 |       setActiveTab(null);
287 |     }
288 |     setIsConfigModalOpen(true);
289 |   };
290 | 
291 | 
292 | 
293 |   const closeConfigModal = () => {
294 |     setIsConfigModalOpen(false);
295 |     setSelectedServer(null);
296 |     setActiveTab(null);
297 |   };
298 | 
299 |   // Server operations
300 |   const saveServer = async () => {
301 |     try {
302 |       setIsLoading(true);
303 |       
304 |   
305 |       
306 |       if (selectedServer) {
307 |         await axios.put(`${API_BASE}/hana-servers/${selectedServer.name}`, formData);
308 |         toast.success('Database updated successfully');
309 |       } else {
310 |         await axios.post(`${API_BASE}/hana-servers`, formData);
311 |         toast.success('Database created successfully');
312 |       }
313 | 
314 |       closeConfigModal();
315 |       await loadHanaServers();
316 |     } catch (error) {
317 |       console.error('Error saving server:', error);
318 |       toast.error(error.response?.data?.error || 'Failed to save database');
319 |     } finally {
320 |       setIsLoading(false);
321 |     }
322 |   };
323 | 
324 |   const deleteServer = async (serverName) => {
325 |     try {
326 |       setIsLoading(true);
327 |       await axios.delete(`${API_BASE}/hana-servers/${serverName}`);
328 |       toast.success('Database deleted successfully');
329 |       await loadHanaServers();
330 |     } catch (error) {
331 |       console.error('Error deleting server:', error);
332 |       toast.error('Failed to delete database');
333 |     } finally {
334 |       setIsLoading(false);
335 |     }
336 |   };
337 | 
338 |   // Claude operations
339 |   const openEnvironmentSelector = (serverName) => {
340 |     setDeploymentTarget(serverName);
341 |     setIsEnvironmentSelectorOpen(true);
342 |   };
343 | 
344 |   const deployToClaude = async (environment) => {
345 |     try {
346 |       setIsLoading(true);
347 |       await axios.post(`${API_BASE}/apply-to-claude`, {
348 |         serverName: deploymentTarget,
349 |         environment: environment
350 |       });
351 |       toast.success(`Added ${deploymentTarget} (${environment}) to Claude Desktop configuration`);
352 |       setIsEnvironmentSelectorOpen(false);
353 |       setDeploymentTarget(null);
354 |       await loadClaudeServers();
355 |       await loadActiveEnvironments();
356 |     } catch (error) {
357 |       console.error('Error adding to Claude:', error);
358 |       toast.error(error.response?.data?.error || 'Failed to add to Claude configuration');
359 |     } finally {
360 |       setIsLoading(false);
361 |     }
362 |   };
363 | 
364 |   const removeFromClaude = async (serverName) => {
365 |     try {
366 |       setIsLoading(true);
367 |       await axios.delete(`${API_BASE}/claude/${encodeURIComponent(serverName)}`);
368 |       toast.success(`Removed ${serverName} from Claude Desktop`);
369 |       await loadClaudeServers();
370 |       await loadActiveEnvironments();
371 |     } catch (error) {
372 |       console.error('Error removing from Claude:', error);
373 |       toast.error('Failed to remove from Claude');
374 |     } finally {
375 |       setIsLoading(false);
376 |     }
377 |   };
378 | 
379 |   // Claude path operations
380 |   const saveClaudePath = async () => {
381 |     try {
382 |       setIsLoading(true);
383 |       await axios.post(`${API_BASE}/claude/config-path`, {
384 |         configPath: pathInput
385 |       });
386 |       setClaudeConfigPath(pathInput);
387 |       setIsPathSetupOpen(false);
388 |       toast.success('Claude config path saved successfully');
389 |       await loadClaudeServers();
390 |     } catch (error) {
391 |       console.error('Error saving Claude path:', error);
392 |       toast.error('Failed to save Claude config path');
393 |     } finally {
394 |       setIsLoading(false);
395 |     }
396 |   };
397 | 
398 |   // Render main content based on active view
399 |   const renderMainContent = () => {
400 |     switch (activeView) {
401 |       case 'dashboard':
402 |         return (
403 |           <DashboardView
404 |             hanaServers={hanaServers}
405 |             claudeServers={claudeServers}
406 |             activeEnvironments={activeEnvironments}
407 |             onQuickAction={handleQuickAction}
408 |           />
409 |         );
410 |       case 'databases':
411 |         return (
412 |           <DatabaseListView
413 |             hanaServers={hanaServers}
414 |             claudeServers={claudeServers}
415 |             activeEnvironments={activeEnvironments}
416 |             onEditServer={openConfigModal}
417 |             onAddToClaudeServer={openEnvironmentSelector}
418 |             onDeleteServer={deleteServer}
419 |             onBulkAction={handleBulkAction}
420 |             onAddDatabase={() => openConfigModal()}
421 |           />
422 |         );
423 |       case 'claude':
424 |         return (
425 |           <ClaudeDesktopView
426 |             claudeConfigPath={claudeConfigPath}
427 |             claudeServers={claudeServers}
428 |             activeEnvironments={activeEnvironments}
429 |             onSetupPath={() => setIsPathSetupOpen(true)}
430 |             onRemoveConnection={removeFromClaude}
431 |             onViewConnection={(connection) => {
432 |               setSelectedConnection(connection);
433 |               setIsConnectionDetailsOpen(true);
434 |             }}
435 |             onRefresh={refreshClaudeData}
436 |             onConfigPathChange={handleConfigPathChange}
437 |           />
438 |         );
439 |       
440 | 
441 |       default:
442 |         return (
443 |           <DashboardView
444 |             hanaServers={hanaServers}
445 |             claudeServers={claudeServers}
446 |             activeEnvironments={activeEnvironments}
447 |             onQuickAction={handleQuickAction}
448 |           />
449 |         );
450 |     }
451 |   };
452 | 
453 |   return (
454 |     <GlassWindow maxWidth="full" maxHeight="full">
455 |       <div className="flex h-full bg-transparent p-3 sm:p-4 overflow-hidden">
456 |         {/* Loading Overlay */}
457 |         {isLoading && (
458 |           <LoadingOverlay message="Processing your request..." />
459 |         )}
460 | 
461 |         {/* Floating Sidebar */}
462 |         <div className="flex-shrink-0 h-full">
463 |           <VerticalSidebar
464 |             activeView={activeView}
465 |             onViewChange={handleViewChange}
466 |             databaseCount={Object.keys(hanaServers).length}
467 |             activeConnections={claudeServers.length}
468 |             claudeConfigured={!!claudeConfigPath}
469 |           />
470 |         </div>
471 | 
472 |         {/* Main Content */}
473 |         <div className="flex-1 flex flex-col overflow-hidden bg-white/50 backdrop-blur-sm rounded-r-2xl sm:rounded-r-3xl ml-3">
474 |           {/* Main Content Area */}
475 |           <main className="flex-1 overflow-hidden p-4 sm:p-6 pb-8">
476 |             <AnimatePresence mode="wait">
477 |               <motion.div
478 |                 key={activeView}
479 |                 initial={{ opacity: 0, x: 20 }}
480 |                 animate={{ opacity: 1, x: 0 }}
481 |                 exit={{ opacity: 0, x: -20 }}
482 |                 transition={{ duration: 0.2 }}
483 |                 className="h-full"
484 |               >
485 |                 {renderMainContent()}
486 |               </motion.div>
487 |             </AnimatePresence>
488 |           </main>
489 |         </div>
490 |       </div>
491 | 
492 |       {/* Modals */}
493 |       {isConfigModalOpen && (
494 |         <ConfigurationModal
495 |           isOpen={isConfigModalOpen}
496 |           onClose={closeConfigModal}
497 |           server={selectedServer}
498 |           formData={formData}
499 |           activeTab={activeTab}
500 |           setActiveTab={setActiveTab}
501 |           onFormChange={handleFormChange}
502 |           onServerInfoChange={handleServerInfoChange}
503 |           onSave={saveServer}
504 |           isLoading={isLoading}
505 |         />
506 |       )}
507 | 
508 |       {isEnvironmentSelectorOpen && deploymentTarget && (
509 |         <EnvironmentSelector
510 |           isOpen={isEnvironmentSelectorOpen}
511 |           onClose={() => {
512 |             setIsEnvironmentSelectorOpen(false);
513 |             setDeploymentTarget(null);
514 |           }}
515 |           serverName={deploymentTarget}
516 |           environments={hanaServers[deploymentTarget]?.environments || {}}
517 |           activeEnvironment={activeEnvironments[deploymentTarget]}
518 |           onDeploy={deployToClaude}
519 |           isLoading={isLoading}
520 |         />
521 |       )}
522 | 
523 |       {isPathSetupOpen && (
524 |         <PathSetupModal
525 |           isOpen={isPathSetupOpen}
526 |           onClose={() => setIsPathSetupOpen(false)}
527 |           pathInput={pathInput}
528 |           setPathInput={setPathInput}
529 |           onSave={saveClaudePath}
530 |           isLoading={isLoading}
531 |         />
532 |       )}
533 | 
534 |       <ConnectionDetailsModal
535 |         isOpen={isConnectionDetailsOpen}
536 |         onClose={() => {
537 |           setIsConnectionDetailsOpen(false);
538 |           setSelectedConnection(null);
539 |         }}
540 |         connection={selectedConnection}
541 |       />
542 |     </GlassWindow>
543 |   );
544 | };
545 | 
546 | export default MainApp;
547 | 
```

--------------------------------------------------------------------------------
/hana-mcp-ui/server/index.js:
--------------------------------------------------------------------------------

```javascript
  1 | import express from 'express';
  2 | import cors from 'cors';
  3 | import fs from 'fs-extra';
  4 | import { join, dirname } from 'path';
  5 | import { fileURLToPath } from 'url';
  6 | import { homedir } from 'os';
  7 | import { existsSync } from 'fs';
  8 | 
  9 | const __filename = fileURLToPath(import.meta.url);
 10 | const __dirname = dirname(__filename);
 11 | 
 12 | const app = express();
 13 | const PORT = process.env.PORT || 3001;
 14 | 
 15 | // Middleware
 16 | app.use(cors());
 17 | app.use(express.json());
 18 | 
 19 | // Storage paths - ensure they work regardless of where the server is started from
 20 | const UI_ROOT = dirname(__dirname); // This is the hana-mcp-ui directory
 21 | const DATA_DIR = join(UI_ROOT, 'data');
 22 | const SERVERS_FILE = join(DATA_DIR, 'hana-servers.json');
 23 | const CONFIG_FILE = join(DATA_DIR, 'config.json');
 24 | const BACKUPS_DIR = join(DATA_DIR, 'backups');
 25 | const BACKUP_HISTORY_FILE = join(DATA_DIR, 'backup-history.json');
 26 | 
 27 | // Ensure data directory exists
 28 | fs.ensureDirSync(DATA_DIR);
 29 | fs.ensureDirSync(BACKUPS_DIR);
 30 | 
 31 | // Default Claude config paths by OS
 32 | const getDefaultClaudeConfigPath = () => {
 33 |   const platform = process.platform;
 34 |   switch (platform) {
 35 |     case 'darwin':
 36 |       return join(homedir(), 'Library/Application Support/Claude/claude_desktop_config.json');
 37 |     case 'win32':
 38 |       return join(homedir(), 'AppData/Roaming/Claude/claude_desktop_config.json');
 39 |     default:
 40 |       return join(homedir(), '.config/claude/claude_desktop_config.json');
 41 |   }
 42 | };
 43 | 
 44 | // Helper functions
 45 | const loadServers = async () => {
 46 |   try {
 47 |     if (await fs.pathExists(SERVERS_FILE)) {
 48 |       return await fs.readJson(SERVERS_FILE);
 49 |     }
 50 |     return {};
 51 |   } catch (error) {
 52 |     console.error('Error loading servers:', error);
 53 |     return {};
 54 |   }
 55 | };
 56 | 
 57 | const saveServers = async (servers) => {
 58 |   try {
 59 |     await fs.writeJson(SERVERS_FILE, servers, { spaces: 2 });
 60 |   } catch (error) {
 61 |     console.error('Error saving servers:', error);
 62 |     throw error;
 63 |   }
 64 | };
 65 | 
 66 | const loadConfig = async () => {
 67 |   try {
 68 |     if (await fs.pathExists(CONFIG_FILE)) {
 69 |       return await fs.readJson(CONFIG_FILE);
 70 |     }
 71 |     return {};
 72 |   } catch (error) {
 73 |     console.error('Error loading config:', error);
 74 |     return {};
 75 |   }
 76 | };
 77 | 
 78 | const saveConfig = async (config) => {
 79 |   try {
 80 |     await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
 81 |   } catch (error) {
 82 |     console.error('Error saving config:', error);
 83 |     throw error;
 84 |   }
 85 | };
 86 | 
 87 | const loadClaudeConfig = async (configPath) => {
 88 |   try {
 89 |     if (await fs.pathExists(configPath)) {
 90 |       return await fs.readJson(configPath);
 91 |     }
 92 |     return { mcpServers: {} };
 93 |   } catch (error) {
 94 |     console.error('Error loading Claude config:', error);
 95 |     return { mcpServers: {} };
 96 |   }
 97 | };
 98 | 
 99 | const saveClaudeConfig = async (configPath, config, skipBackup = false) => {
100 |   try {
101 |     // Create backup before saving (unless explicitly skipped)
102 |     if (!skipBackup && await fs.pathExists(configPath)) {
103 |       await createBackup(configPath, 'Auto backup before save');
104 |     }
105 |     
106 |     await fs.ensureDir(dirname(configPath));
107 |     await fs.writeJson(configPath, config, { spaces: 2 });
108 |   } catch (error) {
109 |     console.error('Error saving Claude config:', error);
110 |     throw error;
111 |   }
112 | };
113 | 
114 | // Helper function to identify HANA MCP servers
115 | const isHanaMcpServer = (server) => {
116 |   // Must have the correct command
117 |   if (server.command !== 'hana-mcp-server') {
118 |     return false;
119 |   }
120 |   
121 |   // Must have HANA-specific environment variables for a complete HANA server
122 |   if (!server.env) {
123 |     return false;
124 |   }
125 |   
126 |   const hasHanaHost = server.env.HANA_HOST;
127 |   const hasHanaUser = server.env.HANA_USER;
128 |   const hasHanaSchema = server.env.HANA_SCHEMA;
129 |   
130 |   // Must have all core HANA environment variables
131 |   return hasHanaHost && hasHanaUser && hasHanaSchema;
132 | };
133 | 
134 | // Helper function to create composite server name
135 | const createCompositeServerName = (serverName, environment) => {
136 |   return `${serverName} - ${environment}`;
137 | };
138 | 
139 | // Helper function to parse composite server name
140 | const parseCompositeServerName = (compositeName) => {
141 |   const parts = compositeName.split(' - ');
142 |   if (parts.length >= 2) {
143 |     const environment = parts.pop(); // Last part is environment
144 |     const serverName = parts.join(' - '); // Everything else is server name
145 |     return { serverName, environment };
146 |   }
147 |   return { serverName: compositeName, environment: null };
148 | };
149 | 
150 | // Helper function to filter only HANA MCP servers from Claude config
151 | const filterHanaMcpServers = (mcpServers) => {
152 |   const hanaServers = {};
153 |   
154 |   for (const [name, server] of Object.entries(mcpServers || {})) {
155 |     if (isHanaMcpServer(server)) {
156 |       hanaServers[name] = server;
157 |     }
158 |   }
159 |   
160 |   return hanaServers;
161 | };
162 | 
163 | // Helper function to merge HANA servers while preserving non-HANA servers
164 | const mergeWithPreservation = (originalConfig, hanaServers) => {
165 |   const newConfig = { ...originalConfig };
166 |   
167 |   // Start with original mcpServers
168 |   newConfig.mcpServers = { ...originalConfig.mcpServers };
169 |   
170 |   // add new HANA servers
171 |   newConfig.mcpServers = {
172 |     ...newConfig.mcpServers,
173 |     ...hanaServers
174 |   };
175 |   
176 |   return newConfig;
177 | };
178 | 
179 | // Backup management functions
180 | const loadBackupHistory = async () => {
181 |   try {
182 |     if (await fs.pathExists(BACKUP_HISTORY_FILE)) {
183 |       return await fs.readJson(BACKUP_HISTORY_FILE);
184 |     }
185 |     return [];
186 |   } catch (error) {
187 |     console.error('Error loading backup history:', error);
188 |     return [];
189 |   }
190 | };
191 | 
192 | const saveBackupHistory = async (history) => {
193 |   try {
194 |     await fs.writeJson(BACKUP_HISTORY_FILE, history, { spaces: 2 });
195 |   } catch (error) {
196 |     console.error('Error saving backup history:', error);
197 |     throw error;
198 |   }
199 | };
200 | 
201 | const createBackup = async (configPath, reason = 'Manual backup') => {
202 |   try {
203 |     if (!await fs.pathExists(configPath)) {
204 |       throw new Error('Config file does not exist');
205 |     }
206 | 
207 |     const timestamp = new Date().toISOString();
208 |     const backupId = `backup_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
209 |     const backupFileName = `${backupId}.json`;
210 |     const backupFilePath = join(BACKUPS_DIR, backupFileName);
211 | 
212 |     // Read and backup the config
213 |     const config = await fs.readJson(configPath);
214 |     await fs.writeJson(backupFilePath, config, { spaces: 2 });
215 | 
216 |     // Create backup metadata
217 |     const backupEntry = {
218 |       id: backupId,
219 |       timestamp,
220 |       fileName: backupFileName,
221 |       reason,
222 |       size: JSON.stringify(config).length,
223 |       mcpServerCount: Object.keys(config.mcpServers || {}).length,
224 |       hanaServerCount: Object.keys(filterHanaMcpServers(config.mcpServers || {})).length
225 |     };
226 | 
227 |     // Update history
228 |     const history = await loadBackupHistory();
229 |     history.unshift(backupEntry); // Add to beginning (most recent first)
230 | 
231 |     // Keep only last 50 backups
232 |     if (history.length > 50) {
233 |       const oldBackups = history.splice(50);
234 |       // Delete old backup files
235 |       for (const oldBackup of oldBackups) {
236 |         const oldBackupPath = join(BACKUPS_DIR, oldBackup.fileName);
237 |         if (await fs.pathExists(oldBackupPath)) {
238 |           await fs.remove(oldBackupPath);
239 |         }
240 |       }
241 |     }
242 | 
243 |     await saveBackupHistory(history);
244 |     return backupEntry;
245 |   } catch (error) {
246 |     console.error('Error creating backup:', error);
247 |     throw error;
248 |   }
249 | };
250 | 
251 | const restoreBackup = async (backupId, configPath) => {
252 |   try {
253 |     const history = await loadBackupHistory();
254 |     const backup = history.find(b => b.id === backupId);
255 |     
256 |     if (!backup) {
257 |       throw new Error('Backup not found');
258 |     }
259 | 
260 |     const backupFilePath = join(BACKUPS_DIR, backup.fileName);
261 |     if (!await fs.pathExists(backupFilePath)) {
262 |       throw new Error('Backup file not found');
263 |     }
264 | 
265 |     // Create a backup of current state before restoring
266 |     await createBackup(configPath, `Before restoring to ${backup.timestamp}`);
267 | 
268 |     // Restore the backup
269 |     const backupConfig = await fs.readJson(backupFilePath);
270 |     await saveClaudeConfig(configPath, backupConfig, true); // Skip backup when restoring
271 | 
272 |     return backup;
273 |   } catch (error) {
274 |     console.error('Error restoring backup:', error);
275 |     throw error;
276 |   }
277 | };
278 | 
279 | // API Routes
280 | 
281 | // Get Claude Desktop config path
282 | app.get('/api/claude/config-path', async (req, res) => {
283 |   try {
284 |     const config = await loadConfig();
285 |     res.json({ 
286 |       configPath: config.claudeConfigPath || null,
287 |       defaultPath: getDefaultClaudeConfigPath()
288 |     });
289 |   } catch (error) {
290 |     res.status(500).json({ error: error.message });
291 |   }
292 | });
293 | 
294 | // Set Claude Desktop config path
295 | app.post('/api/claude/config-path', async (req, res) => {
296 |   try {
297 |     const { configPath } = req.body;
298 |     
299 |     if (!configPath) {
300 |       return res.status(400).json({ error: 'Config path is required' });
301 |     }
302 | 
303 |     // Validate path exists or can be created
304 |     const dir = dirname(configPath);
305 |     await fs.ensureDir(dir);
306 | 
307 |     const config = await loadConfig();
308 |     config.claudeConfigPath = configPath;
309 |     await saveConfig(config);
310 | 
311 |     res.json({ success: true, configPath });
312 |   } catch (error) {
313 |     res.status(500).json({ error: error.message });
314 |   }
315 | });
316 | 
317 | // Get all local HANA servers
318 | app.get('/api/hana-servers', async (req, res) => {
319 |   try {
320 |     const servers = await loadServers();
321 |     res.json(servers);
322 |   } catch (error) {
323 |     res.status(500).json({ error: error.message });
324 |   }
325 | });
326 | 
327 | // Create new HANA server
328 | app.post('/api/hana-servers', async (req, res) => {
329 |   try {
330 |     const serverConfig = req.body;
331 |     
332 |     if (!serverConfig.name) {
333 |       return res.status(400).json({ error: 'Server name is required' });
334 |     }
335 | 
336 |     const servers = await loadServers();
337 |     
338 |     if (servers[serverConfig.name]) {
339 |       return res.status(409).json({ error: 'Server with this name already exists' });
340 |     }
341 | 
342 |     // Add metadata
343 |     serverConfig.created = new Date().toISOString();
344 |     serverConfig.modified = new Date().toISOString();
345 |     serverConfig.version = '1.0.0';
346 | 
347 |     servers[serverConfig.name] = serverConfig;
348 |     await saveServers(servers);
349 | 
350 |     res.status(201).json(serverConfig);
351 |   } catch (error) {
352 |     res.status(500).json({ error: error.message });
353 |   }
354 | });
355 | 
356 | // Update HANA server
357 | app.put('/api/hana-servers/:name', async (req, res) => {
358 |   try {
359 |     const { name } = req.params;
360 |     const updatedConfig = req.body;
361 | 
362 |     const servers = await loadServers();
363 |     
364 |     if (!servers[name]) {
365 |       return res.status(404).json({ error: 'Server not found' });
366 |     }
367 | 
368 |     // Preserve creation date, update modified date
369 |     updatedConfig.created = servers[name].created;
370 |     updatedConfig.modified = new Date().toISOString();
371 |     
372 |     servers[name] = updatedConfig;
373 |     await saveServers(servers);
374 | 
375 |     res.json(updatedConfig);
376 |   } catch (error) {
377 |     res.status(500).json({ error: error.message });
378 |   }
379 | });
380 | 
381 | // Delete HANA server
382 | app.delete('/api/hana-servers/:name', async (req, res) => {
383 |   try {
384 |     const { name } = req.params;
385 |     const servers = await loadServers();
386 |     
387 |     if (!servers[name]) {
388 |       return res.status(404).json({ error: 'Server not found' });
389 |     }
390 | 
391 |     delete servers[name];
392 |     await saveServers(servers);
393 | 
394 |     res.json({ success: true });
395 |   } catch (error) {
396 |     res.status(500).json({ error: error.message });
397 |   }
398 | });
399 | 
400 | 
401 | 
402 | // Apply server to Claude Desktop
403 | app.post('/api/apply-to-claude', async (req, res) => {
404 |   try {
405 |     const { serverName, environment } = req.body;
406 |     
407 |     if (!serverName || !environment) {
408 |       return res.status(400).json({ error: 'Server name and environment are required' });
409 |     }
410 | 
411 |     const config = await loadConfig();
412 |     const claudeConfigPath = config.claudeConfigPath;
413 |     
414 |     if (!claudeConfigPath) {
415 |       return res.status(400).json({ error: 'Claude config path not set' });
416 |     }
417 | 
418 |     const servers = await loadServers();
419 |     const server = servers[serverName];
420 |     
421 |     if (!server) {
422 |       return res.status(404).json({ error: 'Server not found' });
423 |     }
424 | 
425 |     // Find environment with case-insensitive matching
426 |     let envConfig = server.environments?.[environment];
427 |     let actualEnvironmentName = environment;
428 |     
429 |     if (!envConfig) {
430 |       // Try case-insensitive matching
431 |       const envKeys = Object.keys(server.environments || {});
432 |       const matchingKey = envKeys.find(key => key.toLowerCase() === environment.toLowerCase());
433 |       
434 |       if (matchingKey) {
435 |         envConfig = server.environments[matchingKey];
436 |         actualEnvironmentName = matchingKey;
437 |       } else {
438 |         return res.status(404).json({ error: 'Environment not found' });
439 |       }
440 |     }
441 | 
442 |     const claudeConfig = await loadClaudeConfig(claudeConfigPath);
443 |     
444 |     // create a new HANA server
445 |     const newHanaServer = {
446 |       [serverName]: {
447 |         command: 'hana-mcp-server',
448 |         env: envConfig
449 |       }
450 |     };
451 |     
452 |     // Merge while preserving non-HANA servers
453 |     const updatedConfig = mergeWithPreservation(claudeConfig, newHanaServer);
454 | 
455 |     await saveClaudeConfig(claudeConfigPath, updatedConfig);
456 | 
457 |     res.json({ success: true, serverName, environment: actualEnvironmentName });
458 |   } catch (error) {
459 |     res.status(500).json({ error: error.message });
460 |   }
461 | });
462 | 
463 | // Remove server from Claude Desktop
464 | app.delete('/api/claude/:serverName', async (req, res) => {
465 |   try {
466 |     const { serverName } = req.params;
467 |     
468 |     const config = await loadConfig();
469 |     const claudeConfigPath = config.claudeConfigPath;
470 |     
471 |     if (!claudeConfigPath) {
472 |       return res.status(400).json({ error: 'Claude config path not set' });
473 |     }
474 | 
475 |     const claudeConfig = await loadClaudeConfig(claudeConfigPath);
476 |     
477 |     const serverToDelete = claudeConfig.mcpServers[serverName];
478 |     if (!serverToDelete) {
479 |       return res.status(404).json({ error: 'Server not found in Claude config' });
480 |     }
481 |     
482 |     // Only delete if it's a HANA MCP server
483 |     if (!isHanaMcpServer(serverToDelete)) {
484 |       return res.status(400).json({ error: 'Cannot delete non-HANA MCP server' });
485 |     }
486 | 
487 |     delete claudeConfig.mcpServers[serverName];
488 |     await saveClaudeConfig(claudeConfigPath, claudeConfig);
489 | 
490 |     res.json({ success: true });
491 |   } catch (error) {
492 |     res.status(500).json({ error: error.message });
493 |   }
494 | });
495 | 
496 | // Get Claude Desktop servers
497 | app.get('/api/claude', async (req, res) => {
498 |   try {
499 |     const config = await loadConfig();
500 |     const claudeConfigPath = config.claudeConfigPath;
501 |     
502 |     if (!claudeConfigPath) {
503 |       return res.json([]);
504 |     }
505 | 
506 |     const claudeConfig = await loadClaudeConfig(claudeConfigPath);
507 |     const claudeServers = [];
508 | 
509 |     // Filter only HANA MCP servers
510 |     const hanaServers = filterHanaMcpServers(claudeConfig.mcpServers);
511 | 
512 |     for (const [serverName, server] of Object.entries(hanaServers)) {
513 |       const serverData = {
514 |         name: serverName,
515 |         environment: server.env?.ENVIRONMENT || 'Development',
516 |         env: server.env || {}
517 |       };
518 |       claudeServers.push(serverData);
519 |     }
520 | 
521 |     res.json(claudeServers);
522 |   } catch (error) {
523 |     console.error('Error loading Claude servers:', error);
524 |     res.status(500).json({ error: error.message });
525 |   }
526 | });
527 | 
528 | // Get active environments
529 | app.get('/api/claude/active-environments', async (req, res) => {
530 |   try {
531 |     const config = await loadConfig();
532 |     const claudeConfigPath = config.claudeConfigPath;
533 |     
534 |     if (!claudeConfigPath) {
535 |       return res.json({});
536 |     }
537 | 
538 |     const claudeConfig = await loadClaudeConfig(claudeConfigPath);
539 |     const servers = await loadServers();
540 |     const activeEnvironments = {};
541 | 
542 |     // Filter only HANA MCP servers
543 |     const hanaServers = filterHanaMcpServers(claudeConfig.mcpServers);
544 | 
545 |     for (const [serverName, claudeServer] of Object.entries(hanaServers)) {
546 |       if (servers[serverName]) {
547 |         // Store the active environment for this server
548 |         activeEnvironments[serverName] = claudeServer.env?.ENVIRONMENT || 'Development';
549 |       }
550 |     }
551 | 
552 |     res.json(activeEnvironments);
553 |   } catch (error) {
554 |     res.status(500).json({ error: error.message });
555 |   }
556 | });
557 | 
558 | // Validate connection
559 | app.post('/api/validate-connection', async (req, res) => {
560 |   try {
561 |     const config = req.body;
562 |     
563 |     // Basic validation
564 |     const required = ['HANA_HOST', 'HANA_USER', 'HANA_PASSWORD', 'HANA_SCHEMA'];
565 |     for (const field of required) {
566 |       if (!config[field]) {
567 |         return res.status(400).json({ 
568 |           valid: false, 
569 |           error: `${field} is required` 
570 |         });
571 |       }
572 |     }
573 | 
574 |     // For now, just validate required fields
575 |     // In a real implementation, you could test the actual connection
576 |     res.json({ valid: true });
577 |   } catch (error) {
578 |     res.status(500).json({ valid: false, error: error.message });
579 |   }
580 | });
581 | 
582 | // Backup Management APIs
583 | 
584 | // Get backup history
585 | app.get('/api/claude/backups', async (req, res) => {
586 |   try {
587 |     const history = await loadBackupHistory();
588 |     res.json(history);
589 |   } catch (error) {
590 |     res.status(500).json({ error: error.message });
591 |   }
592 | });
593 | 
594 | // Create manual backup
595 | app.post('/api/claude/backups', async (req, res) => {
596 |   try {
597 |     const { reason = 'Manual backup' } = req.body;
598 |     
599 |     const config = await loadConfig();
600 |     const claudeConfigPath = config.claudeConfigPath;
601 |     
602 |     if (!claudeConfigPath) {
603 |       return res.status(400).json({ error: 'Claude config path not set' });
604 |     }
605 | 
606 |     const backup = await createBackup(claudeConfigPath, reason);
607 |     res.json(backup);
608 |   } catch (error) {
609 |     res.status(500).json({ error: error.message });
610 |   }
611 | });
612 | 
613 | // Restore backup
614 | app.post('/api/claude/backups/:backupId/restore', async (req, res) => {
615 |   try {
616 |     const { backupId } = req.params;
617 |     
618 |     const config = await loadConfig();
619 |     const claudeConfigPath = config.claudeConfigPath;
620 |     
621 |     if (!claudeConfigPath) {
622 |       return res.status(400).json({ error: 'Claude config path not set' });
623 |     }
624 | 
625 |     const backup = await restoreBackup(backupId, claudeConfigPath);
626 |     res.json({ success: true, backup });
627 |   } catch (error) {
628 |     res.status(500).json({ error: error.message });
629 |   }
630 | });
631 | 
632 | // Delete backup
633 | app.delete('/api/claude/backups/:backupId', async (req, res) => {
634 |   try {
635 |     const { backupId } = req.params;
636 |     
637 |     const history = await loadBackupHistory();
638 |     const backupIndex = history.findIndex(b => b.id === backupId);
639 |     
640 |     if (backupIndex === -1) {
641 |       return res.status(404).json({ error: 'Backup not found' });
642 |     }
643 | 
644 |     const backup = history[backupIndex];
645 |     const backupFilePath = join(BACKUPS_DIR, backup.fileName);
646 |     
647 |     // Remove from history
648 |     history.splice(backupIndex, 1);
649 |     await saveBackupHistory(history);
650 |     
651 |     // Delete backup file
652 |     if (await fs.pathExists(backupFilePath)) {
653 |       await fs.remove(backupFilePath);
654 |     }
655 | 
656 |     res.json({ success: true });
657 |   } catch (error) {
658 |     res.status(500).json({ error: error.message });
659 |   }
660 | });
661 | 
662 | // Get backup details
663 | app.get('/api/claude/backups/:backupId', async (req, res) => {
664 |   try {
665 |     const { backupId } = req.params;
666 |     
667 |     const history = await loadBackupHistory();
668 |     const backup = history.find(b => b.id === backupId);
669 |     
670 |     if (!backup) {
671 |       return res.status(404).json({ error: 'Backup not found' });
672 |     }
673 | 
674 |     const backupFilePath = join(BACKUPS_DIR, backup.fileName);
675 |     if (!await fs.pathExists(backupFilePath)) {
676 |       return res.status(404).json({ error: 'Backup file not found' });
677 |     }
678 | 
679 |     const backupConfig = await fs.readJson(backupFilePath);
680 |     res.json({
681 |       ...backup,
682 |       config: backupConfig
683 |     });
684 |   } catch (error) {
685 |     res.status(500).json({ error: error.message });
686 |   }
687 | });
688 | 
689 | // Health check
690 | app.get('/api/status', async (req, res) => {
691 |   try {
692 |     const config = await loadConfig();
693 |     const claudeConfigPath = config.claudeConfigPath;
694 |     let claudeConfigExists = false;
695 |     
696 |     if (claudeConfigPath) {
697 |       claudeConfigExists = await fs.pathExists(claudeConfigPath);
698 |     }
699 | 
700 |     res.json({
701 |       status: 'healthy',
702 |       version: '1.0.0',
703 |       claudeConfigPath,
704 |       claudeConfigExists,
705 |       timestamp: new Date().toISOString()
706 |     });
707 |   } catch (error) {
708 |     res.status(500).json({ error: error.message });
709 |   }
710 | });
711 | 
712 | app.listen(PORT, () => {
713 |   console.log(`🚀 HANA MCP UI Backend running on port ${PORT}`);
714 | });
```

--------------------------------------------------------------------------------
/hana-mcp-ui/src/components/ConfigurationModal.jsx:
--------------------------------------------------------------------------------

```javascript
  1 | import { motion } from 'framer-motion'
  2 | import { useState, useEffect } from 'react'
  3 | import { XMarkIcon, ServerIcon, PlusIcon, PencilIcon, ExclamationTriangleIcon, TrashIcon } from '@heroicons/react/24/outline'
  4 | import toast from 'react-hot-toast'
  5 | import { 
  6 |   detectDatabaseType, 
  7 |   shouldShowMDCFields,
  8 |   validateForDatabaseType 
  9 | } from '../utils/databaseTypes'
 10 | 
 11 | const ConfigurationModal = ({
 12 |   isOpen,
 13 |   onClose,
 14 |   server,
 15 |   formData,
 16 |   activeTab,
 17 |   setActiveTab,
 18 |   onFormChange,
 19 |   onServerInfoChange,
 20 |   onSave,
 21 |   isLoading
 22 | }) => {
 23 |   const [availableEnvironments, setAvailableEnvironments] = useState([
 24 |     { id: 'development', name: 'Development', color: 'blue' },
 25 |     { id: 'staging', name: 'Staging', color: 'amber' },
 26 |     { id: 'production', name: 'Production', color: 'green' }
 27 |   ])
 28 |   const [selectedEnvironments, setSelectedEnvironments] = useState(new Set())
 29 |   const [showEnvironmentSelector, setShowEnvironmentSelector] = useState(false)
 30 |   const [validationErrors, setValidationErrors] = useState({})
 31 |   const [showDeleteConfirm, setShowDeleteConfirm] = useState(null)
 32 | 
 33 |   useEffect(() => {
 34 |     // Load saved environments
 35 |     const savedEnvironments = localStorage.getItem('hana-environments')
 36 |     if (savedEnvironments) {
 37 |       setAvailableEnvironments(JSON.parse(savedEnvironments))
 38 |     }
 39 |     
 40 |     // Set selected environments based on formData.environments
 41 |     if (formData && formData.environments) {
 42 |       const envKeys = Object.keys(formData.environments)
 43 |       setSelectedEnvironments(new Set(envKeys))
 44 |     } else {
 45 |       // Clear selected environments for new server
 46 |       setSelectedEnvironments(new Set())
 47 |     }
 48 |   }, [formData, isOpen])
 49 | 
 50 |   useEffect(() => {
 51 |     if (!isOpen) return
 52 |     const onKeyDown = (e) => {
 53 |       if (e.key === 'Escape') {
 54 |         onClose()
 55 |       }
 56 |     }
 57 |     window.addEventListener('keydown', onKeyDown)
 58 |     return () => window.removeEventListener('keydown', onKeyDown)
 59 |   }, [isOpen, onClose])
 60 | 
 61 |   // validation function with database type support
 62 |   const validateEnvironment = (envId, envData) => {
 63 |     const detectedType = detectDatabaseType(envData)
 64 |     const manualType = envData.HANA_CONNECTION_TYPE || 'auto'
 65 |     const dbType = manualType === 'auto' ? detectedType : manualType
 66 |     
 67 |     const validation = validateForDatabaseType(envData, dbType)
 68 |     return validation.errors
 69 |   }
 70 | 
 71 |   // Validate all environments
 72 |   const validateAllEnvironments = () => {
 73 |     const allErrors = {}
 74 |     
 75 |     if (formData.environments) {
 76 |       Object.keys(formData.environments).forEach(envId => {
 77 |         const envErrors = validateEnvironment(envId, formData.environments[envId])
 78 |         if (Object.keys(envErrors).length > 0) {
 79 |           allErrors[envId] = envErrors
 80 |         }
 81 |       })
 82 |     }
 83 |     
 84 |     setValidationErrors(allErrors)
 85 |     return Object.keys(allErrors).length === 0
 86 |   }
 87 | 
 88 |   // Check if current environment has validation errors
 89 |   const getCurrentEnvironmentErrors = () => {
 90 |     if (!activeTab || !validationErrors[activeTab]) return {}
 91 |     return validationErrors[activeTab]
 92 |   }
 93 | 
 94 |   // Handle form change with validation
 95 |   const handleFormChange = (environment, field, value) => {
 96 |     onFormChange(environment, field, value)
 97 |     
 98 |     // Clear validation error for this field if it exists
 99 |     if (validationErrors[environment] && validationErrors[environment][field]) {
100 |       const newErrors = { ...validationErrors }
101 |       delete newErrors[environment][field]
102 |       if (Object.keys(newErrors[environment]).length === 0) {
103 |         delete newErrors[environment]
104 |       }
105 |       setValidationErrors(newErrors)
106 |     }
107 |   }
108 | 
109 |   const addEnvironment = (envId) => {
110 | 
111 |     
112 |     const newSelected = new Set(selectedEnvironments)
113 |     newSelected.add(envId)
114 |     setSelectedEnvironments(newSelected)
115 |     
116 |     // Initialize the environment in formData if it doesn't exist
117 |     if (!formData.environments || !formData.environments[envId]) {
118 |       onFormChange(envId, 'ENVIRONMENT', envId.toUpperCase())
119 |     }
120 |     
121 |     // Make the newly added environment active
122 |     setActiveTab(envId)
123 |     setShowEnvironmentSelector(false)
124 |   }
125 | 
126 |   const removeEnvironment = (envId) => {
127 |     
128 |     const newSelected = new Set(selectedEnvironments)
129 |     newSelected.delete(envId)
130 |     
131 |     // Remove the environment from formData as well
132 |     if (formData.environments && formData.environments[envId]) {
133 |       const newFormData = { ...formData }
134 |       delete newFormData.environments[envId]
135 |       // Update the parent's formData
136 |       onServerInfoChange('environments', newFormData.environments)
137 |     }
138 |     
139 |     // Clear validation errors for this environment
140 |     if (validationErrors[envId]) {
141 |       const newErrors = { ...validationErrors }
142 |       delete newErrors[envId]
143 |       setValidationErrors(newErrors)
144 |     }
145 |     
146 |     // If we removed the active tab, switch to the first available
147 |     if (activeTab === envId) {
148 |       const remaining = Array.from(newSelected)
149 |       setActiveTab(remaining.length > 0 ? remaining[0] : null)
150 |     }
151 |     
152 |     setSelectedEnvironments(newSelected)
153 |     setShowDeleteConfirm(null)
154 |     
155 | 
156 |   }
157 | 
158 |   const getAvailableEnvironmentsToAdd = () => {
159 |     const available = availableEnvironments.filter(env => !selectedEnvironments.has(env.id))
160 |     return available
161 |   }
162 | 
163 |   // Handle save with validation
164 |   const handleSave = () => {
165 |     if (!formData.name.trim()) {
166 |       toast.error('Server name is required')
167 |       return
168 |     }
169 | 
170 |     // Validate all environments
171 |     if (!validateAllEnvironments()) {
172 |       toast.error('Please fill in all required fields for selected environments')
173 |       return
174 |     }
175 | 
176 |     onSave()
177 |   }
178 | 
179 |   if (!isOpen) return null
180 | 
181 |   return (
182 |     <motion.div
183 |       className='fixed inset-0 bg-gray-900/20 backdrop-blur-sm z-50 flex items-center justify-center p-4'
184 |       initial={{ opacity: 0 }}
185 |       animate={{ opacity: 1 }}
186 |       exit={{ opacity: 0 }}
187 |       onClick={onClose}
188 |     >
189 |       <motion.div
190 |         className='bg-white rounded-2xl shadow-xl max-w-5xl w-full max-h-[90vh] overflow-hidden border border-gray-200 flex flex-col'
191 |         initial={{ scale: 0.9, opacity: 0, y: 20 }}
192 |         animate={{ scale: 1, opacity: 1, y: 0 }}
193 |         exit={{ scale: 0.9, opacity: 0, y: 20 }}
194 |         transition={{ type: 'spring', stiffness: 300, damping: 25 }}
195 |         onClick={(e) => e.stopPropagation()}
196 |       >
197 |         {/* Fixed Header */}
198 |         <div className='sticky top-0 z-10 px-8 py-6 border-b border-gray-100 bg-white rounded-t-2xl'>
199 |           <div className='flex items-center justify-between'>
200 |             <div className='flex items-center gap-4'>
201 |               <div className='p-3 bg-gray-100 rounded-xl'>
202 |                 {server ? <PencilIcon className='w-5 h-5 text-gray-600' /> : <PlusIcon className='w-5 h-5 text-gray-600' />}
203 |               </div>
204 |               <div>
205 |                 <h2 className='text-2xl font-bold text-gray-900 leading-tight'>
206 |                   {server ? 'Edit HANA Server' : 'Add HANA Server'}
207 |                 </h2>
208 |                 <p className='text-base text-gray-600 mt-2 font-medium'>
209 |                   {server ? 'Update database connection settings' : 'Configure a new database connection'}
210 |                 </p>
211 |               </div>
212 |             </div>
213 |             <button
214 |               onClick={onClose}
215 |               className='p-3 rounded-xl text-gray-400 hover:text-gray-600 hover:bg-gray-50 transition-colors'
216 |             >
217 |               <XMarkIcon className='w-5 h-5' />
218 |             </button>
219 |           </div>
220 |         </div>
221 | 
222 |         {/* Scrollable Body */}
223 |         <div className='flex-1 overflow-y-auto p-8'>
224 |           {/* Server Info */}
225 |           <div className='mb-8'>
226 |             <h3 className='text-xl font-bold text-gray-900 mb-6 flex items-center gap-3'>
227 |               <ServerIcon className='w-5 h-5 text-gray-600' />
228 |               Server Information
229 |             </h3>
230 |             <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
231 |               <div>
232 |                 <label className='block text-base font-semibold text-gray-800 mb-3'>
233 |                   Server Name <span className='text-red-500'>*</span>
234 |                 </label>
235 |                 <input
236 |                   type='text'
237 |                   value={formData.name}
238 |                   onChange={(e) => onServerInfoChange('name', e.target.value)}
239 |                   placeholder='e.g. Production HANA'
240 |                   disabled={!!server}
241 |                   className={`w-full px-4 py-3 border border-gray-300 rounded-xl text-base focus:outline-none focus:ring-2 focus:ring-[#86a0ff] focus:border-[#86a0ff] transition-colors ${
242 |                     server 
243 |                       ? 'bg-gray-100 text-gray-600 cursor-not-allowed' 
244 |                       : 'text-gray-400'
245 |                   }`}
246 |                 />
247 |           
248 |               </div>
249 |               <div>
250 |                 <label className='block text-base font-semibold text-gray-800 mb-3'>
251 |                   Description
252 |                 </label>
253 |                 <input
254 |                   type='text'
255 |                   value={formData.description}
256 |                   onChange={(e) => onServerInfoChange('description', e.target.value)}
257 |                   placeholder='Optional description'
258 |                   className='w-full px-4 py-3 border border-gray-300 rounded-xl text-gray-900 placeholder-gray-400 text-base focus:outline-none focus:ring-2 focus:ring-[#86a0ff] focus:border-[#86a0ff] transition-colors'
259 |                 />
260 |               </div>
261 |             </div>
262 |           </div>
263 | 
264 |           {/* Environment Configuration */}
265 |           <div className='mb-10'>
266 |             <div className='flex items-center justify-between mb-6'>
267 |               <h3 className='text-xl font-bold text-gray-900'>Environment Configuration</h3>
268 |               <span className='text-sm px-4 py-2 bg-gray-100 text-gray-600 rounded-lg font-semibold'>Optional</span>
269 |             </div>
270 |             
271 |             <p className='text-base text-gray-600 mb-6 font-medium'>
272 |               Configure for specific environments:
273 |             </p>
274 |             {/* Configured Environments */}
275 |             {selectedEnvironments.size > 0 && (
276 |               <div className='space-y-3 mb-6'>
277 |                 {Array.from(selectedEnvironments).map((envId) => {
278 |                   const env = availableEnvironments.find(e => e.id === envId)
279 |                   const hasErrors = validationErrors[envId] && Object.keys(validationErrors[envId]).length > 0
280 |                   const isDeleteConfirm = showDeleteConfirm === envId
281 |                   
282 |                   return (
283 |                     <div
284 |                       key={envId}
285 |                       className={`flex items-center justify-between p-4 border rounded-xl group transition-all ${
286 |                         hasErrors 
287 |                           ? 'border-red-200 bg-red-50' 
288 |                           : 'border-gray-200 bg-gray-50'
289 |                       }`}
290 |                     >
291 |                       <div className='flex items-center gap-4'>
292 |                         <div className={`w-4 h-4 rounded-full bg-${env?.color}-500`}></div>
293 |                         <div className='flex items-center gap-3'>
294 |                           <h4 className='text-base font-semibold text-gray-900'>{env?.name}</h4>
295 |                           {hasErrors && (
296 |                             <div className='flex items-center gap-1 text-red-600'>
297 |                               <ExclamationTriangleIcon className='w-4 h-4' />
298 |                               <span className='text-sm font-medium'>
299 |                                 {Object.keys(validationErrors[envId]).length} required field(s) missing
300 |                               </span>
301 |                             </div>
302 |                           )}
303 |                         </div>
304 |                       </div>
305 |                       <div className='flex items-center gap-2'>
306 |                         {isDeleteConfirm ? (
307 |                           <>
308 |                             <button
309 |                               type='button'
310 |                               onClick={() => setShowDeleteConfirm(null)}
311 |                               className='px-3 py-1 text-sm font-medium text-gray-600 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors'
312 |                             >
313 |                               Cancel
314 |                             </button>
315 |                             <button
316 |                               type='button'
317 |                               onClick={() => removeEnvironment(envId)}
318 |                               className='px-3 py-1 text-sm font-medium text-white bg-red-600 border border-red-600 rounded-lg hover:bg-red-700 transition-colors'
319 |                             >
320 |                               Delete
321 |                             </button>
322 |                           </>
323 |                         ) : (
324 |                           <button
325 |                             type='button'
326 |                             onClick={() => setShowDeleteConfirm(envId)}
327 |                             className='p-2 text-gray-400 hover:text-red-500 transition-colors opacity-0 group-hover:opacity-100'
328 |                             title='Delete environment configuration'
329 |                           >
330 |                             <TrashIcon className='w-4 h-4' />
331 |                           </button>
332 |                         )}
333 |                       </div>
334 |                     </div>
335 |                   )
336 |                 })}
337 |               </div>
338 |             )}
339 | 
340 |             {/* Add Environment Button */}
341 |             {getAvailableEnvironmentsToAdd().length > 0 && (
342 |               <button
343 |                 type='button'
344 |                 onClick={() => setShowEnvironmentSelector(true)}
345 |                 className='w-full p-6 border-2 border-dashed border-gray-300 rounded-xl hover:border-[#86a0ff] hover:bg-[#86a0ff]/5 transition-colors flex items-center justify-center gap-4 text-gray-600 hover:text-[#86a0ff] group'
346 |               >
347 |                 <PlusIcon className='w-5 h-5 group-hover:scale-110 transition-transform' />
348 |                 <span className='text-lg font-bold'>Add Environment</span>
349 |               </button>
350 |             )}
351 | 
352 |             {/* Environment Tabs - Only show if environments are selected */}
353 |             {selectedEnvironments.size > 0 && (
354 |               <>
355 |                 <div className='mt-8 mb-6'>
356 |                   <div className='flex border-b border-gray-200 overflow-x-auto'>
357 |                     {Array.from(selectedEnvironments).map((envId) => {
358 |                       const env = availableEnvironments.find(e => e.id === envId)
359 |                       const hasErrors = validationErrors[envId] && Object.keys(validationErrors[envId]).length > 0
360 |                       
361 |                       return (
362 |                         <button
363 |                           key={envId}
364 |                           type='button'
365 |                           onClick={() => setActiveTab(envId)}
366 |                           className={`px-6 py-3 font-semibold text-sm transition-colors border-b-2 whitespace-nowrap flex items-center gap-2 ${
367 |                             activeTab === envId
368 |                               ? 'text-blue-600 border-blue-600'
369 |                               : 'text-gray-500 border-transparent hover:text-gray-700'
370 |                           }`}
371 |                         >
372 |                           <div className='flex items-center gap-3'>
373 |                             <div className={`w-3 h-3 rounded-full bg-${env?.color}-500`}></div>
374 |                             {env?.name || envId}
375 |                           </div>
376 |                           {hasErrors && (
377 |                             <div className='w-2 h-2 rounded-full bg-red-500'></div>
378 |                           )}
379 |                         </button>
380 |                       )
381 |                     })}
382 |                   </div>
383 |                 </div>
384 | 
385 |                 {/* Tab Content - Only show if an environment is selected */}
386 |                 {activeTab && selectedEnvironments.has(activeTab) && (
387 |                   <motion.div
388 |                     key={activeTab}
389 |                     initial={{ opacity: 0, y: 10 }}
390 |                     animate={{ opacity: 1, y: 0 }}
391 |                     transition={{ duration: 0.2 }}
392 |                   >
393 |                     <EnvironmentForm
394 |                       environment={activeTab}
395 |                       data={formData.environments[activeTab] || {}}
396 |                       onChange={handleFormChange}
397 |                       errors={getCurrentEnvironmentErrors()}
398 |                     />
399 |                   </motion.div>
400 |                 )}
401 |               </>
402 |             )}
403 | 
404 |             {/* No environments selected - encouraging message */}
405 |             {selectedEnvironments.size === 0 && (
406 |               <div className='text-center py-12 bg-gray-50 border border-gray-200 rounded-xl'>
407 |                 <div className='w-12 h-12 mx-auto mb-4 bg-gray-100 rounded-full flex items-center justify-center'>
408 |                   <svg className='w-6 h-6 text-gray-600' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
409 |                     <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z' />
410 |                   </svg>
411 |                 </div>
412 |                 <p className='text-lg font-bold text-gray-800'>No environments configured</p>
413 |                 <p className='text-base text-gray-600 mt-2'>Add environment-specific settings or skip for a basic connection</p>
414 |               </div>
415 |             )}
416 |           </div>
417 |         </div>
418 | 
419 |         {/* Fixed Footer */}
420 |         <div className='sticky bottom-0 z-10 px-8 py-6 border-t border-gray-100 bg-gray-50 rounded-b-2xl flex justify-end gap-4'>
421 |           <button
422 |             onClick={onClose}
423 |             disabled={isLoading}
424 |             className='px-8 py-3 text-base font-semibold text-gray-700 bg-white border border-gray-300 rounded-xl hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-gray-400 disabled:opacity-50 transition-colors shadow-sm hover:shadow-md'
425 |           >
426 |             Cancel
427 |           </button>
428 |           <button
429 |             onClick={handleSave}
430 |             disabled={isLoading}
431 |             className='px-10 py-3 text-base font-semibold text-white bg-[#86a0ff] border border-transparent rounded-xl hover:bg-[#7990e6] focus:outline-none focus:ring-2 focus:ring-[#86a0ff] disabled:opacity-50 min-w-[160px] transition-colors shadow-md hover:shadow-lg'
432 |           >
433 |             {isLoading ? 'Saving...' : (server ? 'Update Server' : 'Add Server')}
434 |           </button>
435 |         </div>
436 |       </motion.div>
437 | 
438 |       {/* Environment Selector Modal */}
439 |       {showEnvironmentSelector && (
440 |         <div 
441 |           className='absolute inset-0 bg-black/20 flex items-center justify-center p-4 z-10'
442 |           onClick={() => setShowEnvironmentSelector(false)}
443 |         >
444 |           <motion.div
445 |             initial={{ opacity: 0, scale: 0.98 }}
446 |             animate={{ opacity: 1, scale: 1 }}
447 |             transition={{ duration: 0.15 }}
448 |             className='bg-white rounded-xl shadow-xl max-w-md w-full border border-gray-200'
449 |             onClick={(e) => e.stopPropagation()}
450 |           >
451 |             <div className='p-6 border-b border-gray-200'>
452 |               <div className='flex items-center justify-between'>
453 |                 <h3 className='text-xl font-bold text-gray-900'>Add Environment</h3>
454 |                 <button
455 |                   onClick={() => setShowEnvironmentSelector(false)}
456 |                   className='p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-50 rounded-lg transition-colors'
457 |                 >
458 |                   <XMarkIcon className='w-5 h-5' />
459 |                 </button>
460 |               </div>
461 |               <p className='text-base text-gray-600 mt-2 font-medium'>Select an environment for which you want to configure the connection</p>
462 |             </div>
463 |             
464 |             <div className='p-6 max-h-80 overflow-y-auto'>
465 |               <div className='space-y-3'>
466 |                 {getAvailableEnvironmentsToAdd().map((env) => (
467 |                   <button
468 |                     key={env.id}
469 |                     type='button'
470 |                     onClick={(e) => {
471 |                       e.preventDefault()
472 |                       e.stopPropagation()
473 |                       addEnvironment(env.id)
474 |                     }}
475 |                     className='w-full p-5 text-left border border-gray-200 rounded-xl hover:bg-gray-50 hover:border-[#86a0ff] hover:shadow-sm transition-all duration-200 group'
476 |                   >
477 |                     <div className='flex items-center gap-4'>
478 |                       <div className={`w-5 h-5 rounded-full bg-${env.color}-500 shadow-sm`}></div>
479 |                       <div className='flex-1'>
480 |                         <h4 className='text-lg font-bold text-gray-900 group-hover:text-[#86a0ff] transition-colors'>{env.name}</h4>
481 |                       </div>
482 |                       <svg className='w-5 h-5 text-gray-400 group-hover:text-[#86a0ff] transition-colors' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
483 |                         <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M12 4v16m8-8H4' />
484 |                       </svg>
485 |                     </div>
486 |                   </button>
487 |                 ))}
488 |                 
489 |                 {getAvailableEnvironmentsToAdd().length === 0 && (
490 |                   <div className='text-center py-8 text-gray-500'>
491 |                     <svg className='w-12 h-12 mx-auto mb-4 text-gray-400' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
492 |                       <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z' />
493 |                     </svg>
494 |                     <p className='text-lg font-semibold text-gray-600'>All available environments are already configured</p>
495 |                   </div>
496 |                 )}
497 |               </div>
498 |             </div>
499 |           </motion.div>
500 |         </div>
501 |       )}
502 |     </motion.div>
503 |   )
504 | }
505 | 
506 | // Toggle Switch Component
507 | const ToggleSwitch = ({ label, value, onChange, description }) => {
508 |   const isEnabled = value === 'true' || value === true
509 |   
510 |   return (
511 |     <div className="flex items-center justify-between">
512 |       <div className="flex-1">
513 |         <label className="block text-base font-semibold text-gray-700">{label}</label>
514 |         {description && <p className="text-sm text-gray-500 mt-1 font-medium">{description}</p>}
515 |       </div>
516 |       <button
517 |         type="button"
518 |         onClick={() => onChange(!isEnabled ? 'true' : 'false')}
519 |         className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-[#86a0ff] focus:ring-offset-2 ${
520 |           isEnabled ? 'bg-[#86a0ff]' : 'bg-gray-200'
521 |         }`}
522 |       >
523 |         <span
524 |           className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
525 |             isEnabled ? 'translate-x-6' : 'translate-x-1'
526 |           }`}
527 |         />
528 |       </button>
529 |     </div>
530 |   )
531 | }
532 | 
533 | // Environment Form Component
534 | const EnvironmentForm = ({ environment, data = {}, onChange, errors = {} }) => {
535 |   // Get environment display name
536 |   const getEnvironmentDisplayName = (envId) => {
537 |     const envMap = {
538 |       'development': 'DEVELOPMENT',
539 |       'staging': 'STAGING', 
540 |       'production': 'PRODUCTION',
541 |       'testing': 'TESTING',
542 |       'qa': 'QA'
543 |     }
544 |     return envMap[envId] || envId.toUpperCase()
545 |   }
546 | 
547 |   // Ensure ENVIRONMENT parameter is automatically set
548 |   const environmentValue = data.ENVIRONMENT || getEnvironmentDisplayName(environment)
549 |   
550 |   // Database type state - default to single_container if not specified
551 |   const [manualType, setManualType] = useState(data.HANA_CONNECTION_TYPE || 'single_container')
552 |   
553 |   // Note: We no longer use auto-detect in the UI, users must explicitly select database type
554 |   
555 |   // Database type options for radio buttons
556 |   const databaseTypeOptions = [
557 |     { 
558 |       label: 'Single-Container Database', 
559 |       value: 'single_container',
560 |       description: 'Basic HANA database - HOST:PORT connection',
561 |       required: ['HOST', 'PORT', 'USER', 'PASSWORD', 'SCHEMA']
562 |     },
563 |     { 
564 |       label: 'MDC System Database', 
565 |       value: 'mdc_system',
566 |       description: 'Multi-tenant system database - HOST:PORT;INSTANCE',
567 |       required: ['HOST', 'PORT', 'USER', 'PASSWORD', 'INSTANCE_NUMBER']
568 |     },
569 |     { 
570 |       label: 'MDC Tenant Database', 
571 |       value: 'mdc_tenant',
572 |       description: 'Multi-tenant tenant database - HOST:PORT + DATABASE_NAME',
573 |       required: ['HOST', 'PORT', 'USER', 'PASSWORD', 'INSTANCE_NUMBER', 'DATABASE_NAME']
574 |     }
575 |   ]
576 |   
577 |   // Auto-set default values when component renders
578 |   useEffect(() => {
579 |     const defaults = {
580 |       ENVIRONMENT: environmentValue,
581 |       HANA_PORT: '443',
582 |       HANA_SSL: 'true',
583 |       HANA_ENCRYPT: 'true',
584 |       HANA_VALIDATE_CERT: 'true',
585 |       HANA_CONNECTION_TYPE: 'auto',
586 |       LOG_LEVEL: 'info',
587 |       ENABLE_FILE_LOGGING: 'true',
588 |       ENABLE_CONSOLE_LOGGING: 'false'
589 |     }
590 | 
591 |     // Set any missing default values
592 |     Object.entries(defaults).forEach(([key, defaultValue]) => {
593 |       if (!data[key]) {
594 |         onChange(environment, key, defaultValue)
595 |       }
596 |     })
597 |   }, [environment, data, environmentValue, onChange])
598 |   
599 |   // Handle connection type change
600 |   const handleConnectionTypeChange = (e) => {
601 |     const newType = e.target.value
602 |     setManualType(newType)
603 |     onChange(environment, 'HANA_CONNECTION_TYPE', newType)
604 |   }
605 | 
606 |   // Helper function to render input field with error handling
607 |   const renderInputField = (field, label, type = 'text', placeholder = '', required = false) => {
608 |     const hasError = errors[field]
609 |     
610 |     return (
611 |       <div>
612 |         <label className={`block text-base font-semibold mb-3 ${
613 |           hasError ? 'text-red-700' : 'text-gray-800'
614 |         }`}>
615 |           {label} {required && <span className='text-red-500'>*</span>}
616 |         </label>
617 |         <input
618 |           type={type}
619 |           value={data[field] || ''}
620 |           onChange={(e) => onChange(environment, field, e.target.value)}
621 |           placeholder={placeholder}
622 |           className={`w-full px-4 py-3 border rounded-xl text-gray-900 placeholder-gray-400 text-base focus:outline-none focus:ring-2 transition-colors ${
623 |             hasError 
624 |               ? 'border-red-300 focus:ring-red-500 focus:border-red-500 bg-red-50' 
625 |               : 'border-gray-300 focus:ring-[#86a0ff] focus:border-[#86a0ff]'
626 |           }`}
627 |         />
628 |         {hasError && (
629 |           <p className='text-sm text-red-600 mt-1 font-medium flex items-center gap-1'>
630 |             <ExclamationTriangleIcon className='w-3 h-3' />
631 |             {hasError}
632 |           </p>
633 |         )}
634 |       </div>
635 |     )
636 |   }
637 | 
638 |   return (
639 |     <div className='space-y-8'>
640 | 
641 |       {/* Connection Settings */}
642 |       <div>
643 |         <h4 className='text-lg font-bold text-gray-900 mb-6'>Connection Settings</h4>
644 |         <div className='grid grid-cols-1 lg:grid-cols-3 gap-6'>
645 |           {renderInputField('HANA_HOST', 'Host', 'text', 'your-hana-host.com', true)}
646 |           {renderInputField('HANA_PORT', 'Port', 'number', '443')}
647 |           {renderInputField('HANA_SCHEMA', 'Schema', 'text', 'your-schema', true)}
648 |         </div>
649 |         
650 |         {/* Database Type Selection */}
651 |         <div className='mt-6'>
652 |           <label className='block text-base font-semibold mb-3 text-gray-800'>
653 |             Database Type
654 |           </label>
655 |           <div className='space-y-3'>
656 |             {databaseTypeOptions.map((option) => (
657 |               <label
658 |                 key={option.value}
659 |                 className={`
660 |                   flex items-start gap-4 p-4 rounded-xl border-2 cursor-pointer transition-all duration-200
661 |                   ${manualType === option.value 
662 |                     ? 'border-[#86a0ff] bg-blue-50 shadow-md' 
663 |                     : 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'
664 |                   }
665 |                 `}
666 |               >
667 |                 <input
668 |                   type="radio"
669 |                   name="databaseType"
670 |                   value={option.value}
671 |                   checked={manualType === option.value}
672 |                   onChange={handleConnectionTypeChange}
673 |                   className="mt-1 w-4 h-4 text-[#86a0ff] border-gray-300 focus:ring-[#86a0ff] focus:ring-2"
674 |                 />
675 |                 <div className="flex-1">
676 |                   <div className="mb-1">
677 |                     <span className="font-semibold text-gray-900">{option.label}</span>
678 |                   </div>
679 |                   <p className="text-sm text-gray-600 mb-2">{option.description}</p>
680 |                   <div>
681 |                     <p className="text-xs text-gray-500 mb-1">Required fields:</p>
682 |                     <div className="flex flex-wrap gap-1">
683 |                       {option.required.map((field) => (
684 |                         <span 
685 |                           key={field}
686 |                           className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-700"
687 |                         >
688 |                           {field.replace('HANA_', '')}
689 |                         </span>
690 |                       ))}
691 |                     </div>
692 |                   </div>
693 |                 </div>
694 |               </label>
695 |             ))}
696 |           </div>
697 |         </div>
698 |         
699 |         {/* MDC-specific fields - show conditionally */}
700 |         {shouldShowMDCFields(detectDatabaseType(data), manualType) && (
701 |           <div className='mt-6'>
702 |             <h5 className='text-base font-semibold text-gray-800 mb-2'>MDC Configuration</h5>
703 |             <p className='text-sm text-gray-600 mb-4'>
704 |               {manualType === 'mdc_system' 
705 |                 ? 'MDC System Database requires instance number for connection string format: HOST:PORT;INSTANCE'
706 |                 : 'MDC Tenant Database requires both instance number and database name for connection'
707 |               }
708 |             </p>
709 |             <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
710 |               {renderInputField('HANA_INSTANCE_NUMBER', 'Instance Number', 'number', '10', true)}
711 |               {manualType === 'mdc_tenant' && renderInputField('HANA_DATABASE_NAME', 'Database Name', 'text', 'HQQ', true)}
712 |             </div>
713 |           </div>
714 |         )}
715 |         
716 |         <div className='grid grid-cols-1 md:grid-cols-2 gap-6 mt-6'>
717 |           {renderInputField('HANA_USER', 'Username', 'text', 'your-username', true)}
718 |           {renderInputField('HANA_PASSWORD', 'Password', 'password', '••••••••', true)}
719 |         </div>
720 |       </div>
721 | 
722 |       {/* Security & SSL Configuration */}
723 |       <div>
724 |         <h4 className='text-lg font-bold text-gray-900 mb-6'>Security & SSL</h4>
725 |         <div className='bg-gray-50 rounded-xl p-6 space-y-6'>
726 |           <ToggleSwitch
727 |             label="Enable SSL"
728 |             description="Use SSL/TLS for secure connection"
729 |             value={data.HANA_SSL || 'true'}
730 |             onChange={(value) => onChange(environment, 'HANA_SSL', value)}
731 |           />
732 |           <ToggleSwitch
733 |             label="Encrypt Connection"
734 |             description="Encrypt data transmission"
735 |             value={data.HANA_ENCRYPT || 'true'}
736 |             onChange={(value) => onChange(environment, 'HANA_ENCRYPT', value)}
737 |           />
738 |           <ToggleSwitch
739 |             label="Validate Certificate"
740 |             description="Verify SSL certificate authenticity"
741 |             value={data.HANA_VALIDATE_CERT || 'false'}
742 |             onChange={(value) => onChange(environment, 'HANA_VALIDATE_CERT', value)}
743 |           />
744 |         </div>
745 |       </div>
746 | 
747 |       {/* Logging Configuration */}
748 |       <div>
749 |         <h4 className='text-lg font-bold text-gray-900 mb-6'>Logging Configuration</h4>
750 | 
751 |         <div className='grid grid-cols-1 md:grid-cols-3 gap-6'>
752 |           <div>
753 |             <label className='block text-base font-semibold text-gray-800 mb-3'>Log Level</label>
754 |             <select
755 |               value={data.LOG_LEVEL || 'info'}
756 |               onChange={(e) => onChange(environment, 'LOG_LEVEL', e.target.value)}
757 |               className='w-full px-4 py-3 border border-gray-300 rounded-xl text-gray-900 text-base focus:outline-none focus:ring-2 focus:ring-[#86a0ff] focus:border-[#86a0ff] transition-colors'
758 |             >
759 |               <option value='error'>Error</option>
760 |               <option value='warn'>Warning</option>
761 |               <option value='info'>Info</option>
762 |               <option value='debug'>Debug</option>
763 |             </select>
764 |           </div>
765 |           <div className='flex items-end'>
766 |             <div className='w-full'>
767 |               <ToggleSwitch
768 |                 label="File Logging"
769 |                 description="Save logs to file"
770 |                 value={data.ENABLE_FILE_LOGGING || 'true'}
771 |                 onChange={(value) => onChange(environment, 'ENABLE_FILE_LOGGING', value)}
772 |               />
773 |             </div>
774 |           </div>
775 |           <div className='flex items-end'>
776 |             <div className='w-full'>
777 |               <ToggleSwitch
778 |                 label="Console Logging"
779 |                 description="Display logs in console"
780 |                 value={data.ENABLE_CONSOLE_LOGGING || 'false'}
781 |                 onChange={(value) => onChange(environment, 'ENABLE_CONSOLE_LOGGING', value)}
782 |               />
783 |             </div>
784 |           </div>
785 |         </div>
786 |       </div>
787 |     </div>
788 |   )
789 | }
790 | 
791 | export default ConfigurationModal
```
Page 3/3FirstPrevNextLast