Refactor Ansible Sandbox page: Updated styles for improved layout and user experience, including new sandbox-specific styles, a redesigned navigation link, and enhanced tab functionality for code, output, and status views. Added responsive design adjustments for better mobile compatibility.

This commit is contained in:
Daniel LaForce 2025-04-10 19:08:54 -06:00
parent 0937c4bd7a
commit 7d9df34dde
2 changed files with 382 additions and 326 deletions

View File

@ -99,7 +99,7 @@
left: 0; left: 0;
right: 0; right: 0;
z-index: 1000; z-index: 1000;
padding: 1.25rem 0; padding: 1rem 0;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
@ -190,24 +190,25 @@
gap: 1.25rem; gap: 1.25rem;
} }
.dashboard-link { .back-link {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.5rem;
padding: 0.625rem 1.25rem; padding: 0.5rem 1rem;
font-size: 0.95rem; background-color: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.3);
border-radius: 0.5rem;
font-size: 0.9rem;
font-weight: 500; font-weight: 500;
color: var(--text-primary); color: var(--text-primary);
background-color: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.2);
border-radius: 0.5rem;
text-decoration: none; text-decoration: none;
transition: all var(--transition-normal); transition: all var(--transition-normal);
} }
.dashboard-link:hover { .back-link:hover {
background-color: rgba(59, 130, 246, 0.2); background-color: rgba(59, 130, 246, 0.2);
border-color: rgba(59, 130, 246, 0.3); border-color: var(--accent);
transform: translateY(-2px);
} }
.live-indicator { .live-indicator {
@ -1188,6 +1189,242 @@
transform: scale(1.05); transform: scale(1.05);
} }
} }
/* Sandbox Specific Styles */
.sandbox-content {
padding-top: 8rem;
max-width: 900px;
margin: 0 auto;
}
.sandbox-title {
font-size: 2.5rem;
margin-bottom: 1rem;
text-align: center;
color: var(--text-primary);
background: var(--accent-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.sandbox-description {
text-align: center;
color: var(--text-secondary);
margin-bottom: 3rem;
font-size: 1.1rem;
}
.sandbox-tabs {
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
.preview-tabs {
display: flex;
gap: 2rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.5rem;
width: 100%;
justify-content: center;
}
.preview-tab {
font-size: 1.1rem;
font-weight: 500;
color: var(--text-secondary);
cursor: pointer;
position: relative;
padding: 0.5rem 1rem;
transition: color var(--transition-normal);
}
.preview-tab::after {
content: '';
position: absolute;
bottom: -8px;
left: 0;
width: 0;
height: 3px;
background: var(--accent-gradient);
transition: width var(--transition-normal);
}
.preview-tab:hover {
color: var(--text-primary);
}
.preview-tab.active {
color: var(--accent);
}
.preview-tab.active::after {
width: 100%;
}
.preview-content {
background-color: var(--card-bg);
border-radius: 0.75rem;
overflow: hidden;
border: 1px solid var(--border);
margin-bottom: 2rem;
box-shadow: var(--card-shadow);
min-height: 400px;
}
.tab-content {
display: none;
padding: 2rem;
}
.tab-content.active {
display: block;
}
.tab-content h3 {
margin-bottom: 1rem;
color: var(--text-primary);
}
.preview-status {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1.5rem;
font-size: 1.1rem;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.status-ready {
background-color: var(--info);
box-shadow: 0 0 5px var(--info);
}
.status-running {
background-color: var(--warning);
box-shadow: 0 0 5px var(--warning);
animation: pulse 1.5s infinite;
}
.status-success {
background-color: var(--success);
box-shadow: 0 0 5px var(--success);
}
@keyframes pulse {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.time-remaining {
margin-bottom: 0.75rem;
color: var(--text-secondary);
}
.time-progress {
height: 8px;
background-color: rgba(71, 85, 105, 0.2);
border-radius: 4px;
overflow: hidden;
margin-bottom: 2rem;
}
.time-progress-bar {
height: 100%;
background: var(--accent-gradient);
width: 0;
transition: width 1s ease-in-out;
}
.sandbox-controls {
display: flex;
gap: 1rem;
justify-content: center;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
border-radius: 0.5rem;
cursor: pointer;
transition: all var(--transition-normal);
border: none;
}
.btn-primary {
background: var(--accent-gradient);
color: white;
}
.btn-primary:hover {
box-shadow: var(--accent-glow);
transform: translateY(-2px);
}
.btn-outline {
background-color: transparent;
border: 1px solid var(--accent);
color: var(--accent);
}
.btn-outline:hover {
background-color: rgba(59, 130, 246, 0.1);
transform: translateY(-2px);
}
/* Code editor styles */
.code-editor {
font-family: 'JetBrains Mono', monospace;
background-color: rgba(15, 23, 42, 0.3);
border-radius: 0.5rem;
padding: 1.5rem;
font-size: 0.9rem;
line-height: 1.6;
max-height: 500px;
overflow-y: auto;
}
/* Responsive styles */
@media (max-width: 768px) {
.sandbox-title {
font-size: 2rem;
}
.preview-tabs {
gap: 1rem;
}
.preview-tab {
font-size: 0.9rem;
padding: 0.5rem;
}
.sandbox-controls {
flex-direction: column;
}
.btn {
width: 100%;
}
}
</style> </style>
</head> </head>
<body> <body>
@ -1196,26 +1433,14 @@
<div class="container"> <div class="container">
<div class="logo"> <div class="logo">
<a href="index.html"> <a href="index.html">
<span class="logo-text-glow">Argobox</span> <span class="logo-text-glow">ArgoBox</span><span class="logo-dot-glow">.com</span>
<span class="logo-dot-glow">.com</span>
</a> </a>
</div> </div>
<div class="nav-menu" id="navMenu">
<a href="index.html#home" class="nav-link">Home</a>
<a href="index.html#technologies" class="nav-link">Technologies</a>
<a href="index.html#services" class="nav-link">Services</a>
<a href="index.html#projects" class="nav-link">Projects</a>
<a href="index.html#dashboards" class="nav-link">Dashboards</a>
<a href="index.html#contact" class="nav-link">Contact</a>
</div>
<div class="nav-buttons"> <div class="nav-buttons">
<a href="dashboard.html" class="dashboard-link" target="_blank"> <a href="index.html" class="back-link">
<span class="live-indicator"></span> <i class="fas fa-arrow-left"></i>
<span>Live Dashboard</span> <span>Back to Home</span>
</a> </a>
<button class="menu-toggle" id="menuToggle" aria-label="Toggle menu">
<i class="fas fa-bars"></i>
</button>
</div> </div>
</div> </div>
</nav> </nav>
@ -1228,192 +1453,56 @@
</div> </div>
</div> </div>
<div class="docs-container"> <div class="container">
<aside class="sidebar"> <div class="sandbox-content">
<div class="sidebar-header"> <h1 class="sandbox-title">Ansible Sandbox</h1>
<h2 class="sidebar-title">Ansible Sandbox</h2> <p class="sandbox-description">Deploy infrastructure with Ansible playbooks in an isolated environment.</p>
<a href="index.html" class="back-link">
<i class="fas fa-arrow-left"></i> <div class="sandbox-tabs">
Back to Home <div class="preview-tabs">
</a> <div class="preview-tab active" data-tab="code">Code</div>
<div class="preview-tab" data-tab="output">Output</div>
<div class="preview-tab" data-tab="status">Status</div>
</div>
</div> </div>
<div class="sidebar-section"> <div class="preview-content">
<h3 class="sidebar-section-title">Getting Started</h3> <div id="code-tab" class="tab-content active">
<ul class="sidebar-nav"> <div class="code-editor">
<li class="sidebar-nav-item"> <h3>Ansible Playbook Editor</h3>
<a href="#introduction" class="sidebar-nav-link active">Introduction</a> <p>Edit your playbook here...</p>
</li> </div>
<li class="sidebar-nav-item">
<a href="#prerequisites" class="sidebar-nav-link">Prerequisites</a>
</li>
<li class="sidebar-nav-item">
<a href="#quick-start" class="sidebar-nav-link">Quick Start Guide</a>
</li>
</ul>
</div> </div>
<div class="sidebar-section"> <div id="output-tab" class="tab-content">
<h3 class="sidebar-section-title">Features</h3> <h3>Deployment Output</h3>
<ul class="sidebar-nav"> <p>Output will appear here when you deploy...</p>
<li class="sidebar-nav-item">
<a href="#sandbox-environment" class="sidebar-nav-link">Sandbox Environment</a>
</li>
<li class="sidebar-nav-item">
<a href="#playbook-examples" class="sidebar-nav-link">Playbook Examples</a>
</li>
<li class="sidebar-nav-item">
<a href="#best-practices" class="sidebar-nav-link">Best Practices</a>
</li>
</ul>
</div> </div>
<div class="sidebar-section"> <div id="status-tab" class="tab-content">
<h3 class="sidebar-section-title">Resources</h3> <h3>Deployment Status</h3>
<ul class="sidebar-nav"> <div class="preview-status">
<li class="sidebar-nav-item"> <div class="status-dot status-ready"></div>
<a href="#documentation" class="sidebar-nav-link">Documentation</a> <span>Ready to deploy</span>
</li>
<li class="sidebar-nav-item">
<a href="#troubleshooting" class="sidebar-nav-link">Troubleshooting</a>
</li>
<li class="sidebar-nav-item">
<a href="#community" class="sidebar-nav-link">Community</a>
</li>
</ul>
</div> </div>
</aside> <div class="time-remaining">
<div>Sandbox environment ready</div>
<main class="main-content"> </div>
<header class="docs-header"> <div class="time-progress">
<h1 class="docs-title">Ansible Sandbox Documentation</h1> <div class="time-progress-bar"></div>
<p class="docs-subtitle">Learn, experiment, and master Ansible automation in a safe environment</p>
</header>
<section id="introduction" class="docs-section">
<h2 class="docs-section-title">Introduction</h2>
<p class="docs-text">
Welcome to the ArgoBox Ansible Sandbox! This environment provides a safe and isolated space for learning
and experimenting with Ansible automation. Whether you're new to Ansible or an experienced user, our
sandbox offers the perfect platform to develop and test your automation skills.
</p>
<div class="docs-note">
<div class="docs-note-title">
<i class="fas fa-info-circle"></i>
Note
</div> </div>
<p>The Ansible Sandbox is part of the ArgoBox Lab Environment, designed to provide hands-on experience
with enterprise-grade automation tools.</p>
</div> </div>
</section>
<section id="prerequisites" class="docs-section">
<h2 class="docs-section-title">Prerequisites</h2>
<div class="docs-subsection">
<h3 class="docs-subsection-title">System Requirements</h3>
<ul class="docs-list">
<li>Access to ArgoBox Lab Environment</li>
<li>Basic understanding of YAML syntax</li>
<li>Familiarity with command-line interface</li>
<li>Text editor or IDE for writing playbooks</li>
</ul>
</div> </div>
<div class="docs-subsection"> <div class="sandbox-controls">
<h3 class="docs-subsection-title">Required Knowledge</h3> <button id="deploy-btn" class="btn btn-primary">
<p class="docs-text"> <i class="fas fa-play"></i> Deploy Playbook
While no prior Ansible experience is required, basic understanding of the following concepts will be </button>
helpful: <button id="reset-btn" class="btn btn-outline">
</p> <i class="fas fa-redo"></i> Reset Environment
<ul class="docs-list"> </button>
<li>Linux/Unix command line basics</li>
<li>SSH and basic networking concepts</li>
<li>Version control with Git (optional)</li>
</ul>
</div> </div>
</section>
<section id="quick-start" class="docs-section">
<h2 class="docs-section-title">Quick Start Guide</h2>
<div class="docs-warning">
<div class="docs-warning-title">
<i class="fas fa-exclamation-triangle"></i>
Important
</div> </div>
<p>Make sure you have completed the initial setup and have your credentials ready before proceeding.</p>
</div>
<div class="docs-subsection">
<h3 class="docs-subsection-title">Getting Started Steps</h3>
<ol class="docs-list">
<li>Log in to your ArgoBox environment</li>
<li>Navigate to the Ansible Sandbox section</li>
<li>Create your first playbook using the provided templates</li>
<li>Test your playbook in the sandbox environment</li>
</ol>
</div>
<div class="docs-code">
<pre>
# Example playbook structure
---
- name: My First Playbook
hosts: sandbox
tasks:
- name: Ensure Apache is installed
ansible.builtin.package:
name: apache2
state: present</pre>
</div>
</section>
<section id="sandbox-environment" class="docs-section">
<h2 class="docs-section-title">Sandbox Environment</h2>
<p class="docs-text">
Our sandbox environment provides a fully isolated space where you can experiment with Ansible playbooks
without affecting production systems. The environment includes multiple target hosts, common services, and
pre-configured inventory files.
</p>
<div class="docs-subsection">
<h3 class="docs-subsection-title">Available Resources</h3>
<table class="docs-table">
<thead>
<tr>
<th>Resource</th>
<th>Description</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Control Node</td>
<td>Main Ansible control server</td>
<td>Available</td>
</tr>
<tr>
<td>Target Hosts</td>
<td>3 Ubuntu servers for testing</td>
<td>Available</td>
</tr>
<tr>
<td>Example Playbooks</td>
<td>Pre-configured templates</td>
<td>Available</td>
</tr>
</tbody>
</table>
</div>
</section>
<div class="docs-section">
<a href="construction.html" class="docs-button">
<i class="fas fa-book"></i>
View Full Documentation
</a>
</div>
</main>
</div> </div>
<button class="mobile-menu-toggle"> <button class="mobile-menu-toggle">
@ -1453,64 +1542,21 @@
<!-- JavaScript --> <!-- JavaScript -->
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Set current year for copyright // Get the tabs and tab contents
document.getElementById('current-year').textContent = new Date().getFullYear();
// Mobile menu toggle
const menuToggle = document.getElementById('menuToggle');
const navMenu = document.getElementById('navMenu');
menuToggle.addEventListener('click', function() {
navMenu.classList.toggle('show');
const icon = menuToggle.querySelector('i');
if (navMenu.classList.contains('show')) {
icon.classList.remove('fa-bars');
icon.classList.add('fa-times');
} else {
icon.classList.remove('fa-times');
icon.classList.add('fa-bars');
}
});
// Tab switching
const tabs = document.querySelectorAll('.preview-tab'); const tabs = document.querySelectorAll('.preview-tab');
const tabContents = document.querySelectorAll('.tab-content'); const tabContents = document.querySelectorAll('.tab-content');
// Tab switching functionality
tabs.forEach(tab => { tabs.forEach(tab => {
tab.addEventListener('click', function() { tab.addEventListener('click', () => {
const tabId = this.getAttribute('data-tab'); const target = tab.getAttribute('data-tab');
// Hide all tabs and show the selected one
tabs.forEach(t => t.classList.remove('active')); tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(tc => tc.classList.remove('active')); tabContents.forEach(tc => tc.classList.remove('active'));
this.classList.add('active'); tab.classList.add('active');
document.getElementById(`${tabId}-tab`).classList.add('active'); document.getElementById(`${target}-tab`).classList.add('active');
});
});
// Playbook selection
const playbooks = document.querySelectorAll('.playbook-card');
const previewTitle = document.querySelector('.preview-title span');
playbooks.forEach(playbook => {
playbook.addEventListener('click', function() {
playbooks.forEach(p => p.classList.remove('active'));
this.classList.add('active');
const playbookName = this.querySelector('.playbook-title').textContent;
previewTitle.textContent = playbookName;
// Reset status
const statusIndicator = document.querySelector('.preview-status');
statusIndicator.innerHTML = '<div class="status-dot status-ready"></div><span>Ready to deploy</span>';
// Switch back to code tab
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(tc => tc.classList.remove('active'));
document.querySelector('[data-tab="code"]').classList.add('active');
document.getElementById('code-tab').classList.add('active');
}); });
}); });
@ -1519,16 +1565,25 @@
const resetBtn = document.getElementById('reset-btn'); const resetBtn = document.getElementById('reset-btn');
const statusIndicator = document.querySelector('.preview-status'); const statusIndicator = document.querySelector('.preview-status');
if (deployBtn) {
deployBtn.addEventListener('click', function() { deployBtn.addEventListener('click', function() {
// Show the "running" tab // Show the "running" tab
if (tabs && tabs.length > 0) {
tabs.forEach(t => t.classList.remove('active')); tabs.forEach(t => t.classList.remove('active'));
}
if (tabContents && tabContents.length > 0) {
tabContents.forEach(tc => tc.classList.remove('active')); tabContents.forEach(tc => tc.classList.remove('active'));
}
document.querySelector('[data-tab="output"]').classList.add('active'); const outputTab = document.querySelector('[data-tab="output"]');
document.getElementById('output-tab').classList.add('active'); const outputTabContent = document.getElementById('output-tab');
if (outputTab) outputTab.classList.add('active');
if (outputTabContent) outputTabContent.classList.add('active');
// Update status to "running" // Update status to "running"
if (statusIndicator) {
statusIndicator.innerHTML = '<div class="status-dot status-running"></div><span>Deploying...</span>'; statusIndicator.innerHTML = '<div class="status-dot status-running"></div><span>Deploying...</span>';
}
// Disable the deploy button while "running" // Disable the deploy button while "running"
deployBtn.disabled = true; deployBtn.disabled = true;
@ -1538,7 +1593,9 @@
// Simulate a delay for deployment // Simulate a delay for deployment
setTimeout(function() { setTimeout(function() {
// Update status to "success" // Update status to "success"
if (statusIndicator) {
statusIndicator.innerHTML = '<div class="status-dot status-success"></div><span>Deployment successful</span>'; statusIndicator.innerHTML = '<div class="status-dot status-success"></div><span>Deployment successful</span>';
}
// Re-enable the deploy button // Re-enable the deploy button
deployBtn.disabled = false; deployBtn.disabled = false;
@ -1546,56 +1603,55 @@
deployBtn.style.cursor = "pointer"; deployBtn.style.cursor = "pointer";
// Switch to the VM status tab // Switch to the VM status tab
if (tabs && tabs.length > 0) {
tabs.forEach(t => t.classList.remove('active')); tabs.forEach(t => t.classList.remove('active'));
}
if (tabContents && tabContents.length > 0) {
tabContents.forEach(tc => tc.classList.remove('active')); tabContents.forEach(tc => tc.classList.remove('active'));
}
document.querySelector('[data-tab="status"]').classList.add('active'); const statusTab = document.querySelector('[data-tab="status"]');
document.getElementById('status-tab').classList.add('active'); const statusTabContent = document.getElementById('status-tab');
if (statusTab) statusTab.classList.add('active');
if (statusTabContent) statusTabContent.classList.add('active');
// Start the countdown timer simulation // Start the countdown timer simulation
let timeLeft = 15; // minutes
const timeRemainingDiv = document.querySelector('.time-remaining div'); const timeRemainingDiv = document.querySelector('.time-remaining div');
const timeProgressBar = document.querySelector('.time-progress-bar'); const timeProgressBar = document.querySelector('.time-progress-bar');
// For demo purposes, we'll just set the initial values // For demo purposes, we'll just set the initial values
if (timeRemainingDiv) {
timeRemainingDiv.textContent = "Sandbox environment active for 15 more minutes"; timeRemainingDiv.textContent = "Sandbox environment active for 15 more minutes";
timeProgressBar.style.width = "100%";
// In a real implementation, you would use setInterval to update the countdown
// This is left commented out to avoid unnecessary timer in the demo
/*
const countdownInterval = setInterval(function() {
timeLeft -= 1;
const width = (timeLeft / 15) * 100;
if (timeLeft <= 0) {
clearInterval(countdownInterval);
timeRemainingDiv.textContent = "Sandbox environment expired";
timeProgressBar.style.width = "0%";
// Reset status
statusIndicator.innerHTML = '<div class="status-dot status-ready"></div><span>Ready to deploy</span>';
} else {
timeRemainingDiv.textContent = `Sandbox environment active for ${timeLeft} more minutes`;
timeProgressBar.style.width = `${width}%`;
} }
}, 60000); // Update every minute if (timeProgressBar) {
*/ timeProgressBar.style.width = "100%";
}
}, 3000); // Simulate 3 second deployment }, 3000); // Simulate 3 second deployment
}); });
}
// Reset button // Reset button
if (resetBtn) {
resetBtn.addEventListener('click', function() { resetBtn.addEventListener('click', function() {
// Reset status // Reset status
if (statusIndicator) {
statusIndicator.innerHTML = '<div class="status-dot status-ready"></div><span>Ready to deploy</span>'; statusIndicator.innerHTML = '<div class="status-dot status-ready"></div><span>Ready to deploy</span>';
}
// Switch back to code tab // Switch back to code tab
if (tabs && tabs.length > 0) {
tabs.forEach(t => t.classList.remove('active')); tabs.forEach(t => t.classList.remove('active'));
}
if (tabContents && tabContents.length > 0) {
tabContents.forEach(tc => tc.classList.remove('active')); tabContents.forEach(tc => tc.classList.remove('active'));
}
document.querySelector('[data-tab="code"]').classList.add('active'); const codeTab = document.querySelector('[data-tab="code"]');
document.getElementById('code-tab').classList.add('active'); const codeTabContent = document.getElementById('code-tab');
if (codeTab) codeTab.classList.add('active');
if (codeTabContent) codeTabContent.classList.add('active');
}); });
}
}); });
</script> </script>
</body> </body>

View File

@ -111,7 +111,7 @@
</div> </div>
</div> </div>
<div class="cta-buttons"> <div class="cta-buttons">
<a href="ansible-sandbox.html" class="btn btn-primary"> <a href="ansible-sandbox.html" class="btn btn-primary" target="_blank">
<i class="fab fa-ansible btn-icon"></i> <i class="fab fa-ansible btn-icon"></i>
<span class="btn-text">Try Ansible Sandbox</span> <span class="btn-text">Try Ansible Sandbox</span>
</a> </a>