Files
CompanySite/src/components/Footer.astro
T
platform-mcpandClaude Opus 4.8 01cafba6c1
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 10m48s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m40s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m32s
Ping Search Engines / Notify Search Engines (push) Failing after 11m26s
E2E Test Suite / Security Header Tests (push) Failing after 12m18s
E2E Test Suite / Mobile Device Tests (push) Failing after 13m10s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 14m3s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 14m55s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 22m34s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Test Report Summary (push) Failing after 14m58s
Deploy to Production / Post-Deploy Verification (push) Failing after 10m26s
Deploy to Production / Build & Verify (push) Failing after 14m49s
Deploy to Production / Pre-Deploy Tests (push) Failing after 14m58s
Deploy to Production / Deploy to Railway (push) Failing after 14m5s
Deploy to Production / Deploy to Render (push) Failing after 14m5s
Deploy to Production / Deploy to VPS (PM2) (push) Failing after 13m13s
Deploy to Production / Deploy to Fly.io (push) Failing after 13m13s
Deploy to Production / Notify on Failure (push) Failing after 13m53s
feat: apply WorkRoot logo sitewide and as favicon
- Add WorkRoot Icon as public/favicon.svg, regenerate logo.png (512x512)
  and apple-touch-icon.png (180x180) from the brand vector; add
  favicon-32x32.png raster
- Register 32x32 PNG favicon link in BaseLayout <head> alongside the
  SVG and apple-touch-icon
- Replace placeholder "W" logo boxes with the WorkRoot mark in the
  header, mobile menu, and footer (preserving "WorkRoot" text spans
  required by the dark-mode contrast tests and the home link wrapper)

JSON-LD/SEO logo (/logo.png) and PWA manifest icons now resolve to the
real brand assets. Build passes; BUG-5 dark-mode and navigation tests
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:08:31 +05:30

263 lines
12 KiB
Plaintext

---
// Footer Component for WorkRoot IT Solutions
// Features: Company info, quick links, services, social icons, newsletter subscription
interface Props {
class?: string;
}
const { class: className } = Astro.props;
const quickLinks = [
{ name: 'Home', href: '/' },
{ name: 'About Us', href: '/about' },
{ name: 'Services', href: '/services' },
{ name: 'Portfolio', href: '/portfolio' },
{ name: 'Blog', href: '/blog' },
{ name: 'Contact', href: '/contact' },
];
const services = [
{ name: 'Web Development', href: '/services#web-development' },
{ name: 'Mobile Apps', href: '/services#mobile-development' },
{ name: 'ERP Solutions', href: '/services#erp-solutions' },
{ name: 'Government Systems', href: '/services#government-systems' },
{ name: 'UI/UX Design', href: '/services#ui-ux-design' },
{ name: 'IT Consulting', href: '/services#it-consulting' },
];
const socialLinks = [
{ name: 'LinkedIn', href: 'https://www.linkedin.com/company/workroot', icon: 'linkedin' },
{ name: 'Twitter', href: 'https://x.com/GhanwatAjay', icon: 'twitter' },
{ name: 'GitHub', href: 'https://git.workroot.in/WorkRoot/', icon: 'github' },
{ name: 'Facebook', href: 'https://www.facebook.com/profile.php?id=61576503897897', icon: 'facebook' },
];
const currentYear = new Date().getFullYear();
---
<footer class:list={['bg-secondary-900 text-secondary-200', className]}>
<!-- Main Footer Content -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 lg:py-16">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 lg:gap-12">
<!-- Company Info -->
<div class="lg:col-span-1">
<a href="/" class="inline-flex items-center gap-2 mb-4 group" aria-label="WorkRoot IT Solutions LLP - Home">
<img src="/favicon.svg" alt="WorkRoot" width="40" height="40" class="w-10 h-10 transition-transform group-hover:scale-105" />
<span class="text-xl font-bold text-white">WorkRoot</span>
</a>
<p class="text-secondary-400 text-sm leading-relaxed mb-6">
Building innovative software solutions since 2022. We deliver custom web applications, mobile apps, ERP systems, and government IT solutions across India and beyond.
</p>
<!-- Social Icons -->
<div class="flex items-center gap-3">
{socialLinks.map((social) => (
<a
href={social.href}
target="_blank"
rel="noopener noreferrer"
aria-label={social.name}
class="w-10 h-10 rounded-lg bg-secondary-800 flex items-center justify-center text-secondary-400 hover:bg-primary hover:text-white transition-all duration-200 hover:scale-105"
>
{social.icon === 'linkedin' && (
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
)}
{social.icon === 'twitter' && (
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
)}
{social.icon === 'github' && (
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"/>
</svg>
)}
{social.icon === 'facebook' && (
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clip-rule="evenodd"/>
</svg>
)}
</a>
))}
</div>
</div>
<!-- Quick Links -->
<div>
<h3 class="text-white font-semibold text-lg mb-4">Quick Links</h3>
<ul class="space-y-3">
{quickLinks.map((link) => (
<li>
<a
href={link.href}
class="text-secondary-400 hover:text-primary transition-colors duration-200 text-sm inline-flex items-center gap-1 group"
>
<svg class="w-3 h-3 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
<span>{link.name}</span>
</a>
</li>
))}
</ul>
</div>
<!-- Services -->
<div>
<h3 class="text-white font-semibold text-lg mb-4">Our Services</h3>
<ul class="space-y-3">
{services.map((service) => (
<li>
<a
href={service.href}
class="text-secondary-400 hover:text-primary transition-colors duration-200 text-sm inline-flex items-center gap-1 group"
>
<svg class="w-3 h-3 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
<span>{service.name}</span>
</a>
</li>
))}
</ul>
</div>
<!-- Newsletter Subscription -->
<div>
<h3 class="text-white font-semibold text-lg mb-4">Stay Updated</h3>
<p class="text-secondary-400 text-sm mb-4">
Subscribe to our newsletter for the latest tech insights and company updates.
</p>
<form id="newsletter-form" class="space-y-3">
<div class="relative">
<label for="newsletter-email" class="sr-only">Email address</label>
<input
type="email"
name="email"
id="newsletter-email"
placeholder="Enter your email"
required
autocomplete="email"
aria-required="true"
aria-describedby="newsletter-message"
class="w-full px-4 py-3 bg-secondary-800 border border-secondary-700 rounded-lg text-white placeholder-secondary-500 text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all duration-200"
/>
</div>
<button
type="submit"
class="w-full px-4 py-3 bg-primary text-white font-medium rounded-lg hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-secondary-900 transition-all duration-200 text-sm flex items-center justify-center gap-2 group"
>
<span>Subscribe</span>
<svg class="w-4 h-4 group-hover:translate-x-1 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
</svg>
</button>
<!-- Success/Error Messages -->
<div id="newsletter-message" class="hidden text-sm py-2 px-3 rounded-lg" role="status" aria-live="polite"></div>
</form>
<p class="text-secondary-500 text-xs mt-3">
By subscribing, you agree to our
<a href="/privacy" class="text-primary hover:underline">Privacy Policy</a>.
</p>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div class="border-t border-secondary-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex flex-col md:flex-row justify-between items-center gap-4">
<p class="text-secondary-500 text-sm text-center md:text-left">
&copy; {currentYear} WorkRoot IT Solutions LLP. All rights reserved.
</p>
<div class="flex items-center gap-6">
<a href="/privacy" class="text-secondary-500 hover:text-primary text-sm transition-colors duration-200">
Privacy Policy
</a>
<a href="/terms" class="text-secondary-500 hover:text-primary text-sm transition-colors duration-200">
Terms of Service
</a>
<a href="/cookies" class="text-secondary-500 hover:text-primary text-sm transition-colors duration-200">
Cookie Policy
</a>
</div>
</div>
</div>
</div>
</footer>
<script>
import { trackNewsletterSignup } from '../utils/analytics';
// Newsletter form handling
const form = document.getElementById('newsletter-form') as HTMLFormElement;
const messageEl = document.getElementById('newsletter-message') as HTMLDivElement;
form?.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const email = formData.get('email') as string;
// Show loading state
const submitBtn = form.querySelector('button[type="submit"]') as HTMLButtonElement;
const originalContent = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = `
<svg class="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>Subscribing...</span>
`;
try {
const response = await fetch('/api/newsletter', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
const data = await response.json() as { success: boolean; message?: string; error?: string };
if (response.ok && data.success) {
trackNewsletterSignup(true);
// Show success message
messageEl.classList.remove('hidden', 'bg-red-900/50', 'text-red-300');
messageEl.classList.add('bg-green-900/50', 'text-green-300');
messageEl.textContent = data.message ?? 'Thanks for subscribing! Check your inbox to confirm.';
// Reset form on success only
form.reset();
} else {
trackNewsletterSignup(false);
// Show error message from server
messageEl.classList.remove('hidden', 'bg-green-900/50', 'text-green-300');
messageEl.classList.add('bg-red-900/50', 'text-red-300');
messageEl.textContent = data.error ?? 'Something went wrong. Please try again.';
}
} catch {
trackNewsletterSignup(false);
// Network error
messageEl.classList.remove('hidden', 'bg-green-900/50', 'text-green-300');
messageEl.classList.add('bg-red-900/50', 'text-red-300');
messageEl.textContent = 'Network error. Please check your connection and try again.';
} finally {
// Restore button
submitBtn.disabled = false;
submitBtn.innerHTML = originalContent;
// Hide message after 5 seconds
setTimeout(() => {
messageEl.classList.add('hidden');
}, 5000);
}
});
</script>