GNXSOFT.COM

This commit is contained in:
Iliyan Angelov
2025-09-26 00:15:37 +03:00
commit fe26b7cca4
16323 changed files with 2011881 additions and 0 deletions

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

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']

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

@@ -0,0 +1,220 @@
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...')
# Create About Banner
banner, created = AboutBanner.objects.get_or_create(
title="Powering Enterprise Digital Transformation",
defaults={
'subtitle': "Leading Enterprise Software Solutions",
'description': "We are a leading enterprise software company that empowers Fortune 500 companies and growing businesses with cutting-edge technology solutions. Our mission is to accelerate digital transformation through innovative software platforms, cloud infrastructure, and data-driven insights.",
'badge_text': "Enterprise Software Solutions",
'badge_icon': "fa-solid fa-building",
'cta_text': "Discover 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': '500+', 'label': 'Enterprise Clients', 'order': 1},
{'number': '99.9%', 'label': 'Uptime SLA', 'order': 2},
{'number': '24/7', 'label': 'Enterprise Support', 'order': 3},
{'number': '15+', 'label': 'Years Experience', '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/enterprisesoft-solutions',
'icon': 'fa-brands fa-linkedin-in',
'aria_label': 'Connect with us on LinkedIn',
'order': 1
},
{
'platform': 'GitHub',
'url': 'https://github.com/enterprisesoft',
'icon': 'fa-brands fa-github',
'aria_label': 'Follow us on GitHub',
'order': 2
},
{
'platform': 'Twitter',
'url': 'https://www.twitter.com/enterprisesoft',
'icon': 'fa-brands fa-twitter',
'aria_label': 'Follow us on Twitter',
'order': 3
},
{
'platform': 'Stack Overflow',
'url': 'https://stackoverflow.com/teams/enterprisesoft',
'icon': 'fa-brands fa-stack-overflow',
'aria_label': 'Visit our Stack Overflow team',
'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 Leaders",
defaults={
'subtitle': "About Our Company",
'description': "Founded in 2008, EnterpriseSoft Solutions has emerged as a premier enterprise software company, serving Fortune 500 companies and innovative startups worldwide. Our team of 200+ engineers, architects, and consultants specializes in delivering mission-critical software solutions that drive digital transformation and business growth.",
'badge_text': "About Our Company",
'badge_icon': "fa-solid fa-users",
'cta_text': "Explore Our Solutions",
'cta_link': "service-single",
'is_active': True
}
)
if created:
self.stdout.write(f'Created service: {service.title}')
# Create Service Features
features_data = [
{
'title': 'Enterprise Security',
'description': 'SOC 2 Type II Certified',
'icon': 'fa-solid fa-shield-halved',
'order': 1
},
{
'title': 'Cloud Native',
'description': 'AWS, Azure, GCP Partners',
'icon': 'fa-solid fa-cloud',
'order': 2
},
{
'title': 'Certified Experts',
'description': 'Microsoft, AWS, Google Certified',
'icon': 'fa-solid fa-certificate',
'order': 3
},
{
'title': 'Global Reach',
'description': 'Offices in 5 Countries',
'icon': 'fa-solid fa-globe',
'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 Development Process",
defaults={
'subtitle': "Our Methodology",
'description': "Our proven enterprise development methodology combines agile practices with enterprise-grade security, scalability, and compliance requirements. We follow industry best practices including DevOps, CI/CD, and microservices architecture to deliver robust, scalable solutions.",
'badge_text': "Our Methodology",
'badge_icon': "fa-solid fa-cogs",
'cta_text': "View Our Services",
'cta_link': "service-single",
'is_active': True
}
)
if created:
self.stdout.write(f'Created process: {process.title}')
# Create Process Steps
steps_data = [
{
'step_number': '01',
'title': 'Discovery & Planning',
'description': 'Comprehensive analysis and architecture design',
'order': 1
},
{
'step_number': '02',
'title': 'Development & Testing',
'description': 'Agile development with continuous testing',
'order': 2
},
{
'step_number': '03',
'title': 'Deployment & Integration',
'description': 'Seamless deployment and system integration',
'order': 3
},
{
'step_number': '04',
'title': 'Support & Maintenance',
'description': '24/7 enterprise support and maintenance',
'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="From Startup to Enterprise Leader",
defaults={
'subtitle': "Our Journey",
'description': "Founded in 2008 by three visionary engineers, Itify Technologies began as a small startup with a big dream: to revolutionize how enterprises approach software development. What started as a passion project has grown into a global enterprise software company serving Fortune 500 clients worldwide.",
'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': '2008',
'title': 'Company Founded',
'description': 'Started with 3 engineers',
'order': 1
},
{
'year': '2015',
'title': 'Enterprise Focus',
'description': 'Pivoted to enterprise solutions',
'order': 2
},
{
'year': '2020',
'title': 'Global Expansion',
'description': 'Opened offices in 5 countries',
'order': 3
},
]
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

@@ -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()

View File

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

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'),
]

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
)