update
This commit is contained in:
@@ -1,183 +0,0 @@
|
||||
"use client";
|
||||
import { useState, useRef } from "react";
|
||||
import Image from "next/image";
|
||||
import { useServiceManagement } from "@/lib/hooks/useServices";
|
||||
|
||||
interface ServiceImageUploadProps {
|
||||
serviceSlug: string;
|
||||
currentImageUrl?: string;
|
||||
onImageUploaded?: (service: any) => void;
|
||||
}
|
||||
|
||||
const ServiceImageUpload: React.FC<ServiceImageUploadProps> = ({
|
||||
serviceSlug,
|
||||
currentImageUrl,
|
||||
onImageUploaded,
|
||||
}) => {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { uploadServiceImage, loading, error } = useServiceManagement();
|
||||
|
||||
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('Please select an image file');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (5MB limit)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
alert('File size must be less than 5MB');
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedFile(file);
|
||||
|
||||
// Create preview URL
|
||||
const url = URL.createObjectURL(file);
|
||||
setPreviewUrl(url);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpload = async () => {
|
||||
if (!selectedFile) return;
|
||||
|
||||
try {
|
||||
setUploading(true);
|
||||
const updatedService = await uploadServiceImage(serviceSlug, selectedFile);
|
||||
|
||||
// Clean up preview URL
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
|
||||
setSelectedFile(null);
|
||||
setPreviewUrl(null);
|
||||
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
|
||||
if (onImageUploaded) {
|
||||
onImageUploaded(updatedService);
|
||||
}
|
||||
|
||||
alert('Image uploaded successfully!');
|
||||
} catch (err) {
|
||||
console.error('Upload failed:', err);
|
||||
alert('Failed to upload image. Please try again.');
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setSelectedFile(null);
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
setPreviewUrl(null);
|
||||
}
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="service-image-upload">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="image-upload" className="form-label">
|
||||
Service Image
|
||||
</label>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
id="image-upload"
|
||||
className="form-control"
|
||||
accept="image/*"
|
||||
onChange={handleFileSelect}
|
||||
disabled={uploading || loading}
|
||||
/>
|
||||
<div className="form-text">
|
||||
Select an image file (JPG, PNG, GIF). Maximum size: 5MB.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Current Image */}
|
||||
{currentImageUrl && !previewUrl && (
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Current Image:</label>
|
||||
<div className="current-image">
|
||||
<Image
|
||||
src={currentImageUrl}
|
||||
alt="Current service image"
|
||||
className="img-thumbnail"
|
||||
width={200}
|
||||
height={200}
|
||||
style={{ maxWidth: '200px', maxHeight: '200px', objectFit: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Preview */}
|
||||
{previewUrl && (
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Preview:</label>
|
||||
<div className="image-preview">
|
||||
<Image
|
||||
src={previewUrl}
|
||||
alt="Preview"
|
||||
className="img-thumbnail"
|
||||
width={200}
|
||||
height={200}
|
||||
style={{ maxWidth: '200px', maxHeight: '200px', objectFit: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
{selectedFile && (
|
||||
<div className="d-flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleUpload}
|
||||
disabled={uploading || loading}
|
||||
>
|
||||
{uploading || loading ? (
|
||||
<>
|
||||
<span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
Uploading...
|
||||
</>
|
||||
) : (
|
||||
'Upload Image'
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={handleCancel}
|
||||
disabled={uploading || loading}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceImageUpload;
|
||||
@@ -1,137 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function ContactForm() {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
message: ''
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// Simulate form submission
|
||||
setTimeout(() => {
|
||||
setSuccess(true);
|
||||
setFormData({ name: '', email: '', subject: '', message: '' });
|
||||
setLoading(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<div className="alert alert-success" role="alert">
|
||||
<h4 className="alert-heading">Message Sent!</h4>
|
||||
<p>Thank you for your message. We'll get back to you soon.</p>
|
||||
<button
|
||||
className="btn btn-outline-success"
|
||||
onClick={() => setSuccess(false)}
|
||||
>
|
||||
Send Another Message
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container py-5">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-md-8">
|
||||
<h2 className="text-center mb-4">Contact Us</h2>
|
||||
|
||||
{error && (
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-md-6 mb-3">
|
||||
<label htmlFor="name" className="form-label">Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6 mb-3">
|
||||
<label htmlFor="email" className="form-label">Email *</label>
|
||||
<input
|
||||
type="email"
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="subject" className="form-label">Subject</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="message" className="form-label">Message *</label>
|
||||
<textarea
|
||||
className="form-control"
|
||||
id="message"
|
||||
name="message"
|
||||
rows={5}
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary btn-lg"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||
Sending...
|
||||
</>
|
||||
) : (
|
||||
'Send Message'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export default function ServicesList() {
|
||||
// Static services data
|
||||
const services = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Web Development",
|
||||
description: "Custom web applications built with modern technologies",
|
||||
slug: "web-development",
|
||||
icon: "code",
|
||||
price: 2500,
|
||||
featured: true,
|
||||
display_order: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Mobile Apps",
|
||||
description: "Native and cross-platform mobile applications",
|
||||
slug: "mobile-apps",
|
||||
icon: "phone",
|
||||
price: 3500,
|
||||
featured: false,
|
||||
display_order: 2,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
const handleDeleteService = (id: number) => {
|
||||
if (!confirm('Are you sure you want to delete this service?')) {
|
||||
return;
|
||||
}
|
||||
// Note: This is a demo - in a real app, you'd handle deletion differently
|
||||
console.log('Service deletion requested for ID:', id);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="container py-5">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<h2 className="mb-4">Our Services</h2>
|
||||
<div className="row">
|
||||
{services.map((service) => (
|
||||
<div key={service.id} className="col-md-6 col-lg-4 mb-4">
|
||||
<div className="card h-100">
|
||||
<div className="card-body">
|
||||
<div className="d-flex justify-content-between align-items-start mb-3">
|
||||
<h5 className="card-title">{service.title}</h5>
|
||||
{service.featured && (
|
||||
<span className="badge bg-primary">Featured</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="card-text">{service.description}</p>
|
||||
<div className="mt-auto">
|
||||
<a
|
||||
href={`/services/${service.slug}`}
|
||||
className="btn btn-primary me-2"
|
||||
>
|
||||
Learn More
|
||||
</a>
|
||||
<button
|
||||
className="btn btn-outline-danger btn-sm"
|
||||
onClick={() => handleDeleteService(service.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Autoplay, Navigation } from "swiper/modules";
|
||||
import "swiper/swiper-bundle.css";
|
||||
import Counter from "../../common/Counter";
|
||||
import one from "@/public/images/study/one.png";
|
||||
import two from "@/public/images/study/two.png";
|
||||
import three from "@/public/images/study/three.png";
|
||||
import four from "@/public/images/study/four.png";
|
||||
import five from "@/public/images/study/five.png";
|
||||
|
||||
const AboutCase = () => {
|
||||
return (
|
||||
<section className="tp-study pt-120 pb-120 bg-quinary">
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap align-items-center">
|
||||
<div className="col-12 col-lg-7">
|
||||
<div className="tp-lp-title text-center text-lg-start">
|
||||
<div className="enterprise-badge mb-20">
|
||||
<span className="badge enterprise-badge-content">
|
||||
<i className="fa-solid fa-chart-line"></i>
|
||||
Success Stories
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">
|
||||
Enterprise Success Stories
|
||||
</h2>
|
||||
<p className="cur-lg mt-20">
|
||||
Discover how we've helped Fortune 500 companies and innovative startups
|
||||
achieve their digital transformation goals through cutting-edge technology solutions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-study-arrows d-flex justify-content-center justify-content-lg-end">
|
||||
<button className="prev-study" aria-label="previous study">
|
||||
<span className="material-symbols-outlined">west</span>
|
||||
</button>
|
||||
<button className="next-study" aria-label="next study">
|
||||
<span className="material-symbols-outlined">east</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="tp-study-slider-wrapper">
|
||||
<div className="tp-study-slider-wrap">
|
||||
<Swiper
|
||||
slidesPerView={"auto"}
|
||||
spaceBetween={24}
|
||||
slidesPerGroup={1}
|
||||
freeMode={true}
|
||||
speed={1200}
|
||||
loop={true}
|
||||
roundLengths={true}
|
||||
modules={[Autoplay, Navigation]}
|
||||
autoplay={{
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
}}
|
||||
className="tp-study-slider"
|
||||
navigation={{
|
||||
nextEl: ".prev-study",
|
||||
prevEl: ".next-study",
|
||||
}}
|
||||
>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={one}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={two}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={three}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={four}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={five}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={one}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={two}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={three}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={four}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-study-slider__single">
|
||||
<div className="thumb">
|
||||
<Link href="case-study-single">
|
||||
<Image
|
||||
src={five}
|
||||
className="w-100 mh-400"
|
||||
alt="Image"
|
||||
width={400}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content text-center">
|
||||
<h5 className="mt-8 mb-12 text-white fw-5 text-uppercase">
|
||||
<Link href="case-study-single">ENTERPRISE CRM</Link>
|
||||
</h5>
|
||||
<Link href="case-study-single" className="btn-angle">
|
||||
View
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row pt-120 vertical-column-gap-lg">
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="counter__single text-center">
|
||||
<h2 className="fw-5 text-secondary mt-8 mb-16">
|
||||
<span className="odometer">
|
||||
<Counter value={500} />
|
||||
</span>
|
||||
<span>+</span>
|
||||
</h2>
|
||||
<p className="text-tertiary">Enterprise Projects</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="counter__single text-center">
|
||||
<h2 className="fw-5 text-secondary mt-8 mb-16">
|
||||
<span className="odometer">
|
||||
<Counter value={99} />
|
||||
</span>
|
||||
<span>.9%</span>
|
||||
</h2>
|
||||
<p className="text-tertiary">Uptime SLA</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="counter__single text-center">
|
||||
<h2 className="fw-5 text-secondary mt-8 mb-16">
|
||||
<span className="odometer">
|
||||
<Counter value={15} />
|
||||
</span>
|
||||
<span>+</span>
|
||||
</h2>
|
||||
<p className="text-tertiary">Years Experience</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="counter__single text-center border-0">
|
||||
<h2 className="fw-5 text-secondary mt-8 mb-16">
|
||||
<span className="odometer">
|
||||
<Counter value={200} />
|
||||
</span>
|
||||
<span>+</span>
|
||||
</h2>
|
||||
<p className="text-tertiary">Expert Engineers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutCase;
|
||||
@@ -3,6 +3,7 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useAbout } from "@/lib/hooks/useAbout";
|
||||
import { AboutService as AboutServiceType, AboutProcess } from "@/lib/api/aboutService";
|
||||
import { getValidImageUrl, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import thumb from "@/public/images/service/two.png";
|
||||
import thumbTwo from "@/public/images/service/three.png";
|
||||
|
||||
@@ -62,13 +63,23 @@ const AboutServiceComponent = () => {
|
||||
<Link href="service-single" className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={serviceData?.image_url || thumb}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Technology Solutions"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
{serviceData?.image_url ? (
|
||||
<img
|
||||
src={getValidImageUrl(serviceData.image_url, FALLBACK_IMAGES.SERVICE)}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Technology Solutions"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={thumb}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Technology Solutions"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -241,13 +252,23 @@ const AboutServiceComponent = () => {
|
||||
<Link href="service-single" className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={processData?.image_url || thumbTwo}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Development Process"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
{processData?.image_url ? (
|
||||
<img
|
||||
src={getValidImageUrl(processData.image_url, FALLBACK_IMAGES.SERVICE)}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Development Process"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={thumbTwo}
|
||||
className="w-100 parallax-image"
|
||||
alt="Enterprise Development Process"
|
||||
width={400}
|
||||
height={300}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -3,6 +3,7 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useAbout } from "@/lib/hooks/useAbout";
|
||||
import { AboutJourney } from "@/lib/api/aboutService";
|
||||
import { getValidImageUrl, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import thumb from "@/public/images/start-thumb.png";
|
||||
|
||||
const AboutStarter = () => {
|
||||
@@ -52,7 +53,7 @@ const AboutStarter = () => {
|
||||
const journeyData = data?.journey as AboutJourney | undefined;
|
||||
|
||||
return (
|
||||
<section className="tp-gallery">
|
||||
<section className="tp-gallery about-compact">
|
||||
<div className="container">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-12 col-lg-6">
|
||||
@@ -65,7 +66,7 @@ const AboutStarter = () => {
|
||||
{journeyData?.title || "From Startup to Enterprise Leader"}
|
||||
</h2>
|
||||
<p>
|
||||
{journeyData?.description || "Founded in 2008 by three visionary engineers, Itify Technologies began as a small startup with a big dream: to revolutionize how enterprises approach software development. What started as a passion project has grown into a global enterprise software company serving Fortune 500 clients worldwide."}
|
||||
{journeyData?.description || "Founded in 2020 by three visionary engineers, GNX Technologies began as a small startup with a big dream: to revolutionize how enterprises approach software development. What started as a passion project has grown into a global enterprise software company serving Fortune 500 clients worldwide."}
|
||||
</p>
|
||||
<div className="journey-milestones">
|
||||
{journeyData?.milestones && journeyData.milestones.length > 0 ? (
|
||||
@@ -112,7 +113,7 @@ const AboutStarter = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<div className="cta-wrap">
|
||||
<Link
|
||||
href={journeyData?.cta_link || "services"}
|
||||
className="btn-line"
|
||||
@@ -124,26 +125,123 @@ const AboutStarter = () => {
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="tp-gallery__thumb">
|
||||
<Image
|
||||
src={journeyData?.image_url || thumb}
|
||||
className="w-100"
|
||||
alt="Enterprise Journey"
|
||||
width={500}
|
||||
height={400}
|
||||
style={{
|
||||
objectFit: 'contain',
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.1)',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '500px',
|
||||
width: '100%',
|
||||
height: 'auto'
|
||||
}}
|
||||
/>
|
||||
{journeyData?.image_url ? (
|
||||
<img
|
||||
src={getValidImageUrl(journeyData.image_url, FALLBACK_IMAGES.DEFAULT)}
|
||||
className="w-100 compact-img"
|
||||
alt="Enterprise Journey"
|
||||
width={500}
|
||||
height={400}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={thumb}
|
||||
className="w-100 compact-img"
|
||||
alt="Enterprise Journey"
|
||||
width={500}
|
||||
height={400}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.about-compact {
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.about-compact .enterprise-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.about-compact .enterprise-badge i { font-size: 12px; }
|
||||
|
||||
.about-compact .title-anim {
|
||||
font-size: clamp(20px, 2.2vw, 28px);
|
||||
line-height: 1.25;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
.about-compact p {
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
margin: 0 0 12px 0;
|
||||
color: rgba(255,255,255,0.85);
|
||||
}
|
||||
|
||||
/* Milestones */
|
||||
.about-compact .journey-milestones {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.about-compact .journey-milestones { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
.about-compact .milestone-item {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: start;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
background: rgba(255,255,255,0.03);
|
||||
border-radius: 10px;
|
||||
}
|
||||
.about-compact .year-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
border-radius: 6px;
|
||||
background: rgba(255,255,255,0.08);
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.about-compact .milestone-content h6 {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.about-compact .milestone-content p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: rgba(255,255,255,0.75);
|
||||
}
|
||||
|
||||
/* CTA */
|
||||
.about-compact .cta-wrap { margin-top: 12px; }
|
||||
.about-compact :global(.btn-line) {
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Image */
|
||||
.about-compact .tp-gallery__thumb { margin-top: 8px; }
|
||||
@media (min-width: 992px) {
|
||||
.about-compact .tp-gallery__thumb { margin-top: 0; }
|
||||
}
|
||||
.about-compact :global(.compact-img),
|
||||
.about-compact .compact-img {
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
|
||||
max-height: 260px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,6 @@ const PostFilterButtons = ({ handleClick, active }: any) => {
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('Error loading categories:', error);
|
||||
// Fallback to showing "All" button only
|
||||
return (
|
||||
<div className="row">
|
||||
|
||||
@@ -61,7 +61,6 @@ const PostFilterItems = ({ currentPage, onPageChange, onTotalPagesChange, postsP
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('Error loading posts:', error);
|
||||
return (
|
||||
<>
|
||||
<PostFilterButtons active={active} handleClick={handleCategoryClick} />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -91,11 +91,11 @@ const JobSingle = ({ job }: JobSingleProps) => {
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="job-header-content" style={{ color: 'white' }}>
|
||||
<div className="job-header-content" style={{ color: '#ffffff' }}>
|
||||
<div className="mb-12 mb-md-16">
|
||||
<span className="badge" style={{
|
||||
backgroundColor: 'rgba(255,255,255,0.2)',
|
||||
color: 'white',
|
||||
color: '#ffffff',
|
||||
padding: '6px 12px',
|
||||
borderRadius: '20px',
|
||||
fontSize: '12px',
|
||||
@@ -110,7 +110,7 @@ const JobSingle = ({ job }: JobSingleProps) => {
|
||||
<h1 className="fw-7 mb-16 mb-md-20 mb-lg-24" style={{
|
||||
fontSize: 'clamp(1.75rem, 5vw, 3.5rem)',
|
||||
lineHeight: '1.2',
|
||||
color: 'white'
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
{job.title}
|
||||
</h1>
|
||||
|
||||
@@ -44,13 +44,13 @@ const CaseItems = () => {
|
||||
<h2 className="mt-8 title-anim fw-7 text-secondary mb-24">
|
||||
Case Studies
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Morbi in non nibh
|
||||
netus tortor. Non vitae ut consectetur sit quam egestas
|
||||
praesent. Enim augue cras donec sed pellentesque tortor
|
||||
maecenas. Tellus duis sit justo neque. Est elit diam quam
|
||||
venenatis sit morbi sed dignissim eros.
|
||||
</p>
|
||||
<p className="cur-lg">
|
||||
Discover how we help enterprises solve complex challenges with
|
||||
secure, scalable solutions. Our case studies highlight real
|
||||
business outcomes accelerated delivery, reduced costs,
|
||||
improved reliability, and data-driven growth powered by modern
|
||||
cloud, AI, and platform engineering.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -93,7 +93,6 @@ const ContactSection = () => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Contact form submission error:', error);
|
||||
setSubmitStatus({
|
||||
type: 'error',
|
||||
message: error instanceof Error ? error.message : 'Failed to submit form. Please try again.'
|
||||
@@ -413,7 +412,7 @@ const ContactSection = () => {
|
||||
required
|
||||
/>
|
||||
<label htmlFor="privacy">
|
||||
I agree to the <a href="/privacy-policy">Privacy Policy</a> and consent to being contacted by our team *
|
||||
I agree to the <a href="/policy?type=privacy">Privacy Policy</a> and consent to being contacted by our team *
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,18 +6,35 @@ const HomeBanner = () => {
|
||||
const [currentTextIndex, setCurrentTextIndex] = useState(0);
|
||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||
|
||||
// Static banner slides data
|
||||
// Fix viewport height for mobile browsers (especially iOS Safari)
|
||||
useEffect(() => {
|
||||
const setVH = () => {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
};
|
||||
|
||||
setVH();
|
||||
window.addEventListener('resize', setVH);
|
||||
window.addEventListener('orientationchange', setVH);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', setVH);
|
||||
window.removeEventListener('orientationchange', setVH);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Static banner slides data based on actual services
|
||||
const carouselTexts = [
|
||||
{
|
||||
id: 1,
|
||||
badge: "Enterprise Solutions",
|
||||
icon: "fa-solid fa-shield-halved",
|
||||
heading: "Secure Enterprise Software",
|
||||
badge: "Custom Development",
|
||||
icon: "fa-solid fa-code",
|
||||
heading: "Tailored Enterprise Software ",
|
||||
highlight: "Development",
|
||||
subheading: "for Modern Businesses",
|
||||
description: "We build enterprise-grade software solutions with advanced security, scalability, and 24/7 support. Transform your business with our custom development services.",
|
||||
subheading: "Aligned with Your Business Goals",
|
||||
description: "We design and build custom digital solutions that deliver reliable, scalable, and future-ready applications, driving measurable value and competitive advantage for your enterprise.",
|
||||
button_text: "Explore Solutions",
|
||||
button_url: "/services",
|
||||
button_url: "/services/custom-software-development",
|
||||
is_active: true,
|
||||
display_order: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
@@ -25,14 +42,14 @@ const HomeBanner = () => {
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
badge: "API Integration",
|
||||
icon: "fa-solid fa-plug",
|
||||
heading: "Seamless API",
|
||||
highlight: "Integration",
|
||||
subheading: "& System Connectivity",
|
||||
description: "Connect all your systems with our robust API development and integration services. Enable smooth data flow and unified workflows across your organization.",
|
||||
button_text: "Learn More",
|
||||
button_url: "/services/api-development",
|
||||
badge: "Business Intelligence",
|
||||
icon: "fa-solid fa-brain",
|
||||
heading: "AI-Powered ",
|
||||
highlight: "Analytics",
|
||||
subheading: "Transform Data into Insights",
|
||||
description: "Turn enterprise data into actionable intelligence with advanced AI and machine learning, enabling smarter decisions, performance optimization, and data-driven innovation.",
|
||||
button_text: "Discover AI Solutions",
|
||||
button_url: "/services/ai-powered-business-intelligence",
|
||||
is_active: true,
|
||||
display_order: 2,
|
||||
created_at: new Date().toISOString(),
|
||||
@@ -40,18 +57,33 @@ const HomeBanner = () => {
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
badge: "Cloud Migration",
|
||||
icon: "fa-solid fa-cloud",
|
||||
heading: "Cloud-First",
|
||||
highlight: "Solutions",
|
||||
subheading: "for Enterprise Scale",
|
||||
description: "Migrate to the cloud with confidence. Our cloud solutions provide improved scalability, security, and cost-effectiveness for your enterprise operations.",
|
||||
button_text: "Start Migration",
|
||||
button_url: "/services/cloud-solutions",
|
||||
badge: "System Integration",
|
||||
icon: "fa-solid fa-plug",
|
||||
heading: "Enterprise Systems ",
|
||||
highlight: "Integration",
|
||||
subheading: "Seamless Connectivity",
|
||||
description: "Connect everything — from payment systems and ERP platforms to cloud services, enabling your enterprise to operate seamlessly across physical and digital environments.",
|
||||
button_text: "View Integrations",
|
||||
button_url: "/services/external-systems-integrations",
|
||||
is_active: true,
|
||||
display_order: 3,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
badge: "Incident Management",
|
||||
icon: "fa-solid fa-bell",
|
||||
heading: "Intelligent Incident ",
|
||||
highlight: "Management",
|
||||
subheading: "Minimize Downtime & Protect Trust",
|
||||
description: "Cloud-based incident management that empowers teams to detect, respond, and resolve issues faster, reducing downtime and maintaining customer confidence.",
|
||||
button_text: "Learn More",
|
||||
button_url: "/services/incident-management-saas",
|
||||
is_active: true,
|
||||
display_order: 4,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,53 +1,77 @@
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import thumb from "@/public/images/service/one.png";
|
||||
import thumb from "@/public/images/leading.jpg";
|
||||
|
||||
const ServiceIntro = () => {
|
||||
return (
|
||||
<section className="tp-service pt-120 pb-120">
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap-md">
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-service__thumb fade-img">
|
||||
<Link href="service-single" className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={thumb}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="row vertical-column-gap-md">
|
||||
<div className="col-12 col-lg-4">
|
||||
<div className="tp-service__thumb" style={{ maxWidth: '400px', border: 'none', padding: 0, margin: 0, overflow: 'hidden', borderRadius: '8px' }}>
|
||||
<Link href="services">
|
||||
<Image
|
||||
src={thumb}
|
||||
alt="Enterprise Software Solutions"
|
||||
width={400}
|
||||
height={500}
|
||||
objectFit="cover"
|
||||
style={{ display: 'block', border: 'none', margin: 0, padding: 0 }}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-12 col-lg-7 col-xl-6 offset-xl-1">
|
||||
<div className="tp-service__content">
|
||||
<h2 className="mt-8 title-anim text-secondary fw-7 mb-30 text-capitalize">
|
||||
Leading Enterprise Software Solutions Provider.
|
||||
</div>
|
||||
<div className="col-12 col-lg-8">
|
||||
<div className="tp-service__content">
|
||||
<div className="tp-section-wrapper">
|
||||
|
||||
<h2 className="title-anim text-secondary fw-7 mb-30">
|
||||
Accelerating Digital Transformation Through<br />
|
||||
Mission-Critical Enterprise Software
|
||||
</h2>
|
||||
<div className="pl-100">
|
||||
<p className="cur-lg">
|
||||
Transform your enterprise with cutting-edge software solutions,
|
||||
system integrations, and digital transformation services. We help
|
||||
Fortune 500 companies modernize their technology infrastructure,
|
||||
enhance security, and achieve operational excellence through
|
||||
innovative software development.
|
||||
</p>
|
||||
<div className="mt-60">
|
||||
<Link href="service-single" className="btn-anim btn-anim-light">
|
||||
Our Solutions
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-50">
|
||||
<p className="cur-lg mb-25">
|
||||
GNX partners with Fortune 40 companies and global enterprises to architect,
|
||||
develop, and deploy business-critical software solutions that drive measurable
|
||||
results. Our engineering teams deliver secure, scalable, and compliant systems
|
||||
that power digital innovation across industries.
|
||||
</p>
|
||||
<p className="cur-lg mb-30">
|
||||
From cloud-native architectures and enterprise integration platforms to
|
||||
AI-powered analytics and legacy modernization, we provide end-to-end
|
||||
technology solutions that reduce operational costs, enhance efficiency,
|
||||
and deliver competitive advantage.
|
||||
</p>
|
||||
<div className="tp-service__features mb-40">
|
||||
<ul className="list-unstyled">
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">Enterprise-Grade Security & Compliance</span>
|
||||
</li>
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">Scalable Cloud-Native Architectures</span>
|
||||
</li>
|
||||
<li className="mb-15">
|
||||
<i className="fa-solid fa-circle-check text-primary me-10"></i>
|
||||
<span className="fw-6">24/7 Support & Dedicated Engineering Teams</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-60">
|
||||
<Link href="services" className="btn-anim btn-anim-light">
|
||||
Explore Our Solutions
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ const Story = () => {
|
||||
// Log when API data is loaded
|
||||
useEffect(() => {
|
||||
if (caseStudies.length > 0) {
|
||||
console.log('Case studies loaded from API:', caseStudies.length);
|
||||
}
|
||||
}, [caseStudies]);
|
||||
|
||||
@@ -143,11 +142,9 @@ const Story = () => {
|
||||
// Just filename or relative path
|
||||
imageUrl = `${API_CONFIG.MEDIA_URL}/${item.thumbnail}`;
|
||||
}
|
||||
console.log('Case study image URL:', item.title, '→', imageUrl);
|
||||
} else {
|
||||
// Fallback to static image
|
||||
imageUrl = getValidImageUrl('/images/case/one.png', FALLBACK_IMAGES.CASE_STUDY);
|
||||
console.log('Using fallback image for:', item.title);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,8 +4,7 @@ import gsap from "gsap";
|
||||
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import thumbOne from "@/public/images/service/thumb-one.png";
|
||||
import thumbTwo from "@/public/images/service/thumb-two.png";
|
||||
|
||||
|
||||
const ServicesBanner = () => {
|
||||
useEffect(() => {
|
||||
@@ -55,12 +54,12 @@ const ServicesBanner = () => {
|
||||
<div className="enterprise-banner__content">
|
||||
<div className="banner-badge mb-4">
|
||||
<span className="enterprise-badge">
|
||||
Professional Services
|
||||
Enterprise Services
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 className="enterprise-title mb-4">
|
||||
The end-to-end bespoke software development agency you need
|
||||
The end-to-end bespoke software development company you need
|
||||
</h1>
|
||||
|
||||
<p className="enterprise-description mb-5">
|
||||
@@ -106,12 +105,7 @@ const ServicesBanner = () => {
|
||||
Scroll
|
||||
<span className="arrow"></span>
|
||||
</Link>
|
||||
<div className="thumb-one">
|
||||
<Image src={thumbOne} alt="Image" width={600} height={400} />
|
||||
</div>
|
||||
<div className="thumb-two">
|
||||
<Image src={thumbTwo} alt="Image" width={600} height={400} />
|
||||
</div>
|
||||
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -55,7 +55,27 @@ const CreateTicketForm = () => {
|
||||
category: undefined
|
||||
});
|
||||
} catch (error: any) {
|
||||
setSubmitError(error.message || 'Failed to submit ticket. Please try again.');
|
||||
console.error('Ticket creation error:', error);
|
||||
|
||||
// Provide user-friendly error messages based on error type
|
||||
let errorMessage = 'Failed to submit ticket. Please try again.';
|
||||
|
||||
if (error.message) {
|
||||
if (error.message.includes('email')) {
|
||||
errorMessage = 'There was an issue with your email address. Please check and try again.';
|
||||
} else if (error.message.includes('network') || error.message.includes('fetch')) {
|
||||
errorMessage = 'Network error. Please check your connection and try again.';
|
||||
} else if (error.message.includes('validation')) {
|
||||
errorMessage = 'Please check all required fields and try again.';
|
||||
} else if (error.message.includes('server') || error.message.includes('500')) {
|
||||
errorMessage = 'Server error. Our team has been notified. Please try again later.';
|
||||
} else {
|
||||
// Use the actual error message if it's user-friendly
|
||||
errorMessage = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
setSubmitError(errorMessage);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ const KnowledgeBaseArticleModal = ({ slug, onClose }: KnowledgeBaseArticleModalP
|
||||
await markArticleHelpful(slug, helpful);
|
||||
setFeedbackGiven(true);
|
||||
} catch (error) {
|
||||
console.error('Error submitting feedback:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface OptimizedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
className?: string;
|
||||
priority?: boolean;
|
||||
fill?: boolean;
|
||||
sizes?: string;
|
||||
quality?: number;
|
||||
style?: React.CSSProperties;
|
||||
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
loading?: 'lazy' | 'eager';
|
||||
}
|
||||
|
||||
/**
|
||||
* OptimizedImage Component
|
||||
*
|
||||
* An enterprise-grade optimized image component that provides:
|
||||
* - Automatic lazy loading
|
||||
* - Responsive images with srcset
|
||||
* - WebP/AVIF format support
|
||||
* - Blur placeholder while loading
|
||||
* - Error handling with fallback
|
||||
* - Performance optimization
|
||||
*
|
||||
* @example
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Hero banner showcasing our services"
|
||||
* width={1200}
|
||||
* height={600}
|
||||
* priority={true}
|
||||
* />
|
||||
*/
|
||||
export default function OptimizedImage({
|
||||
src,
|
||||
alt,
|
||||
width,
|
||||
height,
|
||||
className = '',
|
||||
priority = false,
|
||||
fill = false,
|
||||
sizes,
|
||||
quality = 85,
|
||||
style,
|
||||
objectFit = 'cover',
|
||||
loading,
|
||||
}: OptimizedImageProps) {
|
||||
const [imgSrc, setImgSrc] = useState(src);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
// Fallback image for errors
|
||||
const fallbackImage = '/images/placeholder.png';
|
||||
|
||||
// Handle image load
|
||||
const handleLoad = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
// Handle image error
|
||||
const handleError = () => {
|
||||
setHasError(true);
|
||||
setIsLoading(false);
|
||||
if (imgSrc !== fallbackImage) {
|
||||
setImgSrc(fallbackImage);
|
||||
}
|
||||
};
|
||||
|
||||
// SEO-friendly alt text validation
|
||||
const seoAlt = alt || 'GNX Soft - Enterprise Software Solutions';
|
||||
|
||||
// Validate alt text for SEO
|
||||
if (process.env.NODE_ENV === 'development' && !alt) {
|
||||
console.warn(
|
||||
`OptimizedImage: Missing alt text for image "${src}". Alt text is crucial for SEO and accessibility.`
|
||||
);
|
||||
}
|
||||
|
||||
// Common image props
|
||||
const imageProps = {
|
||||
src: imgSrc,
|
||||
alt: seoAlt,
|
||||
className: `${className} ${isLoading ? 'image-loading' : 'image-loaded'}`,
|
||||
onLoad: handleLoad,
|
||||
onError: handleError,
|
||||
quality,
|
||||
loading: loading || (priority ? 'eager' : 'lazy'),
|
||||
style: {
|
||||
...style,
|
||||
objectFit: objectFit as any,
|
||||
},
|
||||
};
|
||||
|
||||
// Use fill layout for responsive images
|
||||
if (fill) {
|
||||
return (
|
||||
<div className={`optimized-image-wrapper ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
fill
|
||||
sizes={sizes || '100vw'}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.optimized-image-wrapper.has-error {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.1);
|
||||
transition: filter 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Standard layout with explicit dimensions
|
||||
return (
|
||||
<div className={`optimized-image-container ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
width={width}
|
||||
height={height}
|
||||
sizes={sizes}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.optimized-image-container.has-error {
|
||||
background: #f3f4f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.05);
|
||||
transition: filter 0.4s ease, transform 0.4s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage Examples:
|
||||
*
|
||||
* 1. Hero Image (Priority Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Enterprise software development solutions"
|
||||
* width={1920}
|
||||
* height={1080}
|
||||
* priority={true}
|
||||
* sizes="100vw"
|
||||
* />
|
||||
*
|
||||
* 2. Service Card Image (Lazy Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/service/custom-software.jpg"
|
||||
* alt="Custom software development service icon"
|
||||
* width={400}
|
||||
* height={300}
|
||||
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
* />
|
||||
*
|
||||
* 3. Background Image (Fill):
|
||||
* <OptimizedImage
|
||||
* src="/images/background.jpg"
|
||||
* alt="Technology background pattern"
|
||||
* fill={true}
|
||||
* sizes="100vw"
|
||||
* objectFit="cover"
|
||||
* />
|
||||
*
|
||||
* 4. Logo (High Priority):
|
||||
* <OptimizedImage
|
||||
* src="/images/logo.png"
|
||||
* alt="GNX Soft company logo"
|
||||
* width={200}
|
||||
* height={50}
|
||||
* priority={true}
|
||||
* quality={100}
|
||||
* />
|
||||
*/
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode, CSSProperties } from 'react';
|
||||
|
||||
interface ProtectedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
width?: number;
|
||||
height?: number;
|
||||
showWatermark?: boolean;
|
||||
priority?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected Image Component
|
||||
* Wraps images with protection against downloading and copying
|
||||
*/
|
||||
export default function ProtectedImage({
|
||||
src,
|
||||
alt,
|
||||
className = '',
|
||||
style = {},
|
||||
width,
|
||||
height,
|
||||
showWatermark = false,
|
||||
children
|
||||
}: ProtectedImageProps) {
|
||||
const wrapperClass = `protected-image-wrapper ${showWatermark ? 'watermarked-image' : ''} ${className}`;
|
||||
|
||||
return (
|
||||
<div className={wrapperClass} style={style}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
draggable="false"
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onDragStart={(e) => e.preventDefault()}
|
||||
style={{
|
||||
WebkitUserSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,55 +49,116 @@ export const CookieConsentBanner: React.FC = () => {
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{/* Fullscreen overlay to center the banner */}
|
||||
<motion.div
|
||||
initial={{ y: 100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 100, opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeOut' }}
|
||||
className="cookie-consent-banner"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
zIndex: 10000,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'rgba(17, 24, 39, 0.45)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
}}
|
||||
>
|
||||
<div className="cookie-consent-banner__container">
|
||||
<div className="cookie-consent-banner__content">
|
||||
<div className="cookie-consent-banner__icon">
|
||||
<i className="fas fa-cookie-bite"></i>
|
||||
{/* Centered enterprise-style card */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.96, y: 8 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.98, y: 8 }}
|
||||
transition={{ duration: 0.25, ease: 'easeOut' }}
|
||||
className="cookie-consent-banner"
|
||||
style={{
|
||||
width: 'min(680px, 92vw)',
|
||||
background: '#0b1220',
|
||||
color: '#e5e7eb',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
borderRadius: 16,
|
||||
boxShadow: '0 25px 70px rgba(0,0,0,0.45)',
|
||||
padding: 24,
|
||||
}}
|
||||
>
|
||||
<div className="cookie-consent-banner__container" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div className="cookie-consent-banner__content" style={{ display: 'flex', gap: 16 }}>
|
||||
<div
|
||||
className="cookie-consent-banner__icon"
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 12,
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
background: 'linear-gradient(135deg, rgba(199, 213, 236, 0.39), rgba(147,197,253,0.08))',
|
||||
color: '#93c5fd',
|
||||
flex: '0 0 auto',
|
||||
}}
|
||||
>
|
||||
<i className="fas fa-cookie-bite"></i>
|
||||
</div>
|
||||
<div className="cookie-consent-banner__text" style={{ display: 'grid', gap: 6 }}>
|
||||
<h3 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Cookie Preferences</h3>
|
||||
<p style={{ margin: 0, lineHeight: 1.6, color: '#ffffff' }}>
|
||||
We use only essential functional cookies to ensure our website works properly. We do not collect
|
||||
personal data or use tracking cookies. Your privacy is important to us.
|
||||
</p>
|
||||
{config.showPrivacyNotice && (
|
||||
<div className="cookie-consent-banner__links" style={{ marginTop: 6 }}>
|
||||
<a
|
||||
href={config.privacyPolicyUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#93c5fd', textDecoration: 'underline' }}
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="cookie-consent-banner__text">
|
||||
<h3>Cookie Preferences</h3>
|
||||
<p>
|
||||
We use only essential functional cookies to ensure our website works properly.
|
||||
We do not collect personal data or use tracking cookies. Your privacy is important to us.
|
||||
</p>
|
||||
{config.showPrivacyNotice && (
|
||||
<div className="cookie-consent-banner__links">
|
||||
<a href={config.privacyPolicyUrl} target="_blank" rel="noopener noreferrer">
|
||||
Privacy Policy
|
||||
</a>
|
||||
<a href={config.cookiePolicyUrl} target="_blank" rel="noopener noreferrer">
|
||||
Cookie Policy
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="cookie-consent-banner__actions"
|
||||
style={{ display: 'flex', justifyContent: 'flex-end', gap: 12, marginTop: 8 }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="cookie-consent-banner__btn cookie-consent-banner__btn--secondary"
|
||||
onClick={showSettings}
|
||||
style={{
|
||||
padding: '10px 14px',
|
||||
borderRadius: 10,
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: 'transparent',
|
||||
color: '#e5e7eb',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
<i className="fas fa-cog" style={{ marginRight: 8 }}></i>
|
||||
Settings
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="cookie-consent-banner__btn cookie-consent-banner__btn--primary"
|
||||
onClick={acceptNecessary}
|
||||
style={{
|
||||
padding: '10px 14px',
|
||||
borderRadius: 10,
|
||||
border: '1px solid rgba(59,130,246,0.35)',
|
||||
background: 'linear-gradient(135deg, rgba(59,130,246,0.25), rgba(37,99,235,0.35))',
|
||||
color: '#ffffff',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
<i className="fas fa-check" style={{ marginRight: 8 }}></i>
|
||||
Accept Functional Only
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="cookie-consent-banner__actions">
|
||||
<button
|
||||
type="button"
|
||||
className="cookie-consent-banner__btn cookie-consent-banner__btn--secondary"
|
||||
onClick={showSettings}
|
||||
>
|
||||
<i className="fas fa-cog"></i>
|
||||
Settings
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="cookie-consent-banner__btn cookie-consent-banner__btn--primary"
|
||||
onClick={acceptNecessary}
|
||||
>
|
||||
<i className="fas fa-check"></i>
|
||||
Accept Functional Only
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
|
||||
@@ -64,8 +64,8 @@ const defaultPreferences: CookiePreferences = {
|
||||
const defaultConfig: CookieConsentConfig = {
|
||||
version: '2.0',
|
||||
companyName: 'Your Company Name',
|
||||
privacyPolicyUrl: '/privacy-policy',
|
||||
cookiePolicyUrl: '/cookie-policy',
|
||||
privacyPolicyUrl: '/policy?type=privacy',
|
||||
cookiePolicyUrl: '/policy?type=privacy',
|
||||
dataControllerEmail: 'privacy@yourcompany.com',
|
||||
retentionPeriod: 365, // 1 year
|
||||
enableAuditLog: true,
|
||||
@@ -137,7 +137,6 @@ export const CookieConsentProvider: React.FC<{
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load cookie preferences:', error);
|
||||
}
|
||||
|
||||
// Show banner if no valid consent found
|
||||
@@ -171,7 +170,6 @@ export const CookieConsentProvider: React.FC<{
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to save cookie preferences:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,7 +269,6 @@ export const CookieConsentProvider: React.FC<{
|
||||
try {
|
||||
localStorage.removeItem(CONSENT_STORAGE_KEY);
|
||||
} catch (error) {
|
||||
console.warn('Failed to reset cookie preferences:', error);
|
||||
}
|
||||
|
||||
setState({
|
||||
@@ -288,7 +285,6 @@ export const CookieConsentProvider: React.FC<{
|
||||
try {
|
||||
localStorage.removeItem(CONSENT_STORAGE_KEY);
|
||||
} catch (error) {
|
||||
console.warn('Failed to withdraw consent:', error);
|
||||
}
|
||||
|
||||
const auditEntry = config.enableAuditLog ? createAuditLogEntry('consent_withdrawn', defaultPreferences) : null;
|
||||
@@ -386,7 +382,6 @@ export const useFunctional = () => {
|
||||
try {
|
||||
localStorage.setItem(`user-preference-${key}`, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.warn('Failed to save user preference:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -397,7 +392,6 @@ export const useFunctional = () => {
|
||||
const saved = localStorage.getItem(`user-preference-${key}`);
|
||||
return saved ? JSON.parse(saved) : defaultValue;
|
||||
} catch (error) {
|
||||
console.warn('Failed to load user preference:', error);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -406,7 +400,6 @@ export const useFunctional = () => {
|
||||
|
||||
const rememberUserAction = (action: string, data?: any) => {
|
||||
if (isEnabled) {
|
||||
console.log('User Action Remembered:', action, data);
|
||||
// Implement your user action tracking logic here
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,7 +26,6 @@ export const useFunctional = () => {
|
||||
try {
|
||||
localStorage.setItem(`user-preference-${key}`, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.warn('Failed to save user preference:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -37,7 +36,6 @@ export const useFunctional = () => {
|
||||
const saved = localStorage.getItem(`user-preference-${key}`);
|
||||
return saved ? JSON.parse(saved) : defaultValue;
|
||||
} catch (error) {
|
||||
console.warn('Failed to load user preference:', error);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +44,6 @@ export const useFunctional = () => {
|
||||
|
||||
const rememberUserAction = (action: string, data?: any) => {
|
||||
if (canShow) {
|
||||
console.log('User Action Remembered:', action, data);
|
||||
// Implement your user action tracking logic here
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@ const Header = () => {
|
||||
// Find the Services menu item and update its submenu with API data
|
||||
const servicesIndex = baseNavigation.findIndex(item => item.title === "Services");
|
||||
if (servicesIndex !== -1 && apiServices.length > 0) {
|
||||
console.log('Replacing services with API data:', apiServices);
|
||||
baseNavigation[servicesIndex] = {
|
||||
...baseNavigation[servicesIndex],
|
||||
submenu: apiServices.map(service => ({
|
||||
@@ -37,8 +36,6 @@ const Header = () => {
|
||||
updated_at: service.updated_at
|
||||
}))
|
||||
} as any;
|
||||
} else {
|
||||
console.log('Using static services data. API services:', apiServices.length, 'Services index:', servicesIndex);
|
||||
}
|
||||
|
||||
return baseNavigation;
|
||||
|
||||
Reference in New Issue
Block a user