Initial Commit

This commit is contained in:
libertyoms
2025-04-20 09:47:38 +02:00
parent da54bec6ea
commit 211cb3fc6f
14 changed files with 5514 additions and 163 deletions

View File

@ -1,26 +1,149 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&display=swap");
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
@custom-variant dark (&:is(.dark *));
@theme inline {
/* Map semantic utility colors to CSS variables */
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
@media (prefers-color-scheme: dark) {
/* Map specific OMS colors IF you want direct utilities like bg-oms-gold */
/* If you only use semantic vars (like bg-primary), these aren't strictly needed here */
--color-oms-gold: var(--oms-gold);
--color-oms-black: var(--oms-black);
--color-oms-white: var(--oms-white);
--color-oms-light-gray: var(--oms-light-gray);
/* Map font variables */
--font-sans: var(--font-sans);
/* Map radius variables */
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
:root {
--background: #0a0a0a;
--foreground: #ededed;
/* --- Font --- */
--font-sans: "Poppins", sans-serif;
/* --- Radius --- */
--radius: 0.5rem; /* Example: Adjust OMS desired radius */
/* --- Light Mode Colors --- */
--background: #ffffff; /* White */
--foreground: #000000; /* Black */
--card: #ffffff;
--card-foreground: #000000;
--popover: #ffffff;
--popover-foreground: #000000;
--primary: #e1c44a; /* OMS Gold */
--primary-foreground: #000000; /* Black Text on Gold */
--secondary: #f8f9fa; /* OMS Light Gray */
--secondary-foreground: #000000; /* Black Text on Light Gray */
--muted: #f8f9fa; /* OMS Light Gray */
--muted-foreground: #6c757d; /* Muted Gray Text */
--accent: var(--primary); /* Use Primary Gold for Accent */
--accent-foreground: var(
--primary-foreground
); /* Use Black for Accent Text */
--destructive: #dc3545; /* Example Red */
--destructive-foreground: #ffffff; /* White Text on Red */
--border: #dee2e6; /* Light Gray Border */
--input: #dee2e6; /* Light Gray Input Border */
--ring: var(--primary); /* Gold focus ring */
/* --- Specific OMS Colors (Optional if Semantic is Enough) --- */
--oms-gold: #e1c44a;
--oms-black: #000000;
--oms-white: #ffffff;
--oms-light-gray: #f8f9fa;
}
.dark {
/* --- Dark Mode Color Overrides --- */
--background: #121212; /* Very Dark Gray/Black */
--foreground: #e0e0e0; /* Light Gray Text */
--card: #1e1e1e; /* Slightly Lighter Dark */
--card-foreground: #e0e0e0;
--popover: #1e1e1e;
--popover-foreground: #e0e0e0;
--primary: #e1c44a; /* Keep Gold */
--primary-foreground: #000000; /* Keep Black Text on Gold */
--secondary: #2a2a2a; /* Darker Gray */
--secondary-foreground: #e0e0e0; /* Light Text */
--muted: #2a2a2a;
--muted-foreground: #a0a0a0; /* Dimmer Light Text */
--accent: var(--primary); /* Keep Gold */
--accent-foreground: var(--primary-foreground); /* Keep Black */
--destructive: #842029; /* Darker Red */
--destructive-foreground: #f8d7da; /* Lighter text on Dark Red */
--border: #3a3a3a; /* Dark Border */
--input: #3a3a3a;
--ring: var(--primary); /* Keep Gold */
/* --- Specific OMS Colors (Optional) --- */
--oms-gold: #e1c44a;
--oms-black: #000000;
--oms-white: #ffffff;
--oms-light-gray: #2a2a2a; /* Use Dark Gray */
}
@layer base {
* {
border-color: var(--border);
}
body {
background-color: var(--background);
color: var(--foreground);
font-family: var(--font-sans);
@apply flex flex-col min-h-screen;
}
main {
@apply flex-grow;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
@layer components {
}
@layer utilities {
.animation-delay-300 {
animation-delay: 0.3s;
}
.animation-delay-600 {
animation-delay: 0.6s;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fadeInUp 0.8s ease-out forwards;
opacity: 0;
}

View File

@ -1,20 +1,21 @@
// app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Poppins } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import ChatbotWidget from "@/components/ChatbotWidget";
import { ThemeProvider } from "@/providers/theme-provider";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
const poppins = Poppins({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700", "800"],
variable: "--font-poppins",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "OMS - Owethu Managed Services",
description: "Where innovation meets excellence.",
};
export default function RootLayout({
@ -23,11 +24,18 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
<html
lang="en"
className={`${poppins.variable} font-sans`}
suppressHydrationWarning
>
{children}
<body>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<Header />
<main>{children}</main>
<Footer />
<ChatbotWidget />
</ThemeProvider>
</body>
</html>
);

View File

@ -1,103 +1,168 @@
// app/page.tsx
import Image from "next/image";
import Button from "@/components/ui/Button"; // Use the reusable button
import {
FaCogs,
FaProjectDiagram,
FaCode,
FaUsers,
FaBuilding,
FaCar,
FaLaptopCode,
} from "react-icons/fa"; // Import icons
export default function Home() {
export default function HomePage() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<>
{/* Hero Section */}
<section className="relative h-[70vh] md:h-[85vh] flex items-center justify-center text-center bg-dark text-light overflow-hidden">
{/* Background Video/Image Placeholder */}
{/* TODO: Replace with actual <video> or Next/Image */}
<div className="absolute inset-0 z-0 opacity-30">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
src="/hero-bg.jpg" // Add a placeholder image in /public
alt="Digital transformation background"
layout="fill"
objectFit="cover"
quality={75}
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
{/* OR a video element:
<video
autoPlay
loop
muted
playsInline
className="w-full h-full object-cover"
poster="/placeholder-hero-bg.jpg" // Poster image
>
<source src="/your-video.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video> */}
</div>
{/* Overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-black/50 to-black/80 z-10"></div>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
{/* Content */}
<div className="relative z-20 container mx-auto px-6">
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold mb-4 leading-tight text-primary animate-fade-in-up">
Where Innovation <br className="hidden md:block" /> Meets
Excellence.
</h1>
<p className="text-lg md:text-xl max-w-3xl mx-auto mb-8 text-gray-300 animate-fade-in-up animation-delay-300">
Welcome to Owethu Managed Services. We deliver cutting-edge IT
solutions, empowering businesses to thrive in the ever-evolving
digital landscape with unmatched industry expertise.
</p>
<Button
href="/about"
size="lg"
className="animate-fade-in-up animation-delay-600"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
Learn More
</Button>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</section>
{/* Core Services Section */}
<section className="py-20 md:py-28 bg-light-gray">
<div className="container mx-auto px-6 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-dark">
Core Services
</h2>
<p className="text-lg text-gray-600 mb-12 md:mb-16 max-w-2xl mx-auto">
Tailored solutions designed to drive growth, optimize productivity,
and solve your most complex business challenges.
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
{/* Service Card 1: Resource Augmentation */}
<div className="bg-light p-8 rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 text-left">
<FaCogs className="text-primary text-4xl mb-4" />
<h3 className="text-xl font-semibold mb-3 text-dark">
Resource Augmentation
</h3>
<p className="text-gray-600">
Access top-tier IT talent seamlessly integrated with your team.
Skilled professionals for short-term projects or long-term
engagements.
</p>
</div>
{/* Service Card 2: Project Management */}
<div className="bg-light p-8 rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 text-left">
<FaProjectDiagram className="text-primary text-4xl mb-4" />
<h3 className="text-xl font-semibold mb-3 text-dark">
Project Management
</h3>
<p className="text-gray-600">
Expert management ensuring on-time, within-budget delivery with
superior results, risk mitigation, and maximum efficiency.
</p>
</div>
{/* Service Card 3: Product Development */}
<div className="bg-light p-8 rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 text-left">
<FaCode className="text-primary text-4xl mb-4" />
<h3 className="text-xl font-semibold mb-3 text-dark">
Product Development
</h3>
<p className="text-gray-600">
Creating innovative, scalable digital products from concept to
deployment to enhance efficiency and foster business growth.
</p>
</div>
</div>
</div>
</section>
{/* Client Logos Section */}
<section className="py-16 md:py-20 bg-light">
<div className="container mx-auto px-6 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-12 text-dark">
Trusted By Industry Leaders
</h2>
{/* TODO: Implement Auto-Sliding Panel (e.g., using Swiper.js or react-slick) */}
<div className="flex flex-wrap justify-center items-center gap-10 md:gap-16 opacity-70">
{/* Placeholder Logos - Replace with actual client logos */}
<FaBuilding
title="Financial Services Client"
className="text-5xl text-gray-500 hover:text-primary transition-colors"
/>
<FaCar
title="Automotive Client"
className="text-5xl text-gray-500 hover:text-primary transition-colors"
/>
<FaLaptopCode
title="Tech Industry Client"
className="text-5xl text-gray-500 hover:text-primary transition-colors"
/>
<FaUsers
title="Generic Client 1"
className="text-5xl text-gray-500 hover:text-primary transition-colors"
/>
<FaBuilding
title="Generic Client 2"
className="text-5xl text-gray-500 hover:text-primary transition-colors"
/>
</div>
<p className="mt-8 text-gray-500 italic">
Showcasing key clients across financial services, automotive, and
tech industries.
{/* (Auto-sliding panel coming soon!) */}
</p>
</div>
</section>
{/* Call to Action (Optional but good practice) */}
<section className="bg-primary text-dark py-16">
<div className="container mx-auto px-6 text-center">
<h2 className="text-3xl font-bold mb-4">Ready to Innovate?</h2>
<p className="text-lg mb-8 max-w-xl mx-auto">
Let&apos;s discuss how OMS can help transform your business with
cutting-edge technology solutions.
</p>
<Button href="/contact" variant="secondary" size="lg">
Get In Touch
</Button>
</div>
</section>
</>
);
}

View File

@ -0,0 +1,22 @@
// components/ChatbotWidget.tsx
import React from "react";
import { FaComments } from "react-icons/fa";
const ChatbotWidget = () => {
// TODO: Implement actual chat functionality (e.g., integrate a service)
const handleChatOpen = () => {
alert("Chatbot functionality to be implemented!");
};
return (
<button
// onClick={handleChatOpen}
className="fixed bottom-6 right-6 bg-primary text-dark p-4 rounded-full shadow-lg hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all duration-300 z-50"
aria-label="Open Chat"
>
<FaComments size={24} />
</button>
);
};
export default ChatbotWidget;

170
components/Footer.tsx Normal file
View File

@ -0,0 +1,170 @@
// components/Footer.tsx
import React from "react";
import Link from "next/link";
import { FaLinkedin, FaInstagram } from "react-icons/fa"; // npm install react-icons
import Image from "next/image";
const omsLogoUrl = "/oms-logo.svg";
const Footer = () => {
return (
<footer className="bg-dark text-light pt-16 pb-8">
<div className="container mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12 mb-12">
{/* About/Logo */}
<div>
<div className="flex items-center space-x-2 mb-4">
<Image
src={omsLogoUrl} // Use your actual logo path
alt="OMS Logo"
width={40} // Adjust size as needed
height={40}
priority // Load logo quickly
/>
<span className="text-xl font-bold text-light">OMS</span>
</div>
<p className="text-sm text-gray-400">Owethu Managed Services</p>
<p className="text-sm text-gray-400 mt-2">
Where innovation meets excellence.
</p>
</div>
{/* Quick Links */}
<div>
<h5 className="text-lg font-semibold mb-4 text-primary">
Quick Links
</h5>
<ul className="space-y-2">
<li>
<Link
href="/about"
className="hover:text-primary transition-colors duration-300"
>
About Us
</Link>
</li>
<li>
<Link
href="/services"
className="hover:text-primary transition-colors duration-300"
>
Services
</Link>
</li>
<li>
<Link
href="/products"
className="hover:text-primary transition-colors duration-300"
>
Products
</Link>
</li>
<li>
<Link
href="/join-us"
className="hover:text-primary transition-colors duration-300"
>
Join Our Team
</Link>
</li>
<li>
<Link
href="/contact"
className="hover:text-primary transition-colors duration-300"
>
Contact Us
</Link>
</li>
</ul>
</div>
{/* Contact Info */}
<div>
<h5 className="text-lg font-semibold mb-4 text-primary">Contact</h5>
<p className="text-sm text-gray-400 mb-2">
Unit 10 B Centuria Park
<br />
265 Von Willich Avenue
<br />
Die Hoewes, Centurion, 0159
</p>
<p className="text-sm text-gray-400 mb-2">Phone: (012) 051 3282</p>
<p className="text-sm text-gray-400 mb-2">
Email:{" "}
<a href="mailto:hello@oms.africa" className="hover:text-primary">
hello@oms.africa
</a>
</p>
<div className="flex space-x-4 mt-4">
<a
href="https://linkedin.com"
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-primary"
>
<FaLinkedin size={24} />
</a>
<a
href="https://instagram.com"
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-primary"
>
<FaInstagram size={24} />
</a>
</div>
</div>
{/* Newsletter */}
<div>
<h5 className="text-lg font-semibold mb-4 text-primary">
Newsletter
</h5>
<p className="text-sm text-gray-400 mb-3">
Stay updated with our latest news.
</p>
<form className="flex flex-col sm:flex-row gap-2">
<input
type="email"
placeholder="Enter your email"
className="flex-grow px-4 py-2 rounded-md bg-gray-700 text-light border border-gray-600 focus:outline-none focus:ring-2 focus:ring-primary/50"
/>
<button
type="submit"
className="bg-primary text-dark px-4 py-2 rounded-md font-semibold hover:bg-primary/90 transition-colors duration-300"
>
Subscribe
</button>
</form>
{/* TODO: Add Badges */}
<div className="mt-6 space-x-4">
<span className="inline-block bg-gray-600 px-3 py-1 rounded text-xs font-semibold">
Salesforce Partner
</span>
<span className="inline-block bg-gray-600 px-3 py-1 rounded text-xs font-semibold">
BBB-EE Level X
</span>
</div>
</div>
</div>
{/* Bottom Bar */}
<div className="border-t border-gray-700 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-gray-500">
<p>
© {new Date().getFullYear()} Owethu Managed Services. All Rights
Reserved.
</p>
<div className="flex space-x-4 mt-4 md:mt-0">
<Link href="/privacy-policy" className="hover:text-primary">
Privacy Policy
</Link>
<Link href="/paia-popia" className="hover:text-primary">
PAIA & POPIA
</Link>
</div>
</div>
</div>
</footer>
);
};
export default Footer;

314
components/Header.tsx Normal file
View File

@ -0,0 +1,314 @@
// components/Header.tsx
"use client";
import React, { useState } from "react";
import Link from "next/link";
import Image from "next/image";
import { usePathname } from "next/navigation";
import {
FiChevronDown,
FiClipboard,
FiArrowRight,
FiMenu,
FiX,
} from "react-icons/fi";
import ThemeToggle from "./ThemeToggle";
const omsLogoUrl = "/oms-logo.svg";
// --- Basic Dropdown Placeholder ---
type DropdownMenuProps = {
trigger: React.ReactNode;
children: React.ReactNode;
menuClasses?: string;
};
const DropdownMenu = ({
trigger,
children,
menuClasses = "w-48",
}: DropdownMenuProps) => (
<div className="relative group">
<button className="flex items-center space-x-1 text-sm font-medium focus:outline-none inherit-color group">
{trigger}
<FiChevronDown className="w-4 h-4 transition-transform duration-200 group-hover:rotate-180" />
</button>
<div
className={`
absolute left-0 mt-2 ${menuClasses} origin-top-left rounded-md shadow-lg z-50
bg-light border border-gray-200 dark:bg-[var(--color-dark-bg-secondary)] dark:border-[var(--color-dark-border)]
opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200
`}
>
<div className="py-1">{children}</div>
</div>
</div>
);
type DropdownLinkProps = {
href: string;
children: React.ReactNode;
};
const DropdownLink = ({ href, children }: DropdownLinkProps) => (
<Link
href={href}
className="block px-4 py-2 text-sm text-gray-700 dark:text-[var(--color-dark-text-secondary)] hover:bg-gray-100 hover:text-primary dark:hover:bg-gray-700 dark:hover:text-primary"
>
{children}
</Link>
);
// --- End Dropdown Placeholder ---
const Header = () => {
const pathname = usePathname() || "/";
const isActive = (path: string) => pathname === path;
const [isMenuOpen, setIsMenuOpen] = useState(false);
const toggleMenu = () => setIsMenuOpen((open) => !open);
const handleMobileLinkClick = () => setIsMenuOpen(false);
// Optionally switch logo based on a global CSS class or via next-themes
const currentLogo = omsLogoUrl;
return (
<header className="sticky top-0 z-50 shadow-md bg-light dark:bg-dark-bg dark:border-b dark:border-dark-border">
{/* Top Row */}
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<Link
href="/"
className="flex items-center space-x-2"
title="OMS Home"
>
<Image
src={currentLogo}
alt="OMS Logo"
width={40}
height={40}
priority
/>
<span className="text-xl font-bold text-dark dark:text-light hidden sm:inline">
Owethu Managed Services
</span>
</Link>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center space-x-6 lg:space-x-8">
<Link
href="/"
className={`text-sm font-medium ${
isActive("/")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text-secondary)] hover:text-primary"
}`}
>
Home
</Link>
<Link
href="/about"
className={`text-sm font-medium ${
pathname.startsWith("/about")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text-secondary)] hover:text-primary"
}`}
>
About Us
</Link>
<Link
href="/contact"
className={`text-sm font-medium ${
isActive("/contact")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text-secondary)] hover:text-primary"
}`}
>
Contact Us
</Link>
</nav>
{/* Right Utilities (Desktop) */}
<div className="hidden md:flex items-center space-x-4">
<ThemeToggle />
<Link
href="/request-demo"
className="flex items-center text-sm font-medium bg-primary text-dark px-3 py-1.5 rounded-md hover:bg-opacity-90 transition-colors"
title="Request a Demo"
>
<FiClipboard className="w-4 h-4 mr-1.5" />
Request Demo
</Link>
</div>
{/* Mobile Buttons */}
<div className="md:hidden flex items-center">
<ThemeToggle />
<button
onClick={toggleMenu}
className="text-gray-600 dark:text-gray-400 hover:text-primary dark:hover:text-primary focus:outline-none ml-3"
aria-label="Toggle menu"
>
{isMenuOpen ? (
<FiX className="w-6 h-6" />
) : (
<FiMenu className="w-6 h-6" />
)}
</button>
</div>
</div>
</div>
{/* Secondary Row */}
<div className="bg-primary dark:bg-primary">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="hidden md:flex justify-between items-center h-12">
<nav className="flex items-center space-x-6 lg:space-x-8 text-dark">
<DropdownMenu trigger={<span>Services</span>}>
<DropdownLink href="/services/resource-augmentation">
Resource Augmentation
</DropdownLink>
<DropdownLink href="/services/project-management">
Project Management
</DropdownLink>
<DropdownLink href="/services/product-development">
Product Development
</DropdownLink>
</DropdownMenu>
<DropdownMenu trigger={<span>Products</span>}>
<DropdownLink href="/products/obse">OBSE</DropdownLink>
</DropdownMenu>
<DropdownMenu trigger={<span>Join Our Team</span>}>
<DropdownLink href="/join-us/vacancies">Vacancies</DropdownLink>
<DropdownLink href="/join-us/portal">
Recruitment Portal
</DropdownLink>
</DropdownMenu>
</nav>
<Link
href="/services"
className="flex items-center text-sm font-medium text-dark hover:text-opacity-80 transition-opacity group"
>
Explore Our Offerings
<FiArrowRight className="w-4 h-4 ml-1.5 transition-transform duration-200 group-hover:translate-x-1" />
</Link>
</div>
</div>
</div>
{/* Mobile Menu Panel */}
<div
id="mobile-menu"
className={`md:hidden absolute top-full left-0 w-full shadow-lg transition-all duration-300 ease-in-out overflow-hidden bg-light dark:bg-[var(--color-dark-bg-secondary)] border-t border-gray-200 dark:border-[var(--color-dark-border)] ${
isMenuOpen ? "max-h-screen py-4" : "max-h-0 py-0"
}`}
>
<nav className="container mx-auto px-4 sm:px-6 lg:px-8 flex flex-col space-y-3">
<Link
href="/"
onClick={handleMobileLinkClick}
className={`block py-2 text-base font-medium ${
isActive("/")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
}`}
>
Home
</Link>
<Link
href="/about"
onClick={handleMobileLinkClick}
className={`block py-2 text-base font-medium ${
pathname.startsWith("/about")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
}`}
>
About Us
</Link>
<span className="pt-2 text-xs uppercase text-gray-500 dark:text-gray-400">
Services
</span>
<Link
href="/services/resource-augmentation"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
Resource Augmentation
</Link>
<Link
href="/services/project-management"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
Project Management
</Link>
<Link
href="/services/product-development"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
Product Development
</Link>
<span className="pt-2 text-xs uppercase text-gray-500 dark:text-gray-400">
Products
</span>
<Link
href="/products/obse"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
OBSE
</Link>
<span className="pt-2 text-xs uppercase text-gray-500 dark:text-gray-400">
Join Us
</span>
<Link
href="/join-us/vacancies"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
Vacancies
</Link>
<Link
href="/join-us/portal"
onClick={handleMobileLinkClick}
className="block py-1 pl-3 text-base font-medium text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
>
Recruitment Portal
</Link>
<Link
href="/contact"
onClick={handleMobileLinkClick}
className={`block py-2 text-base font-medium ${
isActive("/contact")
? "text-primary font-semibold"
: "text-gray-700 dark:text-[var(--color-dark-text)] hover:text-primary"
}`}
>
Contact Us
</Link>
<div className="pt-4">
<Link
href="/request-demo"
onClick={handleMobileLinkClick}
className="flex w-full justify-center items-center text-sm font-medium bg-primary text-dark px-4 py-2 rounded-md hover:bg-opacity-90 transition-colors"
title="Request a Demo"
>
<FiClipboard className="w-4 h-4 mr-1.5" />
Request Demo
</Link>
</div>
</nav>
</div>
</header>
);
};
export default Header;

197
components/HeaderSingle.tsx Normal file
View File

@ -0,0 +1,197 @@
// components/Header.tsx
import React from "react";
import Link from "next/link";
import Image from "next/image";
import { FiSearch, FiChevronDown, FiClipboard } from "react-icons/fi"; // Added ChevronDown for dropdowns and Clipboard for Demo
// TODO: Ensure this path is correct
const omsLogoUrl = "/oms-logo.svg"; // Or your actual logo file
// Placeholder for Dropdown Menu component (implement separately)
const DropdownMenu = ({
trigger,
children,
}: {
trigger: React.ReactNode;
children: React.ReactNode;
}) => {
// Basic structure - Needs state and proper styling for visibility toggle
return (
<div className="relative group">
<button className="flex items-center space-x-1 text-sm font-medium text-gray-700 hover:text-primary focus:outline-none">
{trigger}
<FiChevronDown className="w-4 h-4 transition-transform duration-200 group-hover:rotate-180" />
</button>
<div className="absolute left-0 mt-2 w-48 bg-light border border-gray-200 rounded-md shadow-lg opacity-0 group-hover:opacity-100 invisible group-hover:visible transition-all duration-200 z-50">
{children}
</div>
</div>
);
};
const Header = () => {
// In a real app use: import { usePathname } from 'next/navigation';
// const pathname = usePathname();
const pathname = "/"; // Placeholder for example
const isActive = (path: string) => pathname === path;
return (
<header className="sticky top-0 z-50 bg-light shadow-md">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="flex-shrink-0">
<Link
href="/"
className="flex items-center space-x-2"
title="Owethu Managed Services Home"
>
<Image
src={omsLogoUrl}
alt="OMS Logo"
width={40}
height={40}
priority
/>
<span className="text-xl font-bold text-dark hidden sm:inline">
Owethu Managed Services
</span>
</Link>
</div>
<nav className="hidden md:flex items-center space-x-6 lg:space-x-8">
<Link
href="/"
className={`text-sm font-medium ${
isActive("/")
? "text-primary font-semibold"
: "text-gray-700 hover:text-primary"
}`}
>
Home
</Link>
{/* About Us Dropdown */}
<DropdownMenu trigger={<span>About Us</span>}>
<Link
href="/about"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Company Overview
</Link>
<Link
href="/about#team"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Our Team
</Link>
{/* Add other sub-links if needed */}
</DropdownMenu>
{/* Services Dropdown */}
<DropdownMenu trigger={<span>Services</span>}>
<Link
href="/services/resource-augmentation"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Resource Augmentation
</Link>
<Link
href="/services/project-management"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Project Management
</Link>
<Link
href="/services/product-development"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Product Development
</Link>
</DropdownMenu>
{/* Products Dropdown */}
<DropdownMenu trigger={<span>Products</span>}>
<Link
href="/products/obse"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
OBSE
</Link>
{/* Add other products if any */}
</DropdownMenu>
{/* Join Our Team Dropdown */}
<DropdownMenu trigger={<span>Join Our Team</span>}>
<Link
href="/join-us/vacancies"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Vacancies
</Link>
<Link
href="/join-us/portal"
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-primary"
>
Recruitment Portal
</Link>
</DropdownMenu>
<Link
href="/contact"
className={`text-sm font-medium ${
isActive("/contact")
? "text-primary font-semibold"
: "text-gray-700 hover:text-primary"
}`}
>
Contact Us
</Link>
</nav>
<div className="flex items-center space-x-4">
<button
className="text-gray-600 hover:text-primary focus:outline-none"
title="Search"
>
<FiSearch className="w-5 h-5" />
<span className="sr-only">Search</span>
</button>
<Link
href="/request-demo" // Link to a demo request page or section
className="flex items-center text-sm font-medium bg-primary text-dark px-3 py-1.5 rounded-md hover:bg-primary/90 transition-colors"
title="Request a Demo"
>
<FiClipboard className="w-4 h-4 mr-1.5" />
Request Demo
</Link>
</div>
<div className="md:hidden flex items-center">
<button className="text-gray-600 focus:outline-none">
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16m-7 6h7"
></path>
</svg>
<span className="sr-only">Open menu</span>
</button>
{/* Mobile menu component would go here */}
</div>
</div>
</div>
</header>
);
};
export default Header;

View File

@ -0,0 +1,30 @@
"use client";
import { useTheme } from "next-themes";
import { DarkModeSwitch } from "react-toggle-dark-mode";
import { useState, useEffect } from "react";
const ThemeToggle = () => {
const { resolvedTheme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
// avoid SSR mismatch
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
// placeholder box so layout doesnt jump
return <div style={{ width: 20, height: 20 }} />;
}
return (
<DarkModeSwitch
checked={resolvedTheme === "dark"}
onChange={(checked) => setTheme(checked ? "dark" : "light")}
size={20}
/>
);
};
export default ThemeToggle;

54
components/ui/Button.tsx Normal file
View File

@ -0,0 +1,54 @@
// components/ui/Button.tsx
import React from "react";
import Link from "next/link";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
href?: string;
variant?: "primary" | "secondary" | "outline";
size?: "sm" | "md" | "lg";
children: React.ReactNode;
className?: string;
}
const Button: React.FC<ButtonProps> = ({
href,
variant = "primary",
size = "md",
children,
className = "",
...props
}) => {
const baseStyle =
"inline-flex items-center justify-center rounded-md font-semibold transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2";
const variantStyles = {
primary: "bg-primary text-dark hover:bg-primary/90 focus:ring-primary/50",
secondary: "bg-dark text-light hover:bg-gray-800 focus:ring-dark/50",
outline:
"border border-primary text-primary hover:bg-primary hover:text-dark focus:ring-primary/50",
};
const sizeStyles = {
sm: "px-4 py-2 text-sm",
md: "px-6 py-3 text-base",
lg: "px-8 py-4 text-lg",
};
const combinedClassName = `${baseStyle} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`;
if (href) {
return (
<Link href={href} className={combinedClassName}>
{children}
</Link>
);
}
return (
<button className={combinedClassName} {...props}>
{children}
</button>
);
};
export default Button;

4394
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,19 +9,22 @@
"lint": "next lint"
},
"dependencies": {
"next": "15.3.1",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.3.1"
"react-icons": "^5.5.0",
"react-toggle-dark-mode": "^1.1.1"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.3.1",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}

View File

@ -0,0 +1,11 @@
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

BIN
public/hero-bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

24
public/oms-logo.svg Normal file
View File

@ -0,0 +1,24 @@
<svg width="1005" height="1005" viewBox="0 0 1005 1005" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M502.085 0L1004.09 502L502.085 1004L0.0844712 502L502.085 0Z" fill="white"/>
<path d="M502.122 92.0752L912.207 502.16L502.122 912.244L92.0376 502.16L502.122 92.0752Z" fill="url(#paint0_linear_67_245)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M954.507 502.085L502 49.5779L49.493 502.085L502 954.592L954.507 502.085ZM502 0.0849609L2.81409e-05 502.085L502 1004.08L1004 502.085L502 0.0849609Z" fill="url(#paint1_linear_67_245)"/>
<path d="M343.932 579.034C324.45 579.034 309.266 574.083 298.379 564.181C287.492 554.279 281.762 539.57 281.189 520.053C281.046 515.891 280.975 509.792 280.975 501.756C280.975 493.576 281.046 487.405 281.189 483.244C281.762 464.158 287.636 449.52 298.809 439.331C309.982 429.142 325.023 424.048 343.932 424.048C362.841 424.048 377.881 429.142 389.055 439.331C400.371 449.52 406.316 464.158 406.889 483.244C407.176 491.567 407.319 497.738 407.319 501.756C407.319 505.631 407.176 511.73 406.889 520.053C406.173 539.57 400.371 554.279 389.485 564.181C378.598 574.083 363.413 579.034 343.932 579.034ZM343.932 554.279C353.529 554.279 361.193 551.409 366.923 545.669C372.653 539.785 375.733 530.888 376.163 518.977C376.449 510.366 376.592 504.554 376.592 501.541C376.592 498.24 376.449 492.428 376.163 484.105C375.733 472.194 372.653 463.368 366.923 457.628C361.193 451.744 353.529 448.803 343.932 448.803C334.478 448.803 326.885 451.744 321.156 457.628C315.426 463.368 312.346 472.194 311.916 484.105C311.773 488.267 311.701 494.078 311.701 501.541C311.701 508.86 311.773 514.672 311.916 518.977C312.346 530.888 315.354 539.785 320.941 545.669C326.671 551.409 334.334 554.279 343.932 554.279Z" fill="black" fill-opacity="0.87"/>
<path d="M440.906 576.881C439.33 576.881 438.041 576.451 437.038 575.59C436.035 574.585 435.534 573.294 435.534 571.715V431.582C435.534 430.003 436.035 428.712 437.038 427.707C438.041 426.703 439.33 426.2 440.906 426.2H457.881C460.889 426.2 463.038 427.564 464.327 430.29L505.797 507.783L547.697 430.29C549.129 427.564 551.278 426.2 554.143 426.2H570.903C572.479 426.2 573.768 426.703 574.771 427.707C575.773 428.712 576.275 430.003 576.275 431.582V571.715C576.275 573.294 575.773 574.585 574.771 575.59C573.768 576.451 572.479 576.881 570.903 576.881H552.854C551.421 576.881 550.204 576.379 549.201 575.374C548.198 574.37 547.697 573.15 547.697 571.715V478.508L517.615 535.982C515.753 539.139 513.317 540.718 510.309 540.718H501.5C498.491 540.718 496.056 539.139 494.194 535.982L464.112 478.508V571.715C464.112 573.15 463.611 574.37 462.608 575.374C461.748 576.379 460.531 576.881 458.955 576.881H440.906Z" fill="black" fill-opacity="0.87"/>
<path d="M661.911 579.034C649.162 579.034 638.203 577.096 629.036 573.222C619.868 569.347 612.92 564.253 608.193 557.938C603.466 551.624 600.959 544.808 600.673 537.489C600.673 536.197 601.102 535.121 601.962 534.26C602.821 533.399 603.896 532.968 605.185 532.968H624.308C626.027 532.968 627.317 533.327 628.176 534.045C629.179 534.619 630.11 535.623 630.969 537.058C632.402 541.938 635.697 546.027 640.854 549.328C646.01 552.629 653.03 554.279 661.911 554.279C672.081 554.279 679.745 552.629 684.902 549.328C690.059 545.884 692.637 541.148 692.637 535.121C692.637 531.103 691.277 527.802 688.555 525.219C685.976 522.636 682.037 520.412 676.737 518.546C671.58 516.681 663.845 514.385 653.531 511.658C636.628 507.64 624.237 502.33 616.358 495.729C608.623 488.984 604.755 479.441 604.755 467.099C604.755 458.776 606.975 451.386 611.416 444.928C616 438.47 622.518 433.376 630.969 429.645C639.564 425.913 649.52 424.048 660.837 424.048C672.583 424.048 682.753 426.129 691.348 430.29C699.943 434.452 706.461 439.69 710.901 446.004C715.485 452.175 717.921 458.346 718.207 464.516C718.207 465.808 717.777 466.884 716.918 467.745C716.058 468.606 714.984 469.037 713.695 469.037H693.712C690.704 469.037 688.627 467.674 687.481 464.947C686.621 460.355 683.756 456.552 678.886 453.538C674.015 450.381 667.999 448.803 660.837 448.803C652.815 448.803 646.512 450.309 641.928 453.323C637.344 456.337 635.052 460.713 635.052 466.454C635.052 470.472 636.198 473.772 638.49 476.356C640.782 478.939 644.363 481.235 649.233 483.244C654.247 485.253 661.409 487.477 670.721 489.917C683.326 492.787 693.354 496.016 700.803 499.603C708.395 503.191 713.981 507.712 717.562 513.165C721.144 518.618 722.934 525.578 722.934 534.045C722.934 543.229 720.356 551.265 715.199 558.154C710.185 564.898 703.023 570.065 693.712 573.652C684.544 577.24 673.944 579.034 661.911 579.034Z" fill="black" fill-opacity="0.87"/>
<defs>
<linearGradient id="paint0_linear_67_245" x1="233.025" y1="357.551" x2="595.992" y2="804.51" gradientUnits="userSpaceOnUse">
<stop offset="0.0767781" stop-color="#E1C44A"/>
<stop offset="0.290113" stop-color="#E1C44A" stop-opacity="0.7"/>
<stop offset="0.463052" stop-color="#E1C44A" stop-opacity="0.26"/>
<stop offset="0.675574" stop-color="#E1C44A" stop-opacity="0.71"/>
<stop offset="1" stop-color="#E1C44A"/>
</linearGradient>
<linearGradient id="paint1_linear_67_245" x1="346.014" y1="314.556" x2="718.674" y2="491.224" gradientUnits="userSpaceOnUse">
<stop stop-color="#E1C44A"/>
<stop offset="0.201382" stop-color="#E1C44A" stop-opacity="0.7"/>
<stop offset="0.463052" stop-color="#E1C44A" stop-opacity="0.09"/>
<stop offset="0.744023" stop-color="#E1C44A" stop-opacity="0.71"/>
<stop offset="1" stop-color="#E1C44A"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB