Initial commit.

This commit is contained in:
2025-06-09 15:18:16 +02:00
commit 47d4ed1cc0
10 changed files with 1858 additions and 0 deletions

110
devbox-front/index.html Normal file
View File

@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DevBox - DevOps Made Simple</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header class="header">
<div class="logo">
<h1>DevBox</h1>
</div>
</header>
<main class="main-content">
<div class="hero-section">
<h2 class="hero-title">DevOps Without the Headaches</h2>
<p class="hero-subtitle">
Focus on building great products while we handle HTTPS, monitoring, CI/CD, security,
logs, and backups. Complete DevOps infrastructure for indie developers and small businesses.
</p>
<form class="email-form" id="emailForm">
<div class="input-group">
<input
type="email"
id="email"
placeholder="Enter your email for early access"
required
class="email-input"
>
<button type="submit" class="submit-btn">
Join the Waitlist
</button>
</div>
<p class="form-note">
Be the first to experience effortless DevOps. No spam, just launch updates.
</p>
</form>
<div class="success-message" id="successMessage">
<div class="success-content">
<div class="success-icon"></div>
<h3>Welcome aboard!</h3>
<p>We'll notify you as soon as DevBox is ready to simplify your deployments.</p>
</div>
</div>
</div>
<div class="features-preview">
<div class="feature-grid">
<div class="feature-item">
<div class="feature-icon">🔒</div>
<h3>Auto HTTPS & Security</h3>
<p>Automatic SSL certificates, security headers, and vulnerability scanning out of the box</p>
</div>
<div class="feature-item">
<div class="feature-icon">📊</div>
<h3>Monitoring & Logs</h3>
<p>Real-time metrics, centralized logging, and intelligent alerting without configuration</p>
</div>
<div class="feature-item">
<div class="feature-icon">🚀</div>
<h3>One-Click CI/CD</h3>
<p>Push to deploy with automated testing, staging environments, and rollback capabilities</p>
</div>
<div class="feature-item">
<div class="feature-icon">💾</div>
<h3>Automated Backups</h3>
<p>Daily encrypted backups with point-in-time recovery and cross-region replication</p>
</div>
<div class="feature-item">
<div class="feature-icon"></div>
<h3>Scale on Demand</h3>
<p>Auto-scaling infrastructure that grows with your business without manual intervention</p>
</div>
<div class="feature-item">
<div class="feature-icon">🎯</div>
<h3>Developer First</h3>
<p>Simple APIs, intuitive dashboard, and integrations with your favorite tools</p>
</div>
</div>
</div>
<div class="value-proposition">
<h3 class="value-title">Stop Fighting Infrastructure</h3>
<p class="value-text">
Spend your time building features your customers love, not wrestling with
Kubernetes configs, SSL certificates, and monitoring dashboards.
DevBox handles the complexity so you don't have to.
</p>
</div>
</main>
<footer class="footer">
<div class="social-links">
<a href="#" class="social-link">Twitter</a>
<a href="#" class="social-link">GitHub</a>
<a href="#" class="social-link">LinkedIn</a>
</div>
<p class="footer-text">© 2025 DevBox. Built for developers, by developers.</p>
</footer>
</div>
<script src="script.js"></script>
</body>
</html>

270
devbox-front/script.js Normal file
View File

@ -0,0 +1,270 @@
document.addEventListener('DOMContentLoaded', function() {
const emailForm = document.getElementById('emailForm');
const successMessage = document.getElementById('successMessage');
const API_BASE_URL = 'http://localhost:8080'; // Backend API URL
emailForm.addEventListener('submit', function(e) {
e.preventDefault();
const emailInput = document.getElementById('email');
const email = emailInput.value.trim();
// Basic email validation
if (!isValidEmail(email)) {
showError('Please enter a valid email address');
return;
}
// Submit email to backend
submitEmail(email);
});
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function submitEmail(email) {
// Show loading state
const submitBtn = document.querySelector('.submit-btn');
const originalText = submitBtn.textContent;
submitBtn.textContent = 'Joining...';
submitBtn.disabled = true;
submitBtn.classList.add('loading');
// Send to backend
fetch(`${API_BASE_URL}/api/subscribe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
source: 'devbox-landing'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Hide form and show success message
emailForm.style.display = 'none';
successMessage.classList.add('show');
console.log('DevBox waitlist email submitted:', email);
// Optional: Track the successful signup
trackEmailSignup(email);
} else {
// Show error message
showError(data.message || 'Email already subscribed or invalid.');
// Reset button
resetSubmitButton(submitBtn, originalText);
}
})
.catch((error) => {
console.error('Error:', error);
showError('Network error. Please check your connection and try again.');
// Reset button
resetSubmitButton(submitBtn, originalText);
});
}
function resetSubmitButton(submitBtn, originalText) {
submitBtn.textContent = originalText;
submitBtn.disabled = false;
submitBtn.classList.remove('loading');
}
function showError(message) {
// Remove existing error messages
const existingError = document.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
// Create and show error message
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.style.cssText = `
background: #ef4444;
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
margin-top: 1rem;
font-size: 0.9rem;
border: 1px solid #dc2626;
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
animation: slideDown 0.3s ease;
`;
errorDiv.textContent = message;
emailForm.appendChild(errorDiv);
// Remove error after 5 seconds
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.style.animation = 'slideUp 0.3s ease';
setTimeout(() => {
errorDiv.remove();
}, 300);
}
}, 5000);
}
function trackEmailSignup(email) {
// Optional: Add analytics tracking here
// Example: Google Analytics, Mixpanel, etc.
console.log(`📧 Email signup tracked: ${email} at ${new Date().toISOString()}`);
// You could also send additional tracking data to your backend
// or third-party analytics services here
}
// Add smooth scrolling for any anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth'
});
}
});
});
// Add subtle animation to feature cards on scroll (Intersection Observer)
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe feature items for animation
document.querySelectorAll('.feature-item').forEach(item => {
// Set initial state
item.style.opacity = '0';
item.style.transform = 'translateY(20px)';
item.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
// Start observing
observer.observe(item);
});
// Add keyboard accessibility
document.addEventListener('keydown', function(e) {
// Close error messages with Escape key
if (e.key === 'Escape') {
const errorMessage = document.querySelector('.error-message');
if (errorMessage) {
errorMessage.remove();
}
}
});
// Form validation enhancements
const emailInput = document.getElementById('email');
emailInput.addEventListener('input', function() {
// Remove error styling when user starts typing
const errorMessage = document.querySelector('.error-message');
if (errorMessage) {
errorMessage.style.opacity = '0.5';
}
// Real-time validation feedback
if (this.value.length > 0) {
if (isValidEmail(this.value)) {
this.style.borderColor = '#10b981';
this.style.boxShadow = '0 0 0 1px rgba(16, 185, 129, 0.2)';
} else {
this.style.borderColor = '#ef4444';
this.style.boxShadow = '0 0 0 1px rgba(239, 68, 68, 0.2)';
}
} else {
// Reset to default styling
this.style.borderColor = '';
this.style.boxShadow = '';
}
});
// Add focus states for better accessibility
emailInput.addEventListener('focus', function() {
this.parentElement.style.boxShadow = '0 0 0 3px rgba(50, 108, 229, 0.1)';
});
emailInput.addEventListener('blur', function() {
this.parentElement.style.boxShadow = '';
// Reset border styling
this.style.borderColor = '';
this.style.boxShadow = '';
});
// Simple analytics: track page engagement
let startTime = Date.now();
let maxScroll = 0;
window.addEventListener('scroll', function() {
const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
maxScroll = Math.max(maxScroll, scrollPercent);
});
window.addEventListener('beforeunload', function() {
const timeOnPage = Math.round((Date.now() - startTime) / 1000);
console.log(`📊 Session stats: ${timeOnPage}s on page, ${Math.round(maxScroll)}% max scroll`);
// You could send this data to your analytics endpoint
// fetch('/api/analytics', { method: 'POST', body: JSON.stringify({ timeOnPage, maxScroll }) });
});
// Add some visual polish: animate elements on load
setTimeout(() => {
document.body.classList.add('loaded');
}, 100);
console.log('🚀 DevBox landing page initialized');
console.log(`📡 API endpoint: ${API_BASE_URL}`);
});
// Add CSS animations via JavaScript (if not in CSS file)
const style = document.createElement('style');
style.textContent = `
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-10px);
}
}
body {
opacity: 0;
transition: opacity 0.3s ease;
}
body.loaded {
opacity: 1;
}
`;
document.head.appendChild(style);

362
devbox-front/styles.css Normal file
View File

@ -0,0 +1,362 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Kubernetes-inspired blue color palette */
--primary-blue: #326ce5;
--dark-blue: #1e3a8a;
--light-blue: #60a5fa;
--navy: #1e293b;
--slate: #334155;
--gray-light: #f8fafc;
--gray-medium: #64748b;
--success-green: #10b981;
--text-dark: #0f172a;
--text-light: #475569;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: var(--text-dark);
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--dark-blue) 50%, var(--navy) 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
.header {
padding: 2rem 0;
text-align: center;
}
.logo h1 {
font-size: 1.8rem;
font-weight: 600;
color: white;
letter-spacing: -0.5px;
}
/* Main Content */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 2rem 0;
}
.hero-section {
max-width: 700px;
margin-bottom: 4rem;
}
.hero-title {
font-size: 3.5rem;
font-weight: 700;
color: white;
margin-bottom: 1.5rem;
letter-spacing: -1px;
line-height: 1.1;
}
.hero-subtitle {
font-size: 1.3rem;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 3rem;
font-weight: 300;
line-height: 1.6;
}
/* Email Form */
.email-form {
margin-bottom: 2rem;
}
.input-group {
display: flex;
max-width: 520px;
margin: 0 auto 1rem;
background: white;
border-radius: 12px;
padding: 4px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(96, 165, 250, 0.3);
}
.email-input {
flex: 1;
border: none;
padding: 1.2rem 1.5rem;
font-size: 1rem;
border-radius: 8px;
outline: none;
background: transparent;
color: var(--text-dark);
}
.email-input::placeholder {
color: var(--gray-medium);
}
.submit-btn {
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--dark-blue) 100%);
color: white;
border: none;
padding: 1.2rem 2rem;
font-size: 1rem;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(50, 108, 229, 0.4);
}
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(50, 108, 229, 0.6);
background: linear-gradient(135deg, var(--light-blue) 0%, var(--primary-blue) 100%);
}
.submit-btn:active {
transform: translateY(0);
}
.form-note {
color: rgba(255, 255, 255, 0.8);
font-size: 0.9rem;
margin-top: 1rem;
}
/* Success Message */
.success-message {
display: none;
background: white;
border-radius: 12px;
padding: 2.5rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
max-width: 450px;
margin: 0 auto;
border: 1px solid rgba(96, 165, 250, 0.3);
}
.success-message.show {
display: block;
animation: slideUp 0.5s ease;
}
.success-icon {
font-size: 3rem;
color: var(--success-green);
margin-bottom: 1rem;
}
.success-content h3 {
color: var(--text-dark);
margin-bottom: 0.5rem;
font-size: 1.5rem;
font-weight: 600;
}
.success-content p {
color: var(--text-light);
margin: 0;
line-height: 1.5;
}
/* Features Preview */
.features-preview {
width: 100%;
max-width: 1000px;
margin-bottom: 4rem;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}
.feature-item {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(15px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
padding: 2.5rem 2rem;
text-align: center;
transition: all 0.3s ease;
}
.feature-item:hover {
transform: translateY(-8px);
background: rgba(255, 255, 255, 0.15);
border-color: rgba(96, 165, 250, 0.4);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 1.5rem;
}
.feature-item h3 {
color: white;
font-size: 1.25rem;
margin-bottom: 1rem;
font-weight: 600;
}
.feature-item p {
color: rgba(255, 255, 255, 0.85);
font-size: 0.95rem;
line-height: 1.6;
}
/* Value Proposition */
.value-proposition {
max-width: 600px;
margin-bottom: 3rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(15px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
padding: 3rem 2.5rem;
}
.value-title {
color: white;
font-size: 2rem;
font-weight: 600;
margin-bottom: 1.5rem;
letter-spacing: -0.5px;
}
.value-text {
color: rgba(255, 255, 255, 0.9);
font-size: 1.1rem;
line-height: 1.7;
font-weight: 300;
}
/* Footer */
.footer {
padding: 2rem 0;
text-align: center;
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 2rem;
}
.social-links {
margin-bottom: 1rem;
}
.social-link {
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
margin: 0 1rem;
font-weight: 500;
transition: all 0.3s ease;
padding: 0.5rem;
border-radius: 6px;
}
.social-link:hover {
color: white;
background: rgba(255, 255, 255, 0.1);
}
.footer-text {
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
}
/* Animations */
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive Design */
@media (max-width: 768px) {
.hero-title {
font-size: 2.5rem;
}
.hero-subtitle {
font-size: 1.1rem;
}
.input-group {
flex-direction: column;
gap: 0.5rem;
max-width: 400px;
}
.submit-btn {
width: 100%;
margin-top: 0.5rem;
}
.feature-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.container {
padding: 0 15px;
}
.value-proposition {
padding: 2rem 1.5rem;
}
.value-title {
font-size: 1.75rem;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: 2rem;
}
.feature-item {
padding: 2rem 1.5rem;
}
.value-proposition {
padding: 1.5rem;
}
}
/* Loading state for button */
.submit-btn.loading {
background: var(--gray-medium);
cursor: not-allowed;
transform: none;
}
.submit-btn.loading:hover {
transform: none;
box-shadow: none;
}