| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>SAML DocGen Wireframe</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .sidebar { |
| scrollbar-width: thin; |
| scrollbar-color: #4b5563 #1f2937; |
| } |
| .sidebar::-webkit-scrollbar { |
| width: 6px; |
| } |
| .sidebar::-webkit-scrollbar-track { |
| background: #1f2937; |
| } |
| .sidebar::-webkit-scrollbar-thumb { |
| background-color: #4b5563; |
| border-radius: 3px; |
| } |
| .code-block { |
| font-family: 'Courier New', monospace; |
| background-color: #1e293b; |
| color: #f8fafc; |
| } |
| .tab-active { |
| border-bottom: 2px solid #3b82f6; |
| color: #3b82f6; |
| } |
| .diagram-placeholder { |
| background: linear-gradient(135deg, #f3f4f6 25%, #e5e7eb 25%, #e5e7eb 50%, #f3f4f6 50%, #f3f4f6 75%, #e5e7eb 75%); |
| background-size: 40px 40px; |
| } |
| .copy-success { |
| animation: copyPulse 0.5s ease-in-out; |
| } |
| @keyframes copyPulse { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.2); } |
| 100% { transform: scale(1); } |
| } |
| .slider-container { |
| position: relative; |
| width: 100%; |
| margin: 20px 0; |
| } |
| .slider { |
| -webkit-appearance: none; |
| width: 100%; |
| height: 8px; |
| border-radius: 4px; |
| background: #d1d5db; |
| outline: none; |
| } |
| .slider::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| width: 20px; |
| height: 20px; |
| border-radius: 50%; |
| background: #3b82f6; |
| cursor: pointer; |
| transition: all 0.2s; |
| } |
| .slider::-webkit-slider-thumb:hover { |
| transform: scale(1.2); |
| box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2); |
| } |
| .slider-labels { |
| display: flex; |
| justify-content: space-between; |
| margin-top: 8px; |
| } |
| .slider-label { |
| font-size: 12px; |
| color: #6b7280; |
| } |
| .slider-value { |
| position: absolute; |
| top: -30px; |
| left: 50%; |
| transform: translateX(-50%); |
| background: #3b82f6; |
| color: white; |
| padding: 4px 8px; |
| border-radius: 4px; |
| font-size: 12px; |
| } |
| .slider-value:after { |
| content: ''; |
| position: absolute; |
| top: 100%; |
| left: 50%; |
| transform: translateX(-50%); |
| border-width: 5px; |
| border-style: solid; |
| border-color: #3b82f6 transparent transparent transparent; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 text-gray-800"> |
| <div class="flex h-screen overflow-hidden"> |
| |
| <div class="sidebar w-64 bg-gray-800 text-gray-200 flex flex-col h-full overflow-y-auto"> |
| <div class="p-4 border-b border-gray-700"> |
| <div class="flex items-center space-x-2"> |
| <div class="w-8 h-8 bg-blue-500 rounded-md flex items-center justify-center"> |
| <i class="fas fa-lock text-white"></i> |
| </div> |
| <h1 class="text-xl font-bold">SAML DocGen</h1> |
| </div> |
| <p class="text-xs text-gray-400 mt-1">v2.4.1</p> |
| </div> |
| |
| <div class="p-4"> |
| <div class="relative"> |
| <input type="text" placeholder="Search components..." class="w-full bg-gray-700 text-gray-200 px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| <i class="fas fa-search absolute right-3 top-3 text-gray-400"></i> |
| </div> |
| </div> |
| |
| <div class="flex-1 overflow-y-auto"> |
| <div class="px-4 py-2"> |
| <h3 class="text-xs uppercase tracking-wider text-gray-400 mb-2">Authentication</h3> |
| <ul> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700 text-blue-400 bg-gray-700"> |
| <i class="fas fa-key mr-2"></i> SAML Flow |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-fingerprint mr-2"></i> Single Sign-On |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-sign-out-alt mr-2"></i> Single Logout |
| </a> |
| </li> |
| </ul> |
| </div> |
| |
| <div class="px-4 py-2 mt-4"> |
| <h3 class="text-xs uppercase tracking-wider text-gray-400 mb-2">Configuration</h3> |
| <ul> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-id-card mr-2"></i> Identity Providers |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px极3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-server mr-2"></i> Service Providers |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-certificate mr-2"></i> Certificates |
| </a> |
| </li> |
| </ul> |
| </div> |
| |
| <div class="px-4 py-2 mt-4"> |
| <h3 class="text-xs uppercase tracking-wider text-gray-400 mb-2">Advanced</h3> |
| <ul> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-code mr-2"></i> Custom Claims |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-shield-alt mr-2"></i> Security Policies |
| </a> |
| </li> |
| <li class="mb-1"> |
| <a href="#" class="block px-3 py-2 rounded-md hover:bg-gray-700"> |
| <i class="fas fa-plug mr-2"></i> Webhooks |
| </a> |
| </li> |
| </ul> |
| </div> |
| </div> |
| |
| <div class="p-4 border-t border-gray-700"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-gray-600 flex items-center justify-center mr-2"> |
| <i class="fas fa-user"></i> |
| </div> |
| <div> |
| <p class="text-sm font-medium">Admin User</p> |
| <p class="text-xs text-gray-400">admin@saml.co.za</p> |
| </div> |
| <button class="ml-auto text-gray-400 hover:text-white"> |
| <i class="fas fa-cog"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex-1 flex flex-col overflow-hidden"> |
| |
| <header class="bg-white border-b border-gray-200 p-4 flex items-center justify-between"> |
| <div> |
| <h2 class="text-xl font-semibold">SAML Authentication Flow</h2> |
| <p class="text-sm text-gray-500">Web Browser SSO Profile (SAML 2.0)</p> |
| </div> |
| <div class="flex space-x-3"> |
| <div class="relative group"> |
| <button class="px-4 py-2 bg-gray-100 rounded-md hover:bg-gray-200 flex items-center"> |
| <i class="fas fa-code-branch mr-2 text-gray-600"></i> |
| <span>Version 2.4.1</span> |
| <i class="fas fa-chevron-down ml-2 text-xs text-gray-500"></i> |
| </button> |
| <div class="absolute hidden group-hover:block bg-white shadow-lg rounded-md mt-1 w-48 z-10 border border-gray-200"> |
| <a href="#" class="block px-4 py-2 text-sm hover:bg-gray-100">Version 2.4.1 (current)</a> |
| <a href="#" class="block px-4 py-2 text-sm hover:bg-gray-100">Version 2.3.0</a> |
| <a href="#" class="block px-4 py-2 text-sm hover:bg-gray-100">Version 2.2.5</a> |
| </div> |
| </div> |
| <button class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center"> |
| <i class="fas fa-download mr-2"></i> |
| <span>Export</span> |
| </button> |
| </div> |
| </header> |
| |
| |
| <div class="flex-1 overflow-y-auto p-6"> |
| <div class="max-w-5xl mx-auto"> |
| |
| <div class="border-b border-gray-200 mb-6"> |
| <nav class="flex space-x-8"> |
| <button class="tab-active py-4 px-1 font-medium">Documentation</button> |
| <button class="py-4 px-1 font-medium text-gray-500 hover:text-gray-700">Examples</button> |
| <button class="py-4 px-1 font-medium text-gray-500 hover:text-gray-700">Configuration</button> |
| <button class="py-4 px-1 font-medium text-gray-500 hover:text-gray-700">Troubleshooting</button> |
| </nav> |
| </div> |
| |
| |
| <div class="bg-white rounded-lg shadow-sm p-6 mb-6"> |
| <div class="flex items-center justify-between mb-4"> |
| <h3 class="text-lg font-semibold">SAML Authentication Flow</h3> |
| <div class="flex space-x-2"> |
| <button class="px-3 py-1 bg-gray-100 rounded-md text-sm hover:bg-gray-200"> |
| <i class="fas fa-bookmark mr-1"></i> Bookmark |
| </button> |
| <button class="px-3 py-1 bg-gray-100 rounded-md text-sm hover:bg-gray-200"> |
| <i class="fas fa-share-alt mr-1"></i> Share |
| </button> |
| </div> |
| </div> |
| |
| <p class="text-gray-600 mb-6"> |
| The SAML authentication flow allows users to authenticate using their identity provider (IdP) credentials. |
| This component implements the standard SAML 2.0 Web Browser SSO Profile with support for both SP-initiated and IdP-initiated flows. |
| </p> |
| |
| <div class="mb-8"> |
| <div class="flex items-center justify-between mb-3"> |
| <h4 class="font-medium">Sequence Diagram</h4> |
| <button class="text-sm text-blue-600 hover:text-blue-800"> |
| <i class="fas fa-expand mr-1"></i> Fullscreen |
| </button> |
| </div> |
| <div class="diagram-placeholder p-4 rounded-md border border-gray-200"> |
| <div class="flex justify-center items-center h-64"> |
| <div class="text-center"> |
| <i class="fas fa-project-diagram text-4xl text-gray-300 mb-2"></i> |
| <p class="text-gray-400">SAML authentication sequence diagram</p> |
| <button class="mt极2 px-3 py-1 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700"> |
| Generate Diagram |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mb-8"> |
| <h4 class="font-medium mb-3">Security Level Configuration</h4> |
| <div class="slider-container"> |
| <div class="slider-value" id="slider-value">Medium</div> |
| <input type="range" min="0" max="2" value="1" class="slider" id="security-level" step="1"> |
| <div class="slider-labels"> |
| <span class="slider-label">Low</span> |
| <span class="slider-label">Medium</span> |
| <span class="slider-label">High</span> |
| </div> |
| </div> |
| <div class="mt-4 p-4 bg-gray-50 rounded-md"> |
| <h5 class="font-medium mb-2">Current Security Settings:</h5> |
| <ul class="list-disc pl-5 text-sm text-gray-600" id="security-settings"> |
| <li>256-bit encryption</li> |
| <li>Password-protected transport</li> |
| <li>Session timeout: 30 minutes</li> |
| </ul> |
| </div> |
| </div> |
| |
| <div class="mb-8"> |
| <h4 class="font-medium mb-3">Configuration Parameters</h4> |
| <div class="overflow-x-auto"> |
| <table class="min-w-full divide-y divide-gray-200"> |
| <thead class="bg-gray-50"> |
| <tr> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Parameter</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Required</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Default</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th> |
| </tr> |
| </thead> |
| <tbody class="bg-white divide-y divide-gray-200"> |
| <tr> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">idpMetadataUrl</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">string</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Yes</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">-</td> |
| <td class="px-6 py-4 text-sm text-gray-500">URL to the IdP metadata XML</td> |
| </tr> |
| <tr class="bg-gray-50"> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">spEntityId</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">string</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Yes</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">-</td> |
| <td class="px-6 py-4 text-sm text-gray-500">Service Provider entity ID</td> |
| </tr> |
| <tr> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">assertEndpoint</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">string</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Yes</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">-</td> |
| <td class="px-6 py-4 text-sm text-gray-500">Assertion consumer service URL</td> |
| </tr> |
| <tr class="bg-gray-50"> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">nameIdFormat</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">string</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">No</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</td> |
| <td class="px-6 py-4 text-sm text-gray-500">Format for NameID</td> |
| </tr> |
| <tr> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">authnContext</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">string</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">No</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</td> |
| <td class="px-6 py-4 text-sm text-gray-500">Authentication context class</td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| </div> |
| |
| <div class="mb-8"> |
| <h4 class="font-medium mb-3">Example Implementation</h4> |
| <div class="mb-4"> |
| <div class="flex justify-between items-center bg-gray-800 text-gray-200 px-4 py-2 rounded-t-md"> |
| <div class="极flex items-center"> |
| <i class="fab fa-js mr-2"></i> |
| <span>JavaScript</span> |
| </div> |
| <button class="text-gray-400 hover:text-white copy-btn"> |
| <i class="far fa-copy"></i> |
| </button> |
| </div> |
| <pre class="code-block p-4 rounded-b-md overflow-x-auto"><code>const samlAuth = new SAMLAuthentication({ |
| // Required configuration |
| idpMetadataUrl: 'https://idp.example.com/metadata.xml', |
| spEntityId: 'urn:example:sp', |
| assertEndpoint: 'https://your-app.com/saml/acs', |
| |
| // Optional configuration |
| name极IdFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', |
| authnContext: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', |
| forceAuthn: false, |
| allowCreate: true, |
| requestBinding: 'HTTP-Redirect', |
| responseBinding: 'HTTP-POST' |
| }); |
|
|
| // Initiate SP-initiated login |
| app.get('/login', (req, res) => { |
| samlAuth.login(req, res, { |
| // Optional parameters |
| RelayState: '/dashboard', |
| AuthnRequestExtensions: '<my:CustomAttribute>value</my:CustomAttribute>' |
| }); |
| }); |
|
|
| // Handle assertion response |
| app.post('/saml/acs', (req, res) => { |
| samlAuth.handleAssertion(req, res) |
| .then(user => { |
| // User authenticated successfully |
| req.session.user = user; |
| res.redirect(req.body.RelayState || '/'); |
| }) |
| .catch(err => { |
| // Handle authentication failure |
| console.error('SAML authentication failed:', err); |
| res.status(401).render('error', { |
| message: 'Authentication failed', |
| details: err.message |
| }); |
| }); |
| }); |
|
|
| // Metadata endpoint |
| app.get('/saml/metadata', (req, res) => { |
| res.type('application/xml'); |
| res.send(samlAuth.generateMetadata()); |
| });</code></pre> |
| </div> |
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div> |
| <div class="flex justify-between items-center bg-gray-800 text-gray-200 px-4 py-2 rounded-t-md"> |
| <div class="flex items-center"> |
| <i class="fab fa-java mr-2"></i> |
| <span>Java</span> |
| </div> |
| <button class="text-gray-400 hover:text-white copy-btn"> |
| <i class="far fa-copy"></i> |
| </button> |
| </div> |
| <pre class="code-block p-4 rounded-b-md overflow-x-auto"><code>@Configuration |
| @EnableWebSecurity |
| public class SecurityConfig extends WebSecurityConfigurerAdapter { |
| |
| @Value("${saml.idp.metadata}") |
| private String idpMetadataUrl; |
| |
| @Bean |
| public SAMLAuthenticationProvider samlAuthenticationProvider() { |
| return new SAMLAuthenticationProvider(); |
| } |
| |
| @Bean |
| public SAMLMetadataFilter metadataFilter() { |
| return new SAMLMetadataFilter(); |
| } |
| |
| @Override |
| protected void configure(HttpSecurity http) throws Exception { |
| http |
| .authorizeRequests() |
| .antMatchers("/saml/**").permitAll() |
| .anyRequest().authenticated() |
| .and() |
| .apply(new SAMLConfigurer()) |
| .idpMetadataUrl(idpMetadataUrl) |
| .spEntityId("urn:example:sp") |
| .assertionConsumerServiceUrl("/saml/acs") |
| .and() |
| .addFilterBefore(metadataFilter(), |
| UsernamePasswordAuthenticationFilter.class); |
| } |
| }</code></pre> |
| </div> |
| <div> |
| <div class="flex justify-between items-center bg-gray-800 text-gray-200 px-4 py-2 rounded-t-md"> |
| <div class="flex items-center"> |
| <i class="fab fa-python mr-2"></i> |
| <span>Python</span> |
| </div> |
| <button class="text-gray-400 hover:text-white copy-btn"> |
| <i class="far fa-copy"></i> |
| </button> |
| </div> |
| <pre class="code-block p-4 rounded-b-md overflow-x-auto"><code>from flask import Flask, request, redirect, session |
| from flask_saml import SAMLAuthentication |
|
|
| app = Flask(__name__) |
| app.secret_key = 'your-secret-key' |
|
|
| saml_auth = SAMLAuthentication( |
| app, |
| idp_metadata_url='https://idp.example.com/metadata.xml', |
| sp_entity_id='urn:example:sp', |
| acs_url='/saml/acs' |
| ) |
|
|
| @app.route('/login') |
| def login(): |
| return saml_auth.login(relay_state='/dashboard') |
|
|
| @app.route('/saml/acs', methods=['POST']) |
| def acs(): |
| user = saml_auth.process_response() |
| session['user'] = user |
| return redirect(request.form.get('RelayState', '/')) |
|
|
| @app.route('/saml/metadata') |
| def metadata(): |
| return saml_auth.generate_metadata()</code></pre> |
| </div> |
| </div> |
| </div> |
| |
| <div> |
| <h4 class="font-medium mb-3">Related Components</h4> |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| <div class="border border-gray-200 rounded-md p-4 hover:border-blue-200 hover:bg-blue-50 transition-colors cursor-pointer"> |
| <div class="flex items-center mb-2"> |
| <div class="w-8 h-8 bg-blue-100 rounded-md flex items-center justify-center mr-2"> |
| <i class="fas fa-sign-out-alt text-blue-600"></i> |
| </div> |
| <h5 class="font-medium">Single Logout</h5> |
| </div> |
| <p class="text-sm text-gray-600">Implement SAML Single Logout (SLO) for session termination</p> |
| </div> |
| <div class="border border-gray-200 rounded-md p-4 hover:border-purple-200 hover:bg-purple-50 transition-colors cursor-pointer"> |
| <div class="flex items-center mb-2"> |
| <div class="w-8 h-8 bg-purple-100 rounded-md flex items-center justify-center mr-2"> |
| <i class="fas fa-id-badge text-purple-600"></i> |
| </div> |
| <h5 class="font-medium">Attribute Mapping</h5> |
| </div> |
| <p class="text-sm text-gray-600">Map SAML attributes to user profile fields</p> |
| </div> |
| <div class="border border-gray-200 rounded-md p-4 hover:border-green-200 hover:bg-green-50 transition-colors cursor-pointer"> |
| <div class="flex items-center mb-2"> |
| <div class="w-8 h-8 bg-green-100 rounded-md flex items-center justify-center mr-2"> |
| <i class="fas fa-shield-alt text-green-600"></i> |
| </div> |
| <h5 class="font-medium">Security Policies</h5> |
| </div> |
| <p class="text-sm text-gray-600">Configure security policies for SAML assertions</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="bg-white rounded-lg shadow-sm p-6"> |
| <div class="flex items-center justify-between mb-4"> |
| <h3 class="text-lg font-semibold">Feedback</h3> |
| <div class="flex space-x-2"> |
| <button id="helpful-btn" class="px-3 py-1 bg-gray-100 rounded-md text-sm hover:bg-gray-200"> |
| <i class="far fa-thumbs-up mr-1"></i> Helpful |
| </button> |
| <button id="improve-btn" class="px-3 py-1 bg-gray-100 rounded-md text-sm hover:bg-gray-200"> |
| <i class="far fa-thumbs-down mr-1"></i> Needs improvement |
| </button> |
| </div> |
| </div> |
| <textarea id="feedback-text" class="w-full border border-gray-300 rounded-md p-3 focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3" placeholder="Have feedback about this component? Let us know..."></textarea> |
| <div class="flex justify-end mt-3"> |
| <button id="submit-feedback" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"> |
| Submit Feedback |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const tabs = document.querySelectorAll('nav button'); |
| tabs.forEach(tab => { |
| tab.addEventListener('click', () => { |
| tabs.forEach(t => { |
| t.classList.remove('tab-active', 'text-blue-600'); |
| t.classList.add('text-gray-500', 'hover:text-gray-700'); |
| }); |
| |
| tab.classList.add('tab-active', 'text-blue-600'); |
| tab.classList.remove('text-gray-500', 'hover:text-gray-700'); |
| |
| |
| console.log(`Switched to tab: ${tab.textContent.trim()}`); |
| }); |
| }); |
| |
| |
| const copyButtons = document.querySelectorAll('.copy-btn'); |
| copyButtons.forEach(button => { |
| button.addEventListener('click', () => { |
| const codeBlock = button.closest('div').nextElementSibling; |
| const code = codeBlock.querySelector('code').textContent; |
| |
| navigator.clipboard.writeText(code).then(() => { |
| const icon = button.querySelector('i'); |
| icon.className = 'fas fa-check text-green-400 copy-success'; |
| |
| setTimeout(() => { |
| icon.className = 'far fa-copy'; |
| }, 2000); |
| }); |
| }); |
| }); |
| |
| |
| const helpfulBtn = document.getElementById('helpful-btn'); |
| const improveBtn = document.getElementById('improve-btn'); |
| const feedbackText = document.getElementById('feedback-text'); |
| const submitFeedback = document.getElementById('submit-feedback'); |
| |
| let feedbackType = null; |
| |
| helpfulBtn.addEventListener('click', () => { |
| feedbackType = 'helpful'; |
| helpfulBtn.classList.add('bg-blue-100', 'text-blue-800'); |
| improveBtn.classList.remove('bg-blue-100', 'text-blue-800'); |
| }); |
| |
| improveBtn.addEventListener('click', () => { |
| feedbackType = 'needs_improvement'; |
| improveBtn.classList.add('bg-blue-100', 'text-blue-800'); |
| helpfulBtn.classList.remove('bg-blue-100', 'text-blue-800'); |
| }); |
| |
| submitFeedback.addEventListener('click', () => { |
| if (!feedbackType) { |
| alert('Please select feedback type first'); |
| return; |
| } |
| |
| const feedback = feedbackText.value.trim(); |
| if (!feedback && feedbackType === 'needs_improvement') { |
| alert('Please provide details about what needs improvement'); |
| return; |
| } |
| |
| |
| console.log('Feedback submitted:', { |
| type: feedbackType, |
| content: feedback, |
| component: 'SAML Authentication Flow', |
| timestamp: new Date().toISOString() |
| }); |
| |
| alert('Thank you for your feedback!'); |
| feedbackText.value = ''; |
| helpfulBtn.classList.remove('bg-blue-100', 'text-blue-800'); |
| improveBtn.classList.remove('bg-blue-100', 'text-blue-800'); |
| feedbackType = null; |
| }); |
| |
| |
| const versionBtn = document.querySelector('.group button'); |
| versionBtn.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| const dropdown = document.querySelector('.group .absolute'); |
| dropdown.classList.toggle('hidden'); |
| }); |
| |
| |
| document.addEventListener('click', () => { |
| const dropdown = document.querySelector('.group .absolute'); |
| if (dropdown && !dropdown.classList.contains('hidden')) { |
| dropdown.classList.add('hidden'); |
| } |
| }); |
| |
| |
| const securitySlider = document.getElementById('security-level'); |
| const sliderValue = document.getElementById('slider-value'); |
| const securitySettings = document.getElementById('security-settings'); |
| |
| |
| updateSliderValue(securitySlider.value); |
| |
| securitySlider.addEventListener('input', function() { |
| updateSliderValue(this.value); |
| }); |
| |
| function updateSliderValue(value) { |
| const numericValue = parseInt(value); |
| let level = ''; |
| let settings = []; |
| |
| switch(numericValue) { |
| case 0: |
| level = 'Low'; |
| settings = [ |
| '128-bit encryption', |
| 'Basic authentication', |
| 'Session timeout: 60 minutes' |
| ]; |
| break; |
| case 1: |
| level = 'Medium'; |
| settings = [ |
| '256-bit encryption', |
| 'Password-protected transport', |
| 'Session timeout: 30 minutes' |
| ]; |
| break; |
| case 2: |
| level = 'High'; |
| settings = [ |
| '512-bit encryption', |
| 'Multi-factor authentication', |
| 'Session timeout: 15 minutes' |
| ]; |
| break; |
| } |
| |
| sliderValue.textContent = level; |
| |
| |
| securitySettings.innerHTML = ''; |
| settings.forEach(setting => { |
| const li = document.createElement('li'); |
| li.textContent = setting; |
| securitySettings.appendChild(li); |
| }); |
| } |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sudzdpn/wireframe" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |