diff --git a/POLICY_API_SETUP.md b/POLICY_API_SETUP.md deleted file mode 100644 index 28e256d6..00000000 --- a/POLICY_API_SETUP.md +++ /dev/null @@ -1,444 +0,0 @@ -# Policy API Setup - Complete Guide - -## šŸŽ‰ Summary - -Successfully created a comprehensive Policy Management System with: -- **3 Policy Types**: Privacy Policy, Terms of Use, Support Policy -- **Professional Enterprise Content**: 13-15 sections per policy with detailed legal and operational information -- **Full CRUD API**: RESTful API endpoints for managing policies -- **Dynamic Frontend**: React-based policy viewer with loading states and error handling - ---- - -## šŸ“‹ What Was Created - -### Backend (Django) - -#### 1. **Models** (`backend/policies/models.py`) -- `Policy`: Main policy document with metadata (type, title, version, effective date) -- `PolicySection`: Individual sections within each policy - -#### 2. **API Views** (`backend/policies/views.py`) -- `PolicyViewSet`: RESTful viewset for policy CRUD operations -- Endpoints support filtering by type -- Retrieve by ID or policy type - -#### 3. **Serializers** (`backend/policies/serializers.py`) -- `PolicySerializer`: Full policy with all sections -- `PolicyListSerializer`: Simplified list view -- `PolicySectionSerializer`: Individual section data - -#### 4. **Admin Interface** (`backend/policies/admin.py`) -- Full admin panel integration -- Inline section editing -- List filters and search functionality - -#### 5. **Management Command** (`backend/policies/management/commands/populate_policies.py`) -- Command: `python manage.py populate_policies` -- Creates/updates all 3 policies with professional content -- 42 total sections across all policies - -### Frontend (Next.js) - -#### 1. **API Service** (`lib/api/policyService.ts`) -- `getPolicies()`: Fetch all policies -- `getPolicyByType(type)`: Fetch specific policy -- `getPolicyById(id)`: Fetch by ID - -#### 2. **React Hook** (`lib/hooks/usePolicy.ts`) -- `usePolicies()`: Hook for all policies -- `usePolicy(type)`: Hook for specific policy -- `usePolicyById(id)`: Hook for policy by ID - -#### 3. **Policy Page** (`app/policy/page.tsx`) -- Dynamic page showing policy content -- Query parameter: `?type=privacy|terms|support` -- Loading and error states -- Responsive design -- Styled with inline styles - -#### 4. **Support Center Integration** (`components/pages/support/SupportCenterHero.tsx`) -- Added 3 new clickable cards: - - Privacy Policy → `/policy?type=privacy` - - Terms of Use → `/policy?type=terms` - - Support Policy → `/policy?type=support` - ---- - -## šŸš€ Setup Complete! - -### What Was Done: - -```bash -# 1. Created migrations -python manage.py makemigrations policies -āœ… Created: policies/migrations/0001_initial.py - -# 2. Applied migrations -python manage.py migrate policies -āœ… Created database tables - -# 3. Populated with content -python manage.py populate_policies -āœ… Privacy Policy: 13 sections -āœ… Terms of Use: 14 sections -āœ… Support Policy: 15 sections -``` - ---- - -## šŸ“” API Endpoints - -### Base URL -``` -http://localhost:8000/api/policies/ -``` - -### Available Endpoints - -#### 1. **List All Policies** -``` -GET /api/policies/ -``` -**Response:** -```json -[ - { - "id": 1, - "type": "privacy", - "title": "Privacy Policy", - "slug": "privacy", - "description": "Our commitment to protecting your privacy and personal data", - "last_updated": "2025-10-08", - "version": "2.1" - }, - ... -] -``` - -#### 2. **Get Specific Policy by Type** -``` -GET /api/policies/privacy/ -GET /api/policies/terms/ -GET /api/policies/support/ -``` -**Response:** -```json -{ - "id": 1, - "type": "privacy", - "title": "Privacy Policy", - "slug": "privacy", - "description": "Our commitment to protecting your privacy and personal data", - "last_updated": "2025-10-08", - "version": "2.1", - "effective_date": "2025-10-08", - "sections": [ - { - "id": 1, - "heading": "1. Introduction and Scope", - "content": "GNX Software Solutions...", - "order": 1 - }, - ... - ] -} -``` - -#### 3. **Get Policy by ID** -``` -GET /api/policies/{id}/ -``` - ---- - -## šŸŽØ Frontend Usage - -### 1. **Direct Navigation** -```typescript -// Link to policies from anywhere -Privacy Policy -Terms of Use -Support Policy -``` - -### 2. **Using the Hook** -```typescript -import { usePolicy } from '@/lib/hooks/usePolicy'; - -function MyComponent() { - const { data: policy, isLoading, error } = usePolicy('privacy'); - - if (isLoading) return
Loading...
; - if (error) return
Error: {error.message}
; - - return ( -
-

{policy.title}

- {policy.sections.map(section => ( -
-

{section.heading}

-

{section.content}

-
- ))} -
- ); -} -``` - -### 3. **Support Center Cards** -The support center hero now has 6 cards: -- **Top Row** (Opens Modals): - - Submit Tickets - - Knowledge Base - - Track Status -- **Bottom Row** (Navigates to Policy Page): - - Privacy Policy - - Terms of Use - - Support Policy - ---- - -## šŸ“ Policy Content Overview - -### Privacy Policy (13 Sections) -1. Introduction and Scope -2. Information We Collect -3. How We Collect Information -4. Use of Information -5. Information Sharing and Disclosure -6. Data Security -7. Data Retention -8. Your Rights and Choices -9. International Data Transfers -10. Cookies and Tracking Technologies -11. Children's Privacy -12. Changes to This Privacy Policy -13. Contact Information - -### Terms of Use (14 Sections) -1. Acceptance of Terms -2. Service Description and Scope -3. User Accounts and Registration -4. Acceptable Use Policy -5. Intellectual Property Rights -6. Service Level Agreements -7. Payment Terms and Billing -8. Term and Termination -9. Disclaimer of Warranties -10. Limitation of Liability -11. Dispute Resolution and Governing Law -12. Modifications to Terms -13. General Provisions -14. Contact Information - -### Support Policy (15 Sections) -1. Support Overview and Commitment -2. Support Coverage and Eligibility -3. Support Channels and Access Methods -4. Business Hours and Availability -5. Priority Levels and Response Times -6. Support Request Submission Requirements -7. Support Scope and Covered Activities -8. Exclusions and Limitations -9. Escalation Procedures -10. Knowledge Base and Self-Service Resources -11. Customer Responsibilities -12. Scheduled Maintenance and Updates -13. Service Level Credits and Remedies -14. Feedback and Continuous Improvement -15. Contact Information and Resources - ---- - -## šŸ”§ Admin Panel - -Access the admin panel to manage policies: - -``` -http://localhost:8000/admin/policies/ -``` - -**Features:** -- āœ… Create/Edit/Delete policies -- āœ… Inline section editing -- āœ… Version management -- āœ… Effective date tracking -- āœ… Active/Inactive toggle -- āœ… Search and filtering - ---- - -## šŸŽÆ Features - -### Backend -- āœ… RESTful API with Django REST Framework -- āœ… Versioned policies -- āœ… Effective date tracking -- āœ… Ordered sections -- āœ… Active/Inactive status -- āœ… Full admin interface -- āœ… Management command for easy population - -### Frontend -- āœ… Dynamic policy loading from API -- āœ… Loading states with spinner -- āœ… Error handling with user-friendly messages -- āœ… Responsive design (mobile-friendly) -- āœ… Version and date display -- āœ… Smooth animations -- āœ… SEO-friendly structure -- āœ… Integration with support center - ---- - -## 🚦 Testing - -### Test the API -```bash -# List all policies -curl http://localhost:8000/api/policies/ - -# Get privacy policy -curl http://localhost:8000/api/policies/privacy/ - -# Get terms of use -curl http://localhost:8000/api/policies/terms/ - -# Get support policy -curl http://localhost:8000/api/policies/support/ -``` - -### Test the Frontend -1. Navigate to: `http://localhost:3000/policy?type=privacy` -2. Navigate to: `http://localhost:3000/policy?type=terms` -3. Navigate to: `http://localhost:3000/policy?type=support` -4. Navigate to: `http://localhost:3000/support-center` -5. Click on any of the 6 feature cards - ---- - -## šŸ“Š Database Schema - -```sql --- Policy Table -CREATE TABLE policies_policy ( - id INTEGER PRIMARY KEY, - type VARCHAR(50) UNIQUE, - title VARCHAR(200), - slug VARCHAR(100) UNIQUE, - description TEXT, - last_updated DATE, - version VARCHAR(20), - is_active BOOLEAN, - effective_date DATE, - created_at TIMESTAMP, - updated_at TIMESTAMP -); - --- PolicySection Table -CREATE TABLE policies_policysection ( - id INTEGER PRIMARY KEY, - policy_id INTEGER REFERENCES policies_policy(id), - heading VARCHAR(300), - content TEXT, - order INTEGER, - is_active BOOLEAN, - created_at TIMESTAMP, - updated_at TIMESTAMP -); -``` - ---- - -## šŸ”„ Updating Policies - -### Via Management Command -```bash -# Re-run to update all policies -cd backend -../venv/bin/python manage.py populate_policies -``` - -### Via Admin Panel -1. Go to `http://localhost:8000/admin/policies/policy/` -2. Click on the policy to edit -3. Modify sections inline -4. Save changes - -### Via API (Programmatic) -```python -from policies.models import Policy, PolicySection - -# Get policy -policy = Policy.objects.get(type='privacy') - -# Add new section -PolicySection.objects.create( - policy=policy, - heading="New Section", - content="Section content...", - order=14 -) -``` - ---- - -## šŸŽØ Customization - -### Styling -The policy page uses inline styles. To customize: -- Edit `/app/policy/page.tsx` -- Modify the ` + + ); + } + + // Standard layout with explicit dimensions + return ( +
+ + +
+ ); +} + +/** + * Usage Examples: + * + * 1. Hero Image (Priority Loading): + * + * + * 2. Service Card Image (Lazy Loading): + * + * + * 3. Background Image (Fill): + * + * + * 4. Logo (High Priority): + * + */ + diff --git a/gnx-react/components/shared/ProtectedImage.tsx b/gnx-react/components/shared/ProtectedImage.tsx new file mode 100644 index 00000000..a7011ba7 --- /dev/null +++ b/gnx-react/components/shared/ProtectedImage.tsx @@ -0,0 +1,57 @@ +'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 ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {alt} e.preventDefault()} + onDragStart={(e) => e.preventDefault()} + style={{ + WebkitUserSelect: 'none', + MozUserSelect: 'none', + msUserSelect: 'none', + userSelect: 'none', + pointerEvents: 'none' + }} + /> + {children} +
+ ); +} + + diff --git a/gnx-react/components/shared/banners/ServiceDetailsBanner.tsx b/gnx-react/components/shared/banners/ServiceDetailsBanner.tsx index 7750658f..acea59a7 100644 --- a/gnx-react/components/shared/banners/ServiceDetailsBanner.tsx +++ b/gnx-react/components/shared/banners/ServiceDetailsBanner.tsx @@ -309,19 +309,6 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => { )} - {service.formatted_price && ( -
-
-
- -
-
- Starting From - {service.formatted_price} -
-
-
- )} {service.featured && (
@@ -355,38 +342,20 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => {
  • - - -
  • -
  • - - - -
  • -
  • -
  • - +
diff --git a/gnx-react/components/shared/layout/footer/Footer.tsx b/gnx-react/components/shared/layout/footer/Footer.tsx index 7379bf59..908658ab 100644 --- a/gnx-react/components/shared/layout/footer/Footer.tsx +++ b/gnx-react/components/shared/layout/footer/Footer.tsx @@ -243,40 +243,19 @@ const Footer = () => {
- - - - - - - - -
diff --git a/gnx-react/components/shared/layout/header/Header.tsx b/gnx-react/components/shared/layout/header/Header.tsx index 6eca117c..df238620 100644 --- a/gnx-react/components/shared/layout/header/Header.tsx +++ b/gnx-react/components/shared/layout/header/Header.tsx @@ -36,7 +36,7 @@ const Header = () => { created_at: service.created_at, updated_at: service.updated_at })) - }; + } as any; } else { console.log('Using static services data. API services:', apiServices.length, 'Services index:', servicesIndex); } @@ -167,30 +167,30 @@ const Header = () => { {/* Desktop Navigation Menu */}
    - {navigationData.map((item, index) => - item.submenu ? ( + {navigationData.map((item) => + item.title === "Support Center" ? null : item.submenu ? (
  • !isMobile && setOpenDropdown(index)} + key={item.id} + onMouseEnter={() => !isMobile && setOpenDropdown(item.id)} onMouseLeave={() => !isMobile && setOpenDropdown(null)} > -
      +
        {item.title === "Services" && servicesLoading ? (
      • Loading services... @@ -218,7 +218,10 @@ const Header = () => {
      ) : ( -
    • +
    • { const [openDropdown, setOpenDropdown] = useState(null); + const [mounted, setMounted] = useState(false); const handleDropdownToggle = (index: any) => { setOpenDropdown((prev) => (prev === index ? null : index)); }; const pathname = usePathname(); + + useEffect(() => { + setMounted(true); + }, []); + useEffect(() => { const parentItems = document.querySelectorAll( ".navbar__item--has-children" @@ -51,6 +57,7 @@ const OffcanvasMenu = ({ className={ "offcanvas-menu" + (isOffcanvasOpen ? " show-offcanvas-menu" : " ") } + suppressHydrationWarning >
@@ -164,7 +171,7 @@ const OffcanvasMenu = ({
  • @@ -173,25 +180,7 @@ const OffcanvasMenu = ({
  • - - -
  • -
  • - - - -
  • -
  • - diff --git a/gnx-react/components/shared/seo/StructuredData.tsx b/gnx-react/components/shared/seo/StructuredData.tsx new file mode 100644 index 00000000..0a2509ca --- /dev/null +++ b/gnx-react/components/shared/seo/StructuredData.tsx @@ -0,0 +1,378 @@ +'use client'; + +import Script from 'next/script'; +import { SITE_CONFIG } from '@/lib/seo/metadata'; + +// Organization Schema +export function OrganizationSchema() { + const schema = { + '@context': 'https://schema.org', + '@type': 'Organization', + name: SITE_CONFIG.name, + legalName: `${SITE_CONFIG.name} LLC`, + url: SITE_CONFIG.url, + logo: `${SITE_CONFIG.url}/images/logo.png`, + foundingDate: SITE_CONFIG.foundedYear.toString(), + description: SITE_CONFIG.description, + email: SITE_CONFIG.email, + telephone: SITE_CONFIG.phone, + address: { + '@type': 'PostalAddress', + streetAddress: SITE_CONFIG.address.street, + addressLocality: SITE_CONFIG.address.city, + addressRegion: SITE_CONFIG.address.state, + postalCode: SITE_CONFIG.address.zip, + addressCountry: SITE_CONFIG.address.country, + }, + sameAs: [ + SITE_CONFIG.social.linkedin, + SITE_CONFIG.social.github, + ], + contactPoint: [ + { + '@type': 'ContactPoint', + telephone: SITE_CONFIG.phone, + contactType: 'Customer Service', + email: SITE_CONFIG.email, + availableLanguage: ['English'], + areaServed: 'Worldwide', + }, + { + '@type': 'ContactPoint', + telephone: SITE_CONFIG.phone, + contactType: 'Sales', + email: `sales@${SITE_CONFIG.email.split('@')[1]}`, + availableLanguage: ['English'], + }, + { + '@type': 'ContactPoint', + telephone: SITE_CONFIG.phone, + contactType: 'Technical Support', + email: `support@${SITE_CONFIG.email.split('@')[1]}`, + availableLanguage: ['English'], + areaServed: 'Worldwide', + }, + ], + }; + + return ( +