diff --git a/actions/apply.ts b/actions/apply.ts new file mode 100644 index 0000000..e655c50 --- /dev/null +++ b/actions/apply.ts @@ -0,0 +1,74 @@ +"use server"; + +import * as z from "zod"; + +// Re-define or import the schema to validate on the server-side +// Ensure this matches the client-side schema +const applicationSchema = z.object({ + vacancyId: z.string(), + vacancyTitle: z.string(), + firstName: z.string().min(1, "First name is required"), + lastName: z.string().min(1, "Last name is required"), + email: z.string().email("Invalid email address"), + phone: z.string().optional(), + linkedinUrl: z.string().url("Invalid URL").optional().or(z.literal("")), + portfolioUrl: z.string().url("Invalid URL").optional().or(z.literal("")), + coverLetter: z.string().optional(), + // Note: File uploads (resume) are not handled in this basic action. + // Handling files requires FormData and different processing. +}); + +type ApplicationFormData = z.infer; + +interface ActionResult { + success: boolean; + message?: string; +} + +export async function submitApplication( + formData: ApplicationFormData +): Promise { + // 1. Validate data on the server + const validatedFields = applicationSchema.safeParse(formData); + + if (!validatedFields.success) { + console.error( + "Server-side validation failed:", + validatedFields.error.flatten().fieldErrors + ); + return { + success: false, + message: "Invalid data provided. Please check the form.", + // Optionally return specific field errors: errors: validatedFields.error.flatten().fieldErrors + }; + } + + const applicationData = validatedFields.data; + + // 2. Process the application (e.g., save to database, send email) + // For this demo, we'll just log the data. + console.log( + "Received application:", + JSON.stringify(applicationData, null, 2) + ); + + // Simulate processing time + await new Promise((resolve) => setTimeout(resolve, 1000)); + + // In a real application: + // - Save applicationData to your database (e.g., using Prisma or Directus SDK) + // - Handle resume file upload (requires FormData, potentially upload to storage like S3/Minio) + // - Send notification emails (to HR, to the applicant) + + // Example of error handling during processing: + // try { + // await saveApplicationToDatabase(applicationData); + // await sendConfirmationEmail(applicationData.email); + // } catch (error) { + // console.error('Failed to process application:', error); + // return { success: false, message: 'Failed to save application.' }; + // } + + // 3. Return success response + return { success: true, message: "Application submitted successfully!" }; +} diff --git a/app/(website)/_components/ClientLogosSection.tsx b/app/(website)/_components/ClientLogosSection.tsx index 8555a1a..2ba0693 100644 --- a/app/(website)/_components/ClientLogosSection.tsx +++ b/app/(website)/_components/ClientLogosSection.tsx @@ -1,84 +1,116 @@ // components/ClientLogosSection.tsx import React from "react"; -import Image from "next/image"; // For actual logos -import { FaBuilding, FaCar, FaLaptopCode, FaUsers } from "react-icons/fa"; // For placeholders +import Image from "next/image"; -// Define structure for client data (adapt as needed) +// Define structure for client data - focusing on logos now type Client = { name: string; - logoUrl?: string; // URL to actual logo image - icon?: React.ElementType; // Placeholder icon component + logoUrl: string; // Expecting actual logo URLs now }; type ClientLogosSectionProps = { title: string; description?: string; clients: Client[]; + /** Control animation speed (lower number = faster). Default: 40s */ + speed?: number; + /** Size of the square background container in pixels. Default: 120 */ + squareSize?: number; }; const ClientLogosSection: React.FC = ({ title, description, - clients, + clients = [], // Default to empty array + speed = 50, //Default speed in seconds for one full cycle + squareSize = 120, // Default size for the square container (e.g., 120px) }) => { + // Need at least one client to render the marquee + if (clients.length === 0) { + return null; // Or render a placeholder message if preferred + } + + // Duplicate the clients for a seamless loop effect + const extendedClients = [...clients, ...clients]; + + const squareDim = `${squareSize}px`; // Convert size to string for inline style + const padding = Math.round(squareSize / 6); // Calculate padding based on size (adjust divisor as needed) + const paddingDim = `${padding}px`; + return ( - // Use semantic background -
+
- {" "} - {/* Use container */} - {/* Use semantic foreground */}

{title}

- {/* TODO: Implement Auto-Sliding Panel (e.g., using Swiper.js or Embla Carousel) */} -
- {" "} - {/* Adjust opacity */} - {clients.map((client, index) => ( +
+ + {/* Marquee container - group allows pausing animation on hover */} +
+ {/* Inner container that will animate */} +
+ {extendedClients.map((client, index) => (
- {client.logoUrl ? ( + {/* Square Background Container */} +
{`${client.name} - ) : client.icon ? ( - // Use semantic muted foreground for icons, primary on hover - React.createElement(client.icon, { - className: - "text-5xl text-muted-foreground/80 hover:text-primary transition-colors", - }) - ) : ( - {client.name} // Fallback text - )} +
))}
- {description && ( -

+ + {/* Optional: Add fade effect at the edges */} +

+
+
+ + {description && ( +
+

{description}

- )} -
+
+ )}
); }; -// Example default data matching the original page (using placeholders) export const defaultClients: Client[] = [ - { name: "Financial Services Client", icon: FaBuilding }, - { name: "Automotive Client", icon: FaCar }, - { name: "Tech Industry Client", icon: FaLaptopCode }, - { name: "Generic Client 1", icon: FaUsers }, - { name: "Generic Client 2", icon: FaBuilding }, + { name: "ABSA", logoUrl: "/images/absa.png" }, + { name: "SYBRIN", logoUrl: "/images/sybrin.svg" }, + { name: "SASOL", logoUrl: "/images/sasol.png" }, + { name: "JACARANDA", logoUrl: "/images/jacaranda.png" }, + { name: "SALESFORCE", logoUrl: "/images/salesforce.png" }, + { name: "BCX", logoUrl: "/images/bcx.png" }, + { name: "TOYOTA", logoUrl: "/images/toyota-logo.png" }, ]; export default ClientLogosSection; diff --git a/app/(website)/about/page.tsx b/app/(website)/about/page.tsx index 917dd0c..2883a63 100644 --- a/app/(website)/about/page.tsx +++ b/app/(website)/about/page.tsx @@ -16,32 +16,32 @@ import { FaProjectDiagram, } from "react-icons/fa"; -const leadershipTeam = [ - { - id: 1, - name: "Michael Shapiro", - title: "Managing Director", - imageUrl: "/images/profile1.jpg", - bio: "A seasoned leader and innovator with over 25 years of experience, driving strategic growth and fostering a culture of excellence within OMS.", - linkedinUrl: "#", - }, - { - id: 2, - name: "Gareth Corbishley", - title: "Director", - imageUrl: "/images/profile2.jpg", - bio: "Expert in operational efficiency and technological implementation, ensuring seamless project delivery and client satisfaction.", - linkedinUrl: "#", - }, - { - id: 3, - name: "Darryl Govender", - title: "Director", - imageUrl: "/images/profile3.jpg", - bio: "Specializes in aligning cutting-edge technology solutions with complex business needs to unlock transformative results.", - linkedinUrl: "#", - }, -]; +// const leadershipTeam = [ +// { +// id: 1, +// name: "Michael Shapiro", +// title: "Managing Director", +// imageUrl: "/images/profile1.jpg", +// bio: "A seasoned leader and innovator with over 25 years of experience, driving strategic growth and fostering a culture of excellence within OMS.", +// linkedinUrl: "#", +// }, +// { +// id: 2, +// name: "Gareth Corbishley", +// title: "Director", +// imageUrl: "/images/profile2.jpg", +// bio: "Expert in operational efficiency and technological implementation, ensuring seamless project delivery and client satisfaction.", +// linkedinUrl: "#", +// }, +// { +// id: 3, +// name: "Darryl Govender", +// title: "Director", +// imageUrl: "/images/profile3.jpg", +// bio: "Specializes in aligning cutting-edge technology solutions with complex business needs to unlock transformative results.", +// linkedinUrl: "#", +// }, +// ]; const coreValues = [ { @@ -131,13 +131,13 @@ const industryExpertise = [ export default function AboutUsPage() { return ( -
+
{" "} - {/* Prevent horizontal scroll */} + {/* Prevent horizontal scroll & Add dark mode base */} {/* Section 1: Hero / Company Overview */}
{/* Optional decorative background elements */} -
+
About Owethu Managed Services -

+

Your strategic partner in navigating the digital frontier. We fuse deep technical expertise with a passion for innovation to craft bespoke IT solutions that drive tangible business results and unlock @@ -159,15 +159,15 @@ export default function AboutUsPage() {

{/* Section 2: Our Genesis & Purpose */} -
+
-

+

Forged in Innovation,
Driven by Purpose

-

+

Expand on the founding story/motivation. E.g., Owethu Managed Services was born from a clear vision: to harness the transformative power of technology not just to solve problems, @@ -176,7 +176,7 @@ export default function AboutUsPage() { technology's potential and the unique challenges modern organizations face.

-

+

Elaborate on the journey. E.g., Our journey has been defined by a relentless pursuit of knowledge, adaptation to the ever-evolving tech landscape, and an unwavering commitment to @@ -187,7 +187,7 @@ export default function AboutUsPage() {

{/* Replace with a relevant, high-quality image representing innovation or teamwork */} Team collaborating on innovative solutions
{/* Section 3: Vision & Mission (Similar layout, refined look) */} -
+
-

+

Our North Star

-

+

Guiding our strategy, actions, and partnerships.

@@ -211,17 +211,17 @@ export default function AboutUsPage() {
{/* Vision Card */}
-

+

Our Vision

-

+

"To be global leaders in delivering cutting-edge IT solutions, pushing the boundaries of what's possible, and transforming industries for the better. We aim to empower @@ -231,17 +231,17 @@ export default function AboutUsPage() {

{/* Mission Card */}
-

+

Our Mission

-

+

"We are dedicated to creating tailored, innovative solutions that drive business success. By combining expertise with cutting-edge technology, we solve complex problems and @@ -254,13 +254,13 @@ export default function AboutUsPage() {

{/* Section 4: Our Approach & Methodology */} -
+
-

+

How We Deliver Excellence

-

+

Our methodology is built on collaboration, precision, and a relentless focus on delivering impactful results. We tailor our process to fit your specific project needs. @@ -270,16 +270,16 @@ export default function AboutUsPage() { {ourApproach.map((item) => (

-

+

{item.title}

-

+

{item.description}

@@ -288,7 +288,7 @@ export default function AboutUsPage() {
{/* Section 5: Industry Expertise */} -
+
@@ -296,10 +296,10 @@ export default function AboutUsPage() { className="text-5xl text-gold-500 mb-4" style={{ color: "#e1c44a" }} /> -

+

Deep Domain Knowledge

-

+

We combine broad technological capabilities with specialized expertise across key industries, understanding the unique challenges and opportunities within each sector. @@ -309,13 +309,13 @@ export default function AboutUsPage() { {industryExpertise.map((exp) => (

-

+

{exp.industry}

-

+

{exp.details}

@@ -331,7 +331,7 @@ export default function AboutUsPage() { className="text-5xl text-gold-500 mx-auto mb-5" style={{ color: "#e1c44a" }} /> -

+

Smart Technology, Applied Wisely

@@ -339,38 +339,38 @@ export default function AboutUsPage() { strategically. Our focus is on building solutions that are:

-
+

Scalable & Future-Proof

-

+

Designed to grow with your business and adapt to future technological advancements.

-
+

Robust & Reliable

-

+

Built with quality and security at the core, ensuring dependable performance.

-
+

Best-Fit Solutions

-

+

Chosen based on your specific needs, budget, and existing infrastructure, not just the latest hype.

@@ -379,25 +379,25 @@ export default function AboutUsPage() {
{/* Section 7: Core Values (Reusing previous structure, adjusting background) */} -
+
-

+

Our Foundational Values

{coreValues.map((value) => (
-

+

{value.title}

-

+

{value.description}

@@ -406,13 +406,13 @@ export default function AboutUsPage() {
{/* Section 8: The OMS Partnership */} -
+
{/* Replace with an image representing partnership or client collaboration */} Client partnership and collaboration -

+

More Than Vendors,
{" "} We're Partners

-

+

We believe the best results come from true collaboration. We invest time in understanding your culture, goals, and challenges, working alongside your team as an extension of your own capabilities.

-

+

This means open communication, shared goals, proactive problem-solving, and a long-term commitment to your success, extending far beyond project completion. @@ -443,14 +443,14 @@ export default function AboutUsPage() {

- {/* Section 9: Our Leadership Team */} -
+ {/* Section 9: Our Leadership Team +
-

+

Meet Our Leadership

-

+

Slightly enhance the intro. E.g., Guided by experience and a passion for innovation, our leadership team fosters a culture of excellence and empowers our experts to deliver outstanding results @@ -459,25 +459,22 @@ export default function AboutUsPage() {

- {/* Added justify-center */} {leadershipTeam.map((member) => (
- {/* Added max-width and mx-auto for centering if fewer than 3 items */}
- {/* Slightly taller image */} {`Photo
-

+

{member.name}

{member.title}

-

+

{member.bio}

{member.linkedinUrl && member.linkedinUrl !== "#" && ( @@ -494,10 +491,9 @@ export default function AboutUsPage() { href={member.linkedinUrl} target="_blank" rel="noopener noreferrer" - className="text-sm font-medium text-blue-600 hover:text-blue-800 transition-colors duration-200 font-poppins" + className="text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 transition-colors duration-200 font-poppins" aria-label={`LinkedIn profile of ${member.name}`} > - {/* Using a simple text link for cleanliness */} Connect on LinkedIn )} @@ -505,20 +501,20 @@ export default function AboutUsPage() {
))}
- {/* Button removed as requested */}
+ */} {/* Section 10: Commitment to Impact (Optional, but adds value) */}
- -

+ +

Driving Measurable Impact

-

+

Ultimately, our success is measured by yours. We are committed to delivering solutions that not only meet technical requirements but also drive efficiency, foster growth, enhance user experiences, and @@ -529,7 +525,7 @@ export default function AboutUsPage() {

Start the Conversation diff --git a/app/(website)/contact/page.tsx b/app/(website)/contact/page.tsx index 3469adf..67c5b9a 100644 --- a/app/(website)/contact/page.tsx +++ b/app/(website)/contact/page.tsx @@ -1,48 +1,73 @@ -import React from "react"; -import type { Metadata } from "next"; -import ContactForm from "@/components/ContactForm"; -import { FiMapPin, FiPhone, FiMail, FiClock } from "react-icons/fi"; // Import icons +"use client"; // Needed for FAQ state -// SEO Metadata for the Contact page -export const metadata: Metadata = { - title: "Contact Us | Owethu Managed Services (OMS)", - description: - "Get in touch with Owethu Managed Services. Contact us for IT solutions, resource augmentation, project management, and custom software development inquiries.", - alternates: { - canonical: "/contact", +import React, { useState } from "react"; +import { + FaMapMarkerAlt, + FaPhone, + FaEnvelope, + FaClock, + FaChevronDown, + FaChevronUp, +} from "react-icons/fa"; // Using Fa icons for consistency +import { COLORS } from "@/constants"; // Using COLORS constant +import ContactForm from "@/components/ContactForm"; + +// Define the structure for FAQ items +interface FAQItem { + id: number; + question: string; + answer: string; +} + +const faqData: FAQItem[] = [ + { + id: 1, + question: "What services does Owethu Managed Services offer?", + answer: + "We offer a comprehensive range of IT services including custom software development, resource augmentation (IT staffing), project management, cloud solutions, system integration, and IT consulting tailored to various industries.", }, - openGraph: { - title: "Contact OMS", - description: "Reach out to OMS for expert IT services and solutions.", - url: "https://oms.africa/contact", // Replace with your actual domain - images: [ - { - url: "/og-image-contact.jpg", // Create a specific OG image for contact - width: 1200, - height: 630, - alt: "Contact Owethu Managed Services", - }, - ], + { + id: 2, + question: "Which industries do you specialize in?", + answer: + "We have deep expertise in Financial Services & Fintech, Automotive, Technology, and Retail/E-commerce sectors, understanding the unique challenges and opportunities within each.", }, - twitter: { - card: "summary_large_image", - title: "Contact OMS", - description: "Get in touch with Owethu Managed Services.", - images: ["/og-image-contact.jpg"], + { + id: 3, + question: "How can I request a quote for a project?", + answer: + "You can request a quote by filling out the contact form on this page with details about your project requirements, calling us directly, or sending an email to hello@oms.africa. We'll get back to you promptly to discuss your needs.", }, -}; + { + id: 4, + question: "What are your business hours?", + answer: + "Our standard business hours are Monday to Friday, 8:00 AM to 5:00 PM South African Standard Time (SAST). We are closed on weekends and public holidays.", + }, +]; // Contact Page Component export default function ContactPage() { + const [openFaqId, setOpenFaqId] = useState(null); + + const toggleFaq = (id: number) => { + setOpenFaqId(openFaqId === id ? null : id); + }; + return ( -
- {/* Hero Section */} -
-
-

+ // Added dark mode base styles +
+ {/* Hero Section - Adjusted padding for mobile */} +
+
+
+

Get In Touch

-

+

We're here to help! Whether you have a question about our services, need assistance, or want to discuss a project, reach out and let us know. @@ -50,23 +75,31 @@ export default function ContactPage() {

- {/* Main Content Area (Form + Info) */} -
-
-
- {/* Contact Information Section */} -
-

+ {/* Main Content Area (Form + Info) - Adjusted padding, added dark mode */} +
+
+ {/* Grid stacks vertically by default, becomes 2 columns on large screens */} +
+ {/* Contact Information Section - Added dark mode styles */} +
+

Contact Information

- +
-

+

Our Office

-
+
+ {/* ... existing address lines ... */} Unit 10 B Centuria Park
265 Von Willich Avenue @@ -75,12 +108,12 @@ export default function ContactPage() {
South Africa
- {/* Optional: Link to Google Maps */} View on Google Maps @@ -88,12 +121,17 @@ export default function ContactPage() {
- +
-

Phone

+

+ Phone +

(012) 051 3282 @@ -101,12 +139,17 @@ export default function ContactPage() {
- +
-

Email

+

+ Email +

hello@oms.africa @@ -114,38 +157,109 @@ export default function ContactPage() {
- +
-

+

Business Hours

-

+

Monday - Friday: 8:00 AM - 5:00 PM (SAST)

-

+

Weekends & Public Holidays: Closed

- {/* Contact Form Section */} -
-

+ {/* Contact Form Section - Added dark mode styles */} +
+

Send Us a Message

+ {/* Assuming ContactForm handles its own dark mode or inherits text colors */}

- {/* Optional: Map Section Placeholder */} - {/*
-
-

[Embedded Map Placeholder - e.g., Google Maps iframe]

-
-
*/} + {/* Map Section - Adjusted padding, added dark mode, responsive height */} +
+
+

+ Find Us Here +

+ {/* Adjusted height for different screen sizes */} +
+ +
+
+
+ + {/* FAQ Section - Adjusted padding, added dark mode */} +
+
+

+ Frequently Asked Questions +

+
+ {faqData.map((faq) => ( +
+ +
+ {faq.answer} +
+
+ ))} +
+
+

); } diff --git a/app/(website)/obse/page-backup.tsx b/app/(website)/obse/page-backup.tsx new file mode 100644 index 0000000..e53e5ff --- /dev/null +++ b/app/(website)/obse/page-backup.tsx @@ -0,0 +1,959 @@ +// // app/obse/page.tsx + +// import Image from "next/image"; +// import Link from "next/link"; // Import Link for internal navigation +// import { +// FaBolt, // Represents speed, automation +// FaExclamationTriangle, // Represents challenges, risks +// FaLightbulb, // Represents solution, intelligence +// FaCogs, // Represents how it works, engine +// FaThumbsUp, // Represents benefits, advantages +// FaChartPie, // Represents data outputs, insights +// FaMicrochip, // Represents technology +// FaBriefcase, // Represents use cases +// FaUsersCog, // Represents customization, partnership +// FaInfoCircle, // Represents about section (alternative) +// FaPhoneAlt, // Represents contact +// FaPlayCircle, // Optional for mini-CTA +// FaArrowRight, // For CTAs or steps +// FaFilePdf, // For PDF/Statement icon +// FaSearchDollar, // For income detection +// FaShieldAlt, // For fraud detection +// FaNetworkWired, // For API integration +// FaTable, // For data structuring +// FaBrain, // For Machine Learning +// FaBusinessTime, // For turnaround time +// FaHourglassHalf, // For reducing time +// FaHandHoldingUsd, // For operational costs +// FaUserCheck, // For accuracy +// FaChartLine, // For efficiency/profitability +// FaClipboardList, // For transaction summary +// FaCalendarAlt, // For debit orders +// FaMoneyBillWave, // For disposable income +// FaBalanceScale, // For affordability +// FaExchangeAlt, // For Income/Expenses +// FaEye, // For review/interact step +// FaUpload, // For upload step +// FaPaperPlane, +// FaCheckCircle, // For access/integrate step +// } from "react-icons/fa"; + +// const keyFeatures = [ +// { +// icon: FaBolt, +// title: "Automated Data Extraction", +// description: +// "Processes statements in seconds (avg. 9-12s), drastically reducing manual effort.", +// }, +// { +// icon: FaUserCheck, // Using accuracy icon here +// title: "Advanced OCR Technology", +// description: +// "High accuracy, reads varied fonts, handles stamps, and distinguishes similar characters ('7' vs 'Z').", +// }, +// { +// icon: FaFilePdf, +// title: "Comprehensive SA Bank Coverage", +// description: +// "Handles statements from all major SA banks and various formats (PDF, scanned, stamped, multi/single page).", +// }, +// { +// icon: FaSearchDollar, +// title: "Intelligent Income Detection", +// description: +// "Auto-identifies salaried/non-salaried income with suggestive logic and exclusionary rules.", +// }, +// { +// icon: FaShieldAlt, +// title: "Enhanced Fraud Detection", +// description: +// "Detects document tampering, fraudulent transaction insertion, and developing ML-based behavioral profiling.", +// }, +// { +// icon: FaCogs, // Using cogs for interaction/processing +// title: "Dynamic Data Interaction", +// description: +// "User-friendly interface for real-time transaction recategorization and immediate recalculations.", +// }, +// { +// icon: FaNetworkWired, +// title: "Seamless API Integration", +// description: +// "Push structured, validated data directly into your credit scoring, LOS, or other internal systems.", +// }, +// { +// icon: FaChartPie, +// title: "Visual Analytics & Reporting", +// description: +// "Intuitive dashboards showing cash flow, income sources, and spending patterns.", +// }, +// { +// icon: FaTable, // Using table for dashboard data +// title: "MI Dashboard & Performance Tracking", +// description: +// "Monitor processing volumes, success rates, processing times, and user performance.", +// }, +// { +// icon: FaTable, +// title: "Multiple Export Formats", +// description: +// "Download results as structured Excel, formatted PDFs, or copy data for easy transfer.", +// }, +// { +// icon: FaCheckCircle, // Reusing from About Us for validation +// title: "Built-in Accuracy Checks", +// description: +// "Includes automated validations and data reviews for uncertain extractions to ensure reliability.", +// }, +// ]; + +// const coreBenefits = [ +// { +// icon: FaHourglassHalf, +// title: "Dramatically Reduce Processing Time", +// description: +// "Cut down statement analysis from hours to mere minutes/seconds.", +// }, +// { +// icon: FaUserCheck, +// title: "Ensure High Accuracy & Reliability", +// description: +// "Minimize human error with advanced OCR and built-in validation rules.", +// }, +// { +// icon: FaHandHoldingUsd, +// title: "Lower Operational Costs", +// description: +// "Reduce reliance on manual data entry staff and associated overheads.", +// }, +// { +// icon: FaShieldAlt, +// title: "Strengthen Fraud Prevention", +// description: +// "Proactively identify tampered documents and suspicious financial activity.", +// }, +// { +// icon: FaBusinessTime, +// title: "Accelerate Turnaround Times", +// description: +// "Speed up loan applications, credit assessments, and customer onboarding.", +// }, +// { +// icon: FaBrain, // Using brain for better decisions +// title: "Improve Decision-Making", +// description: +// "Base assessments on accurate, comprehensive, and rapidly available data.", +// }, +// { +// icon: FaChartLine, +// title: "Enhance Operational Efficiency", +// description: "Streamline workflows, free up staff for higher-value tasks.", +// }, +// { +// icon: FaThumbsUp, // Using thumbs up for compliance +// title: "Improve Compliance", +// description: +// "Ensure consistent and accurate data handling for regulatory requirements.", +// }, +// ]; + +// const dataOutputs = [ +// { +// icon: FaClipboardList, +// title: "Basic Transaction Summary", +// description: +// "Concise overview of transactions for spending pattern analysis.", +// }, +// { +// icon: FaCalendarAlt, +// title: "Debit Order Analysis", +// description: "Clear tracking of recurring payments and regular expenses.", +// }, +// { +// icon: FaMoneyBillWave, +// title: "Disposable Income Calculation", +// description: "Assessment of available funds after essential expenses.", +// }, +// { +// icon: FaBalanceScale, +// title: "Affordability Assessment Data", +// description: "Key data points to support financial capability evaluation.", +// }, +// { +// icon: FaExchangeAlt, +// title: "Income & Expenses Breakdown", +// description: "Detailed insights into financial inflows and outflows.", +// }, +// { +// icon: FaTable, +// title: "Detailed Transaction List", +// description: +// "Full, structured transaction data exportable for deep analysis.", +// }, +// { +// icon: FaExclamationTriangle, +// title: "Fraud Indicators", +// description: +// "Flags for potentially suspicious documents or transaction patterns.", +// }, +// ]; + +// const useCases = [ +// { title: "Lending & Credit Origination (Personal, VAF, SME)" }, +// { title: "Credit Risk Assessment & Scoring Input" }, +// { title: "Automated Income Verification" }, +// { title: "Affordability Calculations & Compliance Checks" }, +// { title: "Faster Customer Onboarding (KYC/Financial)" }, +// { title: "Portfolio Financial Health Monitoring" }, +// { title: "Internal Audit & Reconciliation Support" }, +// { title: "Fraud Investigation Data Preparation" }, +// ]; + +// export default function ObsePage() { +// return ( +// <> +//
+//
+//
+//
+//
+// OMS Logo +//
+//

+// Revolutionize Lending with OBSE +//

+//

+// Automate data extraction, enhance accuracy, detect fraud, and +// accelerate decision-making with South Africa's intelligent bank +// statement solution: Owethu Bank Statement Extraction. +//

+//
+// +// Request a Demo +// +// +// See How It Works +// +//
+//

+// Keywords: Bank Statement Extraction, OCR, Automation, Financial Data +// Analysis, South Africa +//

+//
+//
+ +//
+//
+//
+//
+// +//

+// The High Cost of Manual Bank Statement Processing +//

+//

+// In the rapidly evolving landscape of financial services, where +// precision and speed are paramount, extracting data from bank +// statements manually creates significant roadblocks. Efficiently +// parsing financial information is critical for accurate risk +// assessment, yet reliance on manual methods impacts efficiency +// and decision-making. +//

+//
    +//
  • +// +// +// +// Slow & Labor-Intensive: +// {" "} +// Countless hours spent on manual data entry lead to +// operational bottlenecks. +// +//
  • +//
  • +// {" "} +// {/* Using red for error */} +// +// +// Prone to Human Error: +// {" "} +// Mistakes risk inaccurate assessments, compliance breaches, +// and poor decisions. +// +//
  • +//
  • +// +// +// +// High Operational Costs: +// {" "} +// Significant resources consumed managing large volumes +// manually. +// +//
  • +//
  • +// {" "} +// {/* Using red for ineffective fraud detection */} +// +// +// Limited Fraud Detection: +// {" "} +// Manually identifying sophisticated fraud is difficult and +// time-consuming. +// +//
  • +//
  • +// +// +// +// Long Turnaround Times: +// {" "} +// Slow processing leads to customer dissatisfaction and lost +// opportunities. +// +//
  • +//
+//
+//
+// Manual data entry challenges vs automated efficiency +//
+//
+//
+//
+//
+ +// {/* --- 3. Introducing OBSE: The Intelligent Solution --- */} +//
+//
+//
+//
+// {/* Use the UI screenshot showing upload */} +// OBSE Bank Statement Upload Interface +//
+//
+// +//

+// OBSE: Automated Accuracy, Speed, and Insight +//

+//

+// Owethu Bank Statement Extraction (OBSE) is an advanced OCR and +// data aggregation platform designed specifically for the South +// African financial landscape. It seamlessly extracts +// comprehensive, accurate data from{" "} +// +// any SA bank statement +// {" "} +// (individual or juristic), regardless of format – including PDF +// templates, scanned images, stamped documents, and even +// disorganized statements. +//

+//

+// OBSE transforms raw bank statement data into actionable +// financial intelligence in seconds, eliminating manual drudgery +// and empowering your teams to make faster, smarter decisions. +//

+//
+//
+//
+//
+ +// {/* --- 4. How OBSE Works: Simple Steps, Powerful Results --- */} +//
+//
+//
+// +//

+// From PDF to Insights in Seconds +//

+//

+// OBSE simplifies complex data extraction into a streamlined +// workflow: +//

+//
+// {/* Process Steps - Can use a grid or timeline structure */} +//
+// {/* Step 1: Upload */} +//
+//
+// +//
+//

+// 1. Upload +//

+//

+// Easily upload single or multiple bank statements (PDFs). +//

+//
+// {/* Step 2: Extract */} +//
+//
+// +//
+//

+// 2. Automated Extraction +//

+//

+// Powerful engine reads, structures, and validates data in seconds +//

+//
+// {/* Step 3: Analyze & Check */} +//
+//
+// +//
+//

+// 3. Analysis & Fraud Check +//

+//

+// Analyzes transactions, identifies income, flags potential fraud. +//

+//
+// {/* Step 4: Review */} +//
+//
+// +//
+//

+// 4. Review & Interact (Optional) +//

+//

+// User-friendly interface to review, recategorize (real-time), and +// view insights. +//

+//
+// {/* Step 5: Access/Integrate */} +//
+//
+// +//
+//

+// 5. Access & Integrate +//

+//

+// Export (Excel, PDF), use visual analytics, or integrate via API. +//

+//
+//
+// {/* Visual for the process flow */} +//
+// OBSE Process Flow Visualization +//

+// Process Flow Visual (Replace with Image) +//

+//
+//
+//
+ +// {/* --- 5. Key Features: Powering Your Processes --- */} +//
+//
+//
+// {/* */} +//

+// Unlock Deeper Financial Understanding +//

+//

+// Explore the core capabilities that make OBSE an indispensable tool +// for modern financial analysis. +//

+//
+//
+// {keyFeatures.map((feature) => ( +//
+// +//

+// {feature.title} +//

+//

+// {feature.description} +//

+//
+// ))} +//
+//
+//
+ +// {/* --- 6. Core Benefits: The OBSE Advantage (The Gain) --- */} +//
+//
+//
+// +//

+// Transform Operations & Decision-Making +//

+//

+// Discover the tangible benefits OBSE brings to your financial +// workflows and bottom line. +//

+//
+//
+// {coreBenefits.map((benefit) => ( +//
+// +//

+// {benefit.title} +//

+//

+// {benefit.description} +//

+//
+// ))} +//
+//
+//
+ +// {/* --- 7. Data Outputs & Insights --- */} +//
+//
+//
+//
+// +//

+// Actionable Intelligence at Your Fingertips +//

+//

+// OBSE delivers a suite of standard financial summaries and +// detailed data points essential for informed decisions. Gain +// clarity on: +//

+//
+// {dataOutputs.map((output) => ( +//
+// +//
+//

+// {output.title} +//

+//

+// {output.description} +//

+//
+//
+// ))} +//
+//
+//
+// {/* Visual: Use dashboard examples from Page 6 */} +// OBSE Dashboard Examples showing financial insights +//

+// Dashboard Visual (Replace with Image) +//

+//
+//
+//
+//
+ +// {/* --- 8. Technology Deep Dive --- */} +//
+//
+//
+// +//

+// Leveraging Advanced Technology +//

+//

+// Built on a foundation of robust and intelligent technologies for +// reliable, accurate results. +//

+//
+//
+// {/* OCR Engine */} +//
+// +//

+// Advanced OCR Engine +//

+//

+// Fine-tuned for SA bank statements, handles diverse formats, +// scans, and obscured text. +//

+//
+// {/* Machine Learning */} +//
+// +//

+// Machine Learning +//

+//

+// Continuously improves interpretation, reconciles discrepancies, +// and powers fraud detection models. +//

+//
+// {/* Data Structuring */} +//
+// +//

+// Data Structuring & Validation +//

+//

+// Intelligently structures extracted data (e.g., JSON), validates +// entries, applies rules. +//

+//
+// {/* Secure API */} +//
+// +//

+// Secure API +//

+//

+// Robust and secure API for seamless, safe integration with your +// existing systems. +//

+//
+//
+// {/* Visual: JSON code snippet visual from Page 5 */} +//
+// Example JSON output from OBSE data extraction +//

+// JSON Output Example (Replace with Image) +//

+//
+//
+//
+ +// {/* --- 9. Use Cases & Applications --- */} +//
+//
+//
+// +//

+// Empowering Various Financial Processes +//

+//

+// OBSE delivers value across a wide range of applications within the +// financial services sector. +//

+//
+//
+// {useCases.map((useCase) => ( +//
+// +// +// {useCase.title} +// +//
+// ))} +//
+//
+//
+ +// {/* --- 10. Customization & Partnership --- */} +//
+//
+//
+//
+// OMS and Client Partnership +//
+//
+// +//

+// Flexible Solutions, Tailored To You +//

+//

+// While OBSE offers a comprehensive standard package, we +// understand that each organization has unique needs. If you +// require specific information or analysis not included,{" "} +// +// we are committed to collaborating with you to develop +// tailor-made outputs +// {" "} +// that align perfectly with your requirements. +//

+//

+// Our goal is to ensure OBSE meets and exceeds your expectations, +// providing the exact insights needed for your operational +// processes and decision-making. +//

+// {/* POC Callout */} +//
+//

+// Proof of Concept (POC) Available +//

+//

+// We offer Proof of Concept engagements to demonstrate OBSE's +// capabilities with your specific data and workflows before full +// commitment. Let's discuss your needs. +//

+//
+//
+//
+//
+//
+ +// {/* --- 11. About OMS: Your Trusted Technology Partner --- */} +//
+//
+// {/* Optional: Simple OMS logo here */} +// Owethu Management Solutions Logo +//

+// Your Experienced Technology Partner +//

+//

+// Owethu Management Solutions (OMS) provides professional technology +// services with a proven track record, including participation in +// ABSA's supplier development program since 2020. OBSE is backed by +// our dedicated team of IT Managers, Senior Developers, and UX +// Designers committed to robust, user-friendly solutions. +//

+//

+// We focus on leveraging technology to solve real-world business +// challenges in the financial sector, emphasizing efficiency, +// accuracy, and strong partnerships. +//

+//
+// +// Learn More About OMS +// +//
+//
+//
+ +// {/* --- 12. Call to Action (CTA) --- */} +//
+//
+// +//

+// Ready to Transform Your Bank Statement Processing? +//

+//

+// See OBSE in action! Schedule a live demo tailored to your specific +// use case and discover how OBSE can streamline your operations, +// reduce costs, and mitigate risk. +//

+//
+// +// Request a Personalized Demo +// +// +// Contact Sales Team +// +//
+// {/* Optional Tertiary Link */} +// {/* */} +//
+//
+ +// {/* --- 13. Contact Information --- */} +//
+//
+//
+// +//

+// Get in Touch +//

+//

+// For personalized support or to schedule your demo, reach out to +// us. Let's harness the power of automated data extraction for your +// business. +//

+//
+//
+//
+//

+// Contact Details: +//

+//

+// Phone:{" "} +// +// (012) 051 3281 +// +//

+//

+// Email:{" "} +// +// Zanelem@oms.africa +// +//

+//
+ +// {/* Optional: Placeholder for Contact Form */} +// {/*
+//

Or Send Us a Message

+//

Contact form component would go here.

+// Go to Contact Form +//
*/} +//
+//
+//
+// +// ); +// } diff --git a/app/(website)/obse/page.tsx b/app/(website)/obse/page.tsx new file mode 100644 index 0000000..d2fbc6b --- /dev/null +++ b/app/(website)/obse/page.tsx @@ -0,0 +1,847 @@ +import Image from "next/image"; +import Link from "next/link"; +import { Metadata } from "next"; // Added import +import { + FaArrowRight, + FaCheckCircle, + FaCogs, + FaDatabase, + FaEnvelope, + FaExclamationTriangle, + FaFileAlt, + FaFileInvoiceDollar, + FaFingerprint, + FaHandshake, + FaHistory, + FaLaptopCode, + FaLayerGroup, + FaLightbulb, + FaPhone, + FaPlayCircle, + FaPuzzlePiece, + FaSearchDollar, + FaShieldAlt, + FaShippingFast, + FaSignInAlt, + FaSpinner, + FaSyncAlt, + FaTachometerAlt, + FaUserCheck, + FaUsersCog, + FaWrench, +} from "react-icons/fa"; +import { COLORS } from "@/constants"; // Assuming COLORS constant is available +import ContactForm from "@/components/ContactForm"; // Assuming ContactForm is available + +// SEO Metadata +export const metadata: Metadata = { + title: "Automated Bank Statement Extraction South Africa | OBSE by OMS", + description: + "Revolutionize lending with OBSE: Automated bank statement data extraction, OCR, fraud detection, and analysis for South African financial institutions. Improve accuracy, speed, and decision-making.", + keywords: [ + "Optimised Bank Statement Extractor", + "OBSE", + "bank statement extraction", + "OCR bank statements", + "automated financial data", + "South Africa", + "lending automation", + "credit risk assessment", + "income verification", + "affordability calculation", + "fraud detection", + "financial technology", + "fintech SA", + "OMS", + "Owethu Management Services", + ], + // Optional: Add Open Graph and Twitter Card metadata for social sharing + openGraph: { + title: "Automated Bank Statement Extraction South Africa | OBSE by OMS", + description: + "Streamline your financial processes with OBSE's intelligent bank statement analysis.", + url: "https://www.oms.africa/obse", // Replace with your actual URL + images: [ + { + url: "/images/obse-og-image.png", // Replace with an appropriate OG image URL + width: 1200, + height: 630, + alt: "OBSE Automated Bank Statement Extraction", + }, + ], + locale: "en_ZA", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Automated Bank Statement Extraction South Africa | OBSE by OMS", + description: + "Fast, accurate bank statement processing for SA lenders with OBSE.", + // images: ['/images/obse-twitter-image.png'], // Replace with Twitter-specific image if needed + }, +}; + +// Define structure for features, benefits, etc. if needed for mapping +interface FeatureItem { + icon: React.ElementType; + title: string; + description: string; + isComingSoon?: boolean; +} + +const keyFeatures: FeatureItem[] = [ + { + icon: FaShippingFast, + title: "Automated Data Extraction", + description: + "Processes statements in seconds (avg. 9-12s), drastically reducing manual effort.", + }, + { + icon: FaFileAlt, + title: "Advanced OCR Technology", + description: + "High accuracy, handles various fonts, obscured text (stamps), and similar characters.", + }, + { + icon: FaDatabase, + title: "Comprehensive SA Bank Coverage", + description: + "Handles statements from all major SA banks and various formats (PDF, scanned, stamped).", + }, + { + icon: FaSearchDollar, + title: "Intelligent Income Detection", + description: + "Identifies salaried/non-salaried income, provides explanations, and filters out transfers.", + }, + { + icon: FaFingerprint, + title: "Enhanced Fraud Detection", + description: + "Detects document tampering, fraudulent insertions, and developing behavioral profiling.", + isComingSoon: true, + }, + { + icon: FaSyncAlt, + title: "Dynamic Data Interaction", + description: + "User-friendly interface for real-time transaction recategorization with immediate recalculations.", + }, + { + icon: FaLaptopCode, + title: "Seamless API Integration", + description: + "Push structured, validated data directly into your credit scoring or loan origination systems.", + }, + { + icon: FaTachometerAlt, + title: "Visual Analytics & Reporting", + description: + "Intuitive dashboards for insights into cash flow, income sources, and spending patterns.", + }, + { + icon: FaUsersCog, + title: "MI Dashboard & Performance Tracking", + description: + "Monitor processing volumes, success rates, average times, and user performance.", + }, + { + icon: FaSignInAlt, + title: "Multiple Export Formats", + description: + "Download results as structured Excel, formatted PDFs, or copy data easily.", + }, + { + icon: FaCheckCircle, + title: "Built-in Accuracy Checks", + description: + "Includes automated validations and data reviews for uncertain extractions.", + }, + { + icon: FaHistory, + title: "Comprehensive Audit Trail", + description: + "Maintains a detailed log of processing steps and user interactions for compliance and traceability.", + }, +]; + +const coreBenefits: FeatureItem[] = [ + { + icon: FaShippingFast, + title: "Dramatically Reduce Processing Time", + description: "Cut statement analysis from hours to minutes/seconds.", + }, + { + icon: FaCheckCircle, + title: "Ensure High Accuracy & Reliability", + description: "Minimize human error with advanced OCR and validation.", + }, + { + icon: FaFileInvoiceDollar, + title: "Lower Operational Costs", + description: "Reduce reliance on manual data entry staff and overheads.", + }, + { + icon: FaShieldAlt, + title: "Strengthen Fraud Prevention", + description: + "Proactively identify tampered documents and suspicious activity.", + }, + { + icon: FaTachometerAlt, + title: "Accelerate Turnaround Times", + description: + "Speed up loan applications, credit assessments, and onboarding.", + }, + { + icon: FaLightbulb, + title: "Improve Decision-Making", + description: "Base assessments on accurate, comprehensive, fast data.", + }, + { + icon: FaCogs, + title: "Enhance Operational Efficiency", + description: + "Streamline workflows and free up staff for higher-value tasks.", + }, + { + icon: FaUserCheck, + title: "Improve Compliance", + description: "Ensure consistent and accurate data handling.", + }, +]; + +const dataOutputs = [ + "Basic Transaction Summary", + "Debit Order Analysis", + "Disposable Income Calculation", + "Affordability Assessment Data", + "Income & Expenses Breakdown", + "Detailed Transaction List (Exportable)", + "Fraud Indicators & Flags", +]; + +const useCases = [ + "Lending & Credit Origination (Personal, Vehicle, SME)", + "Credit Risk Assessment", + "Income Verification Automation", + "Affordability Calculations", + "Customer Onboarding (KYC)", + "Financial Health Monitoring", + "Internal Audit & Reconciliation", + "Fraud Investigation Support", +]; + +export default function ObsePage() { + return ( +
+ {/* 1. Hero Section */} +
+
+ {/* Optional: Add a subtle background pattern or image */} + {/*
*/} +
+ {/* Consider adding OMS Logo here */} + {/* OMS Logo */} +

+ Revolutionize Your Lending and Credit Processes with OBSE +

+

+ Automate data extraction, enhance accuracy, detect fraud, and + accelerate decision-making with South Africa's intelligent bank + statement solution. +

+
+ + See How It + Works + + {/* Optional Mini-CTA */} + {/* + Watch a Quick Overview + */} +
+
+
+ + {/* 2. The Challenge Section */} +
+
+
+

+ The High Cost of Manual Bank Statement Processing +

+

+ In the rapidly evolving landscape of financial services, where + precision and speed are paramount, the challenges associated with + extracting data from bank statements can become significant + roadblocks. Efficiently parsing financial information is critical + for accurately assessing financial health and risk. +

+
+
+ {[ + { + icon: FaSpinner, + title: "Slow & Labor-Intensive", + desc: "Teams spend countless hours manually keying in data, causing operational bottlenecks.", + }, + { + icon: FaExclamationTriangle, + title: "Prone to Human Error", + desc: "Manual entry risks inaccurate assessments, compliance breaches, and poor decisions.", + }, + { + icon: FaFileInvoiceDollar, + title: "High Operational Costs", + desc: "Significant resources consumed managing large volumes of statements manually.", + }, + { + icon: FaShieldAlt, + title: "Limited Fraud Detection", + desc: "Identifying sophisticated fraud or tampered documents manually is difficult and slow.", + }, + { + icon: FaTachometerAlt, + title: "Long Turnaround Times", + desc: "Slow processing delays approvals, causing customer dissatisfaction and missed opportunities.", + }, + { + icon: FaLayerGroup, + title: "Scalability Challenges", + desc: "Manual workflows bottleneck quickly, making it difficult to handle growing statement volumes efficiently.", + }, + ].map((item) => ( +
+ +

+ {item.title} +

+

+ {item.desc} +

+
+ ))} +
+
+
+ + {/* 3. Introducing OBSE Section */} +
+
+
+
+

+ OBSE: Automated Accuracy, Speed, and Insight +

+

+ Optimised Bank Statement Extractor (OBSE) is an advanced OCR and + data aggregation platform designed specifically for the South + African financial landscape. It seamlessly extracts + comprehensive, accurate data from any bank statement (individual + or juristic, across all SA banks), regardless of format – + including PDFs, scanned images, stamped documents, and even + disorganized statements. +

+

+ OBSE transforms raw bank statement data into actionable + financial intelligence in seconds, eliminating manual drudgery + and empowering your teams to make faster, smarter decisions. +

+
+
+ {/* Placeholder for UI Screenshot */} + OBSE Upload Interface +
+
+
+
+
+ + {/* 4. How OBSE Works Section */} +
+
+
+

+ From PDF to Insights in Seconds +

+

+ OBSE simplifies complex data extraction into a streamlined + workflow. +

+
+ {/* Consider a visual flow diagram or stepped cards */} +
+ {[ + { + step: 1, + title: "Upload", + desc: "Easily upload single multi-month PDFs or separate monthly files.", + icon: FaSignInAlt, + }, + { + step: 2, + title: "Automated Extraction", + desc: "Powerful engine reads, structures, and validates data in seconds.", + icon: FaCogs, + }, + { + step: 3, + title: "Analysis & Fraud Checks", + desc: "Analyzes transactions, identifies income, flags fraud indicators.", + icon: FaSearchDollar, + }, + { + step: 4, + title: "Review & Interact", + desc: "User-friendly interface to review, recategorize, and view insights.", + icon: FaUserCheck, + }, + { + step: 5, + title: "Access & Integrate", + desc: "Export data (Excel, PDF) or integrate results via API.", + icon: FaLaptopCode, + }, + ].map((item) => ( +
+
+ {item.step} +
+ +

+ {item.title} +

+

+ {item.desc} +

+
+ ))} +
+
+
+ + {/* 5. Key Features Section */} +
+
+
+

+ Unlock Deeper Financial Understanding +

+

+ Explore the powerful capabilities that drive OBSE's + performance. +

+
+
+ {keyFeatures.map((feature) => ( +
+ {feature.isComingSoon && ( + + Coming Soon + + )} + +

+ {feature.title} +

+

+ {feature.description} +

+
+ ))} +
+
+
+ + {/* 6. Core Benefits Section */} +
+
+
+

+ The OBSE Advantage: Transform Your Operations +

+

+ Experience tangible benefits that impact your bottom line and + efficiency. +

+
+
+ {coreBenefits.map((benefit) => ( +
+ +

+ {benefit.title} +

+

+ {benefit.description} +

+
+ ))} +
+
+
+ + {/* 7. Data Outputs & Insights Section */} +
+
+
+
+ {/* Placeholder for Dashboard Example */} + OBSE Dashboard Insights +
+
+

+ Actionable Intelligence at Your Fingertips +

+

+ OBSE delivers a suite of standard financial summaries and + detailed data points essential for informed decisions: +

+
    + {dataOutputs.map((output) => ( +
  • + + + {output} + +
  • + ))} +
+
+
+
+
+ + {/* 8. Technology Deep Dive Section */} +
+
+
+ +

+ Leveraging Advanced Technology for Reliable Results +

+

+ Built on a foundation of cutting-edge technology to ensure + performance and accuracy. +

+
+
+ {[ + { + title: "Advanced OCR Engine", + desc: "Fine-tuned for diverse SA bank statements, handling stamps and challenging inputs.", + icon: FaFileAlt, + }, + { + title: "Machine Learning", + desc: "Continuously improves interpretation, context analysis, and fraud detection models.", + icon: FaLightbulb, + }, + { + title: "Data Structuring & Validation", + desc: "Intelligently structures data, validates entries, and applies rules for consistency.", + icon: FaPuzzlePiece, + }, + { + title: "Secure API", + desc: "Robust and secure API for seamless and safe integration with your existing systems.", + icon: FaShieldAlt, + }, + ].map((item) => ( +
+ +

+ {item.title} +

+

{item.desc}

+
+ ))} +
+ {/* Optional: Add JSON snippet visual */} + {/*
+

+              {`{
+  "transaction_id": "T12345",
+  "date": "2023-10-26",
+  "description": "SALARY ABC CORP",
+  "amount": 15000.00,
+  "type": "credit",
+  "category": "Income/Salary"
+}`}
+            
+
*/} +
+
+ + {/* 9. Use Cases & Applications Section */} +
+
+
+ +

+ Empowering Various Financial Processes +

+

+ OBSE provides critical data and automation for a wide range of + applications. +

+
+
+ {useCases.map((useCase) => ( +
+

+ {useCase} +

+
+ ))} +
+
+
+ + {/* 10. Customization & Partnership Section */} +
+
+ +

+ Flexible Solutions Tailored to Your Needs +

+

+ Beyond our standard offerings, we understand unique needs. If you + require specific information or analysis not in our standard + package, we'll collaborate to develop tailor-made outputs + aligned with your requirements. Our goal is to ensure OBSE exceeds + your expectations. +

+

+ We offer Proof of Concept (POC) engagements to demonstrate + OBSE's capabilities with your data and workflows, allowing you + to evaluate its potential impact before full commitment. +

+
+
+ + {/* 11. About OMS Section (Brief) */} +
+
+ {/* Optional: OMS Logo */} +

+ Your Experienced Partner in Financial Technology +

+

+ Owethu Management Solutions (OMS) provides professional technology + services with a proven track record, including participation in + ABSA's supplier development program since 2020. OBSE is backed + by a dedicated team committed to delivering robust, user-friendly + solutions that solve real-world business challenges. +

+ + Learn More About OMS + +
+
+ + {/* 12. Call to Action (CTA) Section */} +
+
+

+ Ready to Transform Your Bank Statement Processing? +

+

+ See OBSE in action! Schedule a live demo tailored to your specific + use case and discover how OBSE can streamline your operations, + reduce costs, and mitigate risk. +

+
+ + Request a Personalized Demo + + + Contact Sales Team + +
+ {/* Optional: Download Brochure Link */} + {/* */} +
+
+ + {/* 13. Contact Information Section */} +
+
+
+

+ Get in Touch +

+

+ For personalized support or to schedule a demo, reach out to us. + Our team is here to help you harness the power of automated data + extraction. Take the first step toward enhancing your efficiency + and driving your success! +

+
+
+ {/* Contact Details */} +
+
+ +
+

+ Phone +

+ + (012) 051 3281 + +
+
+
+ +
+

+ Email +

+ + Zanelem@oms.africa + +
+
+ {/* Optional: Link to main contact page */} + + Go to Full Contact Page + +
+ + {/* Optional: Simple Contact Form (if ContactForm component is not used/available) */} + {/*
+
+ + +
+
+ + +
+
+ + +
+ +
*/} + + {/* OR Use the ContactForm Component */} +
+

+ Send a Quick Inquiry +

+ +
+
+
+
+
+ ); +} diff --git a/app/(website)/page.tsx b/app/(website)/page.tsx index 4b376d1..56e3af1 100644 --- a/app/(website)/page.tsx +++ b/app/(website)/page.tsx @@ -12,48 +12,37 @@ import WhyChooseUsSection, { import FeaturedProductSection, { defaultObseFeatures, } from "./_components/FeaturedProductSection"; -// import HeroSectionModern from "./_components/HeroSectionModern"; -// import HeroSectionDynamic from "./_components/HeroSectionDynamic"; +import { getHome } from "@/lib/query/home"; + +export default async function HomePage() { + // Explicitly type the data variable, assuming getHome returns HeroSectionType or null/undefined + const data = await getHome(); + + // Handle case where data might be null or undefined + if (!data) { + // Optionally return a loading state or default content + return
Loading hero section...
; + // Or render the HeroSection with default props if preferred + } -export default function HomePage() { return ( <> - {" "} - {/* - Where Innovation Meets Excellence} // Simplified title for this layout - subtitle="We deliver cutting-edge IT solutions, empowering businesses to thrive in the ever-evolving digital landscape with unmatched industry expertise." - buttonText="Explore Solutions" // Changed button text slightly - buttonHref="/services" // Point to services maybe? - imageUrl="/hero-bg.jpg" // Use a different, high-quality background image - /> - - - - - Where Innovation
Meets - Excellence. - - } - subtitle="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." - buttonText="Learn More" - buttonHref="/about" - imageUrl="/hero-bg.jpg" // Specify your hero image - /> -*/} - Where Innovation
Meets - Excellence. + {data?.hero_title}
} - subtitle="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." - buttonText="Learn More" - buttonHref="/about" - imageUrl="/hero-bg.jpg" // Specify your hero image + subtitle={ + data.hero_subtitle || "Your trusted partner in technology solutions." + } // Use optional chaining and provide a default + buttonText={data.hero_buttons?.[0]?.label || "Learn More"} // Use optional chaining and provide a default + buttonHref={data.hero_buttons?.[0]?.link || "/about"} // Use optional chaining and provide a default + imageUrl={ + data.hero_cover + ? `${process.env.DIRECTUS_API_ENDPOINT}/assets/${data.hero_cover}` + : "/hero-bg.jpg" + } // Use optional chaining and provide a default /> + +

+ Recruitment Portal Coming Soon! +

+

+ We are currently building our dedicated recruitment portal to connect + talented individuals with exciting career opportunities at OMS. +

+

+ Please check back later for updates. +

+ {/* Optional: Link back to home or contact */} + {/* + Go to Homepage + */} +
+ ); +} diff --git a/app/(website)/services/page.tsx b/app/(website)/services/page.tsx new file mode 100644 index 0000000..d084eeb --- /dev/null +++ b/app/(website)/services/page.tsx @@ -0,0 +1,284 @@ +import Image from "next/image"; +import Link from "next/link"; +import { Metadata } from "next"; +import { + FaArrowRight, + FaBriefcase, + FaCogs, + FaHandshake, + FaLaptopCode, + FaProjectDiagram, + FaUsers, + FaWrench, + FaShippingFast, +} from "react-icons/fa"; +import { COLORS } from "@/constants"; // Assuming COLORS constant is available + +// SEO Metadata +export const metadata: Metadata = { + title: "IT Services & Solutions | OMS South Africa", + description: + "Explore IT services by Owethu Managed Services (OMS): Custom Software Development, IT Resource Augmentation, and the OBSE Bank Statement Extractor. Partner with us for innovative solutions.", + keywords: [ + "IT services South Africa", + "custom software development", + "IT resource augmentation", + "managed IT services", + "OBSE", + "bank statement automation", + "fintech solutions", + "IT consulting", + "OMS", + "Owethu Managed Services", + "Centurion", + "Gauteng", + ], + openGraph: { + title: "Comprehensive IT Services & Solutions | OMS South Africa", + description: + "OMS offers tailored IT solutions including custom development, resource augmentation, and specialized products like OBSE.", + url: "https://oms.africa/services", // Update with the final URL + images: [ + { + url: "/images/oms-services-og.png", // Replace with an appropriate OG image URL + width: 1200, + height: 630, + alt: "OMS IT Services", + }, + ], + locale: "en_ZA", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Comprehensive IT Services & Solutions | OMS South Africa", + description: + "Partner with OMS for expert IT services, from custom builds to resource scaling.", + // images: ['/images/oms-services-twitter.png'], // Replace if needed + }, +}; + +// Data for Service Areas +const serviceAreas = [ + { + title: "Custom Software Development & Consulting", + icon: FaLaptopCode, + description: + "We design, build, and implement bespoke software solutions tailored to your unique business challenges and objectives. Our consulting services help align technology with your strategic goals.", + imageUrl: "/images/team.svg", // Reusing image from About + link: "/contact?subject=Custom Development Inquiry", // Link to contact or a future dedicated page + linkText: "Discuss Your Project", + }, + { + title: "IT Resource Augmentation", + icon: FaUsers, + description: + "Scale your team effectively with our flexible resource augmentation model. Access skilled IT professionals (BAs, Testers, Developers, Designers) on demand or via managed milestone-based projects.", + imageUrl: "/images/team-collaborative.png", // Reusing image from Resource Augmentation + link: "/services/resource-augmentation", + linkText: "Explore Augmentation", + }, + { + title: "OBSE - Bank Statement Extractor", + icon: FaShippingFast, + description: + "Automate bank statement processing with OBSE. Our intelligent OCR solution extracts data accurately and quickly, enhancing efficiency, reducing risk, and speeding up financial assessments.", + imageUrl: "/images/obse.svg", // Reusing image from OBSE + link: "/obse", + linkText: "Learn About OBSE", + }, +]; + +const ourApproachItems = [ + { + icon: FaProjectDiagram, + title: "Strategic Alignment", + description: "Understanding your goals to ensure solutions deliver value.", + }, + { + icon: FaHandshake, + title: "Client-Centric Collaboration", + description: "Working as true partners, involving you throughout.", + }, + { + icon: FaCogs, + title: "Agile & Adaptive Delivery", + description: "Flexible methodologies for faster, relevant results.", + }, + { + icon: FaWrench, + title: "Technical Excellence", + description: "Building robust, scalable, and maintainable solutions.", + }, +]; + +export default function ServicesPage() { + return ( +
+ {/* 1. Hero Section */} +
+
+
+

+ Tailored IT Services & Solutions +

+

+ Owethu Managed Services empowers businesses through innovative + technology solutions, strategic consulting, flexible resource + augmentation, and specialized product offerings. +

+ + Explore Our Services + +
+
+ + {/* 2. Core Service Areas Section */} +
+
+
+

+ Our Expertise, Your Advantage +

+

+ We offer a comprehensive suite of services designed to address + your specific IT needs and drive business growth. +

+
+
+ {serviceAreas.map((service) => ( +
+
+ {`${service.title} +
+
+
+ +

+ {service.title} +

+
+

+ {service.description} +

+ + {service.linkText} + +
+
+ ))} +
+
+
+ + {/* 3. Our Approach Section */} +
+
+
+

+ How We Deliver Excellence +

+

+ Our methodology ensures collaboration, precision, and impactful + results tailored to your project needs. +

+
+
+ {ourApproachItems.map((item) => ( +
+ +

+ {item.title} +

+

+ {item.description} +

+
+ ))} +
+
+
+ + {/* 4. Industry Expertise Snippet (Optional) */} +
+
+ +

+ Serving Diverse Industries +

+

+ We apply our technical expertise across various sectors, + understanding the unique challenges and opportunities within each. + Key areas include Financial Services, Automotive, Technology, and + more. +

+ + Learn More About Our Industry Focus{" "} + + +
+
+ + {/* 5. Call to Action (CTA) Section */} +
+
+

+ Let's Build Your Solution +

+

+ Ready to discuss how OMS can help you achieve your technology goals? + Contact us today for a consultation. +

+ + Get in Touch + +
+
+
+ ); +} diff --git a/app/(website)/services/resource-augmentation/page.tsx b/app/(website)/services/resource-augmentation/page.tsx new file mode 100644 index 0000000..d0cf95c --- /dev/null +++ b/app/(website)/services/resource-augmentation/page.tsx @@ -0,0 +1,930 @@ +// /app/resource-augmentation/page.tsx (or wherever you place your page components) +import Image from "next/image"; +import Link from "next/link"; +import { Metadata } from "next"; +import { + FaArrowRight, + FaBriefcase, + FaCheckCircle, + FaClipboardList, // For BA + FaClock, // For Time & Material + FaCode, // For Development (General) + FaUsersCog, // For Managed Services/Teams + FaPaintBrush, // For UX/UI Design + FaProjectDiagram, // For Architecture/Milestones + FaPuzzlePiece, // For Integration/Solutions + FaRegHandshake, // For Partnership/Client Relations + FaShieldAlt, // For Reliability/QA + FaSitemap, // For Process/Architecture + FaSyncAlt, // For Agile/Flexibility + FaTasks, // For Project Management/Milestones + FaVial, // For Testing + FaUsers, // For Teams/Resources + FaChartLine, // For Reporting/MI + FaLightbulb, // For Innovation/Solutions + FaPhone, // Contact + FaEnvelope, // Contact + FaTools, // General Capabilities + FaLayerGroup, // Frameworks (SAFe, etc.) + FaFileInvoiceDollar, // Cost/Pricing + FaBusinessTime, // Experience/Past Projects +} from "react-icons/fa"; +import { COLORS } from "@/constants"; // Assuming COLORS constant is available +import ContactForm from "@/components/ContactForm"; // Assuming ContactForm is available + +// SEO Metadata +export const metadata: Metadata = { + title: "IT Resource Augmentation & Managed Services | OMS South Africa", + description: + "Flexible IT resource augmentation by Owethu Managed Services (OMS). Access expert Business Analysts, Developers, Testers, UX/UI Designers, and more on-demand or via milestone-based projects. Centurion, Gauteng.", + keywords: [ + "IT resource augmentation", + "managed IT services", + "IT staffing South Africa", + "project resources", + "IT project delivery", + "Business Analyst", + "Software Developer", + "Test Analyst", + "UX/UI Designer", + "Process Engineer", + "Salesforce Partner", + "time and material IT", + "milestone-based projects", + "IT outsourcing", + "flexible IT resources", + "Centurion", + "Gauteng", + "OMS", + "Owethu Managed Services", + "black female owned IT", + ], + openGraph: { + title: "Expert IT Resource Augmentation Services | OMS South Africa", + description: + "Scale your IT projects effectively with OMS's flexible resource augmentation and managed services.", + url: "https://oms.cvevolve.com/resource-augmentation", // Update with the final URL + images: [ + { + // Replace with an appropriate OG image URL if you have one + url: "/images/oms-resource-augmentation-og.png", + width: 1200, + height: 630, + alt: "OMS IT Resource Augmentation Services", + }, + ], + locale: "en_ZA", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Expert IT Resource Augmentation Services | OMS South Africa", + description: + "Get skilled IT professionals on demand. OMS offers flexible resource augmentation in South Africa.", + // images: ['/images/oms-resource-augmentation-twitter.png'], // Replace if needed + }, +}; + +// --- Data Structures from PDF --- + +interface ServiceModel { + icon: React.ElementType; + title: string; + description: string; + details: string[]; + pricingModel: string; +} + +const serviceModels: ServiceModel[] = [ + { + icon: FaClock, + title: "Resource Augmentation Model (Time & Material)", + description: + "Access skilled IT professionals on demand to supplement your existing team.", + details: [ + "Provides required Technology and Human Resources.", + "Client manages the project end-to-end.", + "Ideal for adding specific skills or capacity quickly.", + "Resources-on-Demand ensures no long-term FTE costs after project completion.", + ], + pricingModel: "Time-and-Material pricing structure.", + }, + { + icon: FaTasks, + title: "Milestone-Based Model (Managed Service)", + description: + "OMS takes full responsibility for delivering specific IT project phases or entire projects.", + details: [ + "Includes Project Planning & Management.", + "Covers End-to-end Process Design (Requirements, BPD, Arch, UX/UI).", + "Includes Data & MI Design (Architecture, Transformation, BI).", + "Comprehensive Testing & QA (Strategy, Implementation, Management).", + "OMS manages all deployed team members, deliverables, and velocity.", + ], + pricingModel: + "Milestone-Based pricing, payable on delivery of key milestones.", + }, +]; + +interface Capability { + role: string; + icon: React.ElementType; + description: string; + skills: { + level: string; // e.g., 'Senior', 'Intermediate', 'Junior' + points: string[]; + }[]; // Simplified for web display +} + +// Simplified representation of Page 4 for web display +const capabilities: Capability[] = [ + { + role: "Business / System Analysts", + icon: FaClipboardList, + description: + "Bridge the gap between business needs and technology solutions, defining requirements and processes.", + skills: [ + { + level: "Senior", + points: [ + "Stakeholder engagement & management", + "Lead other BAs", + "Requirements/Backlog management", + "Process Design", + "Report in Steering committees", + ], + }, + { + level: "Intermediate", + points: [ + "Document requirements", + "Design solutions with architects", + "Stakeholder engagement", + ], + }, + { + level: "Junior", + points: ["Elicit requirements", "Document requirements", "UML Design"], + }, + ], + }, + { + role: "Test Analysts (Manual & Automation)", + icon: FaVial, + description: + "Ensure software quality and reliability through rigorous testing strategies and execution.", + skills: [ + { + level: "Senior", + points: [ + "Lead Test Analysts", + "Develop Test Strategy", + "Manage Test Execution", + "UAT Training", + "Stakeholder Management", + "Report in Steering committees", + ], + }, + { + level: "Intermediate", + points: [ + "Document Test Scenarios", + "Write test scripts", + "Manage Test Execution", + "Report progress", + "Execute manual/automated testing", + ], + }, + { + level: "Junior", + points: [ + "Document Test Scenarios", + "Execute manual/automated testing", + "Produce Test Data", + "Defects tracking", + "UAT Training", + ], + }, + ], + }, + { + role: "UX / UI / CX Designers", + icon: FaPaintBrush, + description: + "Craft intuitive, engaging, and effective user experiences and interfaces.", + skills: [ + { + level: "Senior", + points: [ + "Develop Design Led Strategy", + "Lead other designers", + "Define User Journeys", + "Perform user experience testing", + "Stakeholder engagement", + "Produce UI prototypes", + ], + }, + { + level: "Intermediate", + points: [ + "Define User Journeys", + "Perform user experience testing", + "Produce design progress reports", + "Execute designs for UI prototypes", + ], + }, + { + level: "Junior", + points: [ + "Define User Journeys", + "Produce design progress reports", + "Execute designs for UI prototypes", + ], + }, + ], + }, + { + role: "Process Engineers / Designers", + icon: FaSitemap, + description: + "Analyze, design, and optimize business processes for efficiency and effectiveness.", + skills: [ + { + level: "Senior", + points: [ + "Develop Process Design Strategy", + "Manage and lead teams", + "Define Target Operating Model", + "Design ASIS/TOBE Processes", + "Stakeholder management", + ], + }, + { + level: "Intermediate", + points: [ + "Design ASIS/TOBE Processes", + "Stakeholder management", + "Evaluate & remove inefficiencies", + "Optimize current processes", + ], + }, + { + level: "Junior", + points: [ + "Design ASIS/TOBE Processes", + "Validate & remove inefficiencies", + "Optimize current processes", + ], + }, + ], + }, + // Note: Developer roles are mentioned in 'About Us' but not detailed in the table. Add a general entry if desired. + { + role: "Software Developers & Architects", + icon: FaCode, // Or FaProjectDiagram for Architects + description: + "Build, implement, and architect robust and scalable software solutions.", + skills: [ + { + level: "General", + points: [ + "Expertise across various modern technologies (backend, frontend, integration)", + "Solution architecture design", + "Development lifecycle management", + "API development and integration", + "Database design and management", + ], + }, + ], // Keep generic as PDF doesn't detail levels here + }, +]; + +interface Benefit { + icon: React.ElementType; + title: string; + description: string; +} + +const augmentationBenefits: Benefit[] = [ + { + icon: FaSyncAlt, + title: "Ultimate Flexibility", + description: + "Scale your team up or down quickly based on project demands and budget.", + }, + { + icon: FaUsers, + title: "Access to Expertise", + description: + "Gain immediate access to specialized IT skills that may not be available in-house.", + }, + { + icon: FaFileInvoiceDollar, + title: "Cost-Effectiveness", + description: + "Reduce recruitment costs, overheads, and long-term commitments associated with FTEs.", + }, + { + icon: FaChartLine, + title: "Increased Productivity", + description: + "Focus your core team on strategic initiatives while OMS resources handle specific tasks or projects.", + }, + { + icon: FaShieldAlt, + title: "Reduced Hiring Burden", + description: + "Avoid the time-consuming process of sourcing, vetting, hiring, and onboarding new employees.", + }, + { + icon: FaTasks, + title: "Focus on Core Business", + description: + "Outsource IT project execution or specific roles, allowing you to concentrate on your primary objectives.", + }, + { + icon: FaCheckCircle, + title: "Quality Assurance", + description: + "Benefit from experienced professionals and managed delivery (in Milestone model) for high-quality outcomes.", + }, + { + icon: FaLightbulb, + title: "Faster Project Delivery", + description: + "Accelerate project timelines by quickly filling skill gaps and adding capacity.", + }, +]; + +const methodologies = [ + { name: "Agile (SCRUM)", icon: FaSyncAlt }, + { name: "Scaled Agile (SAFe)", icon: FaLayerGroup }, + { name: "Waterfall", icon: FaProjectDiagram }, // Using ProjectDiagram as a proxy +]; + +const clientLogos = [ + { src: "/images/absa.png", alt: "Absa Bank Logo" }, // Replace with actual paths + { src: "/images/sybrin.svg", alt: "Sybrin Logo" }, + { src: "/images/bcx.png", alt: "BCX Logo" }, + { src: "/images/sasol.png", alt: "Sasol Logo" }, + { src: "/images/toyota-logo.png", alt: "Toyota Logo" }, +]; + +// --- Page Component --- + +export default function ResourceAugmentationPage() { + return ( +
+ {/* 1. Hero Section */} +
+
+ {/* Optional: Add a subtle background pattern or image */} + {/*
*/} +
+ {/* Consider adding OMS Logo here if desired */} + {/* OMS Logo */} +

+ Flexible IT Resource Augmentation & Managed Services +

+

+ Scale your IT capabilities effectively with Owethu Managed Services. + Access expert resources on demand or entrust us with full project + delivery through our flexible engagement models. +

+
+ + Explore Our Models + + + Discuss Your Needs + +
+
+
+ + {/* 2. The Need / Why Resource Augmentation? Section */} +
+
+
+

+ Bridge Your IT Skills Gap & Scale Efficiently +

+

+ Modern IT projects demand diverse skills and flexible capacity. + Resource augmentation helps overcome common challenges faced by + businesses today. +

+
+
+ {[ + { + icon: FaPuzzlePiece, + title: "Skill Gaps", + desc: "Struggling to find specialized IT talent for specific project needs or new technologies.", + }, + { + icon: FaChartLine, + title: "Fluctuating Demand", + desc: "Need to scale development or testing teams quickly for peak periods without long-term overhead.", + }, + { + icon: FaClock, + title: "Project Delays", + desc: "Lack of internal resources causing bottlenecks and delaying critical project timelines.", + }, + { + icon: FaFileInvoiceDollar, + title: "Cost Control", + desc: "Wanting to manage IT personnel costs effectively, avoiding expensive recruitment and full-time hires.", + }, + { + icon: FaBriefcase, + title: "Focus on Core Business", + desc: "Needing to free up internal teams to concentrate on strategic goals rather than specific project tasks.", + }, + { + icon: FaSyncAlt, + title: "Need for Flexibility", + desc: "Requiring adaptable staffing solutions that can change as project requirements evolve.", + }, + ].map((item) => ( +
+ +

+ {item.title} +

+

+ {item.desc} +

+
+ ))} +
+
+
+ + {/* 3. Our Solution: OMS Resource Augmentation */} +
+
+
+
+

+ Your Strategic Partner for IT Talent +

+

+ Owethu Managed Services (OMS) provides high-caliber IT + professionals through flexible engagement models tailored to + your specific project requirements and business objectives. As a + 100% Black female-owned organization based in Centurion, + Gauteng, we are committed to excellence and delivering value. +

+

+ Whether you need individual experts to augment your team or a + fully managed service to deliver key project milestones, OMS + offers the expertise and flexibility you need to succeed. We are + also a Salesforce Partner, providing licenses, implementation, + and managed services. +

+ + Learn More About OMS + +
+
+ {/* Placeholder Image - Replace with a relevant image (e.g., team collaboration, office) */} + OMS Team Collaboration +
+
+
+
+
+ + {/* 4. Our Service Models Section */} +
+
+
+

+ Flexible Engagement Models +

+

+ Choose the approach that best suits your project needs, management + style, and budget. +

+
+
+ {serviceModels.map((model) => ( +
+
+ +

+ {model.title} +

+
+

+ {model.description} +

+
    + {model.details.map((detail, index) => ( +
  • + + + {detail} + +
  • + ))} +
+

+ {model.pricingModel} +

+
+ ))} +
+

+ OMS operates using both AGILE{" "} + and Waterfall Frameworks, + depending on client preference and project suitability. +

+
+
+ + {/* 5. Capabilities & Expertise Section */} +
+
+
+ +

+ Our Core Capabilities +

+

+ We provide skilled professionals across key IT disciplines at + various experience levels (Junior, Intermediate, Senior). +

+
+
+ {capabilities.map((capability) => ( +
+ +

+ {capability.role} +

+

+ {capability.description} +

+ {/* Simplified skills display for the web page overview */} +
+ Key Areas of Expertise: +
+
    + {capability.skills[0].points.slice(0, 4).map( + ( + point, + index // Show first few points from senior/general list + ) => ( +
  • {point}
  • + ) + )} + {capability.skills[0].points.length > 4 && ( +
  • And more...
  • + )} +
+
+ ))} +
+

+ Detailed capability breakdowns by seniority level (Junior, + Intermediate, Senior) are available upon request. +

+
+
+ + {/* 6. Benefits of Partnering with OMS Section */} +
+
+
+ +

+ Why Choose OMS for Resource Augmentation? +

+

+ Leverage our expertise and flexible models to gain a competitive + advantage. +

+
+
+ {augmentationBenefits.map((benefit) => ( +
+ +

+ {benefit.title} +

+

+ {benefit.description} +

+
+ ))} +
+
+
+ + {/* 7. Proven Experience / Past Projects (High Level) */} +
+
+
+ +

+ Proven Track Record Across Industries +

+

+ We have successfully delivered resources and managed projects in + complex environments, including: +

+
+
+
+ +

+ Financial Services & Compliance +

+

+ FIC/KYC Remediation (Data Architecture, MI), Core Banking + Systems, Lending/Credit Process Optimisation. +

+
+
+ +

+ Customer Management & Onboarding +

+

+ Design, Testing, Requirements, Process Engineering, UX/UI for + customer-facing platforms. +

+
+
+ +

+ Platform & Process Automation +

+

+ Straight-Through Processing (STP) on Banking Platforms, API + Integration, Salesforce implementations. +

+
+
+ {/* Optional: Mention specific technologies briefly if desired */} + {/*

+ Expertise includes: Salesforce, AWS, Azure, Data Warehousing, BI Tools, BPMN, Various Banking Systems (Sybrin, Flexicube, etc.), Automation Frameworks (Selenium), and more. +

*/} +
+
+ + {/* 8. Implementation Approaches / Methodologies */} +
+
+ +

+ Adaptable Delivery Methodologies +

+

+ We are proficient in various project management frameworks and adapt + our approach to align with your organizational standards and project + needs. +

+
+ {methodologies.map((method) => ( +
+ + + {method.name} + +
+ ))} +
+
+
+ + {/* 9. Clients & Partners Section */} +
+
+

+ Trusted by Leading Organizations +

+ {/* Updated the grid classes here */} +
+ {clientLogos.map((logo) => ( +
+ {logo.alt} +
+ ))} +
+
+
+ + {/* 10. Call to Action (CTA) Section */} +
+
+

+ Ready to Enhance Your IT Team? +

+

+ Let's discuss how OMS resource augmentation or managed services + can help you achieve your project goals. Contact us today for a + consultation tailored to your specific needs. +

+
+ + Request a Consultation + + + Email Us Directly + +
+
+
+ + {/* 11. Contact Information Section */} +
+
+
+

+ Get in Touch +

+

+ We're ready to assist you. Reach out via phone, email, or use + the form below to start the conversation about your IT resource + needs. +

+
+
+ {/* Contact Details */} +
+

+ Contact Information +

+
+ +
+

+ Phone +

+ {/* Using specific numbers from PDF */} + + Zanele: (012) 051 3281 + + + Lindiwe: (012) 051 3282 + +
+
+
+ +
+

+ Email +

+ {/* Using specific emails from PDF */} + + Zanelem@oms.africa + + + Lindiwes@oms.africa + + + admin@oms.africa (General) + +
+
+ {/* Optional: Link to main contact page */} + + Go to Full Contact Page + +
+ + {/* Contact Form */} +
+

+ Send a Quick Inquiry +

+ {/* Reuse your existing contact form */} +
+
+
+
+
+ ); +} diff --git a/app/(website)/tech-talk/[slug]/page.tsx b/app/(website)/tech-talk/[slug]/page.tsx new file mode 100644 index 0000000..d4bddf7 --- /dev/null +++ b/app/(website)/tech-talk/[slug]/page.tsx @@ -0,0 +1,298 @@ +import { notFound } from "next/navigation"; +import Image from "next/image"; +import { getPostBySlug, getPosts } from "@/lib/query/post"; +import type { Metadata } from "next"; +import { format } from "date-fns"; +import { + ContentBlock, + OutputData, + ParagraphData, + HeaderData, + ListData, + ImageData, + QuoteData, + CodeData, +} from "@/types"; + +export async function generateStaticParams() { + const posts = await getPosts(); + if (!Array.isArray(posts)) { + console.error("getPosts did not return an array:", posts); + return []; + } + return posts.map((post) => ({ + slug: post.slug, + })); +} + +function extractDescriptionFromBlocks( + content: OutputData | null +): string | null { + if ( + !content || + !content.blocks || + !Array.isArray(content.blocks) || + content.blocks.length === 0 + ) { + return null; + } + const firstParagraph = content.blocks.find( + (block): block is ContentBlock & { data: ParagraphData } => + block.type === "paragraph" + ); + if (firstParagraph?.data?.text) { + const plainText = firstParagraph.data.text + .replace(/<[^>]*>/g, "") + .replace(/&[^;]+;/g, ""); + return plainText.substring(0, 160) + (plainText.length > 160 ? "..." : ""); + } + return null; +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const post = await getPostBySlug(slug, { + fields: [ + "slug", + "status", + "user_created", + "date_created", + "user_updated", + "date_updated", + "title", + "content", + "excerpt", + "featured_image", + ], + }); + + if (!post) { + return { + title: "Post Not Found", + }; + } + + const imageUrl = post.featured_image; + + const descriptionFromContent = + post.content && + typeof post.content === "object" && + Array.isArray(post.content.blocks) + ? extractDescriptionFromBlocks(post.content as OutputData) + : null; + + const description = + post.excerpt || descriptionFromContent || "Read this OMS TechTalk article."; + + const publishedTime = post.date_created + ? new Date(post.date_created).toISOString() + : ""; + + return { + title: `${post.title} | OMS TechTalk`, + description: description, + alternates: { + canonical: `/tech-talk/${post.slug}`, + }, + openGraph: { + title: post.title, + description: description, + url: `https://oms.africa/tech-talk/${post.slug}`, + type: "article", + publishedTime: publishedTime, + ...(imageUrl && { + images: [ + { + url: imageUrl, + width: 1200, + height: 630, + alt: post.title, + }, + ], + }), + }, + twitter: { + card: "summary_large_image", + title: post.title, + description: description, + ...(imageUrl && { images: [imageUrl] }), + }, + }; +} + +const renderBlock = (block: ContentBlock) => { + const renderListItems = (items: string[]) => { + return items.map((item, index) => ( +
  • + )); + }; + + switch (block.type) { + case "header": + const headerBlock = block as ContentBlock & { data: HeaderData }; + const level = headerBlock.data.level || 2; + // Ensure HeaderTag has a type that JSX understands for intrinsic elements. + const HeaderTag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + return ( + + ); + case "paragraph": + const paragraphBlock = block as ContentBlock & { data: ParagraphData }; + return ( +

    + ); + case "list": + const listBlock = block as ContentBlock & { data: ListData }; + const ListTag = listBlock.data.style === "ordered" ? "ol" : "ul"; + return ( + + {listBlock.data.items && + Array.isArray(listBlock.data.items) && + renderListItems(listBlock.data.items)} + + ); + case "image": + const imageBlock = block as ContentBlock & { data: ImageData }; + const imageUrl = imageBlock.data.file?.url; + + if (!imageUrl) return null; + + return ( +

    + {imageBlock.data.caption + {imageBlock.data.caption && ( +

    + {imageBlock.data.caption} +

    + )} +
    + ); + case "quote": + const quoteBlock = block as ContentBlock & { data: QuoteData }; + return ( +
    +

    + {quoteBlock.data.caption && ( +

    + - {quoteBlock.data.caption} +
    + )} +
    + ); + case "code": + const codeBlock = block as ContentBlock & { data: CodeData }; + const codeContent = codeBlock.data.code || ""; + return ( +
    +          {codeContent}
    +        
    + ); + default: + console.warn(`Unknown block type: ${block.type}`); + return null; + } +}; + +export default async function PostPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const post = await getPostBySlug(slug); + + if (!post) { + notFound(); + } + + const contentBlocks = + post.content && + typeof post.content === "object" && + Array.isArray(post.content.blocks) + ? (post.content.blocks as ContentBlock[]) + : null; + + const coverImageUrl = + post.featured_image + "?width=1200&height=600&quality=80&fit=cover"; + + return ( +
    +
    + {coverImageUrl && ( +
    + {`Cover +
    + )} +

    + {post.title} +

    + {post.date_created && ( +

    + Published on {format(new Date(post.date_created), "MMMM dd, yyyy")} +

    + )} + {post.excerpt && ( +

    + {post.excerpt} +

    + )} +
    + +
    +
    + {contentBlocks && contentBlocks.length > 0 ? ( + contentBlocks.map((block) => renderBlock(block)) + ) : ( +

    Content is not available or in an unexpected format.

    + )} +
    +
    +
    + ); +} diff --git a/app/(website)/tech-talk/page.tsx b/app/(website)/tech-talk/page.tsx index 2cef120..4b874d0 100644 --- a/app/(website)/tech-talk/page.tsx +++ b/app/(website)/tech-talk/page.tsx @@ -1,39 +1,8 @@ import React from "react"; import BlogPostCard from "@/components/BlogPostCard"; -import { prisma } from "@/lib/prisma"; // Import Prisma client -import { auth } from "@/auth"; // Import auth to check session -import Button from "@/components/ui/Button"; // Import Button component import type { Metadata } from "next"; - -interface Post { - id: string; - slug: string; - title: string; - content: string; - excerpt?: string | null; - imageUrl?: string | null; - published: boolean; - authorId: string; - createdAt: Date; - updatedAt: Date; - tags?: string[]; -} - -// --- Fetch Posts --- -async function getPublishedPosts() { - try { - const posts = await prisma.post.findMany({ - where: { published: true }, - orderBy: { createdAt: "desc" }, - // select needed fields if not all - }); - return posts; - } catch (error) { - console.error("Failed to fetch posts:", error); - return []; // Return empty array on error - } -} - +import { getPosts } from "@/lib/query/post"; +import { Post } from "@/types"; // --- SEO Metadata --- export const metadata: Metadata = { title: "OMS TechTalk | Insights & Innovation", @@ -65,8 +34,7 @@ export const metadata: Metadata = { // --- Page Component --- const TechTalkPage = async () => { - const posts = await getPublishedPosts(); - const session = await auth(); // Get session info + const posts: Post[] = await getPosts(); return (
    @@ -80,28 +48,21 @@ const TechTalkPage = async () => { Insights, trends, and discussions on the latest in technology, innovation, and digital transformation from the experts at OMS.

    - {/* Conditionally render Create Post button */} - {session?.user && ( -
    - -
    - )}
    {/* Blog Post Grid */} {posts.length > 0 ? (
    {posts.map((post: Post) => ( { + const res = await fetch(`http://localhost:3000/api/vacancies/${slug}`, { + cache: "no-store", + }); + if (!res.ok) { + if (res.status === 404) return null; + console.error(`Failed to fetch vacancy ${slug}: ${res.statusText}`); + throw new Error("Failed to fetch vacancy details"); + } + return res.json(); +} + +export default async function VacancyDetailsPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const vacancy = await getVacancy(slug); + + if (!vacancy) { + notFound(); + } + + const shareUrl = `${process.env.WEBSITE_URL}/vacancies/${slug}`; + const shareTitle = encodeURIComponent( + `Job Opening: ${vacancy.title} at ${ + vacancy.company?.name || "Owethu Managed Services" + }` + ); + + return ( +
    + +
    + ); +} diff --git a/app/(website)/vacancies/_components/Badge.tsx b/app/(website)/vacancies/_components/Badge.tsx new file mode 100644 index 0000000..64a1393 --- /dev/null +++ b/app/(website)/vacancies/_components/Badge.tsx @@ -0,0 +1,12 @@ +export const Badge = ({ + children, + icon: Icon, +}: { + children: React.ReactNode; + icon?: React.ElementType; +}) => ( + + {Icon && } + {children} + +); diff --git a/app/(website)/vacancies/_components/ListSection.tsx b/app/(website)/vacancies/_components/ListSection.tsx new file mode 100644 index 0000000..ebe7b03 --- /dev/null +++ b/app/(website)/vacancies/_components/ListSection.tsx @@ -0,0 +1,31 @@ +import { COLORS } from "@/constants"; + +export const ListSection = ({ + title, + items, + icon: Icon, +}: { + title: string; + items?: string[]; + icon?: React.ElementType; +}) => { + if (!items || items.length === 0) return null; + return ( +
    +

    + {Icon && ( + + )} + {title} +

    +
      + {items.map((item, index) => ( +
    • {item}
    • + ))} +
    +
    + ); +}; diff --git a/app/(website)/vacancies/_components/MetadataItem.tsx b/app/(website)/vacancies/_components/MetadataItem.tsx new file mode 100644 index 0000000..2e4e387 --- /dev/null +++ b/app/(website)/vacancies/_components/MetadataItem.tsx @@ -0,0 +1,22 @@ +import { COLORS } from "@/constants"; + +export const MetadataItem = ({ + icon: Icon, + label, + value, +}: { + icon: React.ElementType; + label: string; + value: React.ReactNode; +}) => ( +
    +
    +); diff --git a/app/(website)/vacancies/_components/VacancyClientContent.tsx b/app/(website)/vacancies/_components/VacancyClientContent.tsx new file mode 100644 index 0000000..1f11451 --- /dev/null +++ b/app/(website)/vacancies/_components/VacancyClientContent.tsx @@ -0,0 +1,349 @@ +"use client"; + +import { + useState /* useRef, useEffect - Remove these if no longer needed */, +} from "react"; // Updated imports +import Link from "next/link"; +import { Vacancy } from "@/types"; +import VacancyApplicationForm from "@/components/VacancyApplicationForm"; + +import { + FaMapMarkerAlt, + FaBriefcase, + FaClock, + FaCalendarAlt, + FaDollarSign, + FaGraduationCap, + FaShareAlt, + FaCheckCircle, + FaBuilding, + FaLink, + FaListUl, + FaInfoCircle, + FaStar, + FaGift, + FaTools, +} from "react-icons/fa"; +import { formatDate } from "@/lib/helpers"; +import { COLORS } from "@/constants"; +import Image from "next/image"; +import { MetadataItem } from "./MetadataItem"; +import { Badge } from "./Badge"; +import { ListSection } from "./ListSection"; +import Button from "@/components/ui/Button"; // Assuming you have a Button component +import Modal from "@/components/ui/Modal"; + +interface VacancyClientContentProps { + vacancy: Vacancy & { + company?: { name: string; logoUrl?: string; websiteUrl?: string }; + skills?: string[]; + }; + shareUrl: string; + shareTitle: string; +} + +export default function VacancyClientContent({ + vacancy, + shareUrl, + shareTitle, +}: VacancyClientContentProps) { + // State to control modal visibility + const [isApplyModalOpen, setIsApplyModalOpen] = useState(false); + // const applyFormRef = useRef(null); // Remove this ref + // Remove the useEffect for scrolling + + const handleOpenApplyModal = () => { + setIsApplyModalOpen(true); + }; + + const handleCloseApplyModal = () => { + setIsApplyModalOpen(false); + }; + + return ( +
    + {" "} + {/* Adjusted padding */} + {/* --- Company Header --- */} + {vacancy.company && ( +
    +
    + {" "} + {/* Added flex-grow and min-w-0 for wrapping */} + {vacancy.company.logoUrl ? ( + {`${vacancy.company.name} + ) : ( +
    + +
    + )} +
    + {" "} + {/* Added min-w-0 here too */} +

    + {" "} + {/* Consider truncate */} + {vacancy.title} +

    +

    + at {vacancy.company.name} +

    + {vacancy.company.websiteUrl && ( + + Visit website{" "} + + + )} +
    +
    + {/* Apply Button in Header */} + +
    + )} + {/* --- End Company Header --- */} + {/* --- Main Grid Layout --- */} +
    + {/* --- Main Content Area (Left Column) --- */} +
    + {/* Title if no company header */} + {!vacancy.company && ( +
    +

    + {vacancy.title} +

    + {/* Apply Button if no header */} + +
    + )} + + {/* Job Description */} +
    +

    + {" "} + Job Description +

    +
    + {/* Or render as text if plain:

    {vacancy.description}

    */} +
    +
    + + {/* --- List Sections (Responsibilities, Qualifications, Skills, Benefits) --- */} + {/* Assuming ListSection renders conditionally if items are empty */} + + + + + {/* Skills Section */} + {vacancy.skills && vacancy.skills.length > 0 && ( +
    +

    + {" "} + Skills +

    +
    + {vacancy.skills.map((skill, index) => ( + {skill} + ))} +
    +
    + )} + + {/* Benefits Section */} + {vacancy.benefits && vacancy.benefits.length > 0 && ( +
    +

    + {" "} + Benefits +

    +
      + {vacancy.benefits.map((item, index) => ( +
    • + + {item} +
    • + ))} +
    +
    + )} + {/* --- End List Sections --- */} + + {/* Apply button below main content (redundant if header button exists, keep or remove as needed) */} +
    + +
    +
    + {/* --- End Main Content Area --- */} + + {/* --- Sidebar (Right Column) --- */} +
    + {/* Metadata Card */} +
    +

    + Job Overview +

    +
    + + + {vacancy.location.city}, {vacancy.location.country}{" "} + {vacancy.location.remote && Remote Possible} + + } + /> + {vacancy.employmentType}} + /> + + {vacancy.salary && ( + + {vacancy.salary.min.toLocaleString()} -{" "} + {vacancy.salary.max.toLocaleString()}{" "} + {vacancy.salary.currency} {vacancy.salary.period} + + } + /> + )} + + {vacancy.applicationDeadline && ( + + )} +
    +
    + + {/* Share Card */} +
    +

    + {" "} + Share this opening +

    + +
    +
    +
    + + + +
    + ); +} diff --git a/app/(website)/vacancies/page.tsx b/app/(website)/vacancies/page.tsx new file mode 100644 index 0000000..3bde9a2 --- /dev/null +++ b/app/(website)/vacancies/page.tsx @@ -0,0 +1,236 @@ +"use client"; // <-- Add this line to use state + +import Link from "next/link"; +import { useState } from "react"; // <-- Import useState +import { + FaMapMarkerAlt, + FaBriefcase, + FaPaperPlane, + FaArrowRight, + FaRegClock, + FaSearch, +} from "react-icons/fa"; +import { demoVacancies } from "@/lib/demo-data/vacancies"; +import { Vacancy } from "@/types"; +import Button from "@/components/ui/Button"; +import Modal from "@/components/ui/Modal"; // <-- Import your Modal component +import { COLORS } from "@/constants"; +import VacancyApplicationForm from "@/components/VacancyApplicationForm"; + +// Metadata object might need adjustment depending on your setup with client components +// If using App Router, keep it, Next.js handles it. +/* +export const metadata: Metadata = { + title: "Current Vacancies | OMS", + description: + "Explore exciting career opportunities at OMS. Find your perfect role or submit your CV for future consideration.", +}; +*/ + +// --- VacancyCard Component (no changes needed here) --- +interface VacancyCardProps { + vacancy: Vacancy; +} + +function VacancyCard({ vacancy }: VacancyCardProps) { + const formatDate = (dateString: string | undefined) => { + if (!dateString) return "Date N/A"; + try { + return new Date(dateString).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + } catch { + return "Invalid Date"; + } + }; + const postedDate = formatDate(vacancy.postedDate); + + return ( + +
    +
    +

    + {vacancy.title} +

    +
    + + + + + {vacancy.postedDate && ( + + + )} +
    +
    +
    + + View Details + +
    +
    + + ); +} +// --- End Vacancy Card Component --- + +// --- Vacancies Page --- +export default function VacanciesPage() { + // TODO: Replace demoVacancies with actual API call if needed client-side, + // or fetch server-side and pass as props if using Pages Router. + // For App Router, `async function` fetches server-side by default. + const vacancies = demoVacancies; + + // --- State for the "Future Positions" Modal --- + const [isFuturePositionModalOpen, setIsFuturePositionModalOpen] = + useState(false); + + const handleOpenFuturePositionModal = () => + setIsFuturePositionModalOpen(true); + const handleCloseFuturePositionModal = () => + setIsFuturePositionModalOpen(false); + // --- End State --- + + return ( +
    + {/* Section 1: Hero / Page Header */} +
    +
    +
    +

    + Career Opportunities +

    +

    + Join our team of innovators and experts. Explore current openings at + OMS or submit your CV for future consideration. +

    +
    +
    + + {/* Section 2: Vacancy List */} +
    +
    +

    + Current Openings +

    + {vacancies.length > 0 ? ( +
    + {vacancies.map((vacancy) => ( + + ))} +
    + ) : ( +
    + +

    + No Open Vacancies Right Now +

    +

    + We're not actively hiring for specific roles at the moment, + but we're always looking for passionate and talented + individuals. Check back soon or submit your CV below! +

    +
    + )} +
    +
    + + {/* Section 3: Future Positions / CV Submission */} +
    +
    + {" "} + {/* Ensure icon contrast */} +

    + {" "} + {/* Ensure heading contrast */} + Don't See the Right Fit? +

    +

    + {" "} + {/* Ensure text contrast */} + We're always looking for talented individuals to join our + journey. Submit your CV, and we'll keep you in mind for future + openings that match your profile. +

    +
    + {/* --- Updated Button --- */} + + {/* --- End Updated Button --- */} +
    +
    +
    + + {/* --- Modal for Future Position Application --- */} + + + +
    + ); +} diff --git a/app/achievements/_data/achievementsDefaults.ts b/app/achievements/_data/achievementsDefaults.ts new file mode 100644 index 0000000..90a8a8d --- /dev/null +++ b/app/achievements/_data/achievementsDefaults.ts @@ -0,0 +1,158 @@ +// /app/achievements/_data/achievementsDefaults.ts (or similar) + +export interface Award { + id: string | number; + name: string; + year: number | string; + awardingBody: string; + description?: string; + imageUrl?: string; // URL for an award logo or image +} + +export interface Certification { + id: string | number; + name: string; + issuingBody: string; + logoUrl?: string; + details?: string; +} + +export interface Partner { + id: string | number; + name: string; + logoUrl: string; // Partner logo is usually essential + websiteUrl?: string; + description?: string; +} + +export interface GalleryImage { + id: string | number; + imageUrl: string; + caption?: string; + alt: string; + event?: string; // Optional: Name of the event + date?: string; // Optional: Date of the event/photo +} + +export interface Milestone { + id: string | number; + description: string; + date: string; // e.g., "Q4 2023", "June 2022" +} + +// --- Default Placeholder Data --- + +export const defaultAwards: Award[] = [ + { + id: 1, + name: "Top BEE Level 1 Contributor", + year: 2023, + awardingBody: "SANAS Accreditation Body", + description: + "Recognized for commitment to Broad-Based Black Economic Empowerment.", + imageUrl: "/images/awards/bee-level1.png", + }, // Replace with actual paths + { + id: 2, + name: "Supplier Development Excellence", + year: 2022, + awardingBody: "Absa Enterprise Development", + description: + "Acknowledged for impactful participation in the Absa supplier development program.", + imageUrl: "/images/awards/absa-supplier.png", + }, + // Add more awards... +]; + +export const defaultCertifications: Certification[] = [ + { + id: 1, + name: "Salesforce Certified Partner", + issuingBody: "Salesforce", + logoUrl: "/images/certs/salesforce-partner.png", + }, + { + id: 2, + name: "ISO 9001:2015 (Pending/In Progress)", + issuingBody: "Relevant Body", + details: + "Actively working towards certification for quality management systems.", + }, // Example of in-progress + // Add more certifications +]; + +export const defaultPartners: Partner[] = [ + { + id: 1, + name: "Absa Bank", + logoUrl: "/clients/absa-logo.png", + websiteUrl: "https://www.absa.co.za/", + }, // Using client logos for partners + { + id: 2, + name: "Salesforce", + logoUrl: "/images/certs/salesforce-partner.png", + websiteUrl: "https://www.salesforce.com/", + }, + { + id: 3, + name: "Sybrin", + logoUrl: "/clients/sybrin-logo.png", + websiteUrl: "https://www.sybrin.com/", + }, + // Add other key strategic partners if distinct from clients +]; + +export const defaultGalleryImages: GalleryImage[] = [ + { + id: 1, + imageUrl: "/images/gallery/team-event-1.jpg", + caption: "Annual Team Building Day 2023", + alt: "OMS Team at Annual Event", + event: "Team Building", + date: "Nov 2023", + }, + { + id: 2, + imageUrl: "/images/gallery/award-ceremony-1.jpg", + caption: "Receiving the Supplier Development Award", + alt: "OMS receiving award", + event: "Absa Awards Night", + date: "Oct 2022", + }, + { + id: 3, + imageUrl: "/images/gallery/office-launch.jpg", + caption: "Celebrating our new Centurion office opening", + alt: "OMS new office launch", + event: "Office Launch", + date: "May 2023", + }, + { + id: 4, + imageUrl: "/images/gallery/client-workshop.jpg", + caption: "Collaborative workshop session with a key client", + alt: "OMS client workshop", + event: "Client Workshop", + date: "Sep 2023", + }, + // Add more gallery images +]; + +export const defaultMilestones: Milestone[] = [ + { id: 1, description: "Established Owethu Managed Services", date: "2019" }, // Adjust start date if needed + { + id: 2, + description: "Joined Absa Supplier Development Programme", + date: "2020", + }, + { id: 3, description: "Became an official Salesforce Partner", date: "2022" }, + { id: 4, description: "Launched the OBSE Product", date: "Q1 2023" }, + { id: 5, description: "Achieved BEE Level 1 Status", date: "Q3 2023" }, + { + id: 6, + description: "Opened new Head Office in Centurion", + date: "May 2023", + }, + // Add more significant milestones +]; diff --git a/app/achievements/page.tsx b/app/achievements/page.tsx new file mode 100644 index 0000000..aa25d8b --- /dev/null +++ b/app/achievements/page.tsx @@ -0,0 +1,361 @@ +// /app/achievements/page.tsx +import Image from "next/image"; +import { Metadata } from "next"; +import { + FaAward, + FaCertificate, + FaHandshake, + FaCamera, + FaTrophy, + FaStar, + FaExternalLinkAlt, +} from "react-icons/fa"; +import { COLORS } from "@/constants"; // Assuming COLORS is available +// import HeroSection from "../_components/HeroSection"; // Reuse HeroSection component + +// Import the placeholder data +import { + defaultAwards, + defaultCertifications, + defaultPartners, + defaultGalleryImages, + defaultMilestones, +} from "./_data/achievementsDefaults"; // Adjust path as needed +import CallToActionSection from "../(website)/_components/CallToActionSection"; + +// SEO Metadata +export const metadata: Metadata = { + title: "Achievements & Recognition | Owethu Managed Services (OMS)", + description: + "Explore the awards, certifications, partnerships, and milestones achieved by OMS. See our commitment to excellence and empowerment.", + keywords: [ + "OMS achievements", + "Owethu Managed Services awards", + "BEE Level 1", + "Salesforce Partner", + "company milestones", + "IT company recognition", + "supplier development program", + "South Africa IT awards", + "OMS gallery", + "OMS partnerships", + ], + openGraph: { + title: "Achievements & Recognition | OMS", + description: + "Discover OMS's journey of success, awards, and key partnerships.", + url: "https://oms.cvevolve.com/achievements", // Update with the final URL + // Add an appropriate OG image URL if available + // images: [{ url: '/images/oms-achievements-og.png', width: 1200, height: 630, alt: 'OMS Achievements' }], + locale: "en_ZA", + type: "website", + }, + // Add twitter card if desired +}; + +export default function AchievementsPage() { + // In a real app, fetch data here using functions like getAchievements(), getAwards(), etc. + const awards = defaultAwards; + const certifications = defaultCertifications; + const partners = defaultPartners; + const galleryImages = defaultGalleryImages; + const milestones = defaultMilestones; + + return ( + <> + {/* 1. Hero Section */} + {/* + Our Journey &
    + Recognition + + } + subtitle="Celebrating our milestones, awards, and the partnerships that drive our success." + // Optional button: + // buttonText="Explore Our Services" + // buttonHref="/services" + imageUrl="/images/hero/achievements-hero.jpg" // Replace with a suitable hero image (e.g., abstract success, team celebration) + /> */} + + {/* 2. Introduction (Optional) */} +
    +
    +

    + At Owethu Managed Services, we are proud of the progress we've + made and the recognition we've earned. These achievements + reflect our commitment to excellence, empowerment, and delivering + value to our clients and community. +

    +
    +
    + + {/* 3. Awards Section */} + {awards && awards.length > 0 && ( +
    +
    +

    + {" "} + Awards & Accolades +

    +
    + {awards.map((award) => ( +
    + {award.imageUrl && ( +
    + {`${award.name} +
    + )} + {!award.imageUrl && ( + + )} +

    + {award.name} +

    +

    + {award.awardingBody} - {award.year} +

    + {award.description && ( +

    + {award.description} +

    + )} +
    + ))} +
    +
    +
    + )} + + {/* 4. Certifications & Partnerships Section */} + {((certifications && certifications.length > 0) || + (partners && partners.length > 0)) && ( +
    +
    +

    + {" "} + Certifications &{" "} + {" "} + Partnerships +

    +
    + {/* Certifications Column */} + {certifications && certifications.length > 0 && ( +
    +

    + Our Certifications +

    +
    + {certifications.map((cert) => ( +
    + {cert.logoUrl && ( +
    + {`${cert.name} +
    + )} + {!cert.logoUrl && ( + + )} +
    +

    + {cert.name} +

    +

    + {cert.issuingBody} +

    + {cert.details && ( +

    + {cert.details} +

    + )} +
    +
    + ))} +
    +
    + )} + {/* Partnerships Column */} + {partners && partners.length > 0 && ( +
    +

    + Key Partners +

    +
    + {partners.map((partner) => ( +
    + + {`${partner.name} + + {partner.websiteUrl && ( + + Visit + + )} + {!partner.websiteUrl && ( + + {partner.name} + + )} +
    + ))} +
    + {partners.length > 6 && ( +

    + And more... +

    + )} +
    + )} +
    +
    +
    + )} + + {/* 5. Milestones Section */} + {milestones && milestones.length > 0 && ( +
    +
    +

    + {" "} + Key Milestones +

    +
    + {milestones.map((milestone) => ( +
    +
    +

    + {milestone.date} +

    +

    + {milestone.description} +

    +
    + ))} +
    +
    +
    + )} + + {/* 6. Gallery Section */} + {galleryImages && galleryImages.length > 0 && ( +
    +
    +

    + {" "} + Moments & Memories +

    +
    + {galleryImages.map((image) => ( +
    + {/* Consider adding a Lightbox library here for better image viewing */} + {image.alt} + {/* Overlay for caption */} +
    +

    + {image.caption || image.alt} +

    +
    +
    + ))} +
    +
    +
    + )} + + {/* 7. Optional CTA Section */} + + + ); +} + +// Helper function to determine background color for milestone border (if needed) +// This is a simplified check, adjust based on how you handle theme toggling +const bgColor = + typeof window !== "undefined" && + document.documentElement.classList.contains("dark") + ? "dark" + : "light"; diff --git a/app/api/vacancies/[slug]/route.ts b/app/api/vacancies/[slug]/route.ts new file mode 100644 index 0000000..3c58651 --- /dev/null +++ b/app/api/vacancies/[slug]/route.ts @@ -0,0 +1,19 @@ +import { NextResponse } from "next/server"; +import { demoVacancies } from "@/lib/demo-data/vacancies"; + +export async function GET( + request: Request, + { params }: { params: Promise<{ slug: string }> } +) { + const { slug } = await params; + // In a real application, you would fetch this data from your CMS (Directus) + const vacancy = demoVacancies.find( + (v) => v.slug === slug && v.status === "Open" + ); + + if (!vacancy) { + return NextResponse.json({ message: "Vacancy not found" }, { status: 404 }); + } + + return NextResponse.json(vacancy); +} diff --git a/app/api/vacancies/route.ts b/app/api/vacancies/route.ts new file mode 100644 index 0000000..bb8e917 --- /dev/null +++ b/app/api/vacancies/route.ts @@ -0,0 +1,9 @@ +import { NextResponse } from "next/server"; +import { demoVacancies } from "@/lib/demo-data/vacancies"; + +export async function GET() { + // In a real application, you would fetch this data from your CMS (Directus) + // For now, we use the demo data + const openVacancies = demoVacancies.filter((v) => v.status === "Open"); + return NextResponse.json(openVacancies); +} diff --git a/app/globals.css b/app/globals.css index df0868a..a86238e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -130,6 +130,21 @@ .animation-delay-600 { animation-delay: 0.6s; } + @keyframes marquee-continuous { + 0% { + transform: translateX(0%); + } + 100% { + transform: translateX(-50%); + } + } + .animate-marquee-continuous { + animation: marquee-continuous linear infinite; + will-change: transform; + } + .group:hover .animate-marquee-continuous { + animation-play-state: paused; + } } /* Optional scale animation for background */ @keyframes floatSlightly { diff --git a/components/BlogPostCard.tsx b/components/BlogPostCard.tsx index 12ce923..9e0f36f 100644 --- a/components/BlogPostCard.tsx +++ b/components/BlogPostCard.tsx @@ -20,22 +20,25 @@ const BlogPostCard: React.FC = ({ author, date, }) => { - console.log("BlogPostCard Props:", { imageUrl }); - return (
    {/* Image Container */} -
    - {title} - {/* Optional: Subtle overlay on hover */} -
    +
    + {imageUrl ? ( + {title} + ) : ( + // Optional: Placeholder if no image +
    + No Image +
    + )}
    {/* Content Area */} diff --git a/components/ContactForm.tsx b/components/ContactForm.tsx index 2705599..a9dd6b2 100644 --- a/components/ContactForm.tsx +++ b/components/ContactForm.tsx @@ -1,40 +1,54 @@ "use client"; -import React, { useActionState, useEffect, useRef } from "react"; -import { useFormStatus } from "react-dom"; +import React, { useEffect, useActionState } from "react"; // Removed useRef +import { useForm } from "react-hook-form"; // Removed SubmitHandler +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; import { submitContactForm, ContactFormState } from "@/actions/contact"; -import Button from "@/components/ui/Button"; // Use your existing Button component +import Button from "@/components/ui/Button"; -// Submit button component with pending state -function SubmitButton() { - const { pending } = useFormStatus(); - return ( - - ); -} +// Zod schema for contact form validation +const contactSchema = z.object({ + name: z.string().min(2, "Full name must be at least 2 characters"), + email: z.string().email("Invalid email address"), + subject: z.string().min(3, "Subject must be at least 3 characters"), + message: z.string().min(10, "Message must be at least 10 characters"), +}); + +type ContactFormData = z.infer; -// The main contact form component export default function ContactForm() { + // React Hook Form setup + const { + register, + formState: { errors, isValid }, + reset, + } = useForm({ + resolver: zodResolver(contactSchema), + mode: "onChange", + }); + const initialState: ContactFormState = { message: null, errors: {}, success: false, }; - const [state, dispatch] = useActionState(submitContactForm, initialState); - const formRef = useRef(null); // Ref to reset the form + const [state, formAction] = useActionState(submitContactForm, initialState); // Renamed dispatch to formAction for clarity - // Reset form on successful submission + // Reset form when server action reports success useEffect(() => { if (state.success) { - formRef.current?.reset(); + reset(); } - }, [state.success]); + }, [state.success, reset]); + // Removed onSubmit handler + + // Pass formAction directly to the form's action prop + // Remove onSubmit={handleSubmit(onSubmit)} return ( -
    - {/* Name Input */} + + {/* Name Field */}
    - {/* Email Input */} + {/* Email Field */}
    - {/* Subject Input */} + {/* Subject Field */}
    - {/* Message Textarea */} + {/* Message Field */}
    - - {/* General Form Message (Success or Error) */} -
    - {state.message && ( + className={`block w-full px-4 py-2 border rounded-lg shadow-sm focus:ring-primary focus:border-primary sm:text-sm bg-input text-foreground placeholder-muted-foreground resize-vertical dark:bg-gray-800 dark:text-gray-200 dark:border-gray-600 dark:placeholder-gray-400 ${ + errors.message + ? "border-destructive" + : "border-border dark:border-gray-600" + }`} + /> + {errors.message && ( )} + {state.errors?.message && + state.errors.message.map((err: string) => ( +

    + {err} +

    + ))}
    + {/* General Form Response */} + {state.message && ( +

    + {state.message} +

    + )} + {/* Submit Button */} + {/* isSubmitting from useFormState is now implicitly handled by the form action */}
    - +
    ); diff --git a/components/Footer.tsx b/components/Footer.tsx index e55fd61..393ce5b 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -178,7 +178,7 @@ const Footer = () => { Salesforce Partner - BBB-EE Level X + BBB-EE Level 1
    diff --git a/components/HeaderClient.tsx b/components/HeaderClient.tsx index aadae6f..7402936 100644 --- a/components/HeaderClient.tsx +++ b/components/HeaderClient.tsx @@ -4,23 +4,25 @@ import React, { useState } from "react"; import Link from "next/link"; import Image from "next/image"; -// Use usePathname only if needed for active state logic not shown here -// import { usePathname } from "next/navigation"; import { FiChevronDown, FiClipboard, FiArrowRight, FiMenu, FiX, - FiLogIn, // Import the login icon + FiLogIn, + FiUsers, // Resource Augmentation + FiBriefcase, // Project Management + FiCpu, // Product Development + FiBox, // OBSE + FiFileText, // Vacancies + FiUserCheck, // Recruitment Portal } from "react-icons/fi"; -import ThemeToggle from "./ThemeToggle"; // Assuming ThemeToggle component exists -import type { Session } from "@auth/core/types"; // Import Session type for props +import ThemeToggle from "./ThemeToggle"; +import type { Session } from "@auth/core/types"; -const omsLogoUrl = "/oms-logo.svg"; // Ensure this is in your /public folder +const omsLogoUrl = "/oms-logo.svg"; -// --- Basic Dropdown Component --- -// (Keep the DropdownMenu and DropdownLink component definitions here as before) type DropdownMenuProps = { trigger: React.ReactNode; children: React.ReactNode; @@ -31,7 +33,6 @@ const DropdownMenu = ({ children, menuClasses = "w-48", }: DropdownMenuProps) => ( - // Using group-focus-within for better keyboard/touch accessibility
    ) : ( - // Logged Out: Icon Button to Sign In )} - {/* --- End Auth Section --- */}
    - {/* Mobile Buttons */}
    - {/* Theme toggle remains */} +
    - {/* Secondary Row (Desktop Only) */} -
    + {/* Secondary Row w/ Mega Menus */} +
    -
    - {/* Mobile Menu Panel */} + {/* Mobile Menu */}
    - {/* Use semantic variable for text color */}
    diff --git a/components/VacancyApplicationForm.tsx b/components/VacancyApplicationForm.tsx new file mode 100644 index 0000000..97735cc --- /dev/null +++ b/components/VacancyApplicationForm.tsx @@ -0,0 +1,374 @@ +"use client"; + +import React, { useState, useCallback, ChangeEvent, DragEvent } from "react"; +import { FaUpload, FaFileAlt, FaTimes } from "react-icons/fa"; +import { useForm, SubmitHandler } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import Button from "./ui/Button"; + +const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB +const ACCEPTED_FILE_TYPES = [ + "application/pdf", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "text/plain", +]; + +const applicationSchema = z.object({ + name: z.string().min(2, "Full name must be at least 2 characters"), + email: z.string().email("Invalid email address"), + phone: z.string().optional(), + linkedin: z.string().url("Invalid URL").optional().or(z.literal("")), + coverLetter: z.string().optional(), + resume: z + .custom((val) => val instanceof File, "Resume/CV is required") + .refine( + (file) => file && file.size <= MAX_FILE_SIZE, + `Max file size is 5MB.` + ) + .refine( + (file) => file && ACCEPTED_FILE_TYPES.includes(file.type), + "Unsupported file format. Please upload PDF, DOC, DOCX, or TXT." + ), +}); + +type ApplicationFormData = z.infer; + +interface VacancyApplicationFormProps { + vacancyId: string; + vacancyTitle: string; + onClose: () => void; +} + +export default function VacancyApplicationForm({ + vacancyId, + vacancyTitle, + onClose, +}: VacancyApplicationFormProps) { + const { + register, + handleSubmit, + setValue, + watch, + formState: { errors, isSubmitting }, + reset, + } = useForm({ + resolver: zodResolver(applicationSchema), + mode: "onChange", + defaultValues: { + name: "", + email: "", + phone: "", + linkedin: "", + coverLetter: "", + resume: null, + }, + }); + + const resumeFile = watch("resume"); + const [isDragging, setIsDragging] = useState(false); + const [submitStatus, setSubmitStatus] = useState< + "idle" | "success" | "error" + >("idle"); + + const handleFileChange = (e: ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) { + setValue("resume", e.target.files[0], { shouldValidate: true }); + e.target.value = ""; + } + }; + + const handleDragEnter = useCallback((e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(true); + }, []); + + const handleDragLeave = useCallback((e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + }, []); + + const handleDragOver = useCallback( + (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (!isDragging) setIsDragging(true); + }, + [isDragging] + ); + + const handleDrop = useCallback( + (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + setValue("resume", e.dataTransfer.files[0], { shouldValidate: true }); + e.dataTransfer.clearData(); + } + }, + [setValue] + ); + + const removeFile = () => { + setValue("resume", null, { shouldValidate: true }); + }; + + const onSubmit: SubmitHandler = async (data) => { + setSubmitStatus("idle"); + const formData = new FormData(); + formData.append("vacancyId", vacancyId); + formData.append("vacancyTitle", vacancyTitle); + formData.append("name", data.name); + formData.append("email", data.email); + if (data.phone) formData.append("phone", data.phone); + if (data.linkedin) formData.append("linkedin", data.linkedin); + if (data.coverLetter) formData.append("coverLetter", data.coverLetter); + if (data.resume) formData.append("resume", data.resume, data.resume.name); + + try { + const response = await fetch("/api/apply", { + method: "POST", + body: formData, + }); + if (!response.ok) + throw new Error(`Submission failed: ${response.statusText}`); + setSubmitStatus("success"); + reset(); + setTimeout(() => { + onClose(); + setSubmitStatus("idle"); + }, 1500); + } catch (error) { + console.error(error); + setSubmitStatus("error"); + } + }; + + const inputBaseStyle = + "block w-full px-4 py-2 mt-1 text-gray-700 bg-white border border-gray-300 rounded-md focus:border-gold-500 focus:ring focus:ring-gold-500 focus:ring-opacity-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:focus:border-gold-500 font-poppins"; + const errorStyle = "border-red-500 focus:border-red-500 focus:ring-red-500"; + const errorMessageStyle = "text-red-600 text-xs mt-1"; + + return ( +
    + {/* Row 1: Name & Email */} +
    + {/* Name Field */} +
    + + + {errors.name && ( +

    + {errors.name.message} +

    + )} +
    + + {/* Email Field */} +
    + + + {errors.email && ( +

    + {errors.email.message} +

    + )} +
    +
    + + {/* Row 2: Phone & LinkedIn */} +
    + {/* Phone Field */} +
    + + + {errors.phone && ( +

    + {errors.phone.message} +

    + )} +
    + + {/* LinkedIn Field */} +
    + + + {errors.linkedin && ( +

    + {errors.linkedin.message} +

    + )} +
    +
    + + {/* Resume/CV Upload */} +
    + + +
    + document.getElementById("resume-upload-hidden")?.click() + } + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") + document.getElementById("resume-upload-hidden")?.click(); + }} + aria-label="Upload Resume/CV" + > + +

    + {isDragging ? "Drop file here" : "Drag & drop your file here"} +

    +

    + or click to browse +

    +

    + (PDF, DOC, DOCX, TXT - Max 5MB) +

    +
    + {errors.resume && ( +

    + {(errors.resume as unknown as { message: string }).message} +

    + )} + {resumeFile && ( +
    +
    + + + {resumeFile.name} + + + ({(resumeFile.size / 1024).toFixed(1)} KB) + +
    + +
    + )} +
    + + {/* Cover Letter Field */} +
    + + + {errors.coverLetter && ( +

    + {errors.coverLetter.message} +

    + )} +
    + + {/* Submit Button & Status */} +
    + + + {submitStatus === "success" && ( +

    + Application submitted successfully! Closing form... +

    + )} + {submitStatus === "error" && ( +

    + Submission failed. Please check your details and try again. +

    + )} +
    +
    + ); +} diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index d7ad96c..85b744b 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -20,7 +20,7 @@ const Button: React.FC = ({ }) => { // Base styles including focus ring using semantic vars const baseStyle = - "inline-flex items-center justify-center rounded-lg font-semibold transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"; + "inline-flex items-center justify-center rounded-lg font-semibold transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background cursor-pointer disabled:pointer-events-none disabled:opacity-50 text-sm"; // Variant styles using semantic vars (Tailwind classes generated via @theme) const variantStyles = { diff --git a/components/ui/CustomButton.tsx b/components/ui/CustomButton.tsx new file mode 100644 index 0000000..d65f3ed --- /dev/null +++ b/components/ui/CustomButton.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +const CustomButton = React.forwardRef< + HTMLButtonElement, + React.ButtonHTMLAttributes +>(({ className, children, ...props }, ref) => { + return ( + + ); +}); +CustomButton.displayName = "CustomButton"; + +export { CustomButton }; diff --git a/components/ui/CustomInput.tsx b/components/ui/CustomInput.tsx new file mode 100644 index 0000000..0f26dc0 --- /dev/null +++ b/components/ui/CustomInput.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +type CustomInputProps = React.InputHTMLAttributes; + +const CustomInput = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + } +); +CustomInput.displayName = "CustomInput"; + +export { CustomInput }; diff --git a/components/ui/CustomLabel.tsx b/components/ui/CustomLabel.tsx new file mode 100644 index 0000000..d7984c6 --- /dev/null +++ b/components/ui/CustomLabel.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +type CustomLabelProps = React.LabelHTMLAttributes; + +const CustomLabel = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +