updates
This commit is contained in:
@@ -227,10 +227,10 @@ const AboutBanner = () => {
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="social-links">
|
||||
<Link href="https://www.linkedin.com/company/gnxtech" target="_blank" className="social-link">
|
||||
<Link href="https://www.linkedin.com" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
<Link href="https://github.com/gnxtech" target="_blank" className="social-link">
|
||||
<Link href="https://github.com" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { useBlogPost } from "@/lib/hooks/useBlog";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import { sanitizeHTML } from "@/lib/security/sanitize";
|
||||
|
||||
const BlogSingle = () => {
|
||||
const params = useParams();
|
||||
@@ -184,7 +185,7 @@ const BlogSingle = () => {
|
||||
{post.content && (
|
||||
<div
|
||||
className="article-content enterprise-content"
|
||||
dangerouslySetInnerHTML={{ __html: post.content }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(post.content) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -199,7 +200,7 @@ const BlogSingle = () => {
|
||||
</h6>
|
||||
<div className="social-share">
|
||||
<Link
|
||||
href={`https://www.linkedin.com/shareArticle?mini=true&url=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}&title=${encodeURIComponent(post.title)}`}
|
||||
href="https://linkedin.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-linkedin"
|
||||
|
||||
@@ -7,7 +7,10 @@ import Link from "next/link";
|
||||
const CareerBanner = () => {
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
if (document.querySelector(".career-banner")) {
|
||||
const careerBanner = document.querySelector(".career-banner");
|
||||
const cpBannerThumb = document.querySelector(".cp-banner-thumb");
|
||||
|
||||
if (careerBanner && cpBannerThumb) {
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: ".career-banner",
|
||||
@@ -114,7 +117,7 @@ const CareerBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
href="https://www.linkedin.com"
|
||||
target="_blank"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
@@ -123,7 +126,7 @@ const CareerBanner = () => {
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/gnxtech"
|
||||
href="https://github.com"
|
||||
target="_blank"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import Image from "next/legacy/image";
|
||||
import time from "@/public/images/time.png";
|
||||
import trans from "@/public/images/trans.png";
|
||||
import support from "@/public/images/support.png";
|
||||
import skill from "@/public/images/skill.png";
|
||||
|
||||
const Thrive = () => {
|
||||
return (
|
||||
@@ -20,7 +16,7 @@ const Thrive = () => {
|
||||
<div className="row vertical-column-gap-lg mt-60">
|
||||
<div className="col-12 col-md-6 fade-top">
|
||||
<div className="thumb">
|
||||
<Image src={time} alt="Image" width={80} height={80} />
|
||||
<Image src="/images/time.png" alt="Image" width={80} height={80} />
|
||||
</div>
|
||||
<div className="content mt-40">
|
||||
<h4 className="mt-8 title-anim fw-7 text-white mb-16">
|
||||
@@ -35,7 +31,7 @@ const Thrive = () => {
|
||||
</div>
|
||||
<div className="col-12 col-md-6 fade-top">
|
||||
<div className="thumb">
|
||||
<Image src={trans} alt="Image" width={80} height={80} />
|
||||
<Image src="/images/trans.png" alt="Image" width={80} height={80} />
|
||||
</div>
|
||||
<div className="content mt-40">
|
||||
<h4 className="mt-8 title-anim fw-7 text-white mb-16">
|
||||
@@ -50,7 +46,7 @@ const Thrive = () => {
|
||||
</div>
|
||||
<div className="col-12 col-md-6 fade-top">
|
||||
<div className="thumb">
|
||||
<Image src={support} alt="Image" width={80} height={80} />
|
||||
<Image src="/images/support.png" alt="Image" width={80} height={80} />
|
||||
</div>
|
||||
<div className="content mt-40">
|
||||
<h4 className="mt-8 title-anim fw-7 text-white mb-16">Support</h4>
|
||||
@@ -63,7 +59,7 @@ const Thrive = () => {
|
||||
</div>
|
||||
<div className="col-12 col-md-6 fade-top">
|
||||
<div className="thumb">
|
||||
<Image src={skill} alt="Image" width={80} height={80} />
|
||||
<Image src="/images/skill.png" alt="Image" width={80} height={80} />
|
||||
</div>
|
||||
<div className="content mt-40">
|
||||
<h4 className="mt-8 title-anim fw-7 text-white mb-16">
|
||||
|
||||
@@ -3,7 +3,6 @@ import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { useCaseStudies } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import one from "@/public/images/case/one.png";
|
||||
|
||||
const CaseItems = () => {
|
||||
const { caseStudies, loading: casesLoading } = useCaseStudies();
|
||||
@@ -56,7 +55,7 @@ const CaseItems = () => {
|
||||
<div className="thumb mb-24">
|
||||
<Link href={`/case-study/${caseStudy.slug}`} className="w-100">
|
||||
<Image
|
||||
src={caseStudy.thumbnail ? getImageUrl(caseStudy.thumbnail) : one}
|
||||
src={caseStudy.thumbnail ? getImageUrl(caseStudy.thumbnail) : "/images/case/one.png"}
|
||||
className="w-100 mh-300"
|
||||
alt={caseStudy.title}
|
||||
width={600}
|
||||
|
||||
@@ -5,8 +5,7 @@ import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import poster from "@/public/images/case/poster.png";
|
||||
import project from "@/public/images/case/project.png";
|
||||
import { sanitizeHTML } from "@/lib/security/sanitize";
|
||||
|
||||
interface CaseSingleProps {
|
||||
slug: string;
|
||||
@@ -204,12 +203,12 @@ const CaseSingle = ({ slug }: CaseSingleProps) => {
|
||||
{caseStudy.project_overview ? (
|
||||
<div
|
||||
className="content-html"
|
||||
dangerouslySetInnerHTML={{ __html: caseStudy.project_overview }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(caseStudy.project_overview) }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="content-html"
|
||||
dangerouslySetInnerHTML={{ __html: caseStudy.description || '' }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(caseStudy.description || '') }}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -217,7 +216,7 @@ const CaseSingle = ({ slug }: CaseSingleProps) => {
|
||||
{caseStudy.description && (
|
||||
<div
|
||||
className="content-html full-description mt-40"
|
||||
dangerouslySetInnerHTML={{ __html: caseStudy.description }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(caseStudy.description) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -345,7 +344,7 @@ const CaseSingle = ({ slug }: CaseSingleProps) => {
|
||||
<h2 className="section-title">Site Map & Process</h2>
|
||||
<div
|
||||
className="content-html"
|
||||
dangerouslySetInnerHTML={{ __html: caseStudy.site_map_content }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(caseStudy.site_map_content) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,8 @@ const Process = ({ slug }: ProcessProps) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const processSteps = caseStudy.process_steps;
|
||||
|
||||
return (
|
||||
<section className="case-study-process luxury-process pt-120 pb-120">
|
||||
<div className="container">
|
||||
@@ -28,7 +30,7 @@ const Process = ({ slug }: ProcessProps) => {
|
||||
</div>
|
||||
<div className="col-12 col-lg-7">
|
||||
<div className="process-steps-list">
|
||||
{caseStudy.process_steps.map((step, index) => (
|
||||
{processSteps.map((step, index) => (
|
||||
<div key={step.id} className="process-step-item">
|
||||
<div className="step-number">
|
||||
{String(step.step_number).padStart(2, '0')}
|
||||
@@ -37,7 +39,7 @@ const Process = ({ slug }: ProcessProps) => {
|
||||
<h4 className="step-title">{step.title}</h4>
|
||||
<p className="step-description">{step.description}</p>
|
||||
</div>
|
||||
{index < caseStudy.process_steps.length - 1 && (
|
||||
{index < processSteps.length - 1 && (
|
||||
<div className="step-connector"></div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import one from "@/public/images/case/one.png";
|
||||
|
||||
interface RelatedCaseProps {
|
||||
slug: string;
|
||||
@@ -34,7 +33,7 @@ const RelatedCase = ({ slug }: RelatedCaseProps) => {
|
||||
<Link href={`/case-study/${relatedCase.slug}`} className="case-link">
|
||||
<div className="case-image-wrapper">
|
||||
<Image
|
||||
src={relatedCase.thumbnail ? getImageUrl(relatedCase.thumbnail) : one}
|
||||
src={relatedCase.thumbnail ? getImageUrl(relatedCase.thumbnail) : "/images/case/one.png"}
|
||||
className="case-image"
|
||||
alt={relatedCase.title}
|
||||
width={400}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import Image from "next/legacy/image";
|
||||
import thumb from "@/public/images/contact-thumb.png";
|
||||
import { contactApiService, ContactFormData } from "@/lib/api/contactService";
|
||||
|
||||
const ContactSection = () => {
|
||||
@@ -32,10 +31,39 @@ const ContactSection = () => {
|
||||
message: string;
|
||||
}>({ type: null, message: '' });
|
||||
|
||||
// Math Captcha state
|
||||
const [captcha, setCaptcha] = useState({ num1: 0, num2: 0, operator: '+', answer: 0 });
|
||||
const [captchaAnswer, setCaptchaAnswer] = useState('');
|
||||
const [captchaError, setCaptchaError] = useState('');
|
||||
|
||||
// Refs for scrolling to status messages
|
||||
const statusRef = useRef<HTMLDivElement>(null);
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
// Generate math captcha
|
||||
const generateCaptcha = () => {
|
||||
const operators = ['+', '-'];
|
||||
const operator = operators[Math.floor(Math.random() * operators.length)];
|
||||
let num1 = Math.floor(Math.random() * 10) + 1; // 1-10
|
||||
let num2 = Math.floor(Math.random() * 10) + 1; // 1-10
|
||||
|
||||
// Ensure subtraction doesn't result in negative numbers
|
||||
if (operator === '-' && num1 < num2) {
|
||||
[num1, num2] = [num2, num1];
|
||||
}
|
||||
|
||||
const answer = operator === '+' ? num1 + num2 : num1 - num2;
|
||||
|
||||
setCaptcha({ num1, num2, operator, answer });
|
||||
setCaptchaAnswer('');
|
||||
setCaptchaError('');
|
||||
};
|
||||
|
||||
// Generate captcha on component mount
|
||||
useEffect(() => {
|
||||
generateCaptcha();
|
||||
}, []);
|
||||
|
||||
// Scroll to status message when it appears
|
||||
useEffect(() => {
|
||||
if (submitStatus.type && statusRef.current) {
|
||||
@@ -62,6 +90,16 @@ const ContactSection = () => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
setSubmitStatus({ type: null, message: '' });
|
||||
setCaptchaError('');
|
||||
|
||||
// Validate captcha
|
||||
const userAnswer = parseInt(captchaAnswer.trim());
|
||||
if (isNaN(userAnswer) || userAnswer !== captcha.answer) {
|
||||
setCaptchaError('Incorrect answer. Please try again.');
|
||||
setIsSubmitting(false);
|
||||
generateCaptcha(); // Generate new captcha on error
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Transform form data to match API requirements
|
||||
@@ -108,6 +146,10 @@ const ContactSection = () => {
|
||||
privacy: false
|
||||
});
|
||||
|
||||
// Reset captcha
|
||||
setCaptchaAnswer('');
|
||||
generateCaptcha();
|
||||
|
||||
} catch (error) {
|
||||
setSubmitStatus({
|
||||
type: 'error',
|
||||
@@ -408,6 +450,49 @@ const ContactSection = () => {
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Math Captcha */}
|
||||
<div className="input-single compact-input captcha-container">
|
||||
<label htmlFor="captcha">
|
||||
Security Verification *
|
||||
<span className="captcha-hint">(Please solve the math problem)</span>
|
||||
</label>
|
||||
<div className="captcha-wrapper">
|
||||
<div className="captcha-question">
|
||||
<span className="captcha-numbers">
|
||||
{captcha.num1} {captcha.operator} {captcha.num2} = ?
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="captcha-refresh"
|
||||
onClick={generateCaptcha}
|
||||
title="Generate new question"
|
||||
aria-label="Refresh captcha"
|
||||
>
|
||||
<i className="fa-solid fa-rotate"></i>
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
name="captcha"
|
||||
id="captcha"
|
||||
value={captchaAnswer}
|
||||
onChange={(e) => {
|
||||
setCaptchaAnswer(e.target.value);
|
||||
setCaptchaError('');
|
||||
}}
|
||||
placeholder="Enter answer"
|
||||
required
|
||||
className={captchaError ? 'error' : ''}
|
||||
/>
|
||||
{captchaError && (
|
||||
<span className="captcha-error">
|
||||
<i className="fa-solid fa-exclamation-circle"></i>
|
||||
{captchaError}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status Message */}
|
||||
|
||||
@@ -5,14 +5,15 @@ import Link from "next/link";
|
||||
import { useMemo } from "react";
|
||||
import { useServices } from "@/lib/hooks/useServices";
|
||||
import { serviceUtils } from "@/lib/api/serviceService";
|
||||
import one from "@/public/images/overview/one.png";
|
||||
import two from "@/public/images/overview/two.png";
|
||||
import three from "@/public/images/overview/three.png";
|
||||
import four from "@/public/images/overview/four.png";
|
||||
import five from "@/public/images/overview/five.png";
|
||||
|
||||
// Default images array for fallback
|
||||
const defaultImages = [one, two, three, four, five];
|
||||
// Default images array for fallback - use string paths
|
||||
const defaultImages = [
|
||||
"/images/overview/one.png",
|
||||
"/images/overview/two.png",
|
||||
"/images/overview/three.png",
|
||||
"/images/overview/four.png",
|
||||
"/images/overview/five.png"
|
||||
];
|
||||
|
||||
const Overview = () => {
|
||||
// Memoize the parameters to prevent infinite re-renders
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import thumb from "@/public/images/leading.jpg";
|
||||
|
||||
const ServiceIntro = () => {
|
||||
return (
|
||||
@@ -11,7 +10,7 @@ const ServiceIntro = () => {
|
||||
<div className="tp-service__thumb" style={{ maxWidth: '400px', border: 'none', padding: 0, margin: 0, overflow: 'hidden', borderRadius: '8px' }}>
|
||||
<Link href="services">
|
||||
<Image
|
||||
src={thumb}
|
||||
src="/images/leading.jpg"
|
||||
alt="Enterprise Software Solutions"
|
||||
width={400}
|
||||
height={500}
|
||||
|
||||
@@ -273,7 +273,9 @@ const Story = () => {
|
||||
</Link>
|
||||
</p>
|
||||
<h5 className="fw-4 mt-12 mb-12 text-white">
|
||||
{item.title}
|
||||
<Link href={`/case-study/${item.slug}`} className="text-white">
|
||||
{item.title}
|
||||
</Link>
|
||||
</h5>
|
||||
<p className="text-xs">{item.excerpt || item.description?.substring(0, 150) + '...'}</p>
|
||||
</div>
|
||||
@@ -300,32 +302,34 @@ const Story = () => {
|
||||
className={`tp-story-thumb ${isActive ? "thumb-active" : ""}`}
|
||||
data-loaded={isLoaded}
|
||||
>
|
||||
<Image
|
||||
src={imageUrl}
|
||||
width={600}
|
||||
height={300}
|
||||
className="w-100 mh-300"
|
||||
alt={item.title || "Case Study"}
|
||||
priority={index === 0}
|
||||
loading={index === 0 ? 'eager' : 'lazy'}
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
objectFit: 'cover',
|
||||
opacity: isLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease'
|
||||
}}
|
||||
onLoad={() => {
|
||||
if (!isLoaded) {
|
||||
setImagesLoaded((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.add(index);
|
||||
return newSet;
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Link href={`/case-study/${item.slug}`} className="w-100">
|
||||
<Image
|
||||
src={imageUrl}
|
||||
width={600}
|
||||
height={300}
|
||||
className="w-100 mh-300"
|
||||
alt={item.title || "Case Study"}
|
||||
priority={index === 0}
|
||||
loading={index === 0 ? 'eager' : 'lazy'}
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
objectFit: 'cover',
|
||||
opacity: isLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease'
|
||||
}}
|
||||
onLoad={() => {
|
||||
if (!isLoaded) {
|
||||
setImagesLoaded((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.add(index);
|
||||
return newSet;
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -84,7 +84,7 @@ const ServicesBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
href="https://www.linkedin.com"
|
||||
target="_blank"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
@@ -93,7 +93,7 @@ const ServicesBanner = () => {
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/gnxtech"
|
||||
href="https://github.com"
|
||||
target="_blank"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
|
||||
@@ -3,8 +3,6 @@ import { useEffect } from "react";
|
||||
import Image from "next/legacy/image";
|
||||
import gsap from "gsap";
|
||||
import ScrollTrigger from "gsap/dist/ScrollTrigger";
|
||||
import thumb from "@/public/images/transform-thumb.png";
|
||||
import teamThumb from "@/public/images/team-thumb.png";
|
||||
import { Service } from "@/lib/api/serviceService";
|
||||
import { serviceUtils } from "@/lib/api/serviceService";
|
||||
|
||||
@@ -16,20 +14,25 @@ const Transform = ({ service }: TransformProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
gsap.set(".foot-fade", {
|
||||
x: -100,
|
||||
opacity: 0,
|
||||
});
|
||||
|
||||
// Check if elements exist before animating
|
||||
const footFadeElements = document.querySelectorAll(".foot-fade");
|
||||
if (footFadeElements.length > 0) {
|
||||
gsap.set(".foot-fade", {
|
||||
x: -100,
|
||||
opacity: 0,
|
||||
});
|
||||
|
||||
ScrollTrigger.batch(".foot-fade", {
|
||||
start: "-100px bottom",
|
||||
onEnter: (elements) =>
|
||||
gsap.to(elements, {
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
stagger: 0.3,
|
||||
}),
|
||||
});
|
||||
ScrollTrigger.batch(".foot-fade", {
|
||||
start: "-100px bottom",
|
||||
onEnter: (elements) =>
|
||||
gsap.to(elements, {
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
stagger: 0.3,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -55,7 +58,7 @@ const Transform = ({ service }: TransformProps) => {
|
||||
<div className="transform__thumb">
|
||||
<div className="enterprise-image-wrapper">
|
||||
<Image
|
||||
src={serviceUtils.getServiceImageUrl(service) || thumb}
|
||||
src={serviceUtils.getServiceImageUrl(service) || "/images/transform-thumb.png"}
|
||||
className="enterprise-service-image"
|
||||
alt={service.title}
|
||||
width={600}
|
||||
|
||||
@@ -27,7 +27,7 @@ const KnowledgeBase = () => {
|
||||
const filtered = allArticles.filter(article =>
|
||||
article.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
article.summary.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
article.content.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
(article.content && article.content.toLowerCase().includes(searchTerm.toLowerCase()))
|
||||
);
|
||||
return {
|
||||
displayArticles: filtered,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useKnowledgeBaseArticle } from '@/lib/hooks/useSupport';
|
||||
import { markArticleHelpful } from '@/lib/api/supportService';
|
||||
import { sanitizeHTML } from '@/lib/security/sanitize';
|
||||
|
||||
interface KnowledgeBaseArticleModalProps {
|
||||
slug: string;
|
||||
@@ -94,7 +95,7 @@ const KnowledgeBaseArticleModal = ({ slug, onClose }: KnowledgeBaseArticleModalP
|
||||
<div className="article-body">
|
||||
<div
|
||||
className="article-content"
|
||||
dangerouslySetInnerHTML={{ __html: article.content || article.summary }}
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHTML(article.content || article.summary) }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
|
||||
type ModalType = 'create' | 'knowledge' | 'status' | null;
|
||||
|
||||
@@ -102,7 +103,7 @@ const SupportCenterHero = ({ onFeatureClick }: SupportCenterHeroProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<a
|
||||
<Link
|
||||
href="/policy?type=privacy"
|
||||
className="feature-item clickable link-item"
|
||||
>
|
||||
@@ -111,10 +112,10 @@ const SupportCenterHero = ({ onFeatureClick }: SupportCenterHeroProps) => {
|
||||
</div>
|
||||
<h3>Privacy Policy</h3>
|
||||
<p>Learn about data protection</p>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<a
|
||||
<Link
|
||||
href="/policy?type=terms"
|
||||
className="feature-item clickable link-item"
|
||||
>
|
||||
@@ -123,10 +124,10 @@ const SupportCenterHero = ({ onFeatureClick }: SupportCenterHeroProps) => {
|
||||
</div>
|
||||
<h3>Terms of Use</h3>
|
||||
<p>Review our service terms</p>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<a
|
||||
<Link
|
||||
href="/policy?type=support"
|
||||
className="feature-item clickable link-item"
|
||||
>
|
||||
@@ -135,7 +136,7 @@ const SupportCenterHero = ({ onFeatureClick }: SupportCenterHeroProps) => {
|
||||
</div>
|
||||
<h3>Support Policy</h3>
|
||||
<p>Understand our support coverage</p>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -148,4 +149,3 @@ const SupportCenterHero = ({ onFeatureClick }: SupportCenterHeroProps) => {
|
||||
};
|
||||
|
||||
export default SupportCenterHero;
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ const SmoothScroll = () => {
|
||||
gestureOrientation: 'vertical',
|
||||
smoothWheel: true,
|
||||
wheelMultiplier: 1,
|
||||
smoothTouch: false,
|
||||
touchMultiplier: 2,
|
||||
infinite: false,
|
||||
});
|
||||
|
||||
@@ -289,7 +289,7 @@ const Footer = () => {
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="social-links justify-content-center justify-content-lg-end">
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
href="https://www.linkedin.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="LinkedIn"
|
||||
@@ -298,7 +298,7 @@ const Footer = () => {
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/gnxtech"
|
||||
href="https://github.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="GitHub"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { usePathname } from "next/navigation";
|
||||
import AnimateHeight from "react-animate-height";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import logoLight from "@/public/images/logo-light.png";
|
||||
|
||||
interface OffcanvasMenuProps {
|
||||
isOffcanvasOpen: boolean;
|
||||
@@ -67,7 +66,7 @@ const OffcanvasMenu = ({
|
||||
<div className="offcanvas-menu__header nav-fade">
|
||||
<div className="logo">
|
||||
<Link href="/" className="logo-img">
|
||||
<Image src={logoLight} priority alt="Image" title="Logo" width={160} height={60} />
|
||||
<Image src="/images/logo-light.png" priority alt="Image" title="Logo" width={160} height={60} />
|
||||
</Link>
|
||||
</div>
|
||||
<button
|
||||
@@ -176,7 +175,7 @@ const OffcanvasMenu = ({
|
||||
<ul className="enterprise-social nav-fade">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
href="https://www.linkedin.com"
|
||||
target="_blank"
|
||||
aria-label="Connect with us on LinkedIn"
|
||||
>
|
||||
@@ -185,7 +184,7 @@ const OffcanvasMenu = ({
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/gnxtech"
|
||||
href="https://github.com"
|
||||
target="_blank"
|
||||
aria-label="View our code on GitHub"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user