Files
GNX-WEB/frontEnd/app/policy/page.tsx
Iliyan Angelov d7ff5c71e6 updates
2025-11-24 08:42:03 +02:00

440 lines
13 KiB
TypeScript

"use client";
import { useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
import Header from "@/components/shared/layout/header/Header";
import Footer from "@/components/shared/layout/footer/Footer";
import { Suspense } from 'react';
import { usePolicy } from '@/lib/hooks/usePolicy';
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
const PolicyContent = () => {
const searchParams = useSearchParams();
const typeParam = searchParams.get('type') || 'privacy';
const type = typeParam as 'privacy' | 'terms' | 'support';
const { data: policy, isLoading, error } = usePolicy(type);
// Update metadata based on policy type
useEffect(() => {
const policyTitles = {
privacy: 'Privacy Policy - Data Protection & Privacy',
terms: 'Terms of Use - Terms & Conditions',
support: 'Support Policy - Support Terms & Guidelines',
};
const policyDescriptions = {
privacy: 'Read GNX Soft\'s Privacy Policy to understand how we collect, use, and protect your personal information and data.',
terms: 'Review GNX Soft\'s Terms of Use and Conditions for using our software services and platforms.',
support: 'Learn about GNX Soft\'s Support Policy, including support terms, response times, and service level agreements.',
};
const metadata = createMetadata({
title: policyTitles[type],
description: policyDescriptions[type],
keywords: [
type === 'privacy' ? 'Privacy Policy' : type === 'terms' ? 'Terms of Use' : 'Support Policy',
'Legal Documents',
'Company Policies',
'Data Protection',
'Terms and Conditions',
],
url: `/policy?type=${type}`,
});
document.title = metadata.title || `${policyTitles[type]} | GNX Soft`;
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 || policyDescriptions[type]);
}, [type]);
if (isLoading) {
return (
<section className="policy-section section-padding">
<div className="container">
<div className="row justify-content-center">
<div className="col-12 col-lg-10">
<div className="loading-state">
<div className="spinner"></div>
<p>Loading policy...</p>
</div>
</div>
</div>
</div>
<style jsx>{`
.policy-section {
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
min-height: 100vh;
padding: 120px 0 80px;
}
.loading-state {
text-align: center;
padding: 4rem 2rem;
}
.spinner {
width: 50px;
height: 50px;
margin: 0 auto 1rem;
border: 4px solid #f3f3f3;
border-top: 4px solid #daa520;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-state p {
color: #64748b;
font-size: 1.1rem;
}
`}</style>
</section>
);
}
if (error || !policy) {
return (
<section className="policy-section section-padding">
<div className="container">
<div className="row justify-content-center">
<div className="col-12 col-lg-10">
<div className="error-state">
<i className="fa-solid fa-exclamation-circle"></i>
<h2>Unable to Load Policy</h2>
<p>{error?.message || 'The requested policy could not be found.'}</p>
<a href="/support-center" className="btn btn-primary">Return to Support Center</a>
</div>
</div>
</div>
</div>
<style jsx>{`
.policy-section {
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
min-height: 100vh;
padding: 120px 0 80px;
}
.error-state {
text-align: center;
padding: 4rem 2rem;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.error-state i {
font-size: 4rem;
color: #ef4444;
margin-bottom: 1rem;
}
.error-state h2 {
font-size: 2rem;
color: #1e293b;
margin-bottom: 1rem;
}
.error-state p {
color: #64748b;
font-size: 1.1rem;
margin-bottom: 2rem;
}
.btn-primary {
display: inline-block;
padding: 0.875rem 2rem;
background: linear-gradient(135deg, #daa520, #d4af37);
color: #0f172a;
text-decoration: none;
border-radius: 8px;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(218, 165, 32, 0.3);
}
`}</style>
</section>
);
}
return (
<section className="policy-section section-padding">
<div className="container">
<div className="row justify-content-center">
<div className="col-12 col-lg-10">
{/* Policy Header */}
<div className="policy-header">
<h1 className="policy-title">{policy.title}</h1>
<div className="policy-meta">
<p className="policy-updated">
Last Updated: {new Date(policy.last_updated).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
<p className="policy-version">Version {policy.version}</p>
<p className="policy-effective">
Effective Date: {new Date(policy.effective_date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
{policy.description && (
<p className="policy-description">{policy.description}</p>
)}
</div>
{/* Policy Content */}
<div className="policy-content">
{policy.sections.map((section) => (
<div key={section.id} className="policy-section-item">
<h2 className="policy-heading">{section.heading}</h2>
<div className="policy-text" dangerouslySetInnerHTML={{
__html: section.content
// First, handle main sections with (a), (b), etc.
.replace(/\(a\)/g, '<br/><br/><strong>(a)</strong>')
.replace(/\(b\)/g, '<br/><br/><strong>(b)</strong>')
.replace(/\(c\)/g, '<br/><br/><strong>(c)</strong>')
.replace(/\(d\)/g, '<br/><br/><strong>(d)</strong>')
.replace(/\(e\)/g, '<br/><br/><strong>(e)</strong>')
.replace(/\(f\)/g, '<br/><br/><strong>(f)</strong>')
.replace(/\(g\)/g, '<br/><br/><strong>(g)</strong>')
.replace(/\(h\)/g, '<br/><br/><strong>(h)</strong>')
.replace(/\(i\)/g, '<br/><br/><strong>(i)</strong>')
.replace(/\(j\)/g, '<br/><br/><strong>(j)</strong>')
.replace(/\(k\)/g, '<br/><br/><strong>(k)</strong>')
.replace(/\(l\)/g, '<br/><br/><strong>(l)</strong>')
// Handle pipe separators for contact information
.replace(/ \| /g, '<br/><strong>')
.replace(/: /g, ':</strong> ')
// Handle semicolon with parenthesis
.replace(/; \(/g, ';<br/><br/>(')
// Add spacing after periods in long sentences
.replace(/\. ([A-Z])/g, '.<br/><br/>$1')
}} />
</div>
))}
</div>
{/* Contact Section */}
<div className="policy-footer">
<div className="contact-box">
<h3>Questions?</h3>
<p>If you have any questions about this policy, please don't hesitate to contact us.</p>
<a href="/contact-us" className="btn btn-primary">Contact Us</a>
</div>
</div>
</div>
</div>
</div>
<style jsx>{`
.policy-section {
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
min-height: 100vh;
padding: 120px 0 80px;
}
.policy-header {
text-align: center;
margin-bottom: 3rem;
padding-bottom: 2rem;
border-bottom: 2px solid #e5e7eb;
}
.policy-title {
font-size: 2.5rem;
font-weight: 700;
color: #0f172a;
margin-bottom: 1rem;
}
.policy-meta {
display: flex;
justify-content: center;
gap: 2rem;
flex-wrap: wrap;
margin-bottom: 1rem;
}
.policy-meta p {
margin: 0;
}
.policy-updated,
.policy-version,
.policy-effective {
color: #64748b;
font-size: 0.95rem;
font-style: italic;
}
.policy-version {
color: #daa520;
font-weight: 600;
font-style: normal;
}
.policy-description {
color: #475569;
font-size: 1.1rem;
margin-top: 1rem;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.policy-content {
margin-bottom: 3rem;
}
.policy-section-item {
margin-bottom: 2.5rem;
padding: 2rem;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.policy-section-item:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.policy-heading {
font-size: 1.5rem;
font-weight: 600;
color: #1e293b;
margin-bottom: 1rem;
}
.policy-text {
color: #475569;
font-size: 1rem;
line-height: 1.8;
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
.policy-text strong {
color: #1e293b;
font-weight: 600;
}
.policy-text :global(br) {
content: "";
display: block;
margin: 0.5rem 0;
}
.policy-footer {
margin-top: 4rem;
padding-top: 3rem;
border-top: 2px solid #e5e7eb;
}
.contact-box {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
padding: 3rem;
border-radius: 12px;
text-align: center;
color: #ffffff;
}
.contact-box h3 {
font-size: 1.75rem;
margin-bottom: 1rem;
color: #ffffff;
}
.contact-box p {
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 1.5rem;
}
.btn-primary {
display: inline-block;
padding: 0.875rem 2rem;
background: linear-gradient(135deg, #daa520, #d4af37);
color: #0f172a;
text-decoration: none;
border-radius: 8px;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(218, 165, 32, 0.3);
}
@media (max-width: 768px) {
.policy-section {
padding: 100px 0 60px;
}
.policy-title {
font-size: 2rem;
}
.policy-meta {
flex-direction: column;
gap: 0.5rem;
}
.policy-section-item {
padding: 1.5rem;
}
.policy-heading {
font-size: 1.25rem;
}
.contact-box {
padding: 2rem;
}
.contact-box h3 {
font-size: 1.5rem;
}
}
`}</style>
</section>
);
};
const PolicyPage = () => {
return (
<div className="tp-app">
<Header />
<main>
<Suspense fallback={<div>Loading...</div>}>
<PolicyContent />
</Suspense>
</main>
<Footer />
</div>
);
};
export default PolicyPage;