diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index e0ce395..cedff70 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/mail.cpython-311.pyc b/core/__pycache__/mail.cpython-311.pyc new file mode 100644 index 0000000..cf89663 Binary files /dev/null and b/core/__pycache__/mail.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 673917c..8e53bb3 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index d529511..1c83655 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py index df3ac14..ab9441c 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,7 +1,7 @@ from django import forms from .models import Truck, Shipment, Bid, Profile, Country, OTPCode, City, TruckType, AppSetting, ContactMessage from django.utils.translation import gettext_lazy as _ -from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.contrib.auth.models import User class UserRegistrationForm(UserCreationForm): @@ -11,6 +11,7 @@ class UserRegistrationForm(UserCreationForm): country_code = forms.ChoiceField(choices=[], widget=forms.Select(attrs={'class': 'form-select'})) phone_number = forms.CharField(max_length=20, required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '123456789'})) subscription_plan = forms.ChoiceField(choices=[('MONTHLY', _('Monthly')), ('ANNUAL', _('Annual'))], required=False, widget=forms.Select(attrs={'class': 'form-select'}), label=_('Subscription Plan')) + otp_method = forms.ChoiceField(choices=[("whatsapp", _("WhatsApp")), ("email", _("Email"))], initial="whatsapp", widget=forms.RadioSelect, label=_("Receive verification code via")) accept_terms = forms.BooleanField(required=True, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}), label=_('I have read and agree to the Terms and Conditions and Privacy Policy')) class Meta(UserCreationForm.Meta): @@ -199,3 +200,18 @@ class ContactForm(forms.ModelForm): 'subject': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Subject')}), 'message': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': _('Your Message')}), } + +class CustomLoginForm(AuthenticationForm): + otp_method = forms.ChoiceField( + choices=[("whatsapp", _("WhatsApp")), ("email", _("Email"))], + initial="whatsapp", + widget=forms.RadioSelect, + label=_("Receive verification code via") + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field in self.fields.values(): + if not isinstance(field.widget, (forms.RadioSelect, forms.CheckboxInput)): + field.widget.attrs.update({"class": "form-control"}) + diff --git a/core/mail.py b/core/mail.py new file mode 100644 index 0000000..18ad8d1 --- /dev/null +++ b/core/mail.py @@ -0,0 +1,40 @@ +import logging +from django.core.mail import send_mail +from django.conf import settings +from django.utils.translation import gettext_lazy as _ + +logger = logging.getLogger(__name__) + +def send_otp_email(email, code): + """ + Sends an OTP code via email. + """ + subject = _("Your verification code for MASAR CARGO") + message = _("Your verification code for MASAR CARGO is: %(code)s") % {"code": code} + from_email = settings.DEFAULT_FROM_EMAIL + recipient_list = [email] + + try: + send_mail(subject, message, from_email, recipient_list) + logger.info(f"OTP email sent to {email}") + return True + except Exception as e: + logger.exception(f"Exception while sending OTP email: {str(e)}") + return False + +def send_contact_message(name, email, message): + """ + Sends a contact message to the admin. + """ + subject = _("New contact message from %(name)s") % {"name": name} + full_message = f"Name: {name}\nEmail: {email}\n\nMessage:\n{message}" + from_email = settings.DEFAULT_FROM_EMAIL + recipient_list = settings.CONTACT_EMAIL_TO + + try: + send_mail(subject, full_message, from_email, recipient_list) + logger.info(f"Contact message from {email} sent to admins") + return True + except Exception as e: + logger.exception(f"Exception while sending contact message: {str(e)}") + return False diff --git a/core/migrations/0027_otpcode_email_alter_otpcode_phone_number.py b/core/migrations/0027_otpcode_email_alter_otpcode_phone_number.py new file mode 100644 index 0000000..6a6c3a1 --- /dev/null +++ b/core/migrations/0027_otpcode_email_alter_otpcode_phone_number.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-01-25 02:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0026_remove_banner_admin_phone_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='otpcode', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True), + ), + migrations.AlterField( + model_name='otpcode', + name='phone_number', + field=models.CharField(blank=True, max_length=20, null=True), + ), + ] diff --git a/core/migrations/__pycache__/0027_otpcode_email_alter_otpcode_phone_number.cpython-311.pyc b/core/migrations/__pycache__/0027_otpcode_email_alter_otpcode_phone_number.cpython-311.pyc new file mode 100644 index 0000000..356f778 Binary files /dev/null and b/core/migrations/__pycache__/0027_otpcode_email_alter_otpcode_phone_number.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 1eb5552..b6f951e 100644 --- a/core/models.py +++ b/core/models.py @@ -93,7 +93,8 @@ class Profile(models.Model): return f"{self.user.username} - {self.role}" class OTPCode(models.Model): - phone_number = models.CharField(max_length=20) + phone_number = models.CharField(max_length=20, null=True, blank=True) + email = models.EmailField(null=True, blank=True) code = models.CharField(max_length=6) created_at = models.DateTimeField(auto_now_add=True) is_used = models.BooleanField(default=False) @@ -103,9 +104,9 @@ class OTPCode(models.Model): return not self.is_used and (timezone.now() - self.created_at).total_seconds() < 600 @staticmethod - def generate_code(phone_number): + def generate_code(phone_number=None, email=None): code = ''.join(random.choices(string.digits, k=6)) - return OTPCode.objects.create(phone_number=phone_number, code=code) + return OTPCode.objects.create(phone_number=phone_number, email=email, code=code) class Truck(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trucks') diff --git a/core/templates/registration/login.html b/core/templates/registration/login.html index b0edb65..6444cba 100644 --- a/core/templates/registration/login.html +++ b/core/templates/registration/login.html @@ -18,13 +18,41 @@ {% csrf_token %}
{% trans "Don't have an account?" %} {% trans "Register" %}
diff --git a/core/templates/registration/register.html b/core/templates/registration/register.html index e9596cf..c384426 100644 --- a/core/templates/registration/register.html +++ b/core/templates/registration/register.html @@ -47,6 +47,28 @@- {% if purpose == 'registration' %} - {% trans "We have sent a verification code to your WhatsApp number. Please enter it below to complete your registration." %} + {% if otp_method == 'email' %} + {% if purpose == 'registration' %} + {% trans "We have sent a verification code to your email address. Please enter it below to complete your registration." %} + {% else %} + {% trans "We have sent a verification code to your email address. Please enter it below to log in." %} + {% endif %} {% else %} - {% trans "We have sent a verification code to your WhatsApp number. Please enter it below to log in." %} + {% if purpose == 'registration' %} + {% trans "We have sent a verification code to your WhatsApp number. Please enter it below to complete your registration." %} + {% else %} + {% trans "We have sent a verification code to your WhatsApp number. Please enter it below to log in." %} + {% endif %} {% endif %}