This commit is contained in:
Iliyan Angelov
2025-11-24 03:52:08 +02:00
parent dfcaebaf8c
commit 366f28677a
18241 changed files with 865352 additions and 567 deletions

88
backEnd/about/README.md Normal file
View File

@@ -0,0 +1,88 @@
# About Us API
This Django app provides API endpoints for managing about us page content.
## Models
### AboutBanner
- Main banner section with title, description, badge, CTA button, and image
- Related models: AboutStat, AboutSocialLink
### AboutService
- Service section with company information and features
- Related models: AboutFeature
### AboutProcess
- Development process section with methodology and steps
- Related models: AboutProcessStep
### AboutJourney
- Company journey section with milestones
- Related models: AboutMilestone
## API Endpoints
### Combined Endpoint
- `GET /api/about/page/` - Get all about page data in one request
### Individual Endpoints
#### Banner
- `GET /api/about/banner/` - List all active banners
- `GET /api/about/banner/{id}/` - Get specific banner
#### Service
- `GET /api/about/service/` - List all active services
- `GET /api/about/service/{id}/` - Get specific service
#### Process
- `GET /api/about/process/` - List all active processes
- `GET /api/about/process/{id}/` - Get specific process
#### Journey
- `GET /api/about/journey/` - List all active journeys
- `GET /api/about/journey/{id}/` - Get specific journey
## Management Commands
### Populate Sample Data
```bash
python manage.py populate_about_data
```
This command creates sample data for all about us sections.
## Frontend Integration
The frontend uses the following files:
- `lib/api/aboutService.ts` - API service for fetching data
- `lib/hooks/useAbout.ts` - React hooks for data management
- Components in `components/pages/about/` - Updated to use API data
## Usage Example
```typescript
import { useAbout } from '@/lib/hooks/useAbout';
const AboutPage = () => {
const { data, loading, error } = useAbout();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>{data?.banner.title}</h1>
<p>{data?.banner.description}</p>
{/* Render other sections */}
</div>
);
};
```
## Admin Interface
All models are available in the Django admin interface for easy content management:
- Navigate to `/admin/` after creating a superuser
- Manage about us content through the admin interface
- Upload images and manage relationships between models

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

109
backEnd/about/admin.py Normal file
View File

@@ -0,0 +1,109 @@
from django.contrib import admin
from .models import (
AboutBanner, AboutStat, AboutSocialLink,
AboutService, AboutFeature,
AboutProcess, AboutProcessStep,
AboutJourney, AboutMilestone
)
class AboutStatInline(admin.TabularInline):
model = AboutStat
extra = 0
ordering = ['order']
class AboutSocialLinkInline(admin.TabularInline):
model = AboutSocialLink
extra = 0
ordering = ['order']
@admin.register(AboutBanner)
class AboutBannerAdmin(admin.ModelAdmin):
list_display = ['title', 'is_active', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['title', 'description']
inlines = [AboutStatInline, AboutSocialLinkInline]
readonly_fields = ['created_at', 'updated_at']
class AboutFeatureInline(admin.TabularInline):
model = AboutFeature
extra = 0
ordering = ['order']
@admin.register(AboutService)
class AboutServiceAdmin(admin.ModelAdmin):
list_display = ['title', 'is_active', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['title', 'description']
inlines = [AboutFeatureInline]
readonly_fields = ['created_at', 'updated_at']
class AboutProcessStepInline(admin.TabularInline):
model = AboutProcessStep
extra = 0
ordering = ['order']
@admin.register(AboutProcess)
class AboutProcessAdmin(admin.ModelAdmin):
list_display = ['title', 'is_active', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['title', 'description']
inlines = [AboutProcessStepInline]
readonly_fields = ['created_at', 'updated_at']
class AboutMilestoneInline(admin.TabularInline):
model = AboutMilestone
extra = 0
ordering = ['order']
@admin.register(AboutJourney)
class AboutJourneyAdmin(admin.ModelAdmin):
list_display = ['title', 'is_active', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['title', 'description']
inlines = [AboutMilestoneInline]
readonly_fields = ['created_at', 'updated_at']
# Register individual models for direct access
@admin.register(AboutStat)
class AboutStatAdmin(admin.ModelAdmin):
list_display = ['banner', 'number', 'label', 'order']
list_filter = ['banner']
ordering = ['banner', 'order']
@admin.register(AboutSocialLink)
class AboutSocialLinkAdmin(admin.ModelAdmin):
list_display = ['banner', 'platform', 'url', 'order']
list_filter = ['banner', 'platform']
ordering = ['banner', 'order']
@admin.register(AboutFeature)
class AboutFeatureAdmin(admin.ModelAdmin):
list_display = ['service', 'title', 'order']
list_filter = ['service']
ordering = ['service', 'order']
@admin.register(AboutProcessStep)
class AboutProcessStepAdmin(admin.ModelAdmin):
list_display = ['process', 'step_number', 'title', 'order']
list_filter = ['process']
ordering = ['process', 'order']
@admin.register(AboutMilestone)
class AboutMilestoneAdmin(admin.ModelAdmin):
list_display = ['journey', 'year', 'title', 'order']
list_filter = ['journey']
ordering = ['journey', 'order']

6
backEnd/about/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AboutConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'about'

View File

View File

@@ -0,0 +1,515 @@
"""
Management command to import enterprise-grade sample data for About Us
with images downloaded from Unsplash.
"""
import urllib.request
import urllib.parse
import json
from io import BytesIO
from django.core.management.base import BaseCommand
from django.core.files.base import ContentFile
from django.db import transaction
from about.models import (
AboutBanner, AboutStat, AboutSocialLink,
AboutService, AboutFeature,
AboutProcess, AboutProcessStep,
AboutJourney, AboutMilestone
)
class Command(BaseCommand):
help = 'Import enterprise-grade sample data for About Us with images from Unsplash'
def add_arguments(self, parser):
parser.add_argument(
'--unsplash-key',
type=str,
help='Unsplash API access key (optional, uses Unsplash Source API if not provided)',
)
parser.add_argument(
'--width',
type=int,
default=1200,
help='Image width in pixels (default: 1200)',
)
parser.add_argument(
'--height',
type=int,
default=800,
help='Image height in pixels (default: 800)',
)
parser.add_argument(
'--update-existing',
action='store_true',
help='Update existing about data instead of skipping it',
)
def download_image_from_unsplash(self, keyword, width=1200, height=800, api_key=None):
"""
Download an image from Unsplash based on keyword.
Args:
keyword: Search keyword for the image
width: Image width in pixels
height: Image height in pixels
api_key: Optional Unsplash API access key
Returns:
BytesIO object containing the image data, or None if download fails
"""
try:
if api_key:
# Use Unsplash API (requires API key)
url = "https://api.unsplash.com/photos/random"
params = {
'query': keyword,
'orientation': 'landscape',
'w': width,
'h': height,
'client_id': api_key
}
query_string = urllib.parse.urlencode(params)
full_url = f"{url}?{query_string}"
req = urllib.request.Request(full_url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode())
image_url = data['urls']['regular']
else:
# Use Unsplash's direct image service (no key required)
# Using curated enterprise/business images
image_url = f"https://images.unsplash.com/photo-1552664730-d307ca884978?w={width}&h={height}&fit=crop"
# Download the actual image
req = urllib.request.Request(image_url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')
with urllib.request.urlopen(req, timeout=30) as img_response:
image_data = BytesIO(img_response.read())
image_data.seek(0)
return image_data
except Exception as e:
self.stdout.write(
self.style.WARNING(f'Failed to download image for "{keyword}": {str(e)}')
)
return None
def handle(self, *args, **options):
unsplash_key = options.get('unsplash_key')
width = options.get('width', 1200)
height = options.get('height', 800)
update_existing = options.get('update_existing', False)
self.stdout.write(self.style.SUCCESS('Starting enterprise About Us data import...'))
with transaction.atomic():
# Clear existing data if updating
if update_existing:
self.stdout.write('Clearing existing about data...')
AboutBanner.objects.all().delete()
AboutService.objects.all().delete()
AboutProcess.objects.all().delete()
AboutJourney.objects.all().delete()
# Create About Banner
banner_data = {
'title': 'Enterprise Software Solutions for Mission-Critical Industries',
'subtitle': 'GNX Soft Ltd - Your Trusted Enterprise Technology Partner',
'description': 'GNX Soft Ltd is a leading Bulgarian enterprise software company delivering cutting-edge technology solutions to mission-critical industries worldwide. We empower organizations in Defense & Aerospace, Healthcare & Medical, Telecommunication, Banking, Public Sector, E-commerce, Food & Beverages, and Oil & Energy sectors with innovative, secure, and scalable software platforms that drive digital transformation and operational excellence. Our commitment to EU-based infrastructure, privacy-by-design principles, and defense-grade security makes us the preferred technology partner for organizations that demand the highest levels of reliability, compliance, and data protection.',
'badge_text': 'Enterprise Software Solutions',
'badge_icon': 'fa-solid fa-building',
'cta_text': 'Explore Enterprise Solutions',
'cta_link': 'services',
'cta_icon': 'fa-solid fa-arrow-trend-up',
'is_active': True
}
banner, created = AboutBanner.objects.get_or_create(
title=banner_data['title'],
defaults=banner_data
)
if not created and update_existing:
for key, value in banner_data.items():
setattr(banner, key, value)
banner.save()
if created or update_existing:
self.stdout.write(f'{"Created" if created else "Updated"} banner: {banner.title}')
# Download banner image
self.stdout.write('Downloading banner image...')
image_data = self.download_image_from_unsplash(
'enterprise business technology office',
width=width,
height=height,
api_key=unsplash_key
)
if image_data:
try:
filename = 'about-banner.jpg'
banner.image.save(
filename,
ContentFile(image_data.read()),
save=True
)
self.stdout.write(self.style.SUCCESS(' ✓ Banner image saved'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Failed to save banner image: {str(e)}'))
# Clear and recreate stats
if update_existing:
AboutStat.objects.filter(banner=banner).delete()
# Create Banner Stats
stats_data = [
{'number': '8', 'label': 'Industry Verticals', 'order': 1},
{'number': '99.9%', 'label': 'Uptime SLA', 'order': 2},
{'number': '24/7', 'label': 'Enterprise Support', 'order': 3},
{'number': '500+', 'label': 'Enterprise Clients', 'order': 4},
{'number': '2020', 'label': 'Founded', 'order': 5},
]
for stat_data in stats_data:
AboutStat.objects.get_or_create(
banner=banner,
label=stat_data['label'],
defaults=stat_data
)
# Clear and recreate social links
if update_existing:
AboutSocialLink.objects.filter(banner=banner).delete()
# Create Social Links
social_links_data = [
{
'platform': 'LinkedIn',
'url': 'https://www.linkedin.com/company/gnxsoft',
'icon': 'fa-brands fa-linkedin-in',
'aria_label': 'Connect with GNX Soft on LinkedIn',
'order': 1
},
{
'platform': 'GitHub',
'url': 'https://github.com/gnxsoft',
'icon': 'fa-brands fa-github',
'aria_label': 'Follow GNX Soft on GitHub',
'order': 2
},
{
'platform': 'Twitter',
'url': 'https://twitter.com/gnxsoft',
'icon': 'fa-brands fa-twitter',
'aria_label': 'Follow GNX Soft on Twitter',
'order': 3
},
{
'platform': 'Email',
'url': 'mailto:info@gnxsoft.com',
'icon': 'fa-solid fa-envelope',
'aria_label': 'Contact GNX Soft via email',
'order': 4
},
]
for social_data in social_links_data:
AboutSocialLink.objects.get_or_create(
banner=banner,
platform=social_data['platform'],
defaults=social_data
)
# Create About Service
service_data = {
'title': 'Enterprise Technology Excellence Across Critical Industries',
'subtitle': 'About GNX Soft Ltd',
'description': 'Founded in 2020 and headquartered in Burgas, Bulgaria, GNX Soft Ltd is a premier enterprise software development company specializing in mission-critical solutions for highly regulated industries. Our expert team of 50+ engineers, architects, and consultants delivers secure, scalable, and compliant software solutions to Defense & Aerospace, Healthcare & Medical, Telecommunication, Banking & Finance, Public Sector, E-commerce, Food & Beverages, and Oil & Energy sectors. With EU-based infrastructure spanning Bulgaria, Germany, and Netherlands, we provide enterprise-grade solutions that meet the highest security and regulatory standards including GDPR, HIPAA, PCI-DSS, and industry-specific compliance requirements. Our commitment to privacy-by-design, defense-grade security, and continuous innovation positions us as a trusted technology partner for Fortune 500 companies and government organizations worldwide.',
'badge_text': 'About GNX Soft Ltd',
'badge_icon': 'fa-solid fa-users',
'cta_text': 'Explore Our Solutions',
'cta_link': 'services',
'is_active': True
}
service, created = AboutService.objects.get_or_create(
title=service_data['title'],
defaults=service_data
)
if not created and update_existing:
for key, value in service_data.items():
setattr(service, key, value)
service.save()
if created or update_existing:
self.stdout.write(f'{"Created" if created else "Updated"} service: {service.title}')
# Download service image
self.stdout.write('Downloading service image...')
image_data = self.download_image_from_unsplash(
'enterprise team collaboration technology',
width=width,
height=height,
api_key=unsplash_key
)
if image_data:
try:
filename = 'about-service.jpg'
service.image.save(
filename,
ContentFile(image_data.read()),
save=True
)
self.stdout.write(self.style.SUCCESS(' ✓ Service image saved'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Failed to save service image: {str(e)}'))
# Clear and recreate features
if update_existing:
AboutFeature.objects.filter(service=service).delete()
# Create Service Features
features_data = [
{
'title': 'EU-Based Company',
'description': 'Headquartered in Burgas, Bulgaria with EU-wide presence',
'icon': 'fa-solid fa-building',
'order': 1
},
{
'title': 'EU Infrastructure',
'description': 'Data centers in Bulgaria, Germany, and Netherlands',
'icon': 'fa-solid fa-server',
'order': 2
},
{
'title': '8 Industry Verticals',
'description': 'Specialized expertise across critical sectors',
'icon': 'fa-solid fa-industry',
'order': 3
},
{
'title': '24/7 Enterprise Support',
'description': 'Round-the-clock support with SLA guarantees',
'icon': 'fa-solid fa-headset',
'order': 4
},
{
'title': '500+ Enterprise Clients',
'description': 'Trusted by Fortune 500 and government organizations',
'icon': 'fa-solid fa-users',
'order': 5
},
{
'title': 'Defense-Grade Security',
'description': 'Bank-level security with compliance certifications',
'icon': 'fa-solid fa-shield-halved',
'order': 6
},
]
for feature_data in features_data:
AboutFeature.objects.get_or_create(
service=service,
title=feature_data['title'],
defaults=feature_data
)
# Create About Process
process_data = {
'title': 'Enterprise-Grade Development Methodology',
'subtitle': 'Our Methodology',
'description': 'GNX Soft Ltd employs a proven enterprise development methodology that combines agile practices with defense-grade security, regulatory compliance, and enterprise scalability. We follow industry best practices including DevOps, CI/CD, microservices architecture, and privacy-by-design principles to deliver robust, secure, and compliant solutions for highly regulated industries. Every project undergoes rigorous security assessments, Data Protection Impact Assessments (DPIAs), penetration testing, and compliance verification to meet the strictest industry standards including GDPR, HIPAA, PCI-DSS, SOC 2, and ISO 27001. Our methodology emphasizes continuous integration, automated testing, infrastructure as code, and comprehensive documentation to ensure long-term maintainability and scalability.',
'badge_text': 'Our Methodology',
'badge_icon': 'fa-solid fa-cogs',
'cta_text': 'View Our Services',
'cta_link': 'services',
'is_active': True
}
process, created = AboutProcess.objects.get_or_create(
title=process_data['title'],
defaults=process_data
)
if not created and update_existing:
for key, value in process_data.items():
setattr(process, key, value)
process.save()
if created or update_existing:
self.stdout.write(f'{"Created" if created else "Updated"} process: {process.title}')
# Download process image
self.stdout.write('Downloading process image...')
image_data = self.download_image_from_unsplash(
'enterprise development methodology process',
width=width,
height=height,
api_key=unsplash_key
)
if image_data:
try:
filename = 'about-process.jpg'
process.image.save(
filename,
ContentFile(image_data.read()),
save=True
)
self.stdout.write(self.style.SUCCESS(' ✓ Process image saved'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Failed to save process image: {str(e)}'))
# Clear and recreate process steps
if update_existing:
AboutProcessStep.objects.filter(process=process).delete()
# Create Process Steps
steps_data = [
{
'step_number': '01',
'title': 'Discovery & Compliance Assessment',
'description': 'Requirements analysis, regulatory assessment, DPIA, and security planning',
'order': 1
},
{
'step_number': '02',
'title': 'Secure Development & Architecture',
'description': 'Privacy-by-design, secure coding practices, microservices architecture, and continuous testing',
'order': 2
},
{
'step_number': '03',
'title': 'Deployment & Integration',
'description': 'EU infrastructure deployment, system integration, and performance optimization',
'order': 3
},
{
'step_number': '04',
'title': 'Security Audit & Compliance',
'description': 'Penetration testing, security audits, compliance verification, and certification',
'order': 4
},
{
'step_number': '05',
'title': 'Support & Continuous Monitoring',
'description': '24/7 enterprise support, monitoring, breach response, and continuous improvement',
'order': 5
},
]
for step_data in steps_data:
AboutProcessStep.objects.get_or_create(
process=process,
step_number=step_data['step_number'],
defaults=step_data
)
# Create About Journey
journey_data = {
'title': 'Building Enterprise Excellence Since 2020',
'subtitle': 'Our Journey',
'description': 'Founded in 2020 in Burgas, Bulgaria, GNX Soft Ltd was established with a clear mission: to deliver world-class enterprise software solutions for mission-critical industries while maintaining the highest standards of security, compliance, and data protection. From our inception, we focused exclusively on enterprise clients in highly regulated sectors including Defense & Aerospace, Healthcare & Medical, Banking, Public Sector, Telecommunication, E-commerce, Food & Beverages, and Oil & Energy. Our commitment to EU-based infrastructure, privacy-by-design principles, and defense-grade security has positioned us as a trusted technology partner for organizations that demand the highest levels of security and regulatory adherence. Today, we serve 500+ enterprise clients across 8 industry verticals, with a team of 50+ experts and EU-wide infrastructure supporting mission-critical operations 24/7.',
'badge_text': 'Our Journey',
'badge_icon': 'fa-solid fa-rocket',
'cta_text': 'Explore Solutions',
'cta_link': 'services',
'is_active': True
}
journey, created = AboutJourney.objects.get_or_create(
title=journey_data['title'],
defaults=journey_data
)
if not created and update_existing:
for key, value in journey_data.items():
setattr(journey, key, value)
journey.save()
if created or update_existing:
self.stdout.write(f'{"Created" if created else "Updated"} journey: {journey.title}')
# Download journey image
self.stdout.write('Downloading journey image...')
image_data = self.download_image_from_unsplash(
'enterprise growth journey success',
width=width,
height=height,
api_key=unsplash_key
)
if image_data:
try:
filename = 'about-journey.jpg'
journey.image.save(
filename,
ContentFile(image_data.read()),
save=True
)
self.stdout.write(self.style.SUCCESS(' ✓ Journey image saved'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Failed to save journey image: {str(e)}'))
# Clear and recreate milestones
if update_existing:
AboutMilestone.objects.filter(journey=journey).delete()
# Create Journey Milestones
milestones_data = [
{
'year': '2020',
'title': 'Company Founded',
'description': 'GNX Soft Ltd established in Burgas, Bulgaria with focus on enterprise solutions',
'order': 1
},
{
'year': '2021',
'title': 'Industry Specialization',
'description': 'Specialized in 8 mission-critical industries with first enterprise clients',
'order': 2
},
{
'year': '2022',
'title': 'EU Infrastructure Expansion',
'description': 'Deployed EU-wide data centers across Bulgaria, Germany, and Netherlands',
'order': 3
},
{
'year': '2023',
'title': 'Enterprise Growth',
'description': 'Reached 300+ enterprise clients with 50+ expert team members',
'order': 4
},
{
'year': '2024',
'title': 'Industry Leadership',
'description': 'Recognized as leading Bulgarian enterprise software provider with 500+ clients',
'order': 5
},
{
'year': '2025',
'title': 'Global Expansion',
'description': 'Expanding services globally while maintaining EU-based infrastructure and compliance',
'order': 6
},
]
for milestone_data in milestones_data:
AboutMilestone.objects.get_or_create(
journey=journey,
year=milestone_data['year'],
defaults=milestone_data
)
self.stdout.write(
self.style.SUCCESS('\n✓ Successfully imported enterprise About Us data!')
)
if not unsplash_key:
self.stdout.write(
self.style.WARNING(
'\nNote: Using Unsplash Source API (no key required). '
'For better reliability, consider using --unsplash-key with an API key from https://unsplash.com/developers'
)
)

View File

@@ -0,0 +1,239 @@
from django.core.management.base import BaseCommand
from about.models import (
AboutBanner, AboutStat, AboutSocialLink,
AboutService, AboutFeature,
AboutProcess, AboutProcessStep,
AboutJourney, AboutMilestone
)
class Command(BaseCommand):
help = 'Populate the database with sample about us data'
def handle(self, *args, **options):
self.stdout.write('Creating sample about us data...')
# Clear existing data first
self.stdout.write('Clearing existing about data...')
AboutBanner.objects.all().delete()
AboutService.objects.all().delete()
AboutProcess.objects.all().delete()
AboutJourney.objects.all().delete()
# Create About Banner
banner, created = AboutBanner.objects.get_or_create(
title="Enterprise Software Solutions for Mission-Critical Industries",
defaults={
'subtitle': "GNX Soft Ltd - Your Trusted Enterprise Technology Partner",
'description': "GNX Soft Ltd is a leading Bulgarian enterprise software company delivering cutting-edge technology solutions to mission-critical industries worldwide. We empower organizations in Defense & Aerospace, Healthcare & Medical, Telecommunication, Banking, Public Sector, E-commerce, Food & Beverages, and Oil & Energy sectors with innovative, secure, and scalable software platforms that drive digital transformation and operational excellence.",
'badge_text': "Enterprise Software Solutions",
'badge_icon': "fa-solid fa-building",
'cta_text': "Explore Enterprise Solutions",
'cta_link': "services",
'cta_icon': "fa-solid fa-arrow-trend-up",
'is_active': True
}
)
if created:
self.stdout.write(f'Created banner: {banner.title}')
# Create Banner Stats
stats_data = [
{'number': '8', 'label': 'Industry Verticals', 'order': 1},
{'number': '99.9%', 'label': 'Uptime SLA', 'order': 2},
{'number': '24/7', 'label': 'Enterprise Support', 'order': 3},
{'number': '2020', 'label': 'Founded', 'order': 4},
]
for stat_data in stats_data:
AboutStat.objects.create(banner=banner, **stat_data)
# Create Social Links
social_links_data = [
{
'platform': 'LinkedIn',
'url': 'https://www.linkedin.com/company/gnxsoft',
'icon': 'fa-brands fa-linkedin-in',
'aria_label': 'Connect with GNX Soft on LinkedIn',
'order': 1
},
{
'platform': 'GitHub',
'url': 'https://github.com/gnxsoft',
'icon': 'fa-brands fa-github',
'aria_label': 'Follow GNX Soft on GitHub',
'order': 2
},
{
'platform': 'Twitter',
'url': 'https://twitter.com/gnxsoft',
'icon': 'fa-brands fa-twitter',
'aria_label': 'Follow GNX Soft on Twitter',
'order': 3
},
{
'platform': 'Email',
'url': 'mailto:info@gnxsoft.com',
'icon': 'fa-solid fa-envelope',
'aria_label': 'Contact GNX Soft via email',
'order': 4
},
]
for social_data in social_links_data:
AboutSocialLink.objects.create(banner=banner, **social_data)
# Create About Service
service, created = AboutService.objects.get_or_create(
title="Enterprise Technology Excellence Across Critical Industries",
defaults={
'subtitle': "About GNX Soft Ltd",
'description': "Founded in 2020 and headquartered in Burgas, Bulgaria, GNX Soft Ltd is a premier enterprise software development company specializing in mission-critical solutions for highly regulated industries. Our expert team delivers secure, scalable, and compliant software solutions to Defense & Aerospace, Healthcare & Medical, Telecommunication, Banking & Finance, Public Sector, E-commerce, Food & Beverages, and Oil & Energy sectors. With EU-based infrastructure, we provide enterprise-grade solutions that meet the highest security and regulatory standards.",
'badge_text': "About GNX Soft Ltd",
'badge_icon': "fa-solid fa-users",
'cta_text': "Explore Our Solutions",
'cta_link': "services",
'is_active': True
}
)
if created:
self.stdout.write(f'Created service: {service.title}')
# Create Service Features
features_data = [
{
'title': 'EU-Based Company',
'description': 'Headquartered in Bulgaria',
'icon': 'fa-solid fa-building',
'order': 1
},
{
'title': 'EU Infrastructure',
'description': 'Bulgaria, Germany, Netherlands',
'icon': 'fa-solid fa-server',
'order': 2
},
{
'title': '8 Industry Verticals',
'description': 'Specialized Expertise',
'icon': 'fa-solid fa-industry',
'order': 3
},
{
'title': '24/7 Support',
'description': 'Enterprise Support Team',
'icon': 'fa-solid fa-headset',
'order': 4
},
]
for feature_data in features_data:
AboutFeature.objects.create(service=service, **feature_data)
# Create About Process
process, created = AboutProcess.objects.get_or_create(
title="Enterprise-Grade Development Methodology",
defaults={
'subtitle': "Our Methodology",
'description': "GNX Soft Ltd employs a proven enterprise development methodology that combines agile practices with defense-grade security, regulatory compliance, and enterprise scalability. We follow industry best practices including DevOps, CI/CD, microservices architecture, and privacy-by-design principles to deliver robust, secure, and compliant solutions for highly regulated industries. Every project undergoes rigorous security assessments, Data Protection Impact Assessments (DPIAs), and compliance verification to meet the strictest industry standards.",
'badge_text': "Our Methodology",
'badge_icon': "fa-solid fa-cogs",
'cta_text': "View Our Services",
'cta_link': "services",
'is_active': True
}
)
if created:
self.stdout.write(f'Created process: {process.title}')
# Create Process Steps
steps_data = [
{
'step_number': '01',
'title': 'Discovery & Compliance',
'description': 'Requirements analysis, regulatory assessment, and DPIA',
'order': 1
},
{
'step_number': '02',
'title': 'Secure Development',
'description': 'Privacy-by-design, secure coding, continuous testing',
'order': 2
},
{
'step_number': '03',
'title': 'Deployment & Integration',
'description': 'EU infrastructure deployment and system integration',
'order': 3
},
{
'step_number': '04',
'title': 'Support & Monitoring',
'description': '24/7 enterprise support with breach response',
'order': 4
},
]
for step_data in steps_data:
AboutProcessStep.objects.create(process=process, **step_data)
# Create About Journey
journey, created = AboutJourney.objects.get_or_create(
title="Building Enterprise Excellence Since 2020",
defaults={
'subtitle': "Our Journey",
'description': "Founded in 2020 in Burgas, Bulgaria, GNX Soft Ltd was established with a clear mission: to deliver world-class enterprise software solutions for mission-critical industries while maintaining the highest standards of security, compliance, and data protection. From our inception, we focused exclusively on enterprise clients in highly regulated sectors including Defense & Aerospace, Healthcare & Medical, Banking, Public Sector, Telecommunication, E-commerce, Food & Beverages, and Oil & Energy. Our commitment to EU-based infrastructure and privacy-by-design principles has positioned us as a trusted technology partner for organizations that demand the highest levels of security and regulatory adherence.",
'badge_text': "Our Journey",
'badge_icon': "fa-solid fa-rocket",
'cta_text': "Explore Solutions",
'cta_link': "services",
'is_active': True
}
)
if created:
self.stdout.write(f'Created journey: {journey.title}')
# Create Journey Milestones
milestones_data = [
{
'year': '2020',
'title': 'Company Founded',
'description': 'GNX Soft Ltd established in Burgas, Bulgaria',
'order': 1
},
{
'year': '2021',
'title': 'Industry Focus',
'description': 'Specialized in 8 mission-critical industries',
'order': 2
},
{
'year': '2022',
'title': 'Industry Expansion',
'description': 'Expanded to 8 specialized industry verticals',
'order': 3
},
{
'year': '2023',
'title': 'EU Infrastructure',
'description': 'Deployed EU-wide data centers across 3 countries',
'order': 4
},
{
'year': '2024',
'title': 'Enterprise Leader',
'description': 'Recognized as leading Bulgarian enterprise software provider',
'order': 5
},
]
for milestone_data in milestones_data:
AboutMilestone.objects.create(journey=journey, **milestone_data)
self.stdout.write(
self.style.SUCCESS('Successfully populated about us data!')
)

View File

@@ -0,0 +1,180 @@
# Generated by Django 4.2.7 on 2025-09-25 16:40
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AboutBanner',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('subtitle', models.CharField(blank=True, max_length=100)),
('description', models.TextField()),
('badge_text', models.CharField(default='Enterprise Software Solutions', max_length=100)),
('badge_icon', models.CharField(default='fa-solid fa-building', max_length=50)),
('cta_text', models.CharField(default='Discover Enterprise Solutions', max_length=100)),
('cta_link', models.CharField(default='services', max_length=100)),
('cta_icon', models.CharField(default='fa-solid fa-arrow-trend-up', max_length=50)),
('image', models.ImageField(blank=True, null=True, upload_to='about/banner/')),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'About Banner',
'verbose_name_plural': 'About Banners',
},
),
migrations.CreateModel(
name='AboutJourney',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('subtitle', models.CharField(blank=True, max_length=100)),
('description', models.TextField()),
('badge_text', models.CharField(default='Our Journey', max_length=100)),
('badge_icon', models.CharField(default='fa-solid fa-rocket', max_length=50)),
('image', models.ImageField(blank=True, null=True, upload_to='about/journey/')),
('cta_text', models.CharField(default='Explore Solutions', max_length=100)),
('cta_link', models.CharField(default='services', max_length=100)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'About Journey',
'verbose_name_plural': 'About Journeys',
},
),
migrations.CreateModel(
name='AboutProcess',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('subtitle', models.CharField(blank=True, max_length=100)),
('description', models.TextField()),
('badge_text', models.CharField(default='Our Methodology', max_length=100)),
('badge_icon', models.CharField(default='fa-solid fa-cogs', max_length=50)),
('image', models.ImageField(blank=True, null=True, upload_to='about/process/')),
('cta_text', models.CharField(default='View Our Services', max_length=100)),
('cta_link', models.CharField(default='service-single', max_length=100)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'About Process',
'verbose_name_plural': 'About Processes',
},
),
migrations.CreateModel(
name='AboutService',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('subtitle', models.CharField(blank=True, max_length=100)),
('description', models.TextField()),
('badge_text', models.CharField(default='About Our Company', max_length=100)),
('badge_icon', models.CharField(default='fa-solid fa-users', max_length=50)),
('image', models.ImageField(blank=True, null=True, upload_to='about/services/')),
('cta_text', models.CharField(default='Explore Our Solutions', max_length=100)),
('cta_link', models.CharField(default='service-single', max_length=100)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'About Service',
'verbose_name_plural': 'About Services',
},
),
migrations.CreateModel(
name='AboutStat',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.CharField(max_length=20)),
('label', models.CharField(max_length=100)),
('order', models.PositiveIntegerField(default=0)),
('banner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stats', to='about.aboutbanner')),
],
options={
'verbose_name': 'About Statistic',
'verbose_name_plural': 'About Statistics',
'ordering': ['order'],
},
),
migrations.CreateModel(
name='AboutSocialLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('platform', models.CharField(max_length=50)),
('url', models.URLField()),
('icon', models.CharField(max_length=50)),
('aria_label', models.CharField(max_length=100)),
('order', models.PositiveIntegerField(default=0)),
('banner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='social_links', to='about.aboutbanner')),
],
options={
'verbose_name': 'About Social Link',
'verbose_name_plural': 'About Social Links',
'ordering': ['order'],
},
),
migrations.CreateModel(
name='AboutProcessStep',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('step_number', models.CharField(max_length=10)),
('title', models.CharField(max_length=100)),
('description', models.CharField(max_length=200)),
('order', models.PositiveIntegerField(default=0)),
('process', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='about.aboutprocess')),
],
options={
'verbose_name': 'About Process Step',
'verbose_name_plural': 'About Process Steps',
'ordering': ['order'],
},
),
migrations.CreateModel(
name='AboutMilestone',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('year', models.CharField(max_length=10)),
('title', models.CharField(max_length=100)),
('description', models.CharField(max_length=200)),
('order', models.PositiveIntegerField(default=0)),
('journey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='milestones', to='about.aboutjourney')),
],
options={
'verbose_name': 'About Milestone',
'verbose_name_plural': 'About Milestones',
'ordering': ['order'],
},
),
migrations.CreateModel(
name='AboutFeature',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('description', models.CharField(max_length=200)),
('icon', models.CharField(max_length=50)),
('order', models.PositiveIntegerField(default=0)),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='about.aboutservice')),
],
options={
'verbose_name': 'About Feature',
'verbose_name_plural': 'About Features',
'ordering': ['order'],
},
),
]

View File

176
backEnd/about/models.py Normal file
View File

@@ -0,0 +1,176 @@
from django.db import models
from django.utils import timezone
class AboutBanner(models.Model):
"""Model for About Us banner section"""
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=100, blank=True)
description = models.TextField()
badge_text = models.CharField(max_length=100, default="Enterprise Software Solutions")
badge_icon = models.CharField(max_length=50, default="fa-solid fa-building")
cta_text = models.CharField(max_length=100, default="Discover Enterprise Solutions")
cta_link = models.CharField(max_length=100, default="services")
cta_icon = models.CharField(max_length=50, default="fa-solid fa-arrow-trend-up")
image = models.ImageField(upload_to='about/banner/', null=True, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "About Banner"
verbose_name_plural = "About Banners"
def __str__(self):
return self.title
class AboutStat(models.Model):
"""Model for About Us statistics"""
banner = models.ForeignKey(AboutBanner, on_delete=models.CASCADE, related_name='stats')
number = models.CharField(max_length=20)
label = models.CharField(max_length=100)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order']
verbose_name = "About Statistic"
verbose_name_plural = "About Statistics"
def __str__(self):
return f"{self.number} - {self.label}"
class AboutSocialLink(models.Model):
"""Model for About Us social links"""
banner = models.ForeignKey(AboutBanner, on_delete=models.CASCADE, related_name='social_links')
platform = models.CharField(max_length=50)
url = models.URLField()
icon = models.CharField(max_length=50)
aria_label = models.CharField(max_length=100)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order']
verbose_name = "About Social Link"
verbose_name_plural = "About Social Links"
def __str__(self):
return f"{self.platform} - {self.banner.title}"
class AboutService(models.Model):
"""Model for About Us service section"""
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=100, blank=True)
description = models.TextField()
badge_text = models.CharField(max_length=100, default="About Our Company")
badge_icon = models.CharField(max_length=50, default="fa-solid fa-users")
image = models.ImageField(upload_to='about/services/', null=True, blank=True)
cta_text = models.CharField(max_length=100, default="Explore Our Solutions")
cta_link = models.CharField(max_length=100, default="service-single")
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "About Service"
verbose_name_plural = "About Services"
def __str__(self):
return self.title
class AboutFeature(models.Model):
"""Model for About Us features"""
service = models.ForeignKey(AboutService, on_delete=models.CASCADE, related_name='features')
title = models.CharField(max_length=100)
description = models.CharField(max_length=200)
icon = models.CharField(max_length=50)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order']
verbose_name = "About Feature"
verbose_name_plural = "About Features"
def __str__(self):
return f"{self.title} - {self.service.title}"
class AboutProcess(models.Model):
"""Model for About Us process section"""
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=100, blank=True)
description = models.TextField()
badge_text = models.CharField(max_length=100, default="Our Methodology")
badge_icon = models.CharField(max_length=50, default="fa-solid fa-cogs")
image = models.ImageField(upload_to='about/process/', null=True, blank=True)
cta_text = models.CharField(max_length=100, default="View Our Services")
cta_link = models.CharField(max_length=100, default="service-single")
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "About Process"
verbose_name_plural = "About Processes"
def __str__(self):
return self.title
class AboutProcessStep(models.Model):
"""Model for About Us process steps"""
process = models.ForeignKey(AboutProcess, on_delete=models.CASCADE, related_name='steps')
step_number = models.CharField(max_length=10)
title = models.CharField(max_length=100)
description = models.CharField(max_length=200)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order']
verbose_name = "About Process Step"
verbose_name_plural = "About Process Steps"
def __str__(self):
return f"Step {self.step_number}: {self.title}"
class AboutJourney(models.Model):
"""Model for About Us journey section"""
title = models.CharField(max_length=200)
subtitle = models.CharField(max_length=100, blank=True)
description = models.TextField()
badge_text = models.CharField(max_length=100, default="Our Journey")
badge_icon = models.CharField(max_length=50, default="fa-solid fa-rocket")
image = models.ImageField(upload_to='about/journey/', null=True, blank=True)
cta_text = models.CharField(max_length=100, default="Explore Solutions")
cta_link = models.CharField(max_length=100, default="services")
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "About Journey"
verbose_name_plural = "About Journeys"
def __str__(self):
return self.title
class AboutMilestone(models.Model):
"""Model for About Us milestones"""
journey = models.ForeignKey(AboutJourney, on_delete=models.CASCADE, related_name='milestones')
year = models.CharField(max_length=10)
title = models.CharField(max_length=100)
description = models.CharField(max_length=200)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order']
verbose_name = "About Milestone"
verbose_name_plural = "About Milestones"
def __str__(self):
return f"{self.year}: {self.title}"

View File

@@ -0,0 +1,130 @@
from rest_framework import serializers
from .models import (
AboutBanner, AboutStat, AboutSocialLink,
AboutService, AboutFeature,
AboutProcess, AboutProcessStep,
AboutJourney, AboutMilestone
)
class AboutStatSerializer(serializers.ModelSerializer):
class Meta:
model = AboutStat
fields = ['number', 'label', 'order']
class AboutSocialLinkSerializer(serializers.ModelSerializer):
class Meta:
model = AboutSocialLink
fields = ['platform', 'url', 'icon', 'aria_label', 'order']
class AboutBannerSerializer(serializers.ModelSerializer):
stats = AboutStatSerializer(many=True, read_only=True)
social_links = AboutSocialLinkSerializer(many=True, read_only=True)
image_url = serializers.SerializerMethodField()
class Meta:
model = AboutBanner
fields = [
'id', 'title', 'subtitle', 'description', 'badge_text', 'badge_icon',
'cta_text', 'cta_link', 'cta_icon', 'image_url', 'is_active',
'stats', 'social_links', 'created_at', 'updated_at'
]
def get_image_url(self, obj):
if obj.image:
request = self.context.get('request')
if request:
return request.build_absolute_uri(obj.image.url)
return obj.image.url
return None
class AboutFeatureSerializer(serializers.ModelSerializer):
class Meta:
model = AboutFeature
fields = ['title', 'description', 'icon', 'order']
class AboutServiceSerializer(serializers.ModelSerializer):
features = AboutFeatureSerializer(many=True, read_only=True)
image_url = serializers.SerializerMethodField()
class Meta:
model = AboutService
fields = [
'id', 'title', 'subtitle', 'description', 'badge_text', 'badge_icon',
'image_url', 'cta_text', 'cta_link', 'is_active',
'features', 'created_at', 'updated_at'
]
def get_image_url(self, obj):
if obj.image:
request = self.context.get('request')
if request:
return request.build_absolute_uri(obj.image.url)
return obj.image.url
return None
class AboutProcessStepSerializer(serializers.ModelSerializer):
class Meta:
model = AboutProcessStep
fields = ['step_number', 'title', 'description', 'order']
class AboutProcessSerializer(serializers.ModelSerializer):
steps = AboutProcessStepSerializer(many=True, read_only=True)
image_url = serializers.SerializerMethodField()
class Meta:
model = AboutProcess
fields = [
'id', 'title', 'subtitle', 'description', 'badge_text', 'badge_icon',
'image_url', 'cta_text', 'cta_link', 'is_active',
'steps', 'created_at', 'updated_at'
]
def get_image_url(self, obj):
if obj.image:
request = self.context.get('request')
if request:
return request.build_absolute_uri(obj.image.url)
return obj.image.url
return None
class AboutMilestoneSerializer(serializers.ModelSerializer):
class Meta:
model = AboutMilestone
fields = ['year', 'title', 'description', 'order']
class AboutJourneySerializer(serializers.ModelSerializer):
milestones = AboutMilestoneSerializer(many=True, read_only=True)
image_url = serializers.SerializerMethodField()
class Meta:
model = AboutJourney
fields = [
'id', 'title', 'subtitle', 'description', 'badge_text', 'badge_icon',
'image_url', 'cta_text', 'cta_link', 'is_active',
'milestones', 'created_at', 'updated_at'
]
def get_image_url(self, obj):
if obj.image:
request = self.context.get('request')
if request:
return request.build_absolute_uri(obj.image.url)
return obj.image.url
return None
class AboutPageSerializer(serializers.Serializer):
"""Combined serializer for the entire about page"""
banner = AboutBannerSerializer()
service = AboutServiceSerializer()
process = AboutProcessSerializer()
journey = AboutJourneySerializer()

3
backEnd/about/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

25
backEnd/about/urls.py Normal file
View File

@@ -0,0 +1,25 @@
from django.urls import path
from . import views
app_name = 'about'
urlpatterns = [
# Combined about page data
path('page/', views.about_page_data, name='about-page-data'),
# Banner endpoints
path('banner/', views.AboutBannerListAPIView.as_view(), name='about-banner-list'),
path('banner/<int:pk>/', views.AboutBannerDetailAPIView.as_view(), name='about-banner-detail'),
# Service endpoints
path('service/', views.AboutServiceListAPIView.as_view(), name='about-service-list'),
path('service/<int:pk>/', views.AboutServiceDetailAPIView.as_view(), name='about-service-detail'),
# Process endpoints
path('process/', views.AboutProcessListAPIView.as_view(), name='about-process-list'),
path('process/<int:pk>/', views.AboutProcessDetailAPIView.as_view(), name='about-process-detail'),
# Journey endpoints
path('journey/', views.AboutJourneyListAPIView.as_view(), name='about-journey-list'),
path('journey/<int:pk>/', views.AboutJourneyDetailAPIView.as_view(), name='about-journey-detail'),
]

151
backEnd/about/views.py Normal file
View File

@@ -0,0 +1,151 @@
from rest_framework import generics, status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import (
AboutBanner, AboutService, AboutProcess, AboutJourney
)
from .serializers import (
AboutBannerSerializer, AboutServiceSerializer,
AboutProcessSerializer, AboutJourneySerializer,
AboutPageSerializer
)
class AboutBannerListAPIView(generics.ListAPIView):
"""API view to get all active about banners"""
queryset = AboutBanner.objects.filter(is_active=True)
serializer_class = AboutBannerSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutBannerDetailAPIView(generics.RetrieveAPIView):
"""API view to get a specific about banner"""
queryset = AboutBanner.objects.filter(is_active=True)
serializer_class = AboutBannerSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutServiceListAPIView(generics.ListAPIView):
"""API view to get all active about services"""
queryset = AboutService.objects.filter(is_active=True)
serializer_class = AboutServiceSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutServiceDetailAPIView(generics.RetrieveAPIView):
"""API view to get a specific about service"""
queryset = AboutService.objects.filter(is_active=True)
serializer_class = AboutServiceSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutProcessListAPIView(generics.ListAPIView):
"""API view to get all active about processes"""
queryset = AboutProcess.objects.filter(is_active=True)
serializer_class = AboutProcessSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutProcessDetailAPIView(generics.RetrieveAPIView):
"""API view to get a specific about process"""
queryset = AboutProcess.objects.filter(is_active=True)
serializer_class = AboutProcessSerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutJourneyListAPIView(generics.ListAPIView):
"""API view to get all active about journeys"""
queryset = AboutJourney.objects.filter(is_active=True)
serializer_class = AboutJourneySerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
class AboutJourneyDetailAPIView(generics.RetrieveAPIView):
"""API view to get a specific about journey"""
queryset = AboutJourney.objects.filter(is_active=True)
serializer_class = AboutJourneySerializer
permission_classes = [AllowAny]
def get_serializer_context(self):
context = super().get_serializer_context()
context['request'] = self.request
return context
@api_view(['GET'])
@permission_classes([AllowAny])
def about_page_data(request):
"""
API endpoint to get all about page data in one request
Returns banner, service, process, and journey data
"""
try:
# Get the first active instance of each section
banner = AboutBanner.objects.filter(is_active=True).first()
service = AboutService.objects.filter(is_active=True).first()
process = AboutProcess.objects.filter(is_active=True).first()
journey = AboutJourney.objects.filter(is_active=True).first()
if not all([banner, service, process, journey]):
return Response(
{'error': 'Some about page sections are not configured'},
status=status.HTTP_404_NOT_FOUND
)
# Serialize each section
banner_serializer = AboutBannerSerializer(banner, context={'request': request})
service_serializer = AboutServiceSerializer(service, context={'request': request})
process_serializer = AboutProcessSerializer(process, context={'request': request})
journey_serializer = AboutJourneySerializer(journey, context={'request': request})
data = {
'banner': banner_serializer.data,
'service': service_serializer.data,
'process': process_serializer.data,
'journey': journey_serializer.data
}
return Response(data, status=status.HTTP_200_OK)
except Exception as e:
return Response(
{'error': f'An error occurred: {str(e)}'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)