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
)

View File

View File

@@ -0,0 +1,283 @@
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from .models import ContactSubmission
@admin.register(ContactSubmission)
class ContactSubmissionAdmin(admin.ModelAdmin):
"""
Admin interface for ContactSubmission model.
Provides comprehensive management of contact form submissions.
"""
list_display = [
'id',
'full_name_display',
'company',
'email',
'project_type_display',
'status_badge',
'priority_badge',
'is_enterprise_client_display',
'created_at',
'assigned_to',
]
list_filter = [
'status',
'priority',
'industry',
'company_size',
'project_type',
'timeline',
'budget',
'newsletter_subscription',
'privacy_consent',
'created_at',
'assigned_to',
]
search_fields = [
'first_name',
'last_name',
'email',
'company',
'job_title',
'message',
]
readonly_fields = [
'id',
'created_at',
'updated_at',
'full_name_display',
'is_enterprise_client_display',
'is_high_priority_display',
]
fieldsets = (
('Personal Information', {
'fields': (
'id',
'first_name',
'last_name',
'full_name_display',
'email',
'phone',
)
}),
('Company Information', {
'fields': (
'company',
'job_title',
'industry',
'company_size',
'is_enterprise_client_display',
)
}),
('Project Details', {
'fields': (
'project_type',
'timeline',
'budget',
'message',
)
}),
('Communication Preferences', {
'fields': (
'newsletter_subscription',
'privacy_consent',
)
}),
('Management', {
'fields': (
'status',
'priority',
'is_high_priority_display',
'assigned_to',
'admin_notes',
)
}),
('Timestamps', {
'fields': (
'created_at',
'updated_at',
),
'classes': ('collapse',)
}),
)
ordering = ['-created_at']
list_per_page = 25
date_hierarchy = 'created_at'
actions = [
'mark_as_contacted',
'mark_as_qualified',
'mark_as_closed',
'set_high_priority',
'set_medium_priority',
'set_low_priority',
]
def full_name_display(self, obj):
"""Display full name with link to detail view."""
return format_html(
'<strong>{}</strong>',
obj.full_name
)
full_name_display.short_description = 'Full Name'
full_name_display.admin_order_field = 'first_name'
def project_type_display(self, obj):
"""Display project type with color coding."""
if not obj.project_type:
return '-'
colors = {
'software-development': '#007bff',
'cloud-migration': '#28a745',
'digital-transformation': '#ffc107',
'data-analytics': '#17a2b8',
'security-compliance': '#dc3545',
'integration': '#6f42c1',
'consulting': '#fd7e14',
}
color = colors.get(obj.project_type, '#6c757d')
return format_html(
'<span style="color: {}; font-weight: bold;">{}</span>',
color,
obj.get_project_type_display()
)
project_type_display.short_description = 'Project Type'
project_type_display.admin_order_field = 'project_type'
def status_badge(self, obj):
"""Display status as a colored badge."""
colors = {
'new': '#007bff',
'in_progress': '#ffc107',
'contacted': '#17a2b8',
'qualified': '#28a745',
'closed': '#6c757d',
}
color = colors.get(obj.status, '#6c757d')
return format_html(
'<span style="background-color: {}; color: white; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: bold;">{}</span>',
color,
obj.get_status_display().upper()
)
status_badge.short_description = 'Status'
status_badge.admin_order_field = 'status'
def priority_badge(self, obj):
"""Display priority as a colored badge."""
colors = {
'urgent': '#dc3545',
'high': '#fd7e14',
'medium': '#ffc107',
'low': '#28a745',
}
color = colors.get(obj.priority, '#6c757d')
return format_html(
'<span style="background-color: {}; color: white; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: bold;">{}</span>',
color,
obj.get_priority_display().upper()
)
priority_badge.short_description = 'Priority'
priority_badge.admin_order_field = 'priority'
def is_enterprise_client_display(self, obj):
"""Display enterprise client status."""
if obj.is_enterprise_client:
return format_html(
'<span style="color: #28a745; font-weight: bold;">✓ Enterprise</span>'
)
return format_html(
'<span style="color: #6c757d;">SMB</span>'
)
is_enterprise_client_display.short_description = 'Client Type'
is_enterprise_client_display.admin_order_field = 'company_size'
def is_high_priority_display(self, obj):
"""Display high priority status."""
if obj.is_high_priority:
return format_html(
'<span style="color: #dc3545; font-weight: bold;">⚠ High Priority</span>'
)
return format_html(
'<span style="color: #6c757d;">Normal</span>'
)
is_high_priority_display.short_description = 'Priority Level'
# Admin Actions
def mark_as_contacted(self, request, queryset):
"""Mark selected submissions as contacted."""
updated = queryset.update(status='contacted')
self.message_user(
request,
f'{updated} submission(s) marked as contacted.'
)
mark_as_contacted.short_description = "Mark selected submissions as contacted"
def mark_as_qualified(self, request, queryset):
"""Mark selected submissions as qualified."""
updated = queryset.update(status='qualified')
self.message_user(
request,
f'{updated} submission(s) marked as qualified.'
)
mark_as_qualified.short_description = "Mark selected submissions as qualified"
def mark_as_closed(self, request, queryset):
"""Mark selected submissions as closed."""
updated = queryset.update(status='closed')
self.message_user(
request,
f'{updated} submission(s) marked as closed.'
)
mark_as_closed.short_description = "Mark selected submissions as closed"
def set_high_priority(self, request, queryset):
"""Set selected submissions to high priority."""
updated = queryset.update(priority='high')
self.message_user(
request,
f'{updated} submission(s) set to high priority.'
)
set_high_priority.short_description = "Set selected submissions to high priority"
def set_medium_priority(self, request, queryset):
"""Set selected submissions to medium priority."""
updated = queryset.update(priority='medium')
self.message_user(
request,
f'{updated} submission(s) set to medium priority.'
)
set_medium_priority.short_description = "Set selected submissions to medium priority"
def set_low_priority(self, request, queryset):
"""Set selected submissions to low priority."""
updated = queryset.update(priority='low')
self.message_user(
request,
f'{updated} submission(s) set to low priority.'
)
set_low_priority.short_description = "Set selected submissions to low priority"
def get_queryset(self, request):
"""Optimize queryset for admin list view."""
return super().get_queryset(request).select_related()
def has_add_permission(self, request):
"""Disable adding new submissions through admin."""
return False
def has_delete_permission(self, request, obj=None):
"""Allow deletion only for superusers."""
return request.user.is_superuser

View File

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

View File

@@ -0,0 +1,313 @@
"""
Email service for contact form notifications.
Production-ready with retry logic and comprehensive error handling.
"""
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.conf import settings
from django.utils.html import strip_tags
from django.core.mail.backends.smtp import EmailBackend
import logging
import time
from typing import Optional, List
logger = logging.getLogger(__name__)
def _send_email_with_retry(email_message, max_retries: int = 3, delay: float = 1.0) -> bool:
"""
Send email with retry logic for production reliability.
Args:
email_message: EmailMultiAlternatives instance
max_retries: Maximum number of retry attempts
delay: Delay between retries in seconds
Returns:
bool: True if email was sent successfully, False otherwise
"""
for attempt in range(max_retries + 1):
try:
# Test connection before sending
connection = get_connection()
connection.open()
connection.close()
# Send the email
email_message.send()
return True
except Exception as e:
logger.warning(f"Email send attempt {attempt + 1} failed: {str(e)}")
if attempt < max_retries:
time.sleep(delay * (2 ** attempt)) # Exponential backoff
continue
else:
logger.error(f"Failed to send email after {max_retries + 1} attempts: {str(e)}")
return False
return False
def _create_email_connection() -> Optional[EmailBackend]:
"""
Create a robust email connection with production settings.
Returns:
EmailBackend instance or None if connection fails
"""
try:
connection = get_connection(
host=settings.EMAIL_HOST,
port=settings.EMAIL_PORT,
username=settings.EMAIL_HOST_USER,
password=settings.EMAIL_HOST_PASSWORD,
use_tls=settings.EMAIL_USE_TLS,
use_ssl=settings.EMAIL_USE_SSL,
timeout=getattr(settings, 'EMAIL_TIMEOUT', 30),
connection_timeout=getattr(settings, 'EMAIL_CONNECTION_TIMEOUT', 10),
read_timeout=getattr(settings, 'EMAIL_READ_TIMEOUT', 10),
)
# Test the connection
connection.open()
connection.close()
return connection
except Exception as e:
logger.error(f"Failed to create email connection: {str(e)}")
return None
def send_contact_submission_notification(submission):
"""
Send email notification for new contact form submission.
Args:
submission: ContactSubmission instance
Returns:
bool: True if email was sent successfully, False otherwise
"""
try:
# Get company email from settings
company_email = getattr(settings, 'COMPANY_EMAIL', None)
if not company_email:
logger.warning("COMPANY_EMAIL not configured in settings")
return False
# Prepare email context
context = {
'submission': submission,
}
# Render email templates
html_content = render_to_string(
'contact/contact_submission_email.html',
context
)
text_content = render_to_string(
'contact/contact_submission_email.txt',
context
)
# Create email subject with priority indicator
priority_emoji = {
'urgent': '🚨',
'high': '⚠️',
'medium': '📋',
'low': '📝'
}.get(submission.priority, '📋')
subject = f"{priority_emoji} New Contact Form Submission - {submission.company} (#{submission.id})"
# Create email message
email = EmailMultiAlternatives(
subject=subject,
body=text_content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[company_email],
reply_to=[submission.email] # Allow direct reply to customer
)
# Add headers for better email handling
email.extra_headers = {
'X-Priority': '1' if submission.priority in ['urgent', 'high'] else '3',
'X-MSMail-Priority': 'High' if submission.priority in ['urgent', 'high'] else 'Normal',
}
# Attach HTML version
email.attach_alternative(html_content, "text/html")
# Send email with retry logic
success = _send_email_with_retry(email)
if success:
logger.info(f"Contact submission notification sent for submission #{submission.id}")
else:
logger.error(f"Failed to send contact submission notification for submission #{submission.id} after retries")
return success
except Exception as e:
logger.error(f"Failed to send contact submission notification for submission #{submission.id}: {str(e)}")
return False
def send_contact_submission_confirmation(submission):
"""
Send confirmation email to the customer who submitted the form.
Args:
submission: ContactSubmission instance
Returns:
bool: True if email was sent successfully, False otherwise
"""
try:
# Prepare email context
context = {
'submission': submission,
}
# Create simple confirmation email
subject = "Thank you for contacting GNX Software Solutions"
# Simple text email for confirmation
message = f"""
Dear {submission.full_name},
Thank you for reaching out to GNX Software Solutions!
We have received your inquiry about {submission.get_project_type_display() if submission.project_type else 'your project'} and will review it carefully.
Here are the details of your submission:
- Submission ID: #{submission.id}
- Company: {submission.company}
- Project Type: {submission.get_project_type_display() if submission.project_type else 'Not specified'}
- Timeline: {submission.get_timeline_display() if submission.timeline else 'Not specified'}
Our team will contact you within 24 hours to discuss your project requirements and how we can help you achieve your goals.
If you have any urgent questions, please don't hesitate to contact us directly.
Best regards,
The GNX Team
---
GNX Software Solutions
Email: {settings.DEFAULT_FROM_EMAIL}
"""
# Create email message
email = EmailMultiAlternatives(
subject=subject,
body=message,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[submission.email]
)
# Send email with retry logic
success = _send_email_with_retry(email)
if success:
logger.info(f"Contact submission confirmation sent to {submission.email} for submission #{submission.id}")
else:
logger.error(f"Failed to send contact submission confirmation for submission #{submission.id} after retries")
return success
except Exception as e:
logger.error(f"Failed to send contact submission confirmation for submission #{submission.id}: {str(e)}")
return False
def check_email_health() -> dict:
"""
Check email service health for production monitoring.
Returns:
dict: Health status information
"""
health_status = {
'email_service': 'unknown',
'connection_test': False,
'configuration_valid': False,
'error_message': None
}
try:
# Check configuration
required_settings = [
'EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER',
'EMAIL_HOST_PASSWORD', 'DEFAULT_FROM_EMAIL', 'COMPANY_EMAIL'
]
missing_settings = []
for setting in required_settings:
if not getattr(settings, setting, None):
missing_settings.append(setting)
if missing_settings:
health_status['error_message'] = f"Missing email settings: {', '.join(missing_settings)}"
return health_status
health_status['configuration_valid'] = True
# Test connection
connection = _create_email_connection()
if connection:
health_status['connection_test'] = True
health_status['email_service'] = 'healthy'
else:
health_status['email_service'] = 'unhealthy'
health_status['error_message'] = 'Failed to establish email connection'
except Exception as e:
health_status['email_service'] = 'error'
health_status['error_message'] = str(e)
return health_status
def send_test_email(to_email: str) -> bool:
"""
Send a test email to verify email configuration.
Args:
to_email: Email address to send test email to
Returns:
bool: True if test email was sent successfully
"""
try:
subject = "GNX Email Service Test"
message = f"""
This is a test email from the GNX contact form system.
If you receive this email, the email service is working correctly.
Timestamp: {time.strftime("%Y-%m-%d %H:%M:%S UTC")}
"""
email = EmailMultiAlternatives(
subject=subject,
body=message,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[to_email]
)
success = _send_email_with_retry(email)
if success:
logger.info(f"Test email sent successfully to {to_email}")
else:
logger.error(f"Failed to send test email to {to_email}")
return success
except Exception as e:
logger.error(f"Error sending test email to {to_email}: {str(e)}")
return False

View File

@@ -0,0 +1,47 @@
# Generated by Django 4.2.7 on 2025-09-25 07:22
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='ContactSubmission',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='First Name')),
('last_name', models.CharField(max_length=100, verbose_name='Last Name')),
('email', models.EmailField(max_length=254, validators=[django.core.validators.EmailValidator()], verbose_name='Business Email')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Phone Number')),
('company', models.CharField(max_length=200, verbose_name='Company Name')),
('job_title', models.CharField(max_length=100, verbose_name='Job Title')),
('industry', models.CharField(blank=True, choices=[('technology', 'Technology'), ('finance', 'Finance'), ('healthcare', 'Healthcare'), ('manufacturing', 'Manufacturing'), ('retail', 'Retail'), ('education', 'Education'), ('government', 'Government'), ('other', 'Other')], max_length=50, null=True, verbose_name='Industry')),
('company_size', models.CharField(blank=True, choices=[('1-10', '1-10 employees'), ('11-50', '11-50 employees'), ('51-200', '51-200 employees'), ('201-1000', '201-1000 employees'), ('1000+', '1000+ employees')], max_length=20, null=True, verbose_name='Company Size')),
('project_type', models.CharField(blank=True, choices=[('software-development', 'Software Development'), ('cloud-migration', 'Cloud Migration'), ('digital-transformation', 'Digital Transformation'), ('data-analytics', 'Data Analytics'), ('security-compliance', 'Security & Compliance'), ('integration', 'System Integration'), ('consulting', 'Consulting Services')], max_length=50, null=True, verbose_name='Project Type')),
('timeline', models.CharField(blank=True, choices=[('immediate', 'Immediate (0-3 months)'), ('short', 'Short-term (3-6 months)'), ('medium', 'Medium-term (6-12 months)'), ('long', 'Long-term (12+ months)'), ('planning', 'Still planning')], max_length=20, null=True, verbose_name='Project Timeline')),
('budget', models.CharField(blank=True, choices=[('under-50k', 'Under €50,000'), ('50k-100k', '€50,000 - €100,000'), ('100k-250k', '€100,000 - €250,000'), ('250k-500k', '€250,000 - €500,000'), ('500k-1m', '€500,000 - €1,000,000'), ('over-1m', 'Over €1,000,000'), ('discuss', 'Prefer to discuss')], max_length=20, null=True, verbose_name='Project Budget Range')),
('message', models.TextField(verbose_name='Project Description')),
('newsletter_subscription', models.BooleanField(default=False, verbose_name='Newsletter Subscription')),
('privacy_consent', models.BooleanField(default=False, verbose_name='Privacy Policy Consent')),
('status', models.CharField(choices=[('new', 'New'), ('in_progress', 'In Progress'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('closed', 'Closed')], default='new', max_length=20, verbose_name='Status')),
('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('urgent', 'Urgent')], default='medium', max_length=10, verbose_name='Priority')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
('admin_notes', models.TextField(blank=True, null=True, verbose_name='Admin Notes')),
('assigned_to', models.CharField(blank=True, max_length=100, null=True, verbose_name='Assigned To')),
],
options={
'verbose_name': 'Contact Submission',
'verbose_name_plural': 'Contact Submissions',
'ordering': ['-created_at'],
'indexes': [models.Index(fields=['email'], name='contact_con_email_394734_idx'), models.Index(fields=['company'], name='contact_con_company_80f428_idx'), models.Index(fields=['status'], name='contact_con_status_b337da_idx'), models.Index(fields=['created_at'], name='contact_con_created_0e637d_idx')],
},
),
]

View File

@@ -0,0 +1,190 @@
from django.db import models
from django.core.validators import EmailValidator
from django.utils import timezone
class ContactSubmission(models.Model):
"""
Model to store contact form submissions from the GNX website.
Based on the comprehensive contact form structure from ContactSection.tsx
"""
# Personal Information
first_name = models.CharField(max_length=100, verbose_name="First Name")
last_name = models.CharField(max_length=100, verbose_name="Last Name")
email = models.EmailField(validators=[EmailValidator()], verbose_name="Business Email")
phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Phone Number")
# Company Information
company = models.CharField(max_length=200, verbose_name="Company Name")
job_title = models.CharField(max_length=100, verbose_name="Job Title")
# Industry and Company Size
INDUSTRY_CHOICES = [
('technology', 'Technology'),
('finance', 'Finance'),
('healthcare', 'Healthcare'),
('manufacturing', 'Manufacturing'),
('retail', 'Retail'),
('education', 'Education'),
('government', 'Government'),
('other', 'Other'),
]
industry = models.CharField(
max_length=50,
choices=INDUSTRY_CHOICES,
blank=True,
null=True,
verbose_name="Industry"
)
COMPANY_SIZE_CHOICES = [
('1-10', '1-10 employees'),
('11-50', '11-50 employees'),
('51-200', '51-200 employees'),
('201-1000', '201-1000 employees'),
('1000+', '1000+ employees'),
]
company_size = models.CharField(
max_length=20,
choices=COMPANY_SIZE_CHOICES,
blank=True,
null=True,
verbose_name="Company Size"
)
# Project Details
PROJECT_TYPE_CHOICES = [
('software-development', 'Software Development'),
('cloud-migration', 'Cloud Migration'),
('digital-transformation', 'Digital Transformation'),
('data-analytics', 'Data Analytics'),
('security-compliance', 'Security & Compliance'),
('integration', 'System Integration'),
('consulting', 'Consulting Services'),
]
project_type = models.CharField(
max_length=50,
choices=PROJECT_TYPE_CHOICES,
blank=True,
null=True,
verbose_name="Project Type"
)
TIMELINE_CHOICES = [
('immediate', 'Immediate (0-3 months)'),
('short', 'Short-term (3-6 months)'),
('medium', 'Medium-term (6-12 months)'),
('long', 'Long-term (12+ months)'),
('planning', 'Still planning'),
]
timeline = models.CharField(
max_length=20,
choices=TIMELINE_CHOICES,
blank=True,
null=True,
verbose_name="Project Timeline"
)
BUDGET_CHOICES = [
('under-50k', 'Under €50,000'),
('50k-100k', '€50,000 - €100,000'),
('100k-250k', '€100,000 - €250,000'),
('250k-500k', '€250,000 - €500,000'),
('500k-1m', '€500,000 - €1,000,000'),
('over-1m', 'Over €1,000,000'),
('discuss', 'Prefer to discuss'),
]
budget = models.CharField(
max_length=20,
choices=BUDGET_CHOICES,
blank=True,
null=True,
verbose_name="Project Budget Range"
)
message = models.TextField(verbose_name="Project Description")
# Privacy & Communication
newsletter_subscription = models.BooleanField(
default=False,
verbose_name="Newsletter Subscription"
)
privacy_consent = models.BooleanField(
default=False,
verbose_name="Privacy Policy Consent"
)
# Metadata
STATUS_CHOICES = [
('new', 'New'),
('in_progress', 'In Progress'),
('contacted', 'Contacted'),
('qualified', 'Qualified'),
('closed', 'Closed'),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='new',
verbose_name="Status"
)
priority = models.CharField(
max_length=10,
choices=[
('low', 'Low'),
('medium', 'Medium'),
('high', 'High'),
('urgent', 'Urgent'),
],
default='medium',
verbose_name="Priority"
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated At")
# Admin notes for internal use
admin_notes = models.TextField(blank=True, null=True, verbose_name="Admin Notes")
assigned_to = models.CharField(
max_length=100,
blank=True,
null=True,
verbose_name="Assigned To"
)
class Meta:
verbose_name = "Contact Submission"
verbose_name_plural = "Contact Submissions"
ordering = ['-created_at']
indexes = [
models.Index(fields=['email']),
models.Index(fields=['company']),
models.Index(fields=['status']),
models.Index(fields=['created_at']),
]
def __str__(self):
return f"{self.first_name} {self.last_name} - {self.company} ({self.created_at.strftime('%Y-%m-%d')})"
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
@property
def is_high_priority(self):
return self.priority in ['high', 'urgent']
@property
def is_enterprise_client(self):
return self.company_size in ['201-1000', '1000+']
def get_industry_display(self):
return dict(self.INDUSTRY_CHOICES).get(self.industry, self.industry)
def get_project_type_display(self):
return dict(self.PROJECT_TYPE_CHOICES).get(self.project_type, self.project_type)
def get_budget_display(self):
return dict(self.BUDGET_CHOICES).get(self.budget, self.budget)

View File

@@ -0,0 +1,196 @@
from rest_framework import serializers
from .models import ContactSubmission
class ContactSubmissionSerializer(serializers.ModelSerializer):
"""
Serializer for ContactSubmission model.
Handles both creation and retrieval of contact form submissions.
"""
# Computed fields
full_name = serializers.ReadOnlyField()
is_high_priority = serializers.ReadOnlyField()
is_enterprise_client = serializers.ReadOnlyField()
industry_display = serializers.SerializerMethodField()
project_type_display = serializers.SerializerMethodField()
budget_display = serializers.SerializerMethodField()
class Meta:
model = ContactSubmission
fields = [
'id',
'first_name',
'last_name',
'full_name',
'email',
'phone',
'company',
'job_title',
'industry',
'industry_display',
'company_size',
'project_type',
'project_type_display',
'timeline',
'budget',
'budget_display',
'message',
'newsletter_subscription',
'privacy_consent',
'status',
'priority',
'is_high_priority',
'is_enterprise_client',
'created_at',
'updated_at',
'admin_notes',
'assigned_to',
]
read_only_fields = [
'id',
'status',
'priority',
'created_at',
'updated_at',
'admin_notes',
'assigned_to',
]
def get_industry_display(self, obj):
return obj.get_industry_display()
def get_project_type_display(self, obj):
return obj.get_project_type_display()
def get_budget_display(self, obj):
return obj.get_budget_display()
def validate_email(self, value):
"""
Custom email validation to ensure it's a business email.
"""
if value and not any(domain in value.lower() for domain in ['@gmail.com', '@yahoo.com', '@hotmail.com']):
return value
# Allow personal emails but log them
return value
def validate_privacy_consent(self, value):
"""
Ensure privacy consent is given.
"""
if not value:
raise serializers.ValidationError("Privacy consent is required to submit the form.")
return value
def validate(self, attrs):
"""
Cross-field validation.
"""
# Ensure required fields are present
required_fields = ['first_name', 'last_name', 'email', 'company', 'job_title', 'message']
for field in required_fields:
if not attrs.get(field):
raise serializers.ValidationError(f"{field.replace('_', ' ').title()} is required.")
# Validate enterprise client indicators
if attrs.get('company_size') in ['201-1000', '1000+'] and attrs.get('budget') in ['under-50k', '50k-100k']:
# This might be a mismatch, but we'll allow it and flag for review
pass
return attrs
class ContactSubmissionCreateSerializer(serializers.ModelSerializer):
"""
Simplified serializer for creating contact submissions.
Only includes fields that should be provided by the frontend.
"""
class Meta:
model = ContactSubmission
fields = [
'first_name',
'last_name',
'email',
'phone',
'company',
'job_title',
'industry',
'company_size',
'project_type',
'timeline',
'budget',
'message',
'newsletter_subscription',
'privacy_consent',
]
def validate_privacy_consent(self, value):
"""
Ensure privacy consent is given.
"""
if not value:
raise serializers.ValidationError("Privacy consent is required to submit the form.")
return value
def validate(self, attrs):
"""
Cross-field validation for creation.
"""
# Ensure required fields are present
required_fields = ['first_name', 'last_name', 'email', 'company', 'job_title', 'message']
for field in required_fields:
if not attrs.get(field):
raise serializers.ValidationError(f"{field.replace('_', ' ').title()} is required.")
return attrs
class ContactSubmissionListSerializer(serializers.ModelSerializer):
"""
Simplified serializer for listing contact submissions.
Used in admin views and API listings.
"""
full_name = serializers.ReadOnlyField()
is_high_priority = serializers.ReadOnlyField()
is_enterprise_client = serializers.ReadOnlyField()
class Meta:
model = ContactSubmission
fields = [
'id',
'full_name',
'email',
'company',
'job_title',
'project_type',
'status',
'priority',
'is_high_priority',
'is_enterprise_client',
'created_at',
]
class ContactSubmissionUpdateSerializer(serializers.ModelSerializer):
"""
Serializer for updating contact submissions (admin use).
"""
class Meta:
model = ContactSubmission
fields = [
'status',
'priority',
'admin_notes',
'assigned_to',
]
def validate_status(self, value):
"""
Validate status transitions.
"""
# Add business logic for status transitions if needed
return value

View File

@@ -0,0 +1,437 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Contact Form Submission - GNX</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #2c3e50;
background-color: #f8f9fa;
margin: 0;
padding: 20px;
}
.email-container {
max-width: 700px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 30px;
text-align: center;
position: relative;
}
.header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at 20% 80%, rgba(255,255,255,0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(255,255,255,0.1) 0%, transparent 50%);
opacity: 0.3;
}
.header-content {
position: relative;
z-index: 1;
}
.header h1 {
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.header p {
font-size: 16px;
opacity: 0.9;
font-weight: 300;
}
.priority-badge {
display: inline-block;
padding: 8px 16px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 15px;
}
.priority-urgent {
background-color: #ff4757;
color: white;
box-shadow: 0 4px 15px rgba(255, 71, 87, 0.3);
}
.priority-high {
background-color: #ff6b35;
color: white;
box-shadow: 0 4px 15px rgba(255, 107, 53, 0.3);
}
.priority-medium {
background-color: #ffa726;
color: white;
box-shadow: 0 4px 15px rgba(255, 167, 38, 0.3);
}
.priority-low {
background-color: #66bb6a;
color: white;
box-shadow: 0 4px 15px rgba(102, 187, 106, 0.3);
}
.content {
padding: 40px 30px;
}
.section {
margin-bottom: 30px;
background-color: #ffffff;
border-radius: 12px;
border: 1px solid #e9ecef;
overflow: hidden;
transition: all 0.3s ease;
}
.section:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
transform: translateY(-2px);
}
.section-header {
background: linear-gradient(90deg, #f8f9fa 0%, #e9ecef 100%);
padding: 20px 25px;
border-bottom: 1px solid #dee2e6;
}
.section h3 {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin: 0;
display: flex;
align-items: center;
}
.section-icon {
font-size: 20px;
margin-right: 12px;
display: inline-block;
vertical-align: middle;
}
.section-body {
padding: 25px;
}
.field {
margin-bottom: 18px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.field:last-child {
margin-bottom: 0;
}
.field-label {
font-weight: 600;
color: #495057;
min-width: 140px;
margin-bottom: 5px;
font-size: 14px;
}
.field-value {
flex: 1;
color: #2c3e50;
font-size: 15px;
line-height: 1.5;
}
.message-content {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 20px;
margin-top: 10px;
font-style: italic;
line-height: 1.6;
}
.footer {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 30px;
text-align: center;
position: relative;
}
.footer::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%);
}
.footer p {
margin-bottom: 10px;
font-size: 14px;
opacity: 0.9;
}
.footer p:last-child {
margin-bottom: 0;
font-size: 12px;
opacity: 0.7;
}
.company-info {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.company-info strong {
color: #ecf0f1;
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.email-container {
border-radius: 8px;
}
.header {
padding: 30px 20px;
}
.header h1 {
font-size: 24px;
}
.content {
padding: 30px 20px;
}
.section-body {
padding: 20px;
}
.field {
flex-direction: column;
}
.field-label {
min-width: auto;
margin-bottom: 8px;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<div class="header-content">
<h1>📧 New Contact Form Submission</h1>
<p>GNX Software Solutions</p>
<div class="priority-badge priority-{{ submission.priority }}">
{{ submission.get_priority_display }} Priority
</div>
</div>
</div>
<div class="content">
<div class="section">
<div class="section-header">
<h3>
<span class="section-icon">📋</span>
Submission Details
</h3>
</div>
<div class="section-body">
<div class="field">
<span class="field-label">Submission ID:</span>
<span class="field-value"><strong>#{{ submission.id }}</strong></span>
</div>
<div class="field">
<span class="field-label">Priority:</span>
<span class="field-value">
<span class="priority-badge priority-{{ submission.priority }}" style="font-size: 11px; padding: 4px 8px;">
{{ submission.get_priority_display }}
</span>
</span>
</div>
<div class="field">
<span class="field-label">Status:</span>
<span class="field-value">{{ submission.get_status_display }}</span>
</div>
<div class="field">
<span class="field-label">Submitted:</span>
<span class="field-value">{{ submission.created_at|date:"F d, Y \a\t g:i A" }}</span>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<h3>
<span class="section-icon">👤</span>
Contact Information
</h3>
</div>
<div class="section-body">
<div class="field">
<span class="field-label">Name:</span>
<span class="field-value"><strong>{{ submission.full_name }}</strong></span>
</div>
<div class="field">
<span class="field-label">Email:</span>
<span class="field-value">
<a href="mailto:{{ submission.email }}" style="color: #667eea; text-decoration: none;">{{ submission.email }}</a>
</span>
</div>
{% if submission.phone %}
<div class="field">
<span class="field-label">Phone:</span>
<span class="field-value">
<a href="tel:{{ submission.phone }}" style="color: #667eea; text-decoration: none;">{{ submission.phone }}</a>
</span>
</div>
{% endif %}
</div>
</div>
<div class="section">
<div class="section-header">
<h3>
<span class="section-icon">🏢</span>
Company Information
</h3>
</div>
<div class="section-body">
<div class="field">
<span class="field-label">Company:</span>
<span class="field-value"><strong>{{ submission.company }}</strong></span>
</div>
<div class="field">
<span class="field-label">Job Title:</span>
<span class="field-value">{{ submission.job_title }}</span>
</div>
{% if submission.industry %}
<div class="field">
<span class="field-label">Industry:</span>
<span class="field-value">{{ submission.get_industry_display }}</span>
</div>
{% endif %}
{% if submission.company_size %}
<div class="field">
<span class="field-label">Company Size:</span>
<span class="field-value">{{ submission.get_company_size_display }}</span>
</div>
{% endif %}
</div>
</div>
<div class="section">
<div class="section-header">
<h3>
<span class="section-icon">🎯</span>
Project Details
</h3>
</div>
<div class="section-body">
{% if submission.project_type %}
<div class="field">
<span class="field-label">Project Type:</span>
<span class="field-value">{{ submission.get_project_type_display }}</span>
</div>
{% endif %}
{% if submission.timeline %}
<div class="field">
<span class="field-label">Timeline:</span>
<span class="field-value">{{ submission.get_timeline_display }}</span>
</div>
{% endif %}
{% if submission.budget %}
<div class="field">
<span class="field-label">Budget:</span>
<span class="field-value"><strong>{{ submission.get_budget_display }}</strong></span>
</div>
{% endif %}
<div class="field">
<span class="field-label">Message:</span>
<div class="message-content">
{{ submission.message|linebreaks }}
</div>
</div>
</div>
</div>
{% if submission.newsletter_subscription or submission.privacy_consent %}
<div class="section">
<div class="section-header">
<h3>
<span class="section-icon"></span>
Preferences
</h3>
</div>
<div class="section-body">
{% if submission.newsletter_subscription %}
<div class="field">
<span class="field-label">Newsletter Subscription:</span>
<span class="field-value">✅ Yes</span>
</div>
{% endif %}
{% if submission.privacy_consent %}
<div class="field">
<span class="field-label">Privacy Policy Consent:</span>
<span class="field-value">✅ Yes</span>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="footer">
<p>📧 This email was automatically generated from the GNX website contact form.</p>
<p>⏰ Please respond to the customer within 24 hours as per our service commitment.</p>
<div class="company-info">
<p><strong>GNX Software Solutions</strong></p>
<p>Email: support@gnxsoft.com | Web: gnxsoft.com</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,42 @@
NEW CONTACT FORM SUBMISSION - GNX SOFTWARE SOLUTIONS
====================================================
Submission Details:
------------------
Submission ID: #{{ submission.id }}
Priority: {{ submission.get_priority_display }}
Status: {{ submission.get_status_display }}
Submitted: {{ submission.created_at|date:"F d, Y \a\t g:i A" }}
Contact Information:
-------------------
Name: {{ submission.full_name }}
Email: {{ submission.email }}
{% if submission.phone %}Phone: {{ submission.phone }}{% endif %}
Company Information:
-------------------
Company: {{ submission.company }}
Job Title: {{ submission.job_title }}
{% if submission.industry %}Industry: {{ submission.get_industry_display }}{% endif %}
{% if submission.company_size %}Company Size: {{ submission.get_company_size_display }}{% endif %}
Project Details:
---------------
{% if submission.project_type %}Project Type: {{ submission.get_project_type_display }}{% endif %}
{% if submission.timeline %}Timeline: {{ submission.get_timeline_display }}{% endif %}
{% if submission.budget %}Budget: {{ submission.get_budget_display }}{% endif %}
Message:
{{ submission.message }}
{% if submission.newsletter_subscription or submission.privacy_consent %}
Preferences:
-----------
{% if submission.newsletter_subscription %}Newsletter Subscription: Yes{% endif %}
{% if submission.privacy_consent %}Privacy Policy Consent: Yes{% endif %}
{% endif %}
---
This email was automatically generated from the GNX website contact form.
Please respond to the customer within 24 hours as per our service commitment.

View File

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

View File

@@ -0,0 +1,12 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ContactSubmissionViewSet
# Create a router and register our viewsets
router = DefaultRouter()
router.register(r'submissions', ContactSubmissionViewSet, basename='contact-submission')
# The API URLs are now determined automatically by the router
urlpatterns = [
path('', include(router.urls)),
]

View File

@@ -0,0 +1,262 @@
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from django.db.models import Q
from .models import ContactSubmission
from .serializers import (
ContactSubmissionSerializer,
ContactSubmissionCreateSerializer,
ContactSubmissionListSerializer,
ContactSubmissionUpdateSerializer
)
from .email_service import (
send_contact_submission_notification,
send_contact_submission_confirmation,
check_email_health,
send_test_email
)
class ContactSubmissionViewSet(viewsets.ModelViewSet):
"""
ViewSet for managing contact form submissions.
Provides endpoints for:
- Creating new contact submissions (public)
- Listing submissions (admin only)
- Retrieving individual submissions (admin only)
- Updating submission status (admin only)
"""
queryset = ContactSubmission.objects.all()
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status', 'priority', 'industry', 'company_size', 'project_type']
search_fields = ['first_name', 'last_name', 'email', 'company', 'job_title']
ordering_fields = ['created_at', 'updated_at', 'priority']
ordering = ['-created_at']
def get_serializer_class(self):
"""
Return appropriate serializer class based on action.
"""
if self.action == 'create':
return ContactSubmissionCreateSerializer
elif self.action == 'list':
return ContactSubmissionListSerializer
elif self.action in ['update', 'partial_update']:
return ContactSubmissionUpdateSerializer
return ContactSubmissionSerializer
def get_permissions(self):
"""
Set permissions based on action.
"""
if self.action == 'create':
# Allow anyone to create contact submissions
permission_classes = [AllowAny]
else:
# Require authentication for all other actions
permission_classes = [IsAuthenticated]
return [permission() for permission in permission_classes]
def create(self, request, *args, **kwargs):
"""
Create a new contact submission.
Public endpoint for form submissions.
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# Set initial priority based on company size and budget
instance = serializer.save()
self._set_initial_priority(instance)
# Send email notifications
try:
# Send notification to company email
send_contact_submission_notification(instance)
# Send confirmation email to customer
send_contact_submission_confirmation(instance)
except Exception as e:
# Log the error but don't fail the submission
import logging
logger = logging.getLogger(__name__)
logger.error(f"Failed to send email notifications for submission #{instance.id}: {str(e)}")
# Return success response
return Response({
'message': 'Thank you for your submission! We\'ll contact you within 24 hours.',
'submission_id': instance.id,
'status': 'success'
}, status=status.HTTP_201_CREATED)
def _set_initial_priority(self, instance):
"""
Set initial priority based on submission data.
"""
priority = 'medium' # default
# High priority for enterprise clients with large budgets
if (instance.company_size in ['201-1000', '1000+'] and
instance.budget in ['250k-500k', '500k-1m', 'over-1m']):
priority = 'high'
# Urgent for immediate timeline with high budget
elif (instance.timeline == 'immediate' and
instance.budget in ['100k-250k', '250k-500k', '500k-1m', 'over-1m']):
priority = 'urgent'
# Low priority for small companies with small budgets
elif (instance.company_size in ['1-10', '11-50'] and
instance.budget in ['under-50k', '50k-100k']):
priority = 'low'
instance.priority = priority
instance.save(update_fields=['priority'])
@action(detail=True, methods=['post'])
def mark_contacted(self, request, pk=None):
"""
Mark a submission as contacted.
"""
submission = self.get_object()
submission.status = 'contacted'
submission.save(update_fields=['status'])
return Response({
'message': 'Submission marked as contacted',
'status': submission.status
})
@action(detail=True, methods=['post'])
def mark_qualified(self, request, pk=None):
"""
Mark a submission as qualified.
"""
submission = self.get_object()
submission.status = 'qualified'
submission.save(update_fields=['status'])
return Response({
'message': 'Submission marked as qualified',
'status': submission.status
})
@action(detail=True, methods=['post'])
def close_submission(self, request, pk=None):
"""
Close a submission.
"""
submission = self.get_object()
submission.status = 'closed'
submission.save(update_fields=['status'])
return Response({
'message': 'Submission closed',
'status': submission.status
})
@action(detail=False, methods=['get'])
def stats(self, request):
"""
Get statistics about contact submissions.
"""
total = self.get_queryset().count()
new = self.get_queryset().filter(status='new').count()
in_progress = self.get_queryset().filter(status='in_progress').count()
contacted = self.get_queryset().filter(status='contacted').count()
qualified = self.get_queryset().filter(status='qualified').count()
closed = self.get_queryset().filter(status='closed').count()
# Priority breakdown
urgent = self.get_queryset().filter(priority='urgent').count()
high = self.get_queryset().filter(priority='high').count()
medium = self.get_queryset().filter(priority='medium').count()
low = self.get_queryset().filter(priority='low').count()
# Enterprise clients
enterprise = self.get_queryset().filter(
company_size__in=['201-1000', '1000+']
).count()
return Response({
'total_submissions': total,
'status_breakdown': {
'new': new,
'in_progress': in_progress,
'contacted': contacted,
'qualified': qualified,
'closed': closed,
},
'priority_breakdown': {
'urgent': urgent,
'high': high,
'medium': medium,
'low': low,
},
'enterprise_clients': enterprise,
})
@action(detail=False, methods=['get'])
def recent(self, request):
"""
Get recent submissions (last 7 days).
"""
from datetime import datetime, timedelta
recent_date = datetime.now() - timedelta(days=7)
recent_submissions = self.get_queryset().filter(
created_at__gte=recent_date
).order_by('-created_at')[:10]
serializer = ContactSubmissionListSerializer(recent_submissions, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def high_priority(self, request):
"""
Get high priority submissions.
"""
high_priority_submissions = self.get_queryset().filter(
priority__in=['urgent', 'high']
).order_by('-created_at')
serializer = ContactSubmissionListSerializer(high_priority_submissions, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def email_health(self, request):
"""
Check email service health for production monitoring.
"""
health_status = check_email_health()
return Response(health_status)
@action(detail=False, methods=['post'])
def send_test_email(self, request):
"""
Send a test email to verify email configuration.
"""
email = request.data.get('email')
if not email:
return Response({
'error': 'Email address is required'
}, status=status.HTTP_400_BAD_REQUEST)
success = send_test_email(email)
if success:
return Response({
'message': f'Test email sent successfully to {email}',
'status': 'success'
})
else:
return Response({
'error': 'Failed to send test email',
'status': 'error'
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
"""
ASGI config for gnx project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gnx.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,247 @@
"""
Django settings for gnx project.
Generated by 'django-admin startproject' using Django 4.2.7.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
import os
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=True, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=lambda v: [s.strip() for s in v.split(',')])
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third party apps
'rest_framework',
'corsheaders',
'django_filters',
'drf_yasg',
# Local apps
'contact',
'services',
'about',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'gnx.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'gnx.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
# Media files (User uploads)
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# File upload settings
FILE_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10MB
FILE_UPLOAD_PERMISSIONS = 0o644
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Django REST Framework Configuration
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
# CORS Configuration
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # React development server
"http://127.0.0.1:3000",
"http://localhost:3001",
"http://127.0.0.1:3001",
]
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = DEBUG # Only allow all origins in development
# Email Configuration
# Production email settings - use SMTP backend
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
EMAIL_HOST = config('EMAIL_HOST', default='mail.gnxsoft.com')
EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
EMAIL_USE_SSL = config('EMAIL_USE_SSL', default=False, cast=bool)
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL')
# Email timeout settings for production
EMAIL_TIMEOUT = config('EMAIL_TIMEOUT', default=30, cast=int)
# Company email for contact form notifications
COMPANY_EMAIL = config('COMPANY_EMAIL')
# Email connection settings for production reliability
EMAIL_CONNECTION_TIMEOUT = config('EMAIL_CONNECTION_TIMEOUT', default=10, cast=int)
EMAIL_READ_TIMEOUT = config('EMAIL_READ_TIMEOUT', default=10, cast=int)
# Logging Configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': BASE_DIR / 'logs' / 'django.log',
'formatter': 'verbose',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
},
'root': {
'handlers': ['console'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': False,
},
'contact': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
'propagate': False,
},
},
}

View File

@@ -0,0 +1,57 @@
"""
URL configuration for gnx project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
# API Documentation Schema
schema_view = get_schema_view(
openapi.Info(
title="GNX API",
default_version='v1',
description="API for GNX Software Solutions - Contact form submissions and Services management",
terms_of_service="https://www.gnxsoft.com/terms/",
contact=openapi.Contact(email="info@gnxsoft.com"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path('admin/', admin.site.urls),
# API Documentation
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
path('swagger.json', schema_view.without_ui(cache_timeout=0), name='schema-json'),
# API Root - All API endpoints under /api/
path('api/', include([
path('contact/', include('contact.urls')),
path('services/', include('services.urls')),
path('about/', include('about.urls')),
])),
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -0,0 +1,16 @@
"""
WSGI config for gnx project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gnx.settings')
application = get_wsgi_application()

File diff suppressed because it is too large Load Diff

22
gnx-react/backend/manage.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gnx.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -0,0 +1,48 @@
# Production Environment Configuration for GNX Contact Form
# Copy this file to .env and update with your actual values
# Django Settings
SECRET_KEY=your-super-secret-production-key-here
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,your-server-ip
# Database (Production)
DATABASE_URL=postgresql://username:password@localhost:5432/gnx_production
# Email Configuration (Production)
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_USE_SSL=False
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
DEFAULT_FROM_EMAIL=noreply@gnxsoft.com
# Company email for contact form notifications
COMPANY_EMAIL=contact@gnxsoft.com
# Email timeout settings for production reliability
EMAIL_TIMEOUT=30
EMAIL_CONNECTION_TIMEOUT=10
EMAIL_READ_TIMEOUT=10
# Security Settings
SECURE_SSL_REDIRECT=True
SECURE_HSTS_SECONDS=31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS=True
SECURE_HSTS_PRELOAD=True
SECURE_CONTENT_TYPE_NOSNIFF=True
SECURE_BROWSER_XSS_FILTER=True
X_FRAME_OPTIONS=DENY
# CORS Settings (Production)
CORS_ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
CORS_ALLOW_CREDENTIALS=True
# Static Files
STATIC_ROOT=/var/www/gnx/staticfiles/
MEDIA_ROOT=/var/www/gnx/media/
# Logging
LOG_LEVEL=INFO

View File

View File

@@ -0,0 +1,114 @@
from django.contrib import admin
from django.utils.html import format_html
from .models import Service, ServiceFeature, ServiceExpertise, ServiceCategory
class ServiceFeatureInline(admin.TabularInline):
model = ServiceFeature
extra = 0
fields = ['title', 'description', 'icon', 'display_order']
ordering = ['display_order']
class ServiceExpertiseInline(admin.TabularInline):
model = ServiceExpertise
extra = 0
fields = ['title', 'description', 'icon', 'display_order']
ordering = ['display_order']
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ['title', 'slug', 'category', 'price', 'duration', 'deliverables_preview', 'featured', 'display_order', 'is_active', 'created_at']
list_filter = ['featured', 'is_active', 'category', 'created_at']
search_fields = ['title', 'description', 'slug', 'short_description', 'technologies', 'deliverables']
prepopulated_fields = {'slug': ('title',)}
ordering = ['display_order', 'title']
inlines = [ServiceFeatureInline, ServiceExpertiseInline]
actions = ['mark_as_featured', 'mark_as_not_featured']
fieldsets = (
('Basic Information', {
'fields': ('title', 'slug', 'description', 'short_description', 'icon', 'image', 'image_url')
}),
('Category & Classification', {
'fields': ('category', 'featured', 'display_order', 'is_active')
}),
('Project Details', {
'fields': ('duration', 'deliverables', 'technologies', 'process_steps'),
'description': 'Define what the client will receive and the project timeline'
}),
('Section Descriptions', {
'fields': ('features_description', 'deliverables_description', 'process_description', 'why_choose_description', 'expertise_description'),
'description': 'Customize descriptions for each section on the service detail page',
'classes': ('collapse',)
}),
('Pricing', {
'fields': ('price',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
# Make deliverables field more prominent
if 'deliverables' in form.base_fields:
form.base_fields['deliverables'].widget.attrs.update({
'rows': 4,
'cols': 80,
'placeholder': 'Enter what the client will receive (e.g., Complete web application, Admin dashboard, Documentation, Testing, Deployment)'
})
return form
def deliverables_preview(self, obj):
"""Display a preview of deliverables in the list view"""
if obj.deliverables:
# Show first 50 characters of deliverables
preview = obj.deliverables[:50]
if len(obj.deliverables) > 50:
preview += "..."
return format_html('<span title="{}">{}</span>', obj.deliverables, preview)
return format_html('<span style="color: #999;">No deliverables defined</span>')
deliverables_preview.short_description = 'Deliverables Preview'
def mark_as_featured(self, request, queryset):
"""Admin action to mark services as featured"""
updated = queryset.update(featured=True)
self.message_user(request, f'{updated} service(s) marked as featured.')
mark_as_featured.short_description = "Mark selected services as featured"
def mark_as_not_featured(self, request, queryset):
"""Admin action to mark services as not featured"""
updated = queryset.update(featured=False)
self.message_user(request, f'{updated} service(s) marked as not featured.')
mark_as_not_featured.short_description = "Mark selected services as not featured"
@admin.register(ServiceFeature)
class ServiceFeatureAdmin(admin.ModelAdmin):
list_display = ['title', 'service', 'display_order']
list_filter = ['service', 'display_order']
search_fields = ['title', 'description', 'service__title']
ordering = ['service', 'display_order']
@admin.register(ServiceExpertise)
class ServiceExpertiseAdmin(admin.ModelAdmin):
list_display = ['title', 'service', 'display_order']
list_filter = ['service', 'display_order']
search_fields = ['title', 'description', 'service__title']
ordering = ['service', 'display_order']
@admin.register(ServiceCategory)
class ServiceCategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug', 'display_order', 'is_active']
list_filter = ['is_active', 'display_order']
search_fields = ['name', 'description', 'slug']
prepopulated_fields = {'slug': ('name',)}
ordering = ['display_order', 'name']

View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class ServicesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'services'
verbose_name = 'Services'

View File

@@ -0,0 +1,230 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from services.models import Service, ServiceCategory, ServiceFeature
class Command(BaseCommand):
help = 'Import completely fresh service data'
def handle(self, *args, **options):
with transaction.atomic():
# Clear existing data
ServiceFeature.objects.all().delete()
Service.objects.all().delete()
ServiceCategory.objects.all().delete()
self.stdout.write(self.style.WARNING('Cleared existing service data'))
# Create fresh categories
categories = {
'web-development': {
'name': 'Web Development',
'description': 'Custom web applications and modern websites',
'display_order': 1
},
'mobile-development': {
'name': 'Mobile Development',
'description': 'Native and cross-platform mobile applications',
'display_order': 2
},
'api-development': {
'name': 'API Development',
'description': 'RESTful APIs and microservices architecture',
'display_order': 3
},
'cloud-services': {
'name': 'Cloud Services',
'description': 'Cloud migration and infrastructure management',
'display_order': 4
},
'ai-ml': {
'name': 'AI & Machine Learning',
'description': 'Artificial intelligence and machine learning solutions',
'display_order': 5
}
}
created_categories = {}
for slug, data in categories.items():
category, created = ServiceCategory.objects.get_or_create(
slug=slug,
defaults={
'name': data['name'],
'description': data['description'],
'display_order': data['display_order'],
'is_active': True
}
)
created_categories[slug] = category
self.stdout.write(f'Created category: {category.name}')
# Create fresh services with detailed data
services_data = [
{
'title': 'Enterprise Web Application',
'description': 'Build powerful, scalable web applications tailored to your business needs. Our expert developers use modern frameworks and technologies to create solutions that drive growth and efficiency.',
'short_description': 'Custom enterprise web applications with modern architecture and scalable infrastructure.',
'slug': 'enterprise-web-application',
'icon': 'code',
'price': '25000.00',
'category_slug': 'web-development',
'duration': '8-12 weeks',
'deliverables': 'Complete Web Application, Admin Dashboard, User Authentication System, Database Design, API Integration, Responsive Design, Security Implementation, Testing Suite, Deployment Setup, Documentation, Training Materials, 3 Months Support',
'technologies': 'React, Next.js, TypeScript, Node.js, PostgreSQL, Redis, AWS, Docker, Kubernetes, Jest, Cypress',
'process_steps': 'Discovery & Requirements, UI/UX Design, Architecture Planning, Backend Development, Frontend Development, API Development, Database Setup, Security Implementation, Testing & QA, Deployment, Training, Launch Support',
'featured': True,
'display_order': 1,
'features': [
{'title': 'Scalable Architecture', 'description': 'Built with microservices and cloud-native architecture', 'icon': 'cloud'},
{'title': 'Advanced Security', 'description': 'Enterprise-grade security with authentication and authorization', 'icon': 'shield'},
{'title': 'Real-time Features', 'description': 'WebSocket integration for real-time updates and notifications', 'icon': 'bolt'},
{'title': 'Mobile Responsive', 'description': 'Fully responsive design that works on all devices', 'icon': 'mobile'},
{'title': 'Performance Optimized', 'description': 'Optimized for speed and performance with caching strategies', 'icon': 'gauge'},
{'title': 'Analytics Integration', 'description': 'Built-in analytics and reporting capabilities', 'icon': 'chart-bar'}
]
},
{
'title': 'Cross-Platform Mobile App',
'description': 'Create stunning mobile applications for iOS and Android platforms. We deliver native and cross-platform solutions that provide exceptional user experiences and drive engagement.',
'short_description': 'Native and cross-platform mobile applications with modern UI/UX design.',
'slug': 'cross-platform-mobile-app',
'icon': 'mobile',
'price': '35000.00',
'category_slug': 'mobile-development',
'duration': '12-16 weeks',
'deliverables': 'iOS Mobile App, Android Mobile App, Admin Panel, Backend API, Push Notifications, Offline Support, App Store Submission, Google Play Submission, User Documentation, Admin Guide, Testing Suite, 6 Months Support',
'technologies': 'React Native, TypeScript, Node.js, MongoDB, Firebase, AWS, App Store Connect, Google Play Console, Jest, Detox',
'process_steps': 'Market Research, UI/UX Design, Prototyping, Backend Development, Mobile App Development, API Integration, Testing, App Store Optimization, Submission Process, Launch Strategy, Post-launch Support',
'featured': True,
'display_order': 2,
'features': [
{'title': 'Cross-Platform', 'description': 'Single codebase for both iOS and Android platforms', 'icon': 'mobile'},
{'title': 'Native Performance', 'description': 'Optimized performance using native components and modules', 'icon': 'gauge'},
{'title': 'Offline Support', 'description': 'Full offline functionality with data synchronization', 'icon': 'wifi'},
{'title': 'Push Notifications', 'description': 'Real-time push notifications for user engagement', 'icon': 'bell'},
{'title': 'App Store Ready', 'description': 'Complete app store submission and approval process', 'icon': 'store'},
{'title': 'Analytics Dashboard', 'description': 'Comprehensive analytics and user behavior tracking', 'icon': 'chart-line'}
]
},
{
'title': 'RESTful API Development',
'description': 'Build robust, scalable APIs that power your applications and enable seamless integration with third-party services. Our APIs are designed for performance, security, and maintainability.',
'short_description': 'Enterprise-grade RESTful APIs with comprehensive documentation and security.',
'slug': 'restful-api-development',
'icon': 'api',
'price': '15000.00',
'category_slug': 'api-development',
'duration': '4-6 weeks',
'deliverables': 'RESTful API, API Documentation, Authentication System, Rate Limiting, API Testing Suite, Postman Collection, SDK Development, Integration Examples, Performance Monitoring, Security Audit, Deployment Guide, 3 Months Support',
'technologies': 'Node.js, Express, TypeScript, PostgreSQL, Redis, JWT, Swagger, Postman, Jest, AWS API Gateway, Docker',
'process_steps': 'API Planning, Database Design, Authentication Setup, Endpoint Development, Documentation, Testing, Security Review, Performance Optimization, Deployment, Integration Testing, Monitoring Setup',
'featured': False,
'display_order': 3,
'features': [
{'title': 'RESTful Design', 'description': 'Clean, intuitive API design following REST principles', 'icon': 'code'},
{'title': 'Comprehensive Documentation', 'description': 'Interactive API documentation with examples', 'icon': 'book'},
{'title': 'Authentication & Security', 'description': 'JWT-based authentication with rate limiting', 'icon': 'shield'},
{'title': 'Performance Optimized', 'description': 'Caching and optimization for high performance', 'icon': 'gauge'},
{'title': 'SDK Support', 'description': 'Client SDKs for easy integration', 'icon': 'puzzle-piece'},
{'title': 'Monitoring & Analytics', 'description': 'Built-in monitoring and usage analytics', 'icon': 'chart-bar'}
]
},
{
'title': 'Cloud Migration & DevOps',
'description': 'Migrate your existing infrastructure to the cloud with minimal downtime. We help you leverage cloud technologies for improved scalability, security, and cost efficiency.',
'short_description': 'Complete cloud migration with DevOps automation and monitoring.',
'slug': 'cloud-migration-devops',
'icon': 'cloud',
'price': '45000.00',
'category_slug': 'cloud-services',
'duration': '16-20 weeks',
'deliverables': 'Cloud Infrastructure Setup, Application Migration, CI/CD Pipeline, Monitoring Setup, Security Configuration, Backup Strategy, Disaster Recovery Plan, Cost Optimization, Performance Tuning, Documentation, Training, 6 Months Support',
'technologies': 'AWS, Azure, Google Cloud, Docker, Kubernetes, Terraform, Jenkins, GitLab CI, Prometheus, Grafana, ELK Stack',
'process_steps': 'Infrastructure Assessment, Migration Planning, Security Audit, Infrastructure Setup, Application Migration, CI/CD Implementation, Monitoring Setup, Testing, Go-live, Optimization, Documentation, Training',
'featured': True,
'display_order': 4,
'features': [
{'title': 'Zero Downtime Migration', 'description': 'Seamless migration with minimal service interruption', 'icon': 'clock'},
{'title': 'Cost Optimization', 'description': 'Optimized cloud resources for maximum cost efficiency', 'icon': 'dollar-sign'},
{'title': 'Security First', 'description': 'Enhanced security with cloud-native security features', 'icon': 'shield'},
{'title': 'Automated DevOps', 'description': 'Complete CI/CD pipeline with automated deployments', 'icon': 'cogs'},
{'title': 'Monitoring & Alerting', 'description': 'Comprehensive monitoring and alerting system', 'icon': 'bell'},
{'title': 'Scalability', 'description': 'Auto-scaling infrastructure for handling traffic spikes', 'icon': 'expand'}
]
},
{
'title': 'AI-Powered Business Intelligence',
'description': 'Transform your business data into actionable insights with AI-powered analytics and machine learning solutions. Make data-driven decisions with advanced predictive analytics.',
'short_description': 'AI-powered business intelligence and predictive analytics platform.',
'slug': 'ai-powered-business-intelligence',
'icon': 'brain',
'price': '55000.00',
'category_slug': 'ai-ml',
'duration': '20-24 weeks',
'deliverables': 'AI Analytics Platform, Machine Learning Models, Data Pipeline, Dashboard Development, Predictive Analytics, Report Generation, API Integration, Data Visualization, Model Training, Performance Monitoring, Documentation, 12 Months Support',
'technologies': 'Python, TensorFlow, PyTorch, Pandas, NumPy, Scikit-learn, React, D3.js, PostgreSQL, Redis, AWS SageMaker, Docker',
'process_steps': 'Data Analysis, Model Selection, Data Pipeline Development, Model Training, Dashboard Development, API Development, Testing, Deployment, Performance Monitoring, Optimization, Documentation, Training',
'featured': True,
'display_order': 5,
'features': [
{'title': 'Predictive Analytics', 'description': 'Advanced ML models for business forecasting', 'icon': 'chart-line'},
{'title': 'Real-time Insights', 'description': 'Live data processing and real-time analytics', 'icon': 'bolt'},
{'title': 'Interactive Dashboards', 'description': 'Beautiful, interactive data visualization', 'icon': 'chart-bar'},
{'title': 'Automated Reports', 'description': 'Automated report generation and distribution', 'icon': 'file-alt'},
{'title': 'Data Integration', 'description': 'Seamless integration with existing data sources', 'icon': 'plug'},
{'title': 'Scalable Architecture', 'description': 'Cloud-native architecture for handling big data', 'icon': 'cloud'}
]
},
{
'title': 'E-commerce Platform',
'description': 'Build a complete e-commerce solution with modern features, secure payment processing, and advanced analytics. Create an online store that converts visitors into customers.',
'short_description': 'Complete e-commerce platform with payment processing and analytics.',
'slug': 'ecommerce-platform',
'icon': 'shopping-cart',
'price': '30000.00',
'category_slug': 'web-development',
'duration': '10-14 weeks',
'deliverables': 'E-commerce Website, Admin Dashboard, Payment Integration, Inventory Management, Order Management, Customer Portal, Analytics Dashboard, SEO Optimization, Mobile App, Testing Suite, Documentation, 6 Months Support',
'technologies': 'React, Next.js, Node.js, PostgreSQL, Stripe, PayPal, AWS, Redis, Elasticsearch, Jest, Cypress',
'process_steps': 'Requirements Analysis, UI/UX Design, Database Design, Backend Development, Frontend Development, Payment Integration, Testing, SEO Optimization, Performance Tuning, Launch, Marketing Setup, Support',
'featured': False,
'display_order': 6,
'features': [
{'title': 'Secure Payments', 'description': 'Multiple payment gateways with PCI compliance', 'icon': 'credit-card'},
{'title': 'Inventory Management', 'description': 'Advanced inventory tracking and management', 'icon': 'box'},
{'title': 'Customer Analytics', 'description': 'Detailed customer behavior and sales analytics', 'icon': 'chart-bar'},
{'title': 'Mobile Optimized', 'description': 'Fully responsive design for mobile shopping', 'icon': 'mobile'},
{'title': 'SEO Ready', 'description': 'Built-in SEO optimization for better visibility', 'icon': 'search'},
{'title': 'Multi-vendor Support', 'description': 'Support for multiple vendors and marketplace', 'icon': 'store'}
]
}
]
for service_data in services_data:
# Extract features before creating service
features = service_data.pop('features', [])
category_slug = service_data.pop('category_slug')
# Create service
service = Service.objects.create(
category=created_categories[category_slug],
**service_data
)
self.stdout.write(f'Created service: {service.title}')
# Create features for the service
for feature_data in features:
ServiceFeature.objects.create(
service=service,
**feature_data
)
self.stdout.write(f' Added {len(features)} features')
self.stdout.write(
self.style.SUCCESS('Successfully imported fresh service data!')
)
self.stdout.write(
self.style.SUCCESS(f'Created {Service.objects.count()} services with {ServiceFeature.objects.count()} features')
)

View File

@@ -0,0 +1,158 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from services.models import Service, ServiceExpertise
class Command(BaseCommand):
help = 'Populate expertise items for existing services'
def handle(self, *args, **options):
with transaction.atomic():
# Expertise data for each service
expertise_data = {
'enterprise-web-application': [
{
'title': 'Expert Development Team',
'description': 'Our experienced team specializes in enterprise web applications with years of industry expertise. We bring deep knowledge and proven methodologies to every project.',
'icon': 'users',
'display_order': 1
},
{
'title': 'Modern Technology Stack',
'description': 'We use cutting-edge technologies and frameworks to deliver scalable, secure, and future-proof web applications.',
'icon': 'code',
'display_order': 2
},
{
'title': 'Proven Track Record',
'description': 'We have successfully delivered enterprise web applications to numerous clients, helping them achieve their business goals and drive growth.',
'icon': 'chart-line',
'display_order': 3
}
],
'cross-platform-mobile-app': [
{
'title': 'Mobile Development Experts',
'description': 'Our specialized mobile development team has extensive experience in both native and cross-platform app development.',
'icon': 'mobile',
'display_order': 1
},
{
'title': 'Cross-Platform Mastery',
'description': 'We excel in React Native and Flutter development, delivering high-performance apps for both iOS and Android platforms.',
'icon': 'layer-group',
'display_order': 2
},
{
'title': 'App Store Success',
'description': 'We have successfully launched numerous apps in both Apple App Store and Google Play Store with excellent ratings.',
'icon': 'store',
'display_order': 3
}
],
'restful-api-development': [
{
'title': 'API Architecture Experts',
'description': 'Our team specializes in designing and building robust, scalable APIs that power modern applications.',
'icon': 'network-wired',
'display_order': 1
},
{
'title': 'Performance Optimization',
'description': 'We create high-performance APIs with caching, rate limiting, and optimization techniques for maximum efficiency.',
'icon': 'gauge',
'display_order': 2
},
{
'title': 'Comprehensive Documentation',
'description': 'We provide detailed API documentation with interactive examples and SDKs for easy integration.',
'icon': 'book',
'display_order': 3
}
],
'cloud-migration-devops': [
{
'title': 'Cloud Migration Specialists',
'description': 'Our certified cloud engineers have extensive experience in migrating complex infrastructures with minimal downtime.',
'icon': 'cloud',
'display_order': 1
},
{
'title': 'DevOps Automation',
'description': 'We implement complete CI/CD pipelines and automation workflows to streamline your development process.',
'icon': 'cogs',
'display_order': 2
},
{
'title': 'Cost Optimization',
'description': 'We help optimize your cloud costs while maintaining performance and reliability standards.',
'icon': 'dollar-sign',
'display_order': 3
}
],
'ai-powered-business-intelligence': [
{
'title': 'AI & ML Specialists',
'description': 'Our data scientists and AI engineers have deep expertise in machine learning and business intelligence solutions.',
'icon': 'brain',
'display_order': 1
},
{
'title': 'Advanced Analytics',
'description': 'We create sophisticated analytics platforms that transform raw data into actionable business insights.',
'icon': 'chart-bar',
'display_order': 2
},
{
'title': 'Predictive Modeling',
'description': 'Our team builds advanced predictive models that help businesses make data-driven decisions.',
'icon': 'crystal-ball',
'display_order': 3
}
],
'ecommerce-platform': [
{
'title': 'E-commerce Specialists',
'description': 'Our team has extensive experience in building high-converting e-commerce platforms for various industries.',
'icon': 'shopping-cart',
'display_order': 1
},
{
'title': 'Payment Integration',
'description': 'We integrate secure payment gateways and ensure PCI compliance for safe transactions.',
'icon': 'credit-card',
'display_order': 2
},
{
'title': 'SEO & Performance',
'description': 'We optimize e-commerce sites for search engines and performance to maximize conversions.',
'icon': 'search',
'display_order': 3
}
]
}
for service_slug, expertise_items in expertise_data.items():
try:
service = Service.objects.get(slug=service_slug)
# Clear existing expertise items
ServiceExpertise.objects.filter(service=service).delete()
# Create new expertise items
for expertise_data in expertise_items:
ServiceExpertise.objects.create(
service=service,
**expertise_data
)
self.stdout.write(f'Added expertise items for: {service.title}')
except Service.DoesNotExist:
self.stdout.write(
self.style.WARNING(f'Service with slug "{service_slug}" not found')
)
self.stdout.write(
self.style.SUCCESS('Successfully populated expertise items!')
)

View File

@@ -0,0 +1,75 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from services.models import Service
class Command(BaseCommand):
help = 'Populate section descriptions for existing services'
def handle(self, *args, **options):
with transaction.atomic():
# Section descriptions for each service
descriptions_data = {
'enterprise-web-application': {
'features_description': 'Our enterprise web applications are built with cutting-edge technologies and industry best practices to deliver exceptional performance and user experience.',
'deliverables_description': 'Get a complete enterprise-grade web application with advanced features, comprehensive documentation, and ongoing support.',
'process_description': 'We follow a structured 8-step development process that ensures quality, efficiency, and timely delivery of your enterprise web application.',
'why_choose_description': 'Why choose our Enterprise Web Application service?',
'expertise_description': 'Our team brings years of experience in enterprise web development, combining technical expertise with business understanding.'
},
'cross-platform-mobile-app': {
'features_description': 'Our cross-platform mobile apps deliver native-like performance while reducing development time and costs significantly.',
'deliverables_description': 'Receive a fully functional mobile app for both iOS and Android platforms, complete with app store deployment support.',
'process_description': 'Our proven mobile development methodology ensures your app meets all platform requirements and user expectations.',
'why_choose_description': 'Why choose our Cross-Platform Mobile App service?',
'expertise_description': 'We specialize in React Native and Flutter development, delivering high-performance apps that work seamlessly across platforms.'
},
'restful-api-development': {
'features_description': 'Our RESTful APIs are designed for scalability, security, and ease of integration with comprehensive documentation.',
'deliverables_description': 'Get a robust, well-documented API with SDKs, testing tools, and comprehensive integration support.',
'process_description': 'We follow API-first development principles to ensure your API is reliable, scalable, and developer-friendly.',
'why_choose_description': 'Why choose our RESTful API Development service?',
'expertise_description': 'Our API specialists have extensive experience in designing and building enterprise-grade APIs for various industries.'
},
'cloud-migration-devops': {
'features_description': 'Our cloud migration and DevOps services ensure seamless transition to the cloud with zero downtime and improved performance.',
'deliverables_description': 'Complete cloud migration with automated CI/CD pipelines, monitoring, and cost optimization strategies.',
'process_description': 'Our systematic approach to cloud migration minimizes risks while maximizing the benefits of cloud infrastructure.',
'why_choose_description': 'Why choose our Cloud Migration & DevOps service?',
'expertise_description': 'We are certified cloud architects with proven experience in migrating complex infrastructures and implementing DevOps practices.'
},
'ai-powered-business-intelligence': {
'features_description': 'Transform your data into actionable insights with our AI-powered business intelligence solutions.',
'deliverables_description': 'Get a complete BI platform with AI-powered analytics, predictive models, and interactive dashboards.',
'process_description': 'Our data science methodology ensures your BI solution provides accurate insights and drives informed decision-making.',
'why_choose_description': 'Why choose our AI-Powered Business Intelligence service?',
'expertise_description': 'Our data scientists and AI engineers specialize in creating intelligent solutions that turn data into competitive advantage.'
},
'ecommerce-platform': {
'features_description': 'Build a high-converting e-commerce platform with advanced features, secure payments, and mobile optimization.',
'deliverables_description': 'Get a complete e-commerce solution with payment integration, inventory management, and marketing tools.',
'process_description': 'We follow e-commerce best practices to ensure your platform drives sales and provides excellent customer experience.',
'why_choose_description': 'Why choose our E-commerce Platform service?',
'expertise_description': 'Our e-commerce specialists have built successful online stores for businesses across various industries and sizes.'
}
}
for service_slug, descriptions in descriptions_data.items():
try:
service = Service.objects.get(slug=service_slug)
# Update service with new descriptions
for field, value in descriptions.items():
setattr(service, field, value)
service.save()
self.stdout.write(f'Updated section descriptions for: {service.title}')
except Service.DoesNotExist:
self.stdout.write(
self.style.WARNING(f'Service with slug "{service_slug}" not found')
)
self.stdout.write(
self.style.SUCCESS('Successfully populated section descriptions!')
)

View File

@@ -0,0 +1,221 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from services.models import Service, ServiceCategory, ServiceFeature
class Command(BaseCommand):
help = 'Populate the database with sample services'
def handle(self, *args, **options):
with transaction.atomic():
# Create categories
web_dev_category, created = ServiceCategory.objects.get_or_create(
slug='web-development',
defaults={
'name': 'Web Development',
'description': 'Custom web applications and websites',
'display_order': 1,
'is_active': True
}
)
mobile_category, created = ServiceCategory.objects.get_or_create(
slug='mobile-development',
defaults={
'name': 'Mobile Development',
'description': 'Native and cross-platform mobile apps',
'display_order': 2,
'is_active': True
}
)
api_category, created = ServiceCategory.objects.get_or_create(
slug='api-development',
defaults={
'name': 'API Development',
'description': 'RESTful APIs and microservices',
'display_order': 3,
'is_active': True
}
)
# Create services
services_data = [
{
'title': 'Custom Web Application Development',
'description': 'Build powerful, scalable web applications tailored to your business needs. Our expert developers use modern frameworks and technologies to create solutions that drive growth and efficiency.',
'short_description': 'Custom web applications built with modern technologies for optimal performance and scalability.',
'slug': 'custom-web-application-development',
'icon': 'code',
'price': '5000.00',
'category': web_dev_category,
'duration': '4-8 weeks',
'deliverables': 'Responsive Web Application, Admin Dashboard, User Authentication, Database Design, API Integration, Testing & Deployment',
'technologies': 'React, Next.js, Node.js, PostgreSQL, AWS, Docker',
'process_steps': 'Requirements Analysis, System Design, Development, Testing, Deployment, Training',
'featured': True,
'display_order': 1,
'is_active': True
},
{
'title': 'Mobile App Development',
'description': 'Create stunning mobile applications for iOS and Android platforms. We deliver native and cross-platform solutions that provide exceptional user experiences and drive engagement.',
'short_description': 'Native and cross-platform mobile applications for iOS and Android.',
'slug': 'mobile-app-development',
'icon': 'mobile',
'price': '8000.00',
'category': mobile_category,
'duration': '6-12 weeks',
'deliverables': 'Native Mobile App, Cross-platform App, App Store Deployment, Push Notifications, Offline Support, Analytics Integration',
'technologies': 'React Native, Flutter, Swift, Kotlin, Firebase, App Store Connect',
'process_steps': 'UI/UX Design, Development, Testing, App Store Submission, Launch Support',
'featured': True,
'display_order': 2,
'is_active': True
},
{
'title': 'API Development & Integration',
'description': 'Build robust, scalable APIs that power your applications and enable seamless integration with third-party services. Our APIs are designed for performance, security, and maintainability.',
'short_description': 'RESTful APIs and microservices for seamless system integration.',
'slug': 'api-development-integration',
'icon': 'api',
'price': '3000.00',
'category': api_category,
'duration': '2-4 weeks',
'deliverables': 'RESTful API, API Documentation, Authentication System, Rate Limiting, Monitoring, Integration Support',
'technologies': 'Node.js, Python, FastAPI, PostgreSQL, Redis, AWS API Gateway',
'process_steps': 'API Planning, Development, Documentation, Testing, Deployment, Integration',
'featured': False,
'display_order': 3,
'is_active': True
},
{
'title': 'Cloud Migration Services',
'description': 'Migrate your existing infrastructure to the cloud with minimal downtime. We help you leverage cloud technologies for improved scalability, security, and cost efficiency.',
'short_description': 'Seamless migration to cloud platforms with enhanced security and scalability.',
'slug': 'cloud-migration-services',
'icon': 'cloud',
'price': '10000.00',
'category': web_dev_category,
'duration': '8-16 weeks',
'deliverables': 'Cloud Infrastructure Setup, Data Migration, Security Configuration, Monitoring Setup, Training, Ongoing Support',
'technologies': 'AWS, Azure, Google Cloud, Docker, Kubernetes, Terraform',
'process_steps': 'Assessment, Migration Planning, Implementation, Testing, Optimization, Support',
'featured': True,
'display_order': 4,
'is_active': True
}
]
for service_data in services_data:
service, created = Service.objects.get_or_create(
slug=service_data['slug'],
defaults=service_data
)
if created:
self.stdout.write(
self.style.SUCCESS(f'Created service: {service.title}')
)
# Add features for each service
if service.slug == 'custom-web-application-development':
features = [
{
'title': 'Responsive Design',
'description': 'Mobile-first design that works perfectly on all devices',
'icon': 'mobile',
'display_order': 1
},
{
'title': 'Performance Optimization',
'description': 'Fast loading times and optimized user experience',
'icon': 'bolt',
'display_order': 2
},
{
'title': 'Security Features',
'description': 'Built-in security measures to protect your data',
'icon': 'shield',
'display_order': 3
}
]
elif service.slug == 'mobile-app-development':
features = [
{
'title': 'Cross-Platform',
'description': 'Single codebase for both iOS and Android',
'icon': 'mobile',
'display_order': 1
},
{
'title': 'Native Performance',
'description': 'Optimized performance using native components',
'icon': 'gauge',
'display_order': 2
},
{
'title': 'App Store Ready',
'description': 'Complete app store submission and approval process',
'icon': 'store',
'display_order': 3
}
]
elif service.slug == 'api-development-integration':
features = [
{
'title': 'RESTful Design',
'description': 'Clean, intuitive API design following REST principles',
'icon': 'code',
'display_order': 1
},
{
'title': 'Comprehensive Documentation',
'description': 'Detailed API documentation with examples',
'icon': 'book',
'display_order': 2
},
{
'title': 'Rate Limiting',
'description': 'Built-in rate limiting and security measures',
'icon': 'shield',
'display_order': 3
}
]
elif service.slug == 'cloud-migration-services':
features = [
{
'title': 'Zero Downtime',
'description': 'Seamless migration with minimal service interruption',
'icon': 'clock',
'display_order': 1
},
{
'title': 'Cost Optimization',
'description': 'Optimized cloud resources for maximum cost efficiency',
'icon': 'dollar-sign',
'display_order': 2
},
{
'title': 'Security First',
'description': 'Enhanced security with cloud-native security features',
'icon': 'shield',
'display_order': 3
}
]
else:
features = []
for feature_data in features:
ServiceFeature.objects.create(
service=service,
**feature_data
)
else:
self.stdout.write(
self.style.WARNING(f'Service already exists: {service.title}')
)
self.stdout.write(
self.style.SUCCESS('Successfully populated services database')
)

View File

@@ -0,0 +1,70 @@
# Generated by Django 4.2.7 on 2025-09-25 07:54
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Service',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Service title', max_length=200)),
('description', models.TextField(help_text='Service description')),
('slug', models.SlugField(help_text='URL-friendly version of the title', max_length=200, unique=True)),
('icon', models.CharField(help_text="Icon name (e.g., 'code', 'api', 'cloud')", max_length=50)),
('image', models.CharField(help_text='Path to service image', max_length=500)),
('price', models.DecimalField(decimal_places=2, help_text='Service price', max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
('featured', models.BooleanField(default=False, help_text='Whether this service is featured')),
('display_order', models.PositiveIntegerField(default=0, help_text='Order for displaying services')),
('is_active', models.BooleanField(default=True, help_text='Whether this service is active')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Service',
'verbose_name_plural': 'Services',
'ordering': ['display_order', 'created_at'],
},
),
migrations.CreateModel(
name='ServiceCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
('description', models.TextField(blank=True)),
('display_order', models.PositiveIntegerField(default=0)),
('is_active', models.BooleanField(default=True)),
],
options={
'verbose_name': 'Service Category',
'verbose_name_plural': 'Service Categories',
'ordering': ['display_order', 'name'],
},
),
migrations.CreateModel(
name='ServiceFeature',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Feature title', max_length=200)),
('description', models.TextField(blank=True, help_text='Feature description')),
('icon', models.CharField(blank=True, help_text='Feature icon', max_length=50)),
('display_order', models.PositiveIntegerField(default=0, help_text='Order for displaying features')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='services.service')),
],
options={
'verbose_name': 'Service Feature',
'verbose_name_plural': 'Service Features',
'ordering': ['display_order', 'title'],
},
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.2.7 on 2025-09-25 08:04
from django.db import migrations, models
import services.models
class Migration(migrations.Migration):
dependencies = [
('services', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='service',
name='image_url',
field=models.CharField(blank=True, help_text='External image URL (alternative to uploaded image)', max_length=500),
),
migrations.AlterField(
model_name='service',
name='image',
field=models.ImageField(blank=True, help_text='Service image', null=True, upload_to=services.models.service_image_upload_path),
),
]

View File

@@ -0,0 +1,39 @@
# Generated by Django 4.2.7 on 2025-09-25 08:24
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('services', '0002_service_image_url_alter_service_image'),
]
operations = [
migrations.AddField(
model_name='service',
name='category',
field=models.ForeignKey(blank=True, help_text='Service category', null=True, on_delete=django.db.models.deletion.SET_NULL, to='services.servicecategory'),
),
migrations.AddField(
model_name='service',
name='deliverables',
field=models.TextField(blank=True, help_text='What you get with this service'),
),
migrations.AddField(
model_name='service',
name='duration',
field=models.CharField(blank=True, help_text="Project duration (e.g., '2-4 weeks', '3-6 months')", max_length=100),
),
migrations.AddField(
model_name='service',
name='short_description',
field=models.CharField(blank=True, help_text='Short description for cards and previews', max_length=300),
),
migrations.AddField(
model_name='service',
name='technologies',
field=models.TextField(blank=True, help_text='Technologies and tools used'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.7 on 2025-09-25 08:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('services', '0003_service_category_service_deliverables_and_more'),
]
operations = [
migrations.AddField(
model_name='service',
name='process_steps',
field=models.TextField(blank=True, help_text='Process steps in JSON format or comma-separated'),
),
]

Some files were not shown because too many files have changed in this diff Show More