Initial commit
|
@ -0,0 +1,49 @@
|
||||||
|
@keyframes pulse-ring {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.33);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
80%, 100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Particles */
|
||||||
|
.particles-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.particle {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(59, 130, 246, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float-particle {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translate(50px, -50px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translate(100px, 0);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translate(50px, 50px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
function initFormHandling() {
|
||||||
|
const form = document.getElementById('contact-form');
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const submitBtn = form.querySelector('button[type="submit"]');
|
||||||
|
const originalBtnText = submitBtn.innerHTML;
|
||||||
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const data = {
|
||||||
|
name: formData.get('name'),
|
||||||
|
email: formData.get('email'),
|
||||||
|
subject: formData.get('subject'),
|
||||||
|
message: formData.get('message')
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/send-email', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(result.details || result.error || 'Failed to send message');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
showAlert('success', 'Message sent successfully! I will get back to you soon.');
|
||||||
|
form.reset();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Form submission error:', error);
|
||||||
|
showAlert('error', `Something went wrong: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
submitBtn.innerHTML = originalBtnText;
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... existing code ...
|
|
@ -0,0 +1,135 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Case Study: SharePoint & M365 Migrations | Daniel LaForce</title>
|
||||||
|
<meta name="description" content="Enterprise migration case study: Seamless transition from legacy systems to Microsoft 365 with automation, SharePoint, OneDrive, and Teams integration.">
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
.portfolio-header {
|
||||||
|
padding: 2rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-section {
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
margin-top: 0;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<!-- Header/Nav -->
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="logo">
|
||||||
|
<a href="index.html">
|
||||||
|
<span class="logo-text-glow">Daniel LaForce</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<a href="index.html#home" class="nav-link">Home</a>
|
||||||
|
<a href="index.html#portfolio" class="nav-link active">Portfolio</a>
|
||||||
|
<a href="index.html#services" class="nav-link">Services</a>
|
||||||
|
<a href="index.html#lab" class="nav-link">Live Lab</a>
|
||||||
|
<a href="resume.html" class="nav-link">Resume</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Case Study Content -->
|
||||||
|
<section class="portfolio-header">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="portfolio-title">Case Study: SharePoint & M365 Migrations</h1>
|
||||||
|
<p class="portfolio-subtitle">Modernizing enterprise collaboration and storage through Microsoft 365</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="portfolio-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="project-card">
|
||||||
|
<div class="project-content">
|
||||||
|
<h3 class="project-title">Client Overview</h3>
|
||||||
|
<p class="project-description">
|
||||||
|
A mid-sized professional services firm relied on Dropbox, Egnyte, Google Workspace, and on-prem file servers to manage daily operations. The IT environment was fragmented, increasing operational complexity and impeding secure collaboration. They needed a scalable solution to unify infrastructure, reduce support load, and enhance productivity.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 class="project-title">Objectives</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Consolidate file and email services under Microsoft 365</li>
|
||||||
|
<li>Minimize business disruption during migration</li>
|
||||||
|
<li>Strengthen compliance, backup, and access control</li>
|
||||||
|
<li>Enable centralized collaboration with Microsoft Teams</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h3 class="project-title">Solution Strategy</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Provisioned a temporary VM to act as a synchronized landing zone for Egnyte and local file server data</li>
|
||||||
|
<li>Used Egnyte’s API and command-line tools to batch-export permissions and files to the sync VM</li>
|
||||||
|
<li>Ran Microsoft’s SharePoint Migration Tool (SPMT) with JSON-based mapping to import content into SharePoint document libraries</li>
|
||||||
|
<li>Automated pre/post migration checks and permissions auditing via PowerShell and Graph API</li>
|
||||||
|
<li>Staged migration of mail and calendar data using MigrationWiz with coexistence enabled between Google Workspace and Exchange Online</li>
|
||||||
|
<li>Provided Teams onboarding with channel templates and cross-platform training</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h3 class="project-title">Results</h3>
|
||||||
|
<ul>
|
||||||
|
<li>12.4 TB of content migrated across four platforms in under 90 days</li>
|
||||||
|
<li>Zero data loss or permission mismatches confirmed via script-based audits</li>
|
||||||
|
<li>Helpdesk load cut by 40% within the first month post-migration</li>
|
||||||
|
<li>Fully adopted Microsoft Teams structure with defined department-based channels and integrated file repositories</li>
|
||||||
|
<li>End-user training boosted post-migration satisfaction to 97%</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="project-skills">
|
||||||
|
<span class="skill-tag">SharePoint Online</span>
|
||||||
|
<span class="skill-tag">OneDrive</span>
|
||||||
|
<span class="skill-tag">Teams</span>
|
||||||
|
<span class="skill-tag">Exchange Online</span>
|
||||||
|
<span class="skill-tag">PowerShell</span>
|
||||||
|
<span class="skill-tag">Migration Tools</span>
|
||||||
|
<span class="skill-tag">Egnyte API</span>
|
||||||
|
<span class="skill-tag">SPMT</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="project-links">
|
||||||
|
<a href="/index.html#portfolio" class="project-link"><i class="fas fa-arrow-left"></i> Back to Portfolio</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-logo">
|
||||||
|
<span class="logo-text-glow">Daniel LaForce</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-links">
|
||||||
|
<a href="/index.html#home">Home</a>
|
||||||
|
<a href="/index.html#portfolio">Portfolio</a>
|
||||||
|
<a href="/index.html#services">Services</a>
|
||||||
|
<a href="/index.html#lab">Live Lab</a>
|
||||||
|
<a href="/resume.html">Resume</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p>© All rights reserved. LaForceIT.com</p>
|
||||||
|
<p class="disclaimer">Custom-built with HTML, CSS, and JavaScript.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,170 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Under Construction | LaForceIT</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.construction-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-content {
|
||||||
|
text-align: center;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-title {
|
||||||
|
font-size: 3rem;
|
||||||
|
color: #00ff9d;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-subtitle {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-status {
|
||||||
|
background: rgba(0, 255, 157, 0.1);
|
||||||
|
border: 1px solid rgba(0, 255, 157, 0.3);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-line {
|
||||||
|
color: #00ff9d;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-line::before {
|
||||||
|
content: '> ';
|
||||||
|
color: #00ff9d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 2rem 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #00ff9d, #00b8ff);
|
||||||
|
width: 0;
|
||||||
|
animation: progress 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress {
|
||||||
|
0% { width: 0; }
|
||||||
|
50% { width: 100%; }
|
||||||
|
100% { width: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-back {
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.construction-back:hover {
|
||||||
|
color: #00ff9d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binary-rain {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="construction-container">
|
||||||
|
<div class="binary-rain" id="binary-rain"></div>
|
||||||
|
<div class="construction-content">
|
||||||
|
<h1 class="construction-title">UNDER CONSTRUCTION</h1>
|
||||||
|
<p class="construction-subtitle">This service is currently being deployed and configured</p>
|
||||||
|
|
||||||
|
<div class="construction-status">
|
||||||
|
<div class="status-line">Initializing deployment sequence...</div>
|
||||||
|
<div class="status-line">Configuring infrastructure components...</div>
|
||||||
|
<div class="status-line">Deploying service containers...</div>
|
||||||
|
<div class="status-line">Establishing secure connections...</div>
|
||||||
|
<div class="status-line">Optimizing performance parameters...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="construction-progress">
|
||||||
|
<div class="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="https://laforceit.com" class="construction-back">
|
||||||
|
<i class="fas fa-arrow-left"></i>
|
||||||
|
Return to LaForceIT
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Create binary rain effect
|
||||||
|
const binaryRain = document.getElementById('binary-rain');
|
||||||
|
const chars = '01';
|
||||||
|
|
||||||
|
function createBinaryRain() {
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
|
span.style.position = 'absolute';
|
||||||
|
span.style.left = Math.random() * 100 + '%';
|
||||||
|
span.style.top = '-20px';
|
||||||
|
span.style.color = '#00ff9d';
|
||||||
|
span.style.opacity = Math.random() * 0.5 + 0.5;
|
||||||
|
span.style.fontSize = Math.random() * 10 + 10 + 'px';
|
||||||
|
span.style.fontFamily = 'JetBrains Mono, monospace';
|
||||||
|
|
||||||
|
binaryRain.appendChild(span);
|
||||||
|
|
||||||
|
let pos = -20;
|
||||||
|
const fall = setInterval(() => {
|
||||||
|
pos += 2;
|
||||||
|
span.style.top = pos + 'px';
|
||||||
|
|
||||||
|
if (pos > window.innerHeight) {
|
||||||
|
clearInterval(fall);
|
||||||
|
span.remove();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(createBinaryRain, 100);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* Cloudflare Pages Function - /api/send-email
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function onRequestPost(context) {
|
||||||
|
const { name, email, subject, message } = await context.request.json();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if API key is configured
|
||||||
|
if (!context.env.MAILERSEND_API_KEY) {
|
||||||
|
throw new Error('MailerSend API key is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Attempting to send email with MailerSend...');
|
||||||
|
|
||||||
|
const response = await fetch("https://api.mailersend.com/v1/email", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + context.env.MAILERSEND_API_KEY,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ from: {
|
||||||
|
email: email,
|
||||||
|
name: name
|
||||||
|
},
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
email: "upwork@example.com",
|
||||||
|
name: "Contact Form"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
subject: `LaForceIT Contact Form: ${subject}`,
|
||||||
|
html: `
|
||||||
|
<h2>New Contact Message on LaForceIT.com</h2>
|
||||||
|
<p><strong>Name:</strong> ${name}</p>
|
||||||
|
<p><strong>Email:</strong> ${email}</p>
|
||||||
|
<p><strong>Message:</strong><br>${message.replace(/\n/g, "<br>")}</p>
|
||||||
|
`,
|
||||||
|
reply_to: [
|
||||||
|
{
|
||||||
|
email,
|
||||||
|
name
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const responseData = await response.text();
|
||||||
|
console.log("MailerSend Response:", responseData);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorDetail = responseData ? JSON.parse(responseData) : {};
|
||||||
|
throw new Error(`MailerSend API error (${response.status}): ${JSON.stringify(errorDetail)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true }), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Email Send Error:", err);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
error: "Failed to send email",
|
||||||
|
details: err.message,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
# This file ensures the images directory exists in the repository.
|
||||||
|
# You should add the following images:
|
||||||
|
|
||||||
|
# Infrastructure Dashboard Images
|
||||||
|
# - argobox-dashboard.jpg - Screenshot of your ArgoBox dashboard
|
||||||
|
# - ansible-automation.jpg - Visualization of Ansible automation workflows
|
||||||
|
# - zero-trust.jpg - Diagram or screenshot of zero trust architecture
|
||||||
|
# - kubernetes-lab.jpg - Screenshot of K3s cluster dashboard
|
||||||
|
|
||||||
|
# Project Portfolio Images
|
||||||
|
# - prospector-pulse.jpg - Screenshot or mockup of Prospector Pulse mapping interface
|
||||||
|
# - terramappix.jpg - Screenshot or mockup of TerraMappix interface
|
||||||
|
# - cloudflare-ddns.jpg - Visual representation of the DDNS workflow
|
||||||
|
# - car-price-ml.jpg - Screenshot of the car price prediction model interface
|
||||||
|
# - wifi-cage.jpg - Photo or diagram of the Wi-Fi testing cage
|
||||||
|
# - vsadeployer.jpg - Screenshot of the VSADeployer interface
|
||||||
|
|
||||||
|
# These images should be approximately 800x450px in size for optimal display in the portfolio.
|
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 199 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 131 KiB |
After Width: | Height: | Size: 208 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 308 KiB |
After Width: | Height: | Size: 362 KiB |
After Width: | Height: | Size: 233 KiB |
After Width: | Height: | Size: 512 KiB |
After Width: | Height: | Size: 294 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 239 KiB |
After Width: | Height: | Size: 255 KiB |
After Width: | Height: | Size: 245 KiB |
After Width: | Height: | Size: 241 KiB |
After Width: | Height: | Size: 242 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 512 KiB |
After Width: | Height: | Size: 355 KiB |
After Width: | Height: | Size: 393 KiB |
After Width: | Height: | Size: 170 KiB |
After Width: | Height: | Size: 372 KiB |
After Width: | Height: | Size: 717 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 898 KiB |
|
@ -0,0 +1 @@
|
||||||
|
{"name":"LaForceIT","short_name":"LaForceIT","icons":[{"src":"/images/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/images/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#1e293b","background_color":"#0f172a","display":"standalone"}
|
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 279 KiB |
After Width: | Height: | Size: 338 KiB |
After Width: | Height: | Size: 997 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 167 KiB |
|
@ -0,0 +1,927 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Daniel LaForce | Enterprise IT Solutions Architect & Security Expert</title>
|
||||||
|
|
||||||
|
<!-- SEO Meta Tags -->
|
||||||
|
<meta name="description" content="Senior IT Professional specializing in enterprise infrastructure, cybersecurity, and digital transformation. Expert consultant for both project-based and full-time opportunities.">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="images/favicon.png">
|
||||||
|
|
||||||
|
<!-- Google Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- FontAwesome -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
|
||||||
|
<!-- CSS -->
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Resume-specific styles */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-container {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 2rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: var(--card-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-name {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-contact {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-contact-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-contact-item i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-section {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 2rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-section-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: var(--accent);
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-section-title::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 50px;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--accent-gradient);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-company {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-period {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-description {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-achievements {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-achievement {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-job-achievement i {
|
||||||
|
color: var(--accent);
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-skills {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-skill-category {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-skill-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-skill-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-skill {
|
||||||
|
background-color: rgba(59, 130, 246, 0.1);
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education-institution {
|
||||||
|
color: var(--accent);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education-period {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education-grade {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-education-skills {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-projects {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-project {
|
||||||
|
background-color: rgba(59, 130, 246, 0.1);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-project-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-project-tech {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-project-tech-item {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-awards {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-award {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-award i {
|
||||||
|
color: var(--accent);
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume-download {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--dark-bg);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding: 2rem 0;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.resume-skills, .resume-projects {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
.contact-links a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: var(--secondary-bg);
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
.contact-links a:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.contact-links a {
|
||||||
|
color: black;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call to Action Styles */
|
||||||
|
.cta-banner {
|
||||||
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(37, 99, 235, 0.2));
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 2.5rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-content {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-description {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlights {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlight {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlight:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
border-color: rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlight i {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlight span {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-btn {
|
||||||
|
min-width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cta-title {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-description {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-highlight {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Bubble Animation -->
|
||||||
|
<div class="bubbles">
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
<div class="bubble"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="logo">
|
||||||
|
<a href="index.html">
|
||||||
|
<span class="logo-text-glow">Daniel LaForce</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<a href="index.html" class="nav-link active">Home</a>
|
||||||
|
<a href="index.html#portfolio" class="nav-link">Portfolio</a>
|
||||||
|
<a href="index.html#services" class="nav-link">Services</a>
|
||||||
|
<a href="index.html#lab" class="nav-link">Live Lab</a>
|
||||||
|
<a href="resume.html" class="nav-link">Resume</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav-buttons">
|
||||||
|
<a href="dashboard.html" class="dashboard-link" target="_blank">
|
||||||
|
<span class="live-indicator"></span>
|
||||||
|
<span>Live Dashboard</span>
|
||||||
|
</a>
|
||||||
|
<button class="menu-toggle" aria-label="Toggle menu">
|
||||||
|
<i class="fas fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<br>
|
||||||
|
<!-- Resume Content -->
|
||||||
|
<div class="resume-container">
|
||||||
|
<div class="resume-header">
|
||||||
|
<h1 class="resume-name">Daniel LaForce</h1>
|
||||||
|
<h2 class="resume-title">Enterprise IT Solutions Architect & Security Expert</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Experience Section -->
|
||||||
|
<div class="resume-section">
|
||||||
|
<h2 class="resume-section-title">Professional Experience</h2>
|
||||||
|
|
||||||
|
<div class="resume-job">
|
||||||
|
<div class="resume-job-header">
|
||||||
|
<h3 class="resume-job-title">Freelance IT Consultant – Infrastructure, Security & GIS Solutions</h3>
|
||||||
|
<div class="resume-job-company">Inovin LLC</div>
|
||||||
|
<div class="resume-job-period">April 2023 – Present | Colorado Springs, CO</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Developed TerraTracer, a GIS mapping platform used by independent mining claim prospectors to automate boundary plotting, terrain-informed site selection, and BLM/state compliance, reducing manual research by 85%.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Consulted on virtualization and DevOps projects for SMB clients, implementing Proxmox/VMware solutions, containerizing legacy apps with Docker, and orchestrating microservices with Kubernetes.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Built secure cloud environments integrating Azure AD, SSO, and VPN/firewall solutions, increasing operational resilience while aligning with HIPAA and zero-trust principles.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Automated infrastructure tasks via PowerShell, Python, and Bash, optimizing system deployment, backup routines, and monitoring pipelines.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Administered Azure AD security policies and MFA enforcement, reducing monthly vulnerabilities by at least 20% across client environments.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Served as Interim Virtual Chief Security Officer for 11 clients, conducting penetration testing using BURP Professional and managing enterprise security stack deployment including Blackpoint & Webroot (Endpoint), DNSFilter (Network), and ThreatLocker (Application Control).</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-job">
|
||||||
|
<div class="resume-job-header">
|
||||||
|
<h3 class="resume-job-title">Project Engineer</h3>
|
||||||
|
<div class="resume-job-company">Anchor Point IT Solutions</div>
|
||||||
|
<div class="resume-job-period">December 2023 - June 2024</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Executed 5 Microsoft 365 and SharePoint migrations with downtime reduced by 60%.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Implemented comprehensive security stack including BlueMira & DUO (Identity), SentinelOne (Endpoint), ConnectSecure (Network), and Email Mesh Security.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Reduced security incidents by 83% through security implementations and improved system updates by 60%.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Managed approximately 235 clients with environment sizes ranging from 30 to 1000 people.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-job">
|
||||||
|
<div class="resume-job-header">
|
||||||
|
<h3 class="resume-job-title">Network Engineer II</h3>
|
||||||
|
<div class="resume-job-company">NOVUS Professional Services, Inc.</div>
|
||||||
|
<div class="resume-job-period">August 2022 - March 2023</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Designed and implemented a WiFi testing cage for remote firmware testing of router models (Wave2, WiFi6).</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Developed Python automation for Plume device registration and Ansible scripts for network tasks.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Managed RF testing and signal verification across 9 headends and maintained network connectivity for ~100 routers across two sites.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-job">
|
||||||
|
<div class="resume-job-header">
|
||||||
|
<h3 class="resume-job-title">Field Engineer</h3>
|
||||||
|
<div class="resume-job-company">COMPUTEK Dental Systems</div>
|
||||||
|
<div class="resume-job-period">December 2021 - August 2022</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Managed IT infrastructure for 300+ dental practices (3700+ workstations, ~300 Windows Servers).</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Developed VSA Deployer application (Kaseya VSA RMM) for agent installation, reducing deployment time by 80%.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Maintained server, Hyper-V VM, and MySQL/MSSQL backups using N-Able Backups, Windows Server Backups and AIS.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-job">
|
||||||
|
<div class="resume-job-header">
|
||||||
|
<h3 class="resume-job-title">End-to-End Systems Engineer</h3>
|
||||||
|
<div class="resume-job-company">Lockheed Martin</div>
|
||||||
|
<div class="resume-job-period">June 2018 - December 2019</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Maintained 2 IRON databases (AIMS and RAPID) with ~1800 configurations each, ensuring 100% database accuracy.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Collaborated with engineering team to implement Remote Tracking Station (RTS) configurations.</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
<span>Identified and documented an average of 27 critical defects every month.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Skills Section -->
|
||||||
|
<div class="resume-section">
|
||||||
|
<h2 class="resume-section-title">Technical Skills</h2>
|
||||||
|
|
||||||
|
<div class="resume-skills">
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Cloud & Infrastructure</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">Microsoft Azure</span>
|
||||||
|
<span class="resume-skill">Azure AD</span>
|
||||||
|
<span class="resume-skill">SharePoint</span>
|
||||||
|
<span class="resume-skill">Teams</span>
|
||||||
|
<span class="resume-skill">AWS</span>
|
||||||
|
<span class="resume-skill">EC2</span>
|
||||||
|
<span class="resume-skill">S3</span>
|
||||||
|
<span class="resume-skill">GCP</span>
|
||||||
|
<span class="resume-skill">VMware ESXi/vSphere</span>
|
||||||
|
<span class="resume-skill">Hyper-V</span>
|
||||||
|
<span class="resume-skill">Docker</span>
|
||||||
|
<span class="resume-skill">Kubernetes (K3s)</span>
|
||||||
|
<span class="resume-skill">ProxMox</span>
|
||||||
|
<span class="resume-skill">Traefik</span>
|
||||||
|
<span class="resume-skill">CI/CD</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Networking</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">LAN/WAN</span>
|
||||||
|
<span class="resume-skill">VPN</span>
|
||||||
|
<span class="resume-skill">OSPF</span>
|
||||||
|
<span class="resume-skill">EIGRP</span>
|
||||||
|
<span class="resume-skill">STP</span>
|
||||||
|
<span class="resume-skill">HSRP</span>
|
||||||
|
<span class="resume-skill">VTP</span>
|
||||||
|
<span class="resume-skill">IPv4/IPv6</span>
|
||||||
|
<span class="resume-skill">ACLs</span>
|
||||||
|
<span class="resume-skill">NAT/PAT</span>
|
||||||
|
<span class="resume-skill">DNS/DHCP</span>
|
||||||
|
<span class="resume-skill">VLANs</span>
|
||||||
|
<span class="resume-skill">QoS</span>
|
||||||
|
<span class="resume-skill">Cisco Routers</span>
|
||||||
|
<span class="resume-skill">Catalyst Switches</span>
|
||||||
|
<span class="resume-skill">ASA Firewalls</span>
|
||||||
|
<span class="resume-skill">UniFi Systems</span>
|
||||||
|
<span class="resume-skill">pfSense</span>
|
||||||
|
<span class="resume-skill">OPNsense</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Programming/Scripting</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">Python</span>
|
||||||
|
<span class="resume-skill">JavaScript (Node.js)</span>
|
||||||
|
<span class="resume-skill">PowerShell</span>
|
||||||
|
<span class="resume-skill">Bash</span>
|
||||||
|
<span class="resume-skill">C/C++</span>
|
||||||
|
<span class="resume-skill">SQL</span>
|
||||||
|
<span class="resume-skill">MySQL/MSSQL</span>
|
||||||
|
<span class="resume-skill">AutoHotkey</span>
|
||||||
|
<span class="resume-skill">Ansible</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Security</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">Penetration Testing</span>
|
||||||
|
<span class="resume-skill">BURP Professional</span>
|
||||||
|
<span class="resume-skill">SentinelOne</span>
|
||||||
|
<span class="resume-skill">Blackpoint</span>
|
||||||
|
<span class="resume-skill">Webroot</span>
|
||||||
|
<span class="resume-skill">ThreatLocker</span>
|
||||||
|
<span class="resume-skill">Zero-Trust Architecture</span>
|
||||||
|
<span class="resume-skill">HIPAA Compliance</span>
|
||||||
|
<span class="resume-skill">BlueMira</span>
|
||||||
|
<span class="resume-skill">DUO</span>
|
||||||
|
<span class="resume-skill">DNSFilter</span>
|
||||||
|
<span class="resume-skill">Email Mesh Security</span>
|
||||||
|
<span class="resume-skill">MFA</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Operating Systems</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">Windows Server</span>
|
||||||
|
<span class="resume-skill">Active Directory</span>
|
||||||
|
<span class="resume-skill">Group Policy</span>
|
||||||
|
<span class="resume-skill">Exchange Server</span>
|
||||||
|
<span class="resume-skill">Linux</span>
|
||||||
|
<span class="resume-skill">Ubuntu</span>
|
||||||
|
<span class="resume-skill">CentOS</span>
|
||||||
|
<span class="resume-skill">Debian</span>
|
||||||
|
<span class="resume-skill">Fedora</span>
|
||||||
|
<span class="resume-skill">Kali Linux</span>
|
||||||
|
<span class="resume-skill">Cisco IOS</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">GIS & Data Science</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">Geospatial Mapping</span>
|
||||||
|
<span class="resume-skill">GeoPandas</span>
|
||||||
|
<span class="resume-skill">GDAL/OGR</span>
|
||||||
|
<span class="resume-skill">KML Generation</span>
|
||||||
|
<span class="resume-skill">Data Visualization</span>
|
||||||
|
<span class="resume-skill">Machine Learning</span>
|
||||||
|
<span class="resume-skill">TensorFlow</span>
|
||||||
|
<span class="resume-skill">PyTorch</span>
|
||||||
|
<span class="resume-skill">Scikit-learn</span>
|
||||||
|
<span class="resume-skill">Pandas</span>
|
||||||
|
<span class="resume-skill">NumPy</span>
|
||||||
|
<span class="resume-skill">Matplotlib</span>
|
||||||
|
<span class="resume-skill">Seaborn</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-skill-category">
|
||||||
|
<h3 class="resume-skill-title">Management & PSA Tools</h3>
|
||||||
|
<div class="resume-skill-list">
|
||||||
|
<span class="resume-skill">N-able N-central</span>
|
||||||
|
<span class="resume-skill">Kaseya VSA</span>
|
||||||
|
<span class="resume-skill">ConnectWise Automate</span>
|
||||||
|
<span class="resume-skill">ConnectWise PSA</span>
|
||||||
|
<span class="resume-skill">PRTG</span>
|
||||||
|
<span class="resume-skill">SolarWinds</span>
|
||||||
|
<span class="resume-skill">Addigy</span>
|
||||||
|
<span class="resume-skill">ImmyBot</span>
|
||||||
|
<span class="resume-skill">Commit Ticket CRM</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Education Section -->
|
||||||
|
<div class="resume-section">
|
||||||
|
<h2 class="resume-section-title">Education</h2>
|
||||||
|
|
||||||
|
<div class="resume-education">
|
||||||
|
<h3 class="resume-education-title">Bachelor of Science, Computer Science</h3>
|
||||||
|
<div class="resume-education-institution">Western Governors University</div>
|
||||||
|
<div class="resume-education-period">June 2019 - April 2023</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-award"></i>
|
||||||
|
<span>Activities and societies: Sigma Alpha Pi Honors Society (The National Society of Leadership and Success)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-education-skills">
|
||||||
|
<strong>Skills:</strong> Python (Programming Language) · Project Management · Machine Learning · Team Leadership
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-education">
|
||||||
|
<h3 class="resume-education-title">Cyber Ops</h3>
|
||||||
|
<div class="resume-education-institution">Cisco Networking Academy</div>
|
||||||
|
<div class="resume-education-period">2018</div>
|
||||||
|
<div class="resume-education-skills">
|
||||||
|
<strong>Skills:</strong> Networking · Cyber-security · Cisco Technologies
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-education">
|
||||||
|
<h3 class="resume-education-title">Computer Science and Innovation</h3>
|
||||||
|
<div class="resume-education-institution">University of Colorado Colorado Springs (UCCS)</div>
|
||||||
|
<div class="resume-education-period">January 2017 - December 2017</div>
|
||||||
|
<div class="resume-education-grade">Grade: 4.0</div>
|
||||||
|
<div class="resume-education-skills">
|
||||||
|
<strong>Skills:</strong> C++ · Team Leadership
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-education">
|
||||||
|
<h3 class="resume-education-title">Associate's degree, General Studies</h3>
|
||||||
|
<div class="resume-education-institution">Pikes Peak State College</div>
|
||||||
|
<div class="resume-education-period">2015 - 2018</div>
|
||||||
|
<div class="resume-education-grade">Grade: 4.0</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-award"></i>
|
||||||
|
<span>Activities and societies: Phi Theta Kappa Honor Society</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-education-skills">
|
||||||
|
<strong>Skills:</strong> Linux · Virtual Server · C++ · System Administration · Wireless
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-education">
|
||||||
|
<h3 class="resume-education-title">Associate's Degree, Computer Wide Area Networks</h3>
|
||||||
|
<div class="resume-education-institution">Front Range Community College</div>
|
||||||
|
<div class="resume-education-period">2014 - 2016</div>
|
||||||
|
<div class="resume-education-grade">Grade: 3.87</div>
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-award"></i>
|
||||||
|
<span>Activities and societies: Phi Theta Kappa Honor Society</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-award"></i>
|
||||||
|
<span>Magna Cum Laude honors</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resume-education-skills">
|
||||||
|
<strong>Skills:</strong> Virtual Server · C++ · Networking · Wireless
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certifications Section -->
|
||||||
|
<div class="resume-section">
|
||||||
|
<h2 class="resume-section-title">Certifications</h2>
|
||||||
|
|
||||||
|
<div class="resume-job-achievements">
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>Project+ (CompTIA)</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>Network+ (FRCC)</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>A+ (FRCC)</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>CCNA Cyber Ops (2021)</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>CCNA Routing & Switching (2021)</span>
|
||||||
|
</div>
|
||||||
|
<div class="resume-job-achievement">
|
||||||
|
<i class="fas fa-certificate"></i>
|
||||||
|
<span>Virtual Cyber Security Officer Training (vCSO Advanced)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Projects Section -->
|
||||||
|
<div class="resume-section">
|
||||||
|
<h2 class="resume-section-title">Notable Projects</h2>
|
||||||
|
|
||||||
|
<div class="resume-projects">
|
||||||
|
<div class="resume-project">
|
||||||
|
<h3 class="resume-project-title">Enterprise Data Center Deployment</h3>
|
||||||
|
<p>Complete data center build-out from ground up, including rack design, power distribution, cooling optimization, and redundant network topology implementation. Integrated custom-fabricated components using mechanical engineering principles and 3D-printed mounting solutions.</p>
|
||||||
|
<div class="resume-project-tech">
|
||||||
|
<span class="resume-project-tech-item">Infrastructure Design</span>
|
||||||
|
<span class="resume-project-tech-item">Network Engineering</span>
|
||||||
|
<span class="resume-project-tech-item">CAD/3D Modeling</span>
|
||||||
|
<span class="resume-project-tech-item">Thermal Management</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-project">
|
||||||
|
<h3 class="resume-project-title">TerraMappix Land Claim Mapping Service</h3>
|
||||||
|
<p>Developed a Python/Node.js geospatial engine powering TerraMappix that converts BLM coordinates and legal metes-and-bounds into precise, GIS-ready property maps with accuracy down to 1.08 inches, built for mining firms and land managers.</p>
|
||||||
|
<div class="resume-project-tech">
|
||||||
|
<span class="resume-project-tech-item">Python</span>
|
||||||
|
<span class="resume-project-tech-item">Node.js</span>
|
||||||
|
<span class="resume-project-tech-item">Geospatial Analysis</span>
|
||||||
|
<span class="resume-project-tech-item">GIS</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-project">
|
||||||
|
<h3 class="resume-project-title">Infrastructure Monitoring Dashboard</h3>
|
||||||
|
<p>A custom, real-time monitoring solution for home lab infrastructure featuring metrics visualization via Grafana, service status tracking, and resource allocation management, built with web technologies and pulling data from Prometheus.</p>
|
||||||
|
<div class="resume-project-tech">
|
||||||
|
<span class="resume-project-tech-item">Kubernetes (K3s)</span>
|
||||||
|
<span class="resume-project-tech-item">Prometheus</span>
|
||||||
|
<span class="resume-project-tech-item">Grafana</span>
|
||||||
|
<span class="resume-project-tech-item">Docker</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-project">
|
||||||
|
<h3 class="resume-project-title">CPE WiFi Firmware Test Bed Prototype</h3>
|
||||||
|
<p>Designed and built a sophisticated testing environment for customer premises equipment (CPE) with remote access capabilities for firmware development and QA testing, including thermal safeguards and optimized wiring structure with Visio diagrams.</p>
|
||||||
|
<div class="resume-project-tech">
|
||||||
|
<span class="resume-project-tech-item">Network Engineering</span>
|
||||||
|
<span class="resume-project-tech-item">Visio/Diagramming</span>
|
||||||
|
<span class="resume-project-tech-item">Hardware Integration</span>
|
||||||
|
<span class="resume-project-tech-item">Firmware Testing</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resume-download">
|
||||||
|
<!-- Download button removed for Upwork compliance -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-logo">
|
||||||
|
<span class="logo-text-glow">Daniel LaForce</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-links">
|
||||||
|
<a href="#home">Home</a>
|
||||||
|
<a href="#portfolio">Portfolio</a>
|
||||||
|
<a href="#services">Services</a>
|
||||||
|
<a href="#lab">Live Lab</a>
|
||||||
|
<a href="resume.html">Resume</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p>© All rights reserved. LaForceIT.com</p>
|
||||||
|
<p class="disclaimer">Custom-built with HTML, CSS, and JavaScript.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- JavaScript -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,600 @@
|
||||||
|
/**
|
||||||
|
* Main JavaScript file for argobox.com
|
||||||
|
* Handles animations, interactions, and dynamic content
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Initialize all website functionality
|
||||||
|
initNavigation();
|
||||||
|
initParticlesAndIcons();
|
||||||
|
initRoleAnimation();
|
||||||
|
initTerminalTyping();
|
||||||
|
initSolutionsCarousel();
|
||||||
|
initScrollReveal();
|
||||||
|
updateMetrics();
|
||||||
|
updateYear();
|
||||||
|
|
||||||
|
// Initialize form handling
|
||||||
|
const contactForm = document.getElementById('contact-form');
|
||||||
|
if (contactForm) {
|
||||||
|
initFormHandling(contactForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight navigation on scroll
|
||||||
|
setupScrollSpy();
|
||||||
|
|
||||||
|
// Setup role switching with improved timing
|
||||||
|
setupRoleSwitching();
|
||||||
|
|
||||||
|
// Other initializations...
|
||||||
|
setupFloatingIcons();
|
||||||
|
setupParticles();
|
||||||
|
setupRevealAnimations();
|
||||||
|
initializeSlider();
|
||||||
|
|
||||||
|
// Contact form handling
|
||||||
|
setupContactForm();
|
||||||
|
|
||||||
|
// Mobile menu toggle
|
||||||
|
document.querySelector('.menu-toggle').addEventListener('click', function() {
|
||||||
|
document.querySelector('.nav-menu').classList.toggle('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navbar effects on scroll
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
const navbar = document.querySelector('.navbar');
|
||||||
|
|
||||||
|
if (window.scrollY > 50) {
|
||||||
|
navbar.classList.add('scrolled');
|
||||||
|
} else {
|
||||||
|
navbar.classList.remove('scrolled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up navigation functionality - mobile menu and scroll spy
|
||||||
|
*/
|
||||||
|
function initNavigation() {
|
||||||
|
// Mobile menu toggle
|
||||||
|
const menuToggle = document.querySelector('.menu-toggle');
|
||||||
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
|
||||||
|
if (menuToggle && navMenu) {
|
||||||
|
menuToggle.addEventListener('click', function() {
|
||||||
|
navMenu.classList.toggle('active');
|
||||||
|
menuToggle.setAttribute('aria-expanded',
|
||||||
|
menuToggle.getAttribute('aria-expanded') === 'true' ? 'false' : 'true');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation scroll spy
|
||||||
|
const sections = document.querySelectorAll('section[id]');
|
||||||
|
const navLinks = document.querySelectorAll('.nav-link');
|
||||||
|
|
||||||
|
function updateActiveNavLink() {
|
||||||
|
let scrollPosition = window.scrollY + 100;
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
const sectionTop = section.offsetTop;
|
||||||
|
const sectionHeight = section.offsetHeight;
|
||||||
|
const sectionId = section.getAttribute('id');
|
||||||
|
|
||||||
|
if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) {
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.classList.remove('active');
|
||||||
|
if (link.getAttribute('href') === `#${sectionId}`) {
|
||||||
|
link.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navbar style change on scroll
|
||||||
|
const navbar = document.querySelector('.navbar');
|
||||||
|
|
||||||
|
function updateNavbarStyle() {
|
||||||
|
if (window.scrollY > 50) {
|
||||||
|
navbar?.classList.add('scrolled');
|
||||||
|
} else {
|
||||||
|
navbar?.classList.remove('scrolled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
updateActiveNavLink();
|
||||||
|
updateNavbarStyle();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial call to set correct states
|
||||||
|
updateActiveNavLink();
|
||||||
|
updateNavbarStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create background particles and floating tech icons
|
||||||
|
*/
|
||||||
|
function initParticlesAndIcons() {
|
||||||
|
createBackgroundParticles();
|
||||||
|
createFloatingIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create animated background particles
|
||||||
|
*/
|
||||||
|
function createBackgroundParticles() {
|
||||||
|
const particlesContainer = document.getElementById('particles-container');
|
||||||
|
|
||||||
|
if (!particlesContainer) return;
|
||||||
|
|
||||||
|
particlesContainer.innerHTML = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < 40; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.classList.add('particle');
|
||||||
|
|
||||||
|
// Random size, opacity, and position
|
||||||
|
const size = Math.random() * 4 + 1;
|
||||||
|
const opacity = Math.random() * 0.3 + 0.1;
|
||||||
|
|
||||||
|
particle.style.width = `${size}px`;
|
||||||
|
particle.style.height = `${size}px`;
|
||||||
|
particle.style.opacity = opacity;
|
||||||
|
|
||||||
|
// Position randomly with some clustering toward top areas
|
||||||
|
const xPos = Math.random() * 100;
|
||||||
|
const yPos = Math.random() * 100;
|
||||||
|
|
||||||
|
particle.style.left = `${xPos}%`;
|
||||||
|
particle.style.top = `${yPos}%`;
|
||||||
|
|
||||||
|
// Animation properties
|
||||||
|
const duration = Math.random() * 20 + 10;
|
||||||
|
particle.style.animationDuration = `${duration}s`;
|
||||||
|
particle.style.animationDelay = `${Math.random() * -duration}s`;
|
||||||
|
|
||||||
|
// Add particle animation
|
||||||
|
particle.style.animation = `float-particle ${duration}s linear infinite`;
|
||||||
|
|
||||||
|
particlesContainer.appendChild(particle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create floating tech icons in the background
|
||||||
|
*/
|
||||||
|
function createFloatingIcons() {
|
||||||
|
const iconContainer = document.getElementById('floating-icons');
|
||||||
|
|
||||||
|
if (!iconContainer) return;
|
||||||
|
|
||||||
|
iconContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// Tech-related unicode symbols and fontawesome classes
|
||||||
|
const icons = [
|
||||||
|
'⚙️', '💻', '🔒', '🔌', '🌐', '☁️', '📊',
|
||||||
|
'fa-server', 'fa-network-wired', 'fa-database',
|
||||||
|
'fa-code-branch', 'fa-cloud', 'fa-shield-alt'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
const icon = document.createElement('div');
|
||||||
|
icon.classList.add('floating-icon');
|
||||||
|
|
||||||
|
const iconType = icons[Math.floor(Math.random() * icons.length)];
|
||||||
|
|
||||||
|
// Handle both unicode and font awesome
|
||||||
|
if (iconType.startsWith('fa-')) {
|
||||||
|
const faIcon = document.createElement('i');
|
||||||
|
faIcon.className = `fas ${iconType}`;
|
||||||
|
icon.appendChild(faIcon);
|
||||||
|
} else {
|
||||||
|
icon.textContent = iconType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random size and position
|
||||||
|
const size = Math.random() * 24 + 16;
|
||||||
|
icon.style.fontSize = `${size}px`;
|
||||||
|
|
||||||
|
// Position
|
||||||
|
icon.style.left = `${Math.random() * 100}%`;
|
||||||
|
icon.style.bottom = `-50px`;
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
icon.style.animationDuration = `${Math.random() * 30 + 20}s`;
|
||||||
|
icon.style.animationDelay = `${Math.random() * 10}s`;
|
||||||
|
|
||||||
|
iconContainer.appendChild(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize role rotation in the hero section
|
||||||
|
*/
|
||||||
|
function initRoleAnimation() {
|
||||||
|
const roles = document.querySelectorAll('.role');
|
||||||
|
const description = document.getElementById('role-description');
|
||||||
|
let currentIndex = 0;
|
||||||
|
|
||||||
|
function updateRole() {
|
||||||
|
roles.forEach(role => role.classList.remove('active'));
|
||||||
|
roles[currentIndex].classList.add('active');
|
||||||
|
description.textContent = roles[currentIndex].getAttribute('data-description');
|
||||||
|
description.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
description.style.opacity = '1';
|
||||||
|
}, 100);
|
||||||
|
currentIndex = (currentIndex + 1) % roles.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRole(); // Set initial state
|
||||||
|
setInterval(updateRole, 5000); // Switch roles every 5 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize terminal typing animation in the hero section
|
||||||
|
*/
|
||||||
|
function initTerminalTyping() {
|
||||||
|
const terminalText = document.getElementById('terminal-text');
|
||||||
|
if (!terminalText) return;
|
||||||
|
|
||||||
|
const terminalMessages = [
|
||||||
|
"> Ready for deployment...",
|
||||||
|
"> Reducing operational costs by 30%",
|
||||||
|
"> Improving system reliability to 99.9%",
|
||||||
|
"> Accelerating digital transformation",
|
||||||
|
"> Enhancing security compliance",
|
||||||
|
"> Streamlining IT workflows",
|
||||||
|
"> Optimizing infrastructure performance",
|
||||||
|
"> Implementing best practices",
|
||||||
|
"> Supporting business objectives"
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentMessage = 0;
|
||||||
|
|
||||||
|
function typeMessage(message, index = 0) {
|
||||||
|
if (index < message.length) {
|
||||||
|
terminalText.textContent = message.substring(0, index + 1);
|
||||||
|
setTimeout(() => typeMessage(message, index + 1), 50 + Math.random() * 50);
|
||||||
|
} else {
|
||||||
|
// Wait before clearing and typing next message
|
||||||
|
setTimeout(clearAndTypeNext, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAndTypeNext() {
|
||||||
|
// Clear the current text with a backspace effect
|
||||||
|
const currentText = terminalText.textContent;
|
||||||
|
|
||||||
|
function backspace(length = currentText.length) {
|
||||||
|
if (length > 0) {
|
||||||
|
terminalText.textContent = currentText.substring(0, length - 1);
|
||||||
|
setTimeout(() => backspace(length - 1), 20);
|
||||||
|
} else {
|
||||||
|
// Move to next message
|
||||||
|
currentMessage = (currentMessage + 1) % terminalMessages.length;
|
||||||
|
setTimeout(() => typeMessage(terminalMessages[currentMessage]), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backspace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the typing animation with the first message
|
||||||
|
typeMessage(terminalMessages[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the solutions carousel
|
||||||
|
*/
|
||||||
|
function initSolutionsCarousel() {
|
||||||
|
const slides = document.querySelectorAll('.solution-slide');
|
||||||
|
const dots = document.querySelectorAll('.slider-dot');
|
||||||
|
|
||||||
|
if (slides.length === 0 || dots.length === 0) return;
|
||||||
|
|
||||||
|
let currentSlide = 0;
|
||||||
|
let slideInterval;
|
||||||
|
|
||||||
|
function showSlide(index) {
|
||||||
|
// Hide all slides
|
||||||
|
slides.forEach(slide => slide.classList.remove('active'));
|
||||||
|
dots.forEach(dot => dot.classList.remove('active'));
|
||||||
|
|
||||||
|
// Show selected slide
|
||||||
|
slides[index].classList.add('active');
|
||||||
|
dots[index].classList.add('active');
|
||||||
|
|
||||||
|
currentSlide = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextSlide() {
|
||||||
|
const next = (currentSlide + 1) % slides.length;
|
||||||
|
showSlide(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add click events to dots
|
||||||
|
dots.forEach((dot, index) => {
|
||||||
|
dot.addEventListener('click', () => {
|
||||||
|
clearInterval(slideInterval);
|
||||||
|
showSlide(index);
|
||||||
|
|
||||||
|
// Restart automatic rotation
|
||||||
|
slideInterval = setInterval(nextSlide, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start automatic rotation
|
||||||
|
slideInterval = setInterval(nextSlide, 5000);
|
||||||
|
|
||||||
|
// Show first slide initially
|
||||||
|
showSlide(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add scroll reveal animations to elements
|
||||||
|
*/
|
||||||
|
function initScrollReveal() {
|
||||||
|
const revealElements = document.querySelectorAll('.section-header, .service-card, .project-card, .lab-card, .timeline-item, .contact-item');
|
||||||
|
|
||||||
|
revealElements.forEach(element => {
|
||||||
|
element.classList.add('reveal');
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkReveal() {
|
||||||
|
revealElements.forEach(element => {
|
||||||
|
const elementTop = element.getBoundingClientRect().top;
|
||||||
|
const elementVisible = 150;
|
||||||
|
|
||||||
|
if (elementTop < window.innerHeight - elementVisible) {
|
||||||
|
element.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial check
|
||||||
|
checkReveal();
|
||||||
|
|
||||||
|
// Check on scroll
|
||||||
|
window.addEventListener('scroll', checkReveal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update metrics values periodically to simulate live data
|
||||||
|
*/
|
||||||
|
function updateMetrics() {
|
||||||
|
const metrics = {
|
||||||
|
'CPU Usage': { min: 30, max: 60, element: null },
|
||||||
|
'Memory': { min: 45, max: 70, element: null },
|
||||||
|
'Storage': { min: 60, max: 75, element: null },
|
||||||
|
'Network': { min: 15, max: 40, element: null }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all metric elements
|
||||||
|
document.querySelectorAll('.metric').forEach(metric => {
|
||||||
|
const nameElement = metric.querySelector('.metric-name');
|
||||||
|
if (nameElement && metrics[nameElement.textContent]) {
|
||||||
|
metrics[nameElement.textContent].element = metric;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateMetricValues() {
|
||||||
|
Object.keys(metrics).forEach(key => {
|
||||||
|
const metric = metrics[key];
|
||||||
|
if (!metric.element) return;
|
||||||
|
|
||||||
|
const valueEl = metric.element.querySelector('.metric-value');
|
||||||
|
const progressEl = metric.element.querySelector('.metric-progress');
|
||||||
|
|
||||||
|
if (valueEl && progressEl) {
|
||||||
|
const newValue = Math.floor(Math.random() * (metric.max - metric.min)) + metric.min;
|
||||||
|
valueEl.textContent = `${newValue}%`;
|
||||||
|
progressEl.style.width = `${newValue}%`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update metrics every 5 seconds
|
||||||
|
setInterval(updateMetricValues, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize contact form handling
|
||||||
|
*/
|
||||||
|
function initFormHandling(form) {
|
||||||
|
// Prevent multiple initializations
|
||||||
|
if (form.hasAttribute('data-initialized')) return;
|
||||||
|
form.setAttribute('data-initialized', 'true');
|
||||||
|
|
||||||
|
form.addEventListener('submit', async function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const submitButton = form.querySelector('button[type="submit"]');
|
||||||
|
const originalButtonText = submitButton.innerHTML;
|
||||||
|
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
|
||||||
|
submitButton.disabled = true;
|
||||||
|
|
||||||
|
const notification = document.getElementById('form-notification');
|
||||||
|
const notificationIcon = notification.querySelector('i');
|
||||||
|
const notificationText = notification.querySelector('.notification-text');
|
||||||
|
|
||||||
|
// Hide any existing notification with animation
|
||||||
|
if (notification.style.display === 'flex') {
|
||||||
|
notification.classList.add('hiding');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
notification.classList.remove('hiding');
|
||||||
|
notification.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
name: form.elements['name'].value,
|
||||||
|
email: form.elements['email'].value,
|
||||||
|
subject: form.elements['subject'].value,
|
||||||
|
message: form.elements['message'].value
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/send-email", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
notification.style.display = 'flex';
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
notification.classList.remove('error');
|
||||||
|
notification.classList.add('success');
|
||||||
|
notificationIcon.className = 'fas fa-check-circle';
|
||||||
|
notificationText.textContent = "Message sent successfully! We'll get back to you soon.";
|
||||||
|
form.reset();
|
||||||
|
} else {
|
||||||
|
notification.classList.remove('success');
|
||||||
|
notification.classList.add('error');
|
||||||
|
notificationIcon.className = 'fas fa-exclamation-circle';
|
||||||
|
notificationText.textContent = data.error + (data.details ? `: ${data.details}` : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Form submission error:", error);
|
||||||
|
notification.style.display = 'flex';
|
||||||
|
notification.classList.remove('success');
|
||||||
|
notification.classList.add('error');
|
||||||
|
notificationIcon.className = 'fas fa-exclamation-circle';
|
||||||
|
notificationText.textContent = `Error: ${error.message}`;
|
||||||
|
} finally {
|
||||||
|
submitButton.innerHTML = originalButtonText;
|
||||||
|
submitButton.disabled = false;
|
||||||
|
|
||||||
|
// Hide notification after 5 seconds with animation
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.add('hiding');
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.remove('hiding');
|
||||||
|
notification.style.display = 'none';
|
||||||
|
}, 300);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update copyright year in the footer
|
||||||
|
*/
|
||||||
|
function updateYear() {
|
||||||
|
const yearElement = document.getElementById('current-year');
|
||||||
|
if (yearElement) {
|
||||||
|
yearElement.textContent = new Date().getFullYear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up scroll spy functionality to highlight nav links
|
||||||
|
* as the user scrolls through different sections
|
||||||
|
*/
|
||||||
|
function setupScrollSpy() {
|
||||||
|
const sections = document.querySelectorAll('section[id]');
|
||||||
|
const navLinks = document.querySelectorAll('.nav-link');
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
let current = '';
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
const sectionTop = section.offsetTop - 100;
|
||||||
|
const sectionHeight = section.offsetHeight;
|
||||||
|
|
||||||
|
if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) {
|
||||||
|
current = section.getAttribute('id');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.classList.remove('active');
|
||||||
|
if (link.getAttribute('href') === `#${current}`) {
|
||||||
|
link.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up automated role switching with better timing and transitions
|
||||||
|
*/
|
||||||
|
function setupRoleSwitching() {
|
||||||
|
const roles = document.querySelectorAll('.role');
|
||||||
|
const roleDescription = document.getElementById('role-description');
|
||||||
|
let currentRoleIndex = 0;
|
||||||
|
|
||||||
|
// Initial role description
|
||||||
|
if (roles.length > 0 && roleDescription) {
|
||||||
|
roleDescription.textContent = roles[0].getAttribute('data-description');
|
||||||
|
roles[0].classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch roles every 5 seconds with fade transition
|
||||||
|
setInterval(() => {
|
||||||
|
// Remove active class from current role
|
||||||
|
roles[currentRoleIndex].classList.remove('active');
|
||||||
|
|
||||||
|
// Move to the next role or back to the first
|
||||||
|
currentRoleIndex = (currentRoleIndex + 1) % roles.length;
|
||||||
|
|
||||||
|
// Add active class to new role
|
||||||
|
roles[currentRoleIndex].classList.add('active');
|
||||||
|
|
||||||
|
// Update the role description with fade effect
|
||||||
|
if (roleDescription) {
|
||||||
|
roleDescription.style.opacity = '0';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
roleDescription.textContent = roles[currentRoleIndex].getAttribute('data-description');
|
||||||
|
roleDescription.style.opacity = '1';
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the existing setupFloatingIcons with this improved version
|
||||||
|
function setupFloatingIcons() {
|
||||||
|
const container = document.getElementById('floating-icons');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
// Clear existing icons
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
// Tech icons to display
|
||||||
|
const icons = [
|
||||||
|
'fa-server', 'fa-network-wired', 'fa-shield-alt',
|
||||||
|
'fa-cloud', 'fa-code-branch', 'fa-database',
|
||||||
|
'fa-laptop-code', 'fa-lock', 'fa-terminal'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create floating icons
|
||||||
|
icons.forEach(icon => {
|
||||||
|
const iconElement = document.createElement('div');
|
||||||
|
iconElement.className = 'floating-icon';
|
||||||
|
iconElement.innerHTML = `<i class="fas ${icon}"></i>`;
|
||||||
|
|
||||||
|
// Random starting position
|
||||||
|
const randomX = Math.floor(Math.random() * 100);
|
||||||
|
const randomY = Math.floor(Math.random() * 100);
|
||||||
|
const randomDelay = Math.random() * 5;
|
||||||
|
const randomDuration = 20 + Math.random() * 10;
|
||||||
|
|
||||||
|
// Apply styles
|
||||||
|
iconElement.style.left = `${randomX}%`;
|
||||||
|
iconElement.style.top = `${randomY}%`;
|
||||||
|
iconElement.style.animationDelay = `${randomDelay}s`;
|
||||||
|
iconElement.style.animationDuration = `${randomDuration}s`;
|
||||||
|
|
||||||
|
// Add to container
|
||||||
|
container.appendChild(iconElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// More existing code...
|