Initial commit

This commit is contained in:
ifaryd
2026-05-12 16:54:38 +00:00
commit 8638273475
92 changed files with 6861 additions and 0 deletions

0
apps/core/__init__.py Normal file
View File

15
apps/core/admin.py Normal file
View File

@@ -0,0 +1,15 @@
from django.contrib import admin
from .models import ContactRequest
@admin.register(ContactRequest)
class ContactRequestAdmin(admin.ModelAdmin):
list_display = ['last_name', 'first_name', 'email', 'phone', 'created_at', 'is_read']
list_filter = ['is_read', 'created_at']
search_fields = ['last_name', 'first_name', 'email', 'message']
list_editable = ['is_read']
readonly_fields = ['last_name', 'first_name', 'email', 'phone', 'message', 'created_at']
ordering = ['-created_at']
def has_add_permission(self, request):
return False

6
apps/core/apps.py Normal file
View File

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

View File

@@ -0,0 +1,15 @@
from django.conf import settings
def site_context(request):
careers_enabled = getattr(settings, 'CAREERS_ENABLED', False)
open_jobs_count = 0
if careers_enabled:
from apps.careers.models import JobOffer
open_jobs_count = JobOffer.objects.filter(status=JobOffer.Status.PUBLISHED).count()
return {
'careers_enabled': careers_enabled,
'open_jobs_count': open_jobs_count,
}

29
apps/core/forms.py Normal file
View File

@@ -0,0 +1,29 @@
import re
from django import forms
from .models import ContactRequest
class ContactForm(forms.ModelForm):
class Meta:
model = ContactRequest
fields = ['last_name', 'first_name', 'email', 'phone', 'message']
widgets = {
'last_name': forms.TextInput(attrs={'placeholder': 'Nom', 'class': 'cta-input'}),
'first_name': forms.TextInput(attrs={'placeholder': 'Prénom', 'class': 'cta-input'}),
'email': forms.EmailInput(attrs={'placeholder': 'Adresse email', 'class': 'cta-input'}),
'phone': forms.TextInput(attrs={'placeholder': 'Téléphone', 'class': 'cta-input', 'inputmode': 'numeric', 'pattern': '[0-9+ ]{6,20}'}),
'message': forms.Textarea(attrs={'placeholder': 'Votre demande…', 'class': 'cta-input cta-textarea', 'rows': 4}),
}
labels = {
'last_name': '',
'first_name': '',
'email': '',
'phone': '',
'message': '',
}
def clean_phone(self):
phone = self.cleaned_data.get('phone', '').strip()
if phone and not re.fullmatch(r'[0-9+\s\-]{6,20}', phone):
raise forms.ValidationError('Numéro invalide — chiffres uniquement.')
return phone

View File

@@ -0,0 +1,32 @@
# Generated by Django 3.2.25 on 2026-04-14 10:01
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='ContactRequest',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='Prénom')),
('last_name', models.CharField(max_length=100, verbose_name='Nom')),
('email', models.EmailField(max_length=254, verbose_name='Email')),
('phone', models.CharField(blank=True, max_length=20, verbose_name='Téléphone')),
('message', models.TextField(verbose_name='Demande')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Reçu le')),
('is_read', models.BooleanField(default=False, verbose_name='Lu')),
],
options={
'verbose_name': 'Demande de contact',
'verbose_name_plural': 'Demandes de contact',
'ordering': ['-created_at'],
},
),
]

View File

19
apps/core/models.py Normal file
View File

@@ -0,0 +1,19 @@
from django.db import models
class ContactRequest(models.Model):
first_name = models.CharField('Prénom', max_length=100)
last_name = models.CharField('Nom', max_length=100)
email = models.EmailField('Email')
phone = models.CharField('Téléphone', max_length=20, blank=True)
message = models.TextField('Demande')
created_at = models.DateTimeField('Reçu le', auto_now_add=True)
is_read = models.BooleanField('Lu', default=False)
class Meta:
verbose_name = 'Demande de contact'
verbose_name_plural = 'Demandes de contact'
ordering = ['-created_at']
def __str__(self):
return f"{self.first_name} {self.last_name}{self.email}"

3
apps/core/tests.py Normal file
View File

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

17
apps/core/urls.py Normal file
View File

@@ -0,0 +1,17 @@
from django.urls import path
from . import views
app_name = 'core'
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('contact/', views.ContactAjaxView.as_view(), name='contact_ajax'),
path('a-propos/', views.AboutView.as_view(), name='about'),
path('produits/kiriq/', views.KiriqView.as_view(), name='kiriq'),
path('produits/monitor/', views.MonitorView.as_view(), name='monitor'),
path('produits/joolid/', views.JoolidView.as_view(), name='joolid'),
path('confidentialite/', views.PrivacyView.as_view(), name='privacy'),
# SEO
path('robots.txt', views.RobotsTxtView.as_view(), name='robots_txt'),
path('sitemap.xml', views.SitemapXmlView.as_view(), name='sitemap_xml'),
]

75
apps/core/views.py Normal file
View File

@@ -0,0 +1,75 @@
import logging
from django.views.generic import TemplateView
from django.views import View
from django.http import JsonResponse
from django.core.mail import send_mail
from django.conf import settings
from .forms import ContactForm
logger = logging.getLogger(__name__)
class HomeView(TemplateView):
template_name = 'core/home.html'
class ContactAjaxView(View):
"""Reçoit le formulaire en AJAX, répond en JSON — pas de rechargement."""
def post(self, request, *args, **kwargs):
form = ContactForm(request.POST)
if form.is_valid():
contact = form.save()
subject = f"Nouvelle demande de contact — {contact.first_name} {contact.last_name}"
body = (
f"Nom : {contact.last_name}\n"
f"Prénom : {contact.first_name}\n"
f"Email : {contact.email}\n"
f"Téléphone : {contact.phone or ''}\n\n"
f"Demande :\n{contact.message}"
)
try:
send_mail(
subject,
body,
settings.DEFAULT_FROM_EMAIL,
[settings.CONTACT_NOTIFY_EMAIL],
fail_silently=False,
)
logger.info("Mail contact #%s envoyé → %s", contact.pk, settings.CONTACT_NOTIFY_EMAIL)
except Exception as e:
logger.error("Échec mail contact #%s : %s%s", contact.pk, type(e).__name__, e)
return JsonResponse({'ok': True})
else:
errors = {f: e.get_json_data() for f, e in form.errors.items()}
return JsonResponse({'ok': False, 'errors': errors}, status=400)
class AboutView(TemplateView):
template_name = 'core/about.html'
class KiriqView(TemplateView):
template_name = 'core/products/kiriq.html'
class MonitorView(TemplateView):
template_name = 'core/products/monitor.html'
class JoolidView(TemplateView):
template_name = 'core/products/joolid.html'
class PrivacyView(TemplateView):
template_name = 'core/privacy.html'
class RobotsTxtView(TemplateView):
template_name = 'robots.txt'
content_type = 'text/plain'
class SitemapXmlView(TemplateView):
template_name = 'sitemap.xml'
content_type = 'application/xml'