-
{policy.title}
+
{policy.title || 'Policy'}
-
- Last Updated: {new Date(policy.last_updated).toLocaleDateString('en-US', {
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- })}
-
-
Version {policy.version}
-
- Effective Date: {new Date(policy.effective_date).toLocaleDateString('en-US', {
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- })}
-
+ {policy.last_updated && (
+
+ Last Updated: {(() => {
+ try {
+ return new Date(policy.last_updated).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+ } catch {
+ return new Date().toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+ }
+ })()}
+
+ )}
+ {policy.version && (
+
Version {policy.version}
+ )}
+ {policy.effective_date && (
+
+ Effective Date: {(() => {
+ try {
+ return new Date(policy.effective_date).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+ } catch {
+ return new Date().toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ });
+ }
+ })()}
+
+ )}
{policy.description && (
{policy.description}
@@ -203,34 +303,42 @@ const PolicyContent = () => {
{/* Policy Content */}
- {policy.sections.map((section) => (
-
-
{section.heading}
-
(a)')
- .replace(/\(b\)/g, '
(b)')
- .replace(/\(c\)/g, '
(c)')
- .replace(/\(d\)/g, '
(d)')
- .replace(/\(e\)/g, '
(e)')
- .replace(/\(f\)/g, '
(f)')
- .replace(/\(g\)/g, '
(g)')
- .replace(/\(h\)/g, '
(h)')
- .replace(/\(i\)/g, '
(i)')
- .replace(/\(j\)/g, '
(j)')
- .replace(/\(k\)/g, '
(k)')
- .replace(/\(l\)/g, '
(l)')
- // Handle pipe separators for contact information
- .replace(/ \| /g, '
')
- .replace(/: /g, ': ')
- // Handle semicolon with parenthesis
- .replace(/; \(/g, ';
(')
- // Add spacing after periods in long sentences
- .replace(/\. ([A-Z])/g, '.
$1')
- }} />
+ {policy.sections && Array.isArray(policy.sections) && policy.sections.length > 0 ? (
+ policy.sections.map((section) => (
+
+
{section.heading || ''}
+
(a)')
+ .replace(/\(b\)/g, '
(b)')
+ .replace(/\(c\)/g, '
(c)')
+ .replace(/\(d\)/g, '
(d)')
+ .replace(/\(e\)/g, '
(e)')
+ .replace(/\(f\)/g, '
(f)')
+ .replace(/\(g\)/g, '
(g)')
+ .replace(/\(h\)/g, '
(h)')
+ .replace(/\(i\)/g, '
(i)')
+ .replace(/\(j\)/g, '
(j)')
+ .replace(/\(k\)/g, '
(k)')
+ .replace(/\(l\)/g, '
(l)')
+ // Handle pipe separators for contact information
+ .replace(/ \| /g, '
')
+ .replace(/: /g, ': ')
+ // Handle semicolon with parenthesis
+ .replace(/; \(/g, ';
(')
+ // Add spacing after periods in long sentences
+ .replace(/\. ([A-Z])/g, '.
$1')
+ )
+ }} />
+
+ ))
+ ) : (
+
- ))}
+ )}
{/* Contact Section */}
@@ -423,14 +531,17 @@ const PolicyContent = () => {
);
};
+// Wrapper component (no longer needs Suspense since we're not using useSearchParams)
+const PolicyContentWrapper = () => {
+ return
;
+};
+
const PolicyPage = () => {
return (
- Loading...
}>
-
-
+
diff --git a/frontEnd/app/services/[slug]/page.tsx b/frontEnd/app/services/[slug]/page.tsx
index e544c33a..5a4ca70c 100644
--- a/frontEnd/app/services/[slug]/page.tsx
+++ b/frontEnd/app/services/[slug]/page.tsx
@@ -9,9 +9,10 @@ import Transform from "@/components/pages/services/Transform";
import Footer from "@/components/shared/layout/footer/Footer";
import ServicesScrollProgressButton from "@/components/pages/services/ServicesScrollProgressButton";
import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnimations";
-import { serviceService, Service } from "@/lib/api/serviceService";
+import { Service } from "@/lib/api/serviceService";
import { generateServiceMetadata } from "@/lib/seo/metadata";
import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData";
+import { API_CONFIG, getApiHeaders } from "@/lib/config/api";
interface ServicePageProps {
params: Promise<{
@@ -19,23 +20,59 @@ interface ServicePageProps {
}>;
}
-// Generate static params for all services (optional - for better performance)
+// Generate static params for all services at build time (optional - for better performance)
+// This pre-generates known pages, but new pages can still be generated on-demand
export async function generateStaticParams() {
try {
- const services = await serviceService.getServices();
- return services.results.map((service: Service) => ({
+ // Use internal API URL for server-side requests
+ const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
+ const response = await fetch(
+ `${apiUrl}/api/services/`,
+ {
+ method: 'GET',
+ headers: getApiHeaders(),
+ next: { revalidate: 60 }, // Revalidate every minute for faster image updates
+ }
+ );
+
+ if (!response.ok) {
+ console.error('Error fetching services for static params:', response.status);
+ return [];
+ }
+
+ const data = await response.json();
+ const services = data.results || data;
+
+ return services.map((service: Service) => ({
slug: service.slug,
}));
} catch (error) {
+ console.error('Error generating static params for services:', error);
return [];
}
}
// Generate enhanced metadata for each service page
export async function generateMetadata({ params }: ServicePageProps) {
+ const { slug } = await params;
+
try {
- const { slug } = await params;
- const service = await serviceService.getServiceBySlug(slug);
+ // Use internal API URL for server-side requests
+ const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
+ const response = await fetch(
+ `${apiUrl}/api/services/${slug}/`,
+ {
+ method: 'GET',
+ headers: getApiHeaders(),
+ next: { revalidate: 60 }, // Revalidate every minute for faster image updates
+ }
+ );
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const service = await response.json();
return generateServiceMetadata(service);
} catch (error) {
@@ -47,23 +84,34 @@ export async function generateMetadata({ params }: ServicePageProps) {
}
const ServicePage = async ({ params }: ServicePageProps) => {
- let service: Service;
+ const { slug } = await params;
try {
- const { slug } = await params;
- service = await serviceService.getServiceBySlug(slug);
- } catch (error) {
- notFound();
- }
+ // Use internal API URL for server-side requests
+ const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
+ const response = await fetch(
+ `${apiUrl}/api/services/${slug}/`,
+ {
+ method: 'GET',
+ headers: getApiHeaders(),
+ next: { revalidate: 60 }, // Revalidate every minute for faster image updates
+ }
+ );
- // Breadcrumb data for structured data
- const breadcrumbItems = [
- { name: 'Home', url: '/' },
- { name: 'Services', url: '/services' },
- { name: service.title, url: `/services/${service.slug}` },
- ];
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
- return (
+ const service: Service = await response.json();
+
+ // Breadcrumb data for structured data
+ const breadcrumbItems = [
+ { name: 'Home', url: '/' },
+ { name: 'Services', url: '/services' },
+ { name: service.title, url: `/services/${service.slug}` },
+ ];
+
+ return (
{/* SEO Structured Data */}
@@ -82,7 +130,10 @@ const ServicePage = async ({ params }: ServicePageProps) => {
- );
+ );
+ } catch (error) {
+ notFound();
+ }
};
export default ServicePage;
diff --git a/frontEnd/app/support-center/layout.tsx b/frontEnd/app/support-center/layout.tsx
new file mode 100644
index 00000000..df92fd76
--- /dev/null
+++ b/frontEnd/app/support-center/layout.tsx
@@ -0,0 +1,13 @@
+// Force dynamic rendering for support-center pages
+export const dynamic = 'force-dynamic';
+export const dynamicParams = true;
+export const revalidate = 0;
+
+export default function SupportCenterLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return <>{children}>;
+}
+
diff --git a/frontEnd/app/support-center/page.tsx b/frontEnd/app/support-center/page.tsx
index 1feba094..0d3ac26c 100644
--- a/frontEnd/app/support-center/page.tsx
+++ b/frontEnd/app/support-center/page.tsx
@@ -12,31 +12,40 @@ type ModalType = 'create' | 'knowledge' | 'status' | null;
const SupportCenterPage = () => {
// Set metadata for client component
useEffect(() => {
- const metadata = createMetadata({
- title: "Support Center - Enterprise Support & Help Desk",
- description: "Get 24/7 enterprise support from GNX Soft. Access our knowledge base, create support tickets, check ticket status, and get help with our software solutions and services.",
- keywords: [
- "Support Center",
- "Customer Support",
- "Help Desk",
- "Technical Support",
- "Knowledge Base",
- "Support Tickets",
- "Enterprise Support",
- "IT Support",
- ],
- url: "/support-center",
- });
-
- document.title = metadata.title || "Support Center | GNX Soft";
+ // Only run on client side
+ if (typeof window === 'undefined' || typeof document === 'undefined') return;
- let metaDescription = document.querySelector('meta[name="description"]');
- if (!metaDescription) {
- metaDescription = document.createElement('meta');
- metaDescription.setAttribute('name', 'description');
- document.head.appendChild(metaDescription);
+ try {
+ const metadata = createMetadata({
+ title: "Support Center - Enterprise Support & Help Desk",
+ description: "Get 24/7 enterprise support from GNX Soft. Access our knowledge base, create support tickets, check ticket status, and get help with our software solutions and services.",
+ keywords: [
+ "Support Center",
+ "Customer Support",
+ "Help Desk",
+ "Technical Support",
+ "Knowledge Base",
+ "Support Tickets",
+ "Enterprise Support",
+ "IT Support",
+ ],
+ url: "/support-center",
+ });
+
+ const titleString = typeof metadata.title === 'string' ? metadata.title : "Support Center | GNX Soft";
+ document.title = titleString;
+
+ let metaDescription = document.querySelector('meta[name="description"]');
+ if (!metaDescription) {
+ metaDescription = document.createElement('meta');
+ metaDescription.setAttribute('name', 'description');
+ document.head.appendChild(metaDescription);
+ }
+ metaDescription.setAttribute('content', metadata.description || 'Get enterprise support from GNX Soft');
+ } catch (error) {
+ // Silently handle metadata errors
+ console.error('Error setting metadata:', error);
}
- metaDescription.setAttribute('content', metadata.description || 'Get enterprise support from GNX Soft');
}, []);
const [activeModal, setActiveModal] = useState
(null);
diff --git a/frontEnd/components/pages/about/AboutBanner.tsx b/frontEnd/components/pages/about/AboutBanner.tsx
index e274f8c0..8bb89e81 100644
--- a/frontEnd/components/pages/about/AboutBanner.tsx
+++ b/frontEnd/components/pages/about/AboutBanner.tsx
@@ -227,10 +227,10 @@ const AboutBanner = () => {
{/* Social Links */}
-
+
-
+
diff --git a/frontEnd/components/pages/blog/BlogSingle.tsx b/frontEnd/components/pages/blog/BlogSingle.tsx
index 93427ca1..fb86cbc1 100644
--- a/frontEnd/components/pages/blog/BlogSingle.tsx
+++ b/frontEnd/components/pages/blog/BlogSingle.tsx
@@ -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 && (
)}
@@ -199,7 +200,7 @@ const BlogSingle = () => {