GNXSOFT.COM
This commit is contained in:
88
gnx-react/backend/about/README.md
Normal file
88
gnx-react/backend/about/README.md
Normal 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
|
||||
0
gnx-react/backend/about/__init__.py
Normal file
0
gnx-react/backend/about/__init__.py
Normal file
BIN
gnx-react/backend/about/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/admin.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/apps.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/models.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/serializers.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/serializers.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/urls.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gnx-react/backend/about/__pycache__/views.cpython-312.pyc
Normal file
BIN
gnx-react/backend/about/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
109
gnx-react/backend/about/admin.py
Normal file
109
gnx-react/backend/about/admin.py
Normal 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
gnx-react/backend/about/apps.py
Normal file
6
gnx-react/backend/about/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AboutConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'about'
|
||||
0
gnx-react/backend/about/management/__init__.py
Normal file
0
gnx-react/backend/about/management/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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!')
|
||||
)
|
||||
180
gnx-react/backend/about/migrations/0001_initial.py
Normal file
180
gnx-react/backend/about/migrations/0001_initial.py
Normal 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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
gnx-react/backend/about/migrations/__init__.py
Normal file
0
gnx-react/backend/about/migrations/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
176
gnx-react/backend/about/models.py
Normal file
176
gnx-react/backend/about/models.py
Normal 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}"
|
||||
130
gnx-react/backend/about/serializers.py
Normal file
130
gnx-react/backend/about/serializers.py
Normal 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
gnx-react/backend/about/tests.py
Normal file
3
gnx-react/backend/about/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
25
gnx-react/backend/about/urls.py
Normal file
25
gnx-react/backend/about/urls.py
Normal 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
gnx-react/backend/about/views.py
Normal file
151
gnx-react/backend/about/views.py
Normal 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
|
||||
)
|
||||
Reference in New Issue
Block a user