diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 423a636..89a21c8 100644 Binary files a/config/__pycache__/__init__.cpython-311.pyc and b/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 96bce55..e97298a 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94..cc6f4b1 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index 9c49e09..a54dacc 100644 Binary files a/config/__pycache__/wsgi.cpython-311.pyc and b/config/__pycache__/wsgi.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 291d043..066e1f2 100644 --- a/config/settings.py +++ b/config/settings.py @@ -42,6 +42,7 @@ SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SESSION_COOKIE_SAMESITE = "None" CSRF_COOKIE_SAMESITE = "None" +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ @@ -61,6 +62,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -133,7 +135,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'en' TIME_ZONE = 'UTC' @@ -180,3 +182,17 @@ if EMAIL_USE_SSL: # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +from django.utils.translation import gettext_lazy as _ + +LANGUAGES = [ + ('en', _('English')), + ('ar', _('Arabic')), +] + +LOCALE_PATHS = [ + BASE_DIR / 'locale', +] + +MEDIA_URL = 'media/' +MEDIA_ROOT = BASE_DIR / 'media' diff --git a/config/urls.py b/config/urls.py index bcfc074..cfd9fd4 100644 --- a/config/urls.py +++ b/config/urls.py @@ -1,25 +1,10 @@ -""" -URL configuration for config project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/5.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 include, path from django.conf import settings from django.conf.urls.static import static urlpatterns = [ + path('i18n/', include('django.conf.urls.i18n')), path("admin/", admin.site.urls), path("", include("core.urls")), ] @@ -27,3 +12,4 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 74b1112..d90f7be 100644 Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392..214e066 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 6f131d4..f24c7b7 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf223..e9a897c 100644 Binary files a/core/__pycache__/context_processors.cpython-311.pyc and b/core/__pycache__/context_processors.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index e061640..6eff269 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659..244821d 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd6..e5c3557 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 8c38f3f..3e1ca68 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,3 +1,30 @@ from django.contrib import admin +from .models import Profile, Truck, Shipment, Bid, Message -# Register your models here. +@admin.register(Profile) +class ProfileAdmin(admin.ModelAdmin): + list_display = ('user', 'role', 'phone_number') + list_filter = ('role',) + search_fields = ('user__username', 'phone_number') + +@admin.register(Truck) +class TruckAdmin(admin.ModelAdmin): + list_display = ('truck_type', 'model', 'plate_no', 'owner', 'load_capacity') + search_fields = ('plate_no', 'owner__username', 'truck_type') + +@admin.register(Shipment) +class ShipmentAdmin(admin.ModelAdmin): + list_display = ('origin', 'destination', 'shipper', 'status', 'delivery_date') + list_filter = ('status', 'delivery_date') + search_fields = ('origin', 'destination', 'shipper__username') + +@admin.register(Bid) +class BidAdmin(admin.ModelAdmin): + list_display = ('shipment', 'truck_owner', 'amount', 'status') + list_filter = ('status',) + search_fields = ('shipment__origin', 'shipment__destination', 'truck_owner__username') + +@admin.register(Message) +class MessageAdmin(admin.ModelAdmin): + list_display = ('shipment', 'sender', 'timestamp') + search_fields = ('content', 'sender__username') diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..b83d0bc --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.7 on 2026-01-23 07:18 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField(choices=[('SHIPPER', 'Shipper (Need Goods Moved)'), ('TRUCK_OWNER', 'Truck Owner (Service Provider)'), ('ADMIN', 'Administrator')], default='SHIPPER', max_length=20)), + ('phone_number', models.CharField(blank=True, max_length=20)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/0002_shipment_message_truck_shipment_assigned_truck_bid.py b/core/migrations/0002_shipment_message_truck_shipment_assigned_truck_bid.py new file mode 100644 index 0000000..a8a2cef --- /dev/null +++ b/core/migrations/0002_shipment_message_truck_shipment_assigned_truck_bid.py @@ -0,0 +1,76 @@ +# Generated by Django 5.2.7 on 2026-01-23 09:01 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Shipment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.TextField(verbose_name='Goods Description')), + ('weight', models.CharField(max_length=100, verbose_name='Weight/Volume')), + ('origin', models.CharField(max_length=255, verbose_name='Origin')), + ('destination', models.CharField(max_length=255, verbose_name='Destination')), + ('delivery_date', models.DateField(verbose_name='Requested Delivery Date')), + ('status', models.CharField(choices=[('OPEN', 'Open for Bids'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('CANCELLED', 'Cancelled')], default='OPEN', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('shipper', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shipments', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Message', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to=settings.AUTH_USER_MODEL)), + ('shipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='core.shipment')), + ], + ), + migrations.CreateModel( + name='Truck', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('truck_type', models.CharField(max_length=100, verbose_name='Truck Type')), + ('model', models.CharField(max_length=100, verbose_name='Model')), + ('year', models.PositiveIntegerField(verbose_name='Year')), + ('plate_no', models.CharField(max_length=50, verbose_name='Plate No')), + ('load_capacity', models.CharField(max_length=100, verbose_name='Load Capacity')), + ('color', models.CharField(max_length=50, verbose_name='Color')), + ('truck_picture', models.ImageField(blank=True, null=True, upload_to='trucks/', verbose_name='Truck Picture')), + ('registration_front', models.ImageField(blank=True, null=True, upload_to='docs/', verbose_name='Registration Front')), + ('registration_back', models.ImageField(blank=True, null=True, upload_to='docs/', verbose_name='Registration Back')), + ('driver_license', models.ImageField(blank=True, null=True, upload_to='docs/', verbose_name='Driver License')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='trucks', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='shipment', + name='assigned_truck', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_shipments', to='core.truck'), + ), + migrations.CreateModel( + name='Bid', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Offer Amount')), + ('comments', models.TextField(blank=True, verbose_name='Comments')), + ('status', models.CharField(choices=[('PENDING', 'Pending'), ('ACCEPTED', 'Accepted'), ('REJECTED', 'Rejected')], default='PENDING', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('truck_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bids', to=settings.AUTH_USER_MODEL)), + ('shipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bids', to='core.shipment')), + ('truck', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.truck')), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..ea4016a Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0002_shipment_message_truck_shipment_assigned_truck_bid.cpython-311.pyc b/core/migrations/__pycache__/0002_shipment_message_truck_shipment_assigned_truck_bid.cpython-311.pyc new file mode 100644 index 0000000..ef3f7ed Binary files /dev/null and b/core/migrations/__pycache__/0002_shipment_message_truck_shipment_assigned_truck_bid.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 9c833c8..88b26f9 100644 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and b/core/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..de81f2d 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,99 @@ from django.db import models +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.utils.translation import gettext_lazy as _ -# Create your models here. +class Profile(models.Model): + ROLE_CHOICES = ( + ('SHIPPER', _('Shipper (Need Goods Moved)')), + ('TRUCK_OWNER', _('Truck Owner (Service Provider)')), + ('ADMIN', _('Administrator')), + ) + user = models.OneToOneField(User, on_delete=models.CASCADE) + role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='SHIPPER') + phone_number = models.CharField(max_length=20, blank=True) + + def __str__(self): + return f"{self.user.username} - {self.role}" + +class Truck(models.Model): + owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trucks') + truck_type = models.CharField(_('Truck Type'), max_length=100) + model = models.CharField(_('Model'), max_length=100) + year = models.PositiveIntegerField(_('Year')) + plate_no = models.CharField(_('Plate No'), max_length=50) + load_capacity = models.CharField(_('Load Capacity'), max_length=100) + color = models.CharField(_('Color'), max_length=50) + + # Pictures + truck_picture = models.ImageField(_('Truck Picture'), upload_to='trucks/', blank=True, null=True) + registration_front = models.ImageField(_('Registration Front'), upload_to='docs/', blank=True, null=True) + registration_back = models.ImageField(_('Registration Back'), upload_to='docs/', blank=True, null=True) + driver_license = models.ImageField(_('Driver License'), upload_to='docs/', blank=True, null=True) + + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.truck_type} - {self.plate_no}" + +class Shipment(models.Model): + STATUS_CHOICES = ( + ('OPEN', _('Open for Bids')), + ('IN_PROGRESS', _('In Progress')), + ('COMPLETED', _('Completed')), + ('CANCELLED', _('Cancelled')), + ) + shipper = models.ForeignKey(User, on_delete=models.CASCADE, related_name='shipments') + description = models.TextField(_('Goods Description')) + weight = models.CharField(_('Weight/Volume'), max_length=100) + origin = models.CharField(_('Origin'), max_length=255) + destination = models.CharField(_('Destination'), max_length=255) + delivery_date = models.DateField(_('Requested Delivery Date')) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='OPEN') + + assigned_truck = models.ForeignKey(Truck, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_shipments') + + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.origin} to {self.destination} - {self.status}" + +class Bid(models.Model): + STATUS_CHOICES = ( + ('PENDING', _('Pending')), + ('ACCEPTED', _('Accepted')), + ('REJECTED', _('Rejected')), + ) + shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='bids') + truck_owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bids') + truck = models.ForeignKey(Truck, on_delete=models.CASCADE) + amount = models.DecimalField(_('Offer Amount'), max_digits=10, decimal_places=2) + comments = models.TextField(_('Comments'), blank=True) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') + + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"Bid by {self.truck_owner.username} for {self.shipment}" + +class Message(models.Model): + shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='messages') + sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_messages') + content = models.TextField() + timestamp = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"From {self.sender.username} at {self.timestamp}" + +@receiver(post_save, sender=User) +def create_user_profile(sender, instance, created, **kwargs): + if created: + Profile.objects.create(user=instance) + +@receiver(post_save, sender=User) +def save_user_profile(sender, instance, **kwargs): + if hasattr(instance, 'profile'): + instance.profile.save() + else: + Profile.objects.create(user=instance) diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..7ea38ab 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,156 @@ +{% load static i18n %} - + - {% block title %}Knowledge Base{% endblock %} + + {% block title %}MASAR CARGO{% endblock %} {% if project_description %} - - {% endif %} - {% if project_image_url %} - - + + + + {% if request.LANGUAGE_CODE == 'ar' %} + {% endif %} - {% load static %} + + + + {% block head %}{% endblock %} + - {% block content %}{% endblock %} + + + {% if messages %} +
+ {% for message in messages %} + + {% endfor %} +
+ {% endif %} + +
+ {% block content %}{% endblock %} +
+ + + + + - + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..23febdf 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,142 @@ -{% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% extends 'base.html' %} +{% load i18n static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… + +
+
+
+
+

+ {% trans "Smart Cargo Solutions" %}
+ {% trans "Locally & Abroad" %} +

+

+ {% trans "The most reliable platform connecting shippers with truck owners across the region. Transparent, fast, and secure." %} +

+ +
+
+ Logistics +
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
- + + + +
+
+
+

{% trans "How would you like to use MASAR?" %}

+

{% trans "Choose your path to get started with our platform." %}

+
+
+ +
+
+
+ +
+

{% trans "I am a Shipper" %}

+

+ {% trans "I need to move goods locally or abroad. Post your shipment, receive offers from verified drivers, and track your cargo in real-time." %} +

+
    +
  • {% trans "Post shipments easily" %}
  • +
  • {% trans "Compare competitive bids" %}
  • +
  • {% trans "Real-time tracking" %}
  • +
+ {% trans "Find a Truck" %} +
+
+ +
+
+
+ +
+

{% trans "I am a Truck Owner" %}

+

+ {% trans "I have trucks and want to find cargo to transport. Register your fleet, bid on available jobs, and grow your business." %} +

+
    +
  • {% trans "Access daily cargo leads" %}
  • +
  • {% trans "Flexible bidding system" %}
  • +
  • {% trans "Direct chat with shippers" %}
  • +
+ {% trans "Register Your Truck" %} +
+
+
+
+
+ + +
+
+
+
+

{% trans "Everything you need for seamless logistics" %}

+
+
+
+ +
+
+
+
{% trans "WhatsApp Integration" %}
+

{% trans "Receive instant updates and communicate easily via WhatsApp API." %}

+
+
+
+
+
+ +
+
+
+
{% trans "Multilingual Support" %}
+

{% trans "Fully accessible in both Arabic and English for all users." %}

+
+
+
+
+
+ +
+
+
+
{% trans "Secure Documentation" %}
+

{% trans "Digital verification of truck registration and driver licenses." %}

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + +
+
+
+

{% trans "Ready to move your cargo?" %}

+

{% trans "Join thousands of shippers and drivers on MASAR today." %}

+ {% trans "Join Now" %} +
+
+
+ {% endblock %} \ No newline at end of file diff --git a/core/templates/core/marketplace.html b/core/templates/core/marketplace.html new file mode 100644 index 0000000..d3c8358 --- /dev/null +++ b/core/templates/core/marketplace.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+

{% trans "Shipment Marketplace" %}

+
+ {% for shipment in shipments %} +
+
+
+
+ {% trans "Open for Bids" %} + {{ shipment.created_at|timesince }} {% trans "ago" %} +
+
{{ shipment.origin }} {{ shipment.destination }}
+

{{ shipment.description|truncatechars:100 }}

+
+
+ {% trans "Weight" %} + {{ shipment.weight }} +
+
+ {% trans "Delivery Date" %} + {{ shipment.delivery_date }} +
+
+ {% trans "Place an Offer" %} +
+
+
+ {% empty %} +
+

{% trans "No shipments available at the moment." %}

+
+ {% endfor %} +
+
+{% endblock %} diff --git a/core/templates/core/place_bid.html b/core/templates/core/place_bid.html new file mode 100644 index 0000000..8a4e79e --- /dev/null +++ b/core/templates/core/place_bid.html @@ -0,0 +1,51 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+

{% trans "Place an Offer" %}

+
+ {% trans "Shipment:" %} {{ shipment.origin }} to {{ shipment.destination }}
+ {% trans "Goods:" %} {{ shipment.description }} +
+ + {% if trucks %} +
+ {% csrf_token %} +
+ + +
+
+ +
+ $ + +
+
+
+ + +
+ +
+ {% else %} +
+

{% trans "You must register a truck before placing a bid." %}

+ {% trans "Register Truck Now" %} +
+ {% endif %} +
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/post_shipment.html b/core/templates/core/post_shipment.html new file mode 100644 index 0000000..4a3303e --- /dev/null +++ b/core/templates/core/post_shipment.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+

{% trans "Post a New Shipment" %}

+
+ {% csrf_token %} +
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/shipment_detail.html b/core/templates/core/shipment_detail.html new file mode 100644 index 0000000..e87b32e --- /dev/null +++ b/core/templates/core/shipment_detail.html @@ -0,0 +1,101 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+
+

{{ shipment.origin }} {{ shipment.destination }}

+ + {{ shipment.get_status_display }} + +
+
+
{% trans "Details" %}
+

{{ shipment.description }}

+
+
+ {% trans "Weight" %} + {{ shipment.weight }} +
+
+ {% trans "Delivery Date" %} + {{ shipment.delivery_date }} +
+
+
+
+ + {% if user == shipment.shipper and shipment.status == 'OPEN' %} +
+
+
{% trans "Received Bids" %}
+
+
+
+ + + + + + + + + + + {% for bid in bids %} + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Truck Owner" %}{% trans "Truck" %}{% trans "Amount" %}{% trans "Action" %}
{{ bid.truck_owner.username }}{{ bid.truck.truck_type }}${{ bid.amount }} + {% trans "Accept" %} +
{% trans "No bids received yet." %}
+
+
+
+ {% endif %} + + {% if shipment.status == 'IN_PROGRESS' %} +
+ +
+ {% trans "Shipment in progress!" %}
+ {% trans "Assigned Truck:" %} {{ shipment.assigned_truck.truck_type }} ({{ shipment.assigned_truck.plate_no }}) +
+
+ + {% endif %} +
+ +
+
+
+
{% trans "Contact Information" %}
+

+ {% trans "Shipper:" %} {{ shipment.shipper.username }}
+ {% if shipment.status == 'IN_PROGRESS' %} + {% trans "Phone:" %} {{ shipment.shipper.profile.phone_number }} + {% endif %} +

+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/shipper_dashboard.html b/core/templates/core/shipper_dashboard.html new file mode 100644 index 0000000..9672079 --- /dev/null +++ b/core/templates/core/shipper_dashboard.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+

{% trans "Shipper Dashboard" %}

+ + {% trans "Post New Shipment" %} + +
+ +
+
+
+
+
{% trans "My Shipments" %}
+
+
+
+ + + + + + + + + + + + + {% for shipment in shipments %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Description" %}{% trans "Route" %}{% trans "Delivery Date" %}{% trans "Status" %}{% trans "Bids" %}{% trans "Action" %}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.delivery_date }} + + {{ shipment.get_status_display }} + + {{ shipment.bids.count }} + {% trans "View Details" %} +
{% trans "No shipments posted yet." %}
+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/truck_owner_dashboard.html b/core/templates/core/truck_owner_dashboard.html new file mode 100644 index 0000000..919e63a --- /dev/null +++ b/core/templates/core/truck_owner_dashboard.html @@ -0,0 +1,79 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+

{% trans "Truck Owner Dashboard" %}

+
+ + {% trans "Find Shipments" %} + + + {% trans "Register Truck" %} + +
+
+ +
+
+
+
+
{% trans "My Trucks" %}
+
+
+ {% if trucks %} +
    + {% for truck in trucks %} +
  • + {{ truck.truck_type }} - {{ truck.plate_no }} + {{ truck.load_capacity }} +
  • + {% endfor %} +
+ {% else %} +

{% trans "No trucks registered." %}

+ {% endif %} +
+
+
+
+
+
+
{% trans "My Active Bids" %}
+
+
+
+ + + + + + + + + + {% for bid in bids %} + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Shipment" %}{% trans "Amount" %}{% trans "Status" %}
{{ bid.shipment.origin }} - {{ bid.shipment.destination }}{{ bid.amount }} + + {{ bid.get_status_display }} + +
{% trans "No bids placed." %}
+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/truck_register.html b/core/templates/core/truck_register.html new file mode 100644 index 0000000..c3ac686 --- /dev/null +++ b/core/templates/core/truck_register.html @@ -0,0 +1,73 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+

{% trans "Register a Truck" %}

+
+ {% csrf_token %} +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
{% trans "Documents & Photos" %}
+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/registration/login.html b/core/templates/registration/login.html new file mode 100644 index 0000000..0f5dc9d --- /dev/null +++ b/core/templates/registration/login.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+
+

{% trans "Login" %}

+
+ {% csrf_token %} +
+ + +
+
+ + +
+ +
+
+

{% trans "Don't have an account?" %} {% trans "Register" %}

+
+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/registration/register.html b/core/templates/registration/register.html new file mode 100644 index 0000000..b5eab21 --- /dev/null +++ b/core/templates/registration/register.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+
+

{% trans "Create your account" %}

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ + +
+ +
+ + +
+ + +
+
+

{% trans "Already have an account?" %} {% trans "Login" %}

+
+
+
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..ba2a209 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,17 @@ from django.urls import path - -from .views import home +from django.contrib.auth import views as auth_views +from . import views urlpatterns = [ - path("", home, name="home"), -] + path("", views.home, name="home"), + path("register/", views.register, name="register"), + path("login/", auth_views.LoginView.as_view(), name="login"), + path("logout/", auth_views.LogoutView.as_view(), name="logout"), + path("dashboard/", views.dashboard, name="dashboard"), + path("truck/register/", views.truck_register, name="truck_register"), + path("shipment/post/", views.post_shipment, name="post_shipment"), + path("marketplace/", views.marketplace, name="marketplace"), + path("shipment//", views.shipment_detail, name="shipment_detail"), + path("shipment//bid/", views.place_bid, name="place_bid"), + path("bid//accept/", views.accept_bid, name="accept_bid"), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..e03d3f5 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,172 @@ -import os -import platform - -from django import get_version as django_version -from django.shortcuts import render +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.contrib.auth import login, authenticate +from django.contrib.auth.forms import UserCreationForm from django.utils import timezone - +from .models import Profile, Truck, Shipment, Bid, Message +from django.contrib import messages +from django.utils.translation import gettext as _ +from django.db.models import Q def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() - + """Render the landing screen for MASAR CARGO.""" + if request.user.is_authenticated: + return redirect('dashboard') context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + "deployment_timestamp": timezone.now().timestamp(), } return render(request, "core/index.html", context) + +def register(request): + if request.method == 'POST': + form = UserCreationForm(request.POST) + role = request.POST.get('role') + phone = request.POST.get('phone_number') + if form.is_valid(): + user = form.save() + profile = user.profile + profile.role = role + profile.phone_number = phone + profile.save() + login(request, user) + return redirect('dashboard') + else: + form = UserCreationForm() + return render(request, 'registration/register.html', {'form': form}) + +@login_required +def dashboard(request): + profile = request.user.profile + if profile.role == 'SHIPPER': + my_shipments = Shipment.objects.filter(shipper=request.user).order_by('-created_at') + return render(request, 'core/shipper_dashboard.html', {'shipments': my_shipments}) + elif profile.role == 'TRUCK_OWNER': + my_trucks = Truck.objects.filter(owner=request.user) + my_bids = Bid.objects.filter(truck_owner=request.user).order_by('-created_at') + return render(request, 'core/truck_owner_dashboard.html', { + 'trucks': my_trucks, + 'bids': my_bids + }) + else: + return redirect('/admin/') + +@login_required +def truck_register(request): + if request.user.profile.role != 'TRUCK_OWNER': + return redirect('dashboard') + + if request.method == 'POST': + truck_type = request.POST.get('truck_type') + model = request.POST.get('model') + year = request.POST.get('year') + plate_no = request.POST.get('plate_no') + load_capacity = request.POST.get('load_capacity') + color = request.POST.get('color') + + truck = Truck.objects.create( + owner=request.user, + truck_type=truck_type, + model=model, + year=year, + plate_no=plate_no, + load_capacity=load_capacity, + color=color, + truck_picture=request.FILES.get('truck_picture'), + registration_front=request.FILES.get('registration_front'), + registration_back=request.FILES.get('registration_back'), + driver_license=request.FILES.get('driver_license') + ) + messages.success(request, _("Truck registered successfully!")) + return redirect('dashboard') + + return render(request, 'core/truck_register.html') + +@login_required +def post_shipment(request): + if request.user.profile.role != 'SHIPPER': + return redirect('dashboard') + + if request.method == 'POST': + description = request.POST.get('description') + weight = request.POST.get('weight') + origin = request.POST.get('origin') + destination = request.POST.get('destination') + delivery_date = request.POST.get('delivery_date') + + Shipment.objects.create( + shipper=request.user, + description=description, + weight=weight, + origin=origin, + destination=destination, + delivery_date=delivery_date + ) + messages.success(request, _("Shipment posted successfully!")) + return redirect('dashboard') + + return render(request, 'core/post_shipment.html') + +@login_required +def marketplace(request): + if request.user.profile.role != 'TRUCK_OWNER': + return redirect('dashboard') + + shipments = Shipment.objects.filter(status='OPEN').order_by('-created_at') + return render(request, 'core/marketplace.html', {'shipments': shipments}) + +@login_required +def place_bid(request, shipment_id): + shipment = get_object_or_404(Shipment, id=shipment_id) + if request.user.profile.role != 'TRUCK_OWNER': + return redirect('dashboard') + + my_trucks = Truck.objects.filter(owner=request.user) + if request.method == 'POST': + truck_id = request.POST.get('truck') + amount = request.POST.get('amount') + comments = request.POST.get('comments') + + truck = get_object_or_404(Truck, id=truck_id, owner=request.user) + Bid.objects.create( + shipment=shipment, + truck_owner=request.user, + truck=truck, + amount=amount, + comments=comments + ) + messages.success(request, _("Bid placed successfully!")) + return redirect('marketplace') + + return render(request, 'core/place_bid.html', {'shipment': shipment, 'trucks': my_trucks}) + +@login_required +def shipment_detail(request, shipment_id): + shipment = get_object_or_404(Shipment, id=shipment_id) + # Security: check if user is shipper or a truck owner who bid + if shipment.shipper != request.user and not Bid.objects.filter(shipment=shipment, truck_owner=request.user).exists(): + if request.user.profile.role != 'ADMIN': + return redirect('dashboard') + + bids = shipment.bids.all() + return render(request, 'core/shipment_detail.html', {'shipment': shipment, 'bids': bids}) + +@login_required +def accept_bid(request, bid_id): + bid = get_object_or_404(Bid, id=bid_id) + if bid.shipment.shipper != request.user: + return redirect('dashboard') + + # Accept this bid + bid.status = 'ACCEPTED' + bid.save() + + # Reject others + bid.shipment.bids.exclude(id=bid_id).update(status='REJECTED') + + # Update shipment + bid.shipment.status = 'IN_PROGRESS' + bid.shipment.assigned_truck = bid.truck + bid.shipment.save() + + messages.success(request, _("Bid accepted! Shipment is now in progress.")) + return redirect('shipment_detail', shipment_id=bid.shipment.id) diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000..993798e Binary files /dev/null and b/locale/ar/LC_MESSAGES/django.mo differ diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..38e53f8 --- /dev/null +++ b/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,619 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-01-23 09:04+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: config/settings.py:188 +msgid "English" +msgstr "الإنجليزية" + +#: config/settings.py:189 +msgid "Arabic" +msgstr "العربية" + +#: core/models.py:9 core/templates/registration/register.html:19 +msgid "Shipper (Need Goods Moved)" +msgstr "شاحن (بحاجة لنقل بضائع)" + +#: core/models.py:10 core/templates/registration/register.html:20 +msgid "Truck Owner (Service Provider)" +msgstr "صاحب شاحنة (مزود خدمة)" + +#: core/models.py:11 +msgid "Administrator" +msgstr "مدير النظام" + +#: core/models.py:22 core/templates/core/truck_register.html:15 +msgid "Truck Type" +msgstr "نوع الشاحنة" + +#: core/models.py:23 core/templates/core/truck_register.html:19 +msgid "Model" +msgstr "الموديل" + +#: core/models.py:24 core/templates/core/truck_register.html:25 +msgid "Year" +msgstr "السنة" + +#: core/models.py:25 core/templates/core/truck_register.html:29 +msgid "Plate No" +msgstr "رقم اللوحة" + +#: core/models.py:26 +msgid "Load Capacity" +msgstr "حمولة الشاحنة" + +#: core/models.py:27 core/templates/core/truck_register.html:33 +msgid "Color" +msgstr "اللون" + +#: core/models.py:30 core/templates/core/truck_register.html:47 +msgid "Truck Picture" +msgstr "صورة الشاحنة" + +#: core/models.py:31 +msgid "Registration Front" +msgstr "التسجيل (الوجه الأمامي)" + +#: core/models.py:32 +#, fuzzy +#| msgid "Register Your Truck" +msgid "Registration Back" +msgstr "سجل شاحنتك" + +#: core/models.py:33 core/templates/core/truck_register.html:51 +msgid "Driver License" +msgstr "رخصة القيادة" + +#: core/models.py:42 core/templates/core/marketplace.html:13 +msgid "Open for Bids" +msgstr "مفتوح للعروض" + +#: core/models.py:43 +msgid "In Progress" +msgstr "قيد التنفيذ" + +#: core/models.py:44 +msgid "Completed" +msgstr "مكتمل" + +#: core/models.py:45 +msgid "Cancelled" +msgstr "ملغي" + +#: core/models.py:48 core/templates/core/post_shipment.html:14 +msgid "Goods Description" +msgstr "وصف البضائع" + +#: core/models.py:49 core/templates/core/post_shipment.html:18 +msgid "Weight/Volume" +msgstr "الوزن/الحجم" + +#: core/models.py:50 core/templates/core/post_shipment.html:23 +msgid "Origin" +msgstr "المصدر" + +#: core/models.py:51 core/templates/core/post_shipment.html:27 +msgid "Destination" +msgstr "الوجهة" + +#: core/models.py:52 core/templates/core/post_shipment.html:32 +msgid "Requested Delivery Date" +msgstr "تاريخ التسليم المطلوب" + +#: core/models.py:64 +msgid "Pending" +msgstr "قيد الانتظار" + +#: core/models.py:65 +msgid "Accepted" +msgstr "مقبول" + +#: core/models.py:66 +msgid "Rejected" +msgstr "مرفوض" + +#: core/models.py:71 +msgid "Offer Amount" +msgstr "قيمة العرض" + +#: core/models.py:72 +msgid "Comments" +msgstr "تعليقات" + +#: core/templates/base.html:49 +msgid "Dashboard" +msgstr "لوحة التحكم" + +#: core/templates/base.html:53 +msgid "Marketplace" +msgstr "السوق" + +#: core/templates/base.html:91 +msgid "Logout" +msgstr "تسجيل الخروج" + +#: core/templates/base.html:98 core/templates/registration/login.html:11 +#: core/templates/registration/login.html:22 +#: core/templates/registration/register.html:32 +msgid "Login" +msgstr "تسجيل الدخول" + +#: core/templates/base.html:101 +msgid "Get Started" +msgstr "ابدأ الآن" + +#: core/templates/base.html:129 +msgid "Empowering logistics with smart technology. Locally and abroad." +msgstr "تمكين الخدمات اللوجستية بتقنيات ذكية. محلياً ودولياً." + +#: core/templates/base.html:132 +msgid "Quick Links" +msgstr "روابط سريعة" + +#: core/templates/base.html:134 +msgid "Privacy Policy" +msgstr "سياسة الخصوصية" + +#: core/templates/base.html:135 +msgid "Terms of Service" +msgstr "شروط الخدمة" + +#: core/templates/base.html:136 +msgid "Contact Us" +msgstr "اتصل بنا" + +#: core/templates/base.html:140 +msgid "Contact" +msgstr "اتصال" + +#: core/templates/core/index.html:11 +msgid "Smart Cargo Solutions" +msgstr "حلول شحن ذكية" + +#: core/templates/core/index.html:12 +msgid "Locally & Abroad" +msgstr "محلياً ودولياً" + +#: core/templates/core/index.html:15 +msgid "" +"The most reliable platform connecting shippers with truck owners across the " +"region. Transparent, fast, and secure." +msgstr "" +"المنصة الأكثر موثوقية لربط الشاحنين مع أصحاب الشاحنات في جميع أنحاء المنطقة. " +"شفافة، سريعة، وآمنة." + +#: core/templates/core/index.html:18 +msgid "Start Shipping" +msgstr "ابدأ الشحن" + +#: core/templates/core/index.html:19 +msgid "Learn More" +msgstr "تعلم المزيد" + +#: core/templates/core/index.html:33 +msgid "How would you like to use MASAR?" +msgstr "كيف تود استخدام مسار؟" + +#: core/templates/core/index.html:34 +msgid "Choose your path to get started with our platform." +msgstr "اختر مسارك للبدء مع منصتنا." + +#: core/templates/core/index.html:43 +msgid "I am a Shipper" +msgstr "أنا شاحن" + +#: core/templates/core/index.html:45 +msgid "" +"I need to move goods locally or abroad. Post your shipment, receive offers " +"from verified drivers, and track your cargo in real-time." +msgstr "" +"أريد نقل بضائع محلياً أو دولياً. انشر شحنتك، واستقبل عروضاً من سائقين موثقين، " +"وتتبع شحنتك في الوقت الفعلي." + +#: core/templates/core/index.html:48 +msgid "Post shipments easily" +msgstr "انشر الشحنات بسهولة" + +#: core/templates/core/index.html:49 +msgid "Compare competitive bids" +msgstr "قارن العروض التنافسية" + +#: core/templates/core/index.html:50 +msgid "Real-time tracking" +msgstr "تتبع في الوقت الفعلي" + +#: core/templates/core/index.html:52 +msgid "Find a Truck" +msgstr "ابحث عن شاحنة" + +#: core/templates/core/index.html:61 +msgid "I am a Truck Owner" +msgstr "أنا صاحب شاحنة" + +#: core/templates/core/index.html:63 +msgid "" +"I have trucks and want to find cargo to transport. Register your fleet, bid " +"on available jobs, and grow your business." +msgstr "" +"لدي شاحنات وأريد العثور على بضائع لنقلها. سجل أسطولك، وقدم عروضك على الوظائف " +"المتاحة، ونمِ عملك." + +#: core/templates/core/index.html:66 +msgid "Access daily cargo leads" +msgstr "الوصول إلى فرص شحن يومية" + +#: core/templates/core/index.html:67 +msgid "Flexible bidding system" +msgstr "نظام عروض مرن" + +#: core/templates/core/index.html:68 +msgid "Direct chat with shippers" +msgstr "دردشة مباشرة مع الشاحنين" + +#: core/templates/core/index.html:70 +msgid "Register Your Truck" +msgstr "سجل شاحنتك" + +#: core/templates/core/index.html:82 +msgid "Everything you need for seamless logistics" +msgstr "كل ما تحتاجه للوجستيات سلسة" + +#: core/templates/core/index.html:90 +msgid "WhatsApp Integration" +msgstr "تكامل واتساب" + +#: core/templates/core/index.html:91 +msgid "Receive instant updates and communicate easily via WhatsApp API." +msgstr "استقبل تحديثات فورية وتواصل بسهولة عبر واجهة برمجة تطبيقات واتساب." + +#: core/templates/core/index.html:101 +msgid "Multilingual Support" +msgstr "دعم متعدد اللغات" + +#: core/templates/core/index.html:102 +msgid "Fully accessible in both Arabic and English for all users." +msgstr "متاح بالكامل باللغتين العربية والإنجليزية لجميع المستخدمين." + +#: core/templates/core/index.html:112 +msgid "Secure Documentation" +msgstr "توثيق آمن" + +#: core/templates/core/index.html:113 +msgid "Digital verification of truck registration and driver licenses." +msgstr "التحقق الرقمي من تسجيل الشاحنات ورخص القيادة." + +#: core/templates/core/index.html:135 +msgid "Ready to move your cargo?" +msgstr "هل أنت مستعد لنقل شحنتك؟" + +#: core/templates/core/index.html:136 +msgid "Join thousands of shippers and drivers on MASAR today." +msgstr "انضم إلى آلاف الشاحنين والسائقين على مسار اليوم." + +#: core/templates/core/index.html:137 +msgid "Join Now" +msgstr "انضم الآن" + +#: core/templates/core/marketplace.html:6 +msgid "Shipment Marketplace" +msgstr "سوق الشحنات" + +#: core/templates/core/marketplace.html:14 +msgid "ago" +msgstr "منذ" + +#: core/templates/core/marketplace.html:20 +#: core/templates/core/shipment_detail.html:21 +msgid "Weight" +msgstr "الوزن" + +#: core/templates/core/marketplace.html:24 +#: core/templates/core/shipment_detail.html:25 +#: core/templates/core/shipper_dashboard.html:26 +msgid "Delivery Date" +msgstr "تاريخ التسليم" + +#: core/templates/core/marketplace.html:28 +#: core/templates/core/place_bid.html:10 +msgid "Place an Offer" +msgstr "قدم عرضاً" + +#: core/templates/core/marketplace.html:34 +msgid "No shipments available at the moment." +msgstr "لا توجد شحنات متاحة حالياً." + +#: core/templates/core/place_bid.html:12 +msgid "Shipment:" +msgstr "الشحنة:" + +#: core/templates/core/place_bid.html:13 +msgid "Goods:" +msgstr "البضائع:" + +#: core/templates/core/place_bid.html:20 +msgid "Select Truck" +msgstr "اختر الشاحنة" + +#: core/templates/core/place_bid.html:28 +msgid "Your Offer Amount" +msgstr "مبلغ عرضك" + +#: core/templates/core/place_bid.html:35 +msgid "Comments/Conditions" +msgstr "التعليقات/الشروط" + +#: core/templates/core/place_bid.html:38 +msgid "Submit Offer" +msgstr "تقديم العرض" + +#: core/templates/core/place_bid.html:42 +msgid "You must register a truck before placing a bid." +msgstr "يجب عليك تسجيل شاحنة قبل تقديم عرض." + +#: core/templates/core/place_bid.html:43 +#, fuzzy +#| msgid "Register Your Truck" +msgid "Register Truck Now" +msgstr "سجل شاحنتك" + +#: core/templates/core/post_shipment.html:10 +#, fuzzy +#| msgid "Post shipments easily" +msgid "Post a New Shipment" +msgstr "انشر الشحنات بسهولة" + +#: core/templates/core/post_shipment.html:15 +msgid "What are you moving? (e.g. 500 boxes of food)" +msgstr "ماذا ستنقل؟ (مثال: 500 صندوق مواد غذائية)" + +#: core/templates/core/post_shipment.html:24 +#: core/templates/core/post_shipment.html:28 +msgid "City, Country" +msgstr "المدينة، الدولة" + +#: core/templates/core/post_shipment.html:35 +#, fuzzy +#| msgid "Post shipments easily" +msgid "Post Shipment" +msgstr "انشر الشحنات بسهولة" + +#: core/templates/core/shipment_detail.html:17 +msgid "Details" +msgstr "التفاصيل" + +#: core/templates/core/shipment_detail.html:35 +msgid "Received Bids" +msgstr "العروض المستلمة" + +#: core/templates/core/shipment_detail.html:42 +#, fuzzy +#| msgid "I am a Truck Owner" +msgid "Truck Owner" +msgstr "أنا صاحب شاحنة" + +#: core/templates/core/shipment_detail.html:43 +msgid "Truck" +msgstr "الشاحنة" + +#: core/templates/core/shipment_detail.html:44 +#: core/templates/core/truck_owner_dashboard.html:51 +msgid "Amount" +msgstr "المبلغ" + +#: core/templates/core/shipment_detail.html:45 +#: core/templates/core/shipper_dashboard.html:29 +msgid "Action" +msgstr "الإجراء" + +#: core/templates/core/shipment_detail.html:55 +msgid "Accept" +msgstr "قبول" + +#: core/templates/core/shipment_detail.html:60 +msgid "No bids received yet." +msgstr "لم يتم استلام عروض بعد." + +#: core/templates/core/shipment_detail.html:74 +msgid "Shipment in progress!" +msgstr "الشحنة قيد التنفيذ!" + +#: core/templates/core/shipment_detail.html:75 +#, fuzzy +#| msgid "Find a Truck" +msgid "Assigned Truck:" +msgstr "ابحث عن شاحنة" + +#: core/templates/core/shipment_detail.html:80 +msgid "Contact Driver on WhatsApp" +msgstr "تواصل مع السائق عبر واتساب" + +#: core/templates/core/shipment_detail.html:89 +msgid "Contact Information" +msgstr "معلومات الاتصال" + +#: core/templates/core/shipment_detail.html:91 +#, fuzzy +#| msgid "I am a Shipper" +msgid "Shipper:" +msgstr "أنا شاحن" + +#: core/templates/core/shipment_detail.html:93 +msgid "Phone:" +msgstr "الهاتف:" + +#: core/templates/core/shipper_dashboard.html:7 +#, fuzzy +#| msgid "Dashboard" +msgid "Shipper Dashboard" +msgstr "لوحة التحكم" + +#: core/templates/core/shipper_dashboard.html:9 +#, fuzzy +#| msgid "Post shipments easily" +msgid "Post New Shipment" +msgstr "انشر الشحنات بسهولة" + +#: core/templates/core/shipper_dashboard.html:17 +msgid "My Shipments" +msgstr "شحناتي" + +#: core/templates/core/shipper_dashboard.html:24 +msgid "Description" +msgstr "الوصف" + +#: core/templates/core/shipper_dashboard.html:25 +msgid "Route" +msgstr "المسار" + +#: core/templates/core/shipper_dashboard.html:27 +#: core/templates/core/truck_owner_dashboard.html:52 +msgid "Status" +msgstr "الحالة" + +#: core/templates/core/shipper_dashboard.html:28 +msgid "Bids" +msgstr "العروض" + +#: core/templates/core/shipper_dashboard.html:45 +msgid "View Details" +msgstr "عرض التفاصيل" + +#: core/templates/core/shipper_dashboard.html:50 +#, fuzzy +#| msgid "Post shipments easily" +msgid "No shipments posted yet." +msgstr "انشر الشحنات بسهولة" + +#: core/templates/core/truck_owner_dashboard.html:7 +#, fuzzy +#| msgid "Dashboard" +msgid "Truck Owner Dashboard" +msgstr "لوحة التحكم" + +#: core/templates/core/truck_owner_dashboard.html:10 +msgid "Find Shipments" +msgstr "ابحث عن شحنات" + +#: core/templates/core/truck_owner_dashboard.html:13 +#, fuzzy +#| msgid "Register Your Truck" +msgid "Register Truck" +msgstr "سجل شاحنتك" + +#: core/templates/core/truck_owner_dashboard.html:22 +msgid "My Trucks" +msgstr "شاحناتي" + +#: core/templates/core/truck_owner_dashboard.html:35 +msgid "No trucks registered." +msgstr "لا توجد شاحنات مسجلة." + +#: core/templates/core/truck_owner_dashboard.html:43 +msgid "My Active Bids" +msgstr "عروضي النشطة" + +#: core/templates/core/truck_owner_dashboard.html:50 +msgid "Shipment" +msgstr "الشحنة" + +#: core/templates/core/truck_owner_dashboard.html:68 +msgid "No bids placed." +msgstr "لا توجد عروض مقدمة." + +#: core/templates/core/truck_register.html:10 +#, fuzzy +#| msgid "Register Your Truck" +msgid "Register a Truck" +msgstr "سجل شاحنتك" + +#: core/templates/core/truck_register.html:38 +msgid "Load Capacity (e.g. 20 Tons)" +msgstr "الحمولة (مثال: 20 طن)" + +#: core/templates/core/truck_register.html:43 +msgid "Documents & Photos" +msgstr "المستندات والصور" + +#: core/templates/core/truck_register.html:57 +msgid "Registration (Front Face)" +msgstr "التسجيل (الوجه الأمامي)" + +#: core/templates/core/truck_register.html:61 +msgid "Registration (Back Face)" +msgstr "التسجيل (الوجه الخلفي)" + +#: core/templates/core/truck_register.html:66 +msgid "Submit Registration" +msgstr "إرسال التسجيل" + +#: core/templates/registration/login.html:15 +msgid "Username" +msgstr "اسم المستخدم" + +#: core/templates/registration/login.html:19 +msgid "Password" +msgstr "كلمة المرور" + +#: core/templates/registration/login.html:25 +msgid "Don't have an account?" +msgstr "ليس لديك حساب؟" + +#: core/templates/registration/login.html:25 +#: core/templates/registration/register.html:29 +msgid "Register" +msgstr "تسجيل" + +#: core/templates/registration/register.html:11 +msgid "Create your account" +msgstr "أنشئ حسابك" + +#: core/templates/registration/register.html:17 +msgid "I am a:" +msgstr "أنا:" + +#: core/templates/registration/register.html:25 +msgid "Phone Number" +msgstr "رقم الهاتف" + +#: core/templates/registration/register.html:32 +msgid "Already have an account?" +msgstr "لديك حساب بالفعل؟" + +#: core/views.py:79 +msgid "Truck registered successfully!" +msgstr "تم تسجيل الشاحنة بنجاح!" + +#: core/views.py:104 +msgid "Shipment posted successfully!" +msgstr "تم نشر الشحنة بنجاح!" + +#: core/views.py:137 +msgid "Bid placed successfully!" +msgstr "تم تقديم العرض بنجاح!" + +#: core/views.py:171 +msgid "Bid accepted! Shipment is now in progress." +msgstr "تم قبول العرض! الشحنة قيد التنفيذ الآن." + +#~ msgid "Features" +#~ msgstr "المميزات" + +#~ msgid "How it Works" +#~ msgstr "كيف يعمل" diff --git a/media/trucks/red_truk.jfif b/media/trucks/red_truk.jfif new file mode 100644 index 0000000..af339ca Binary files /dev/null and b/media/trucks/red_truk.jfif differ diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..48834d9 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,131 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&family=Outfit:wght@400;600;700&display=swap'); + +:root { + --primary-color: #0A1D37; + --secondary-color: #2196F3; + --accent-color: #00BCD4; + --bg-light: #F4F7F6; + --white: #FFFFFF; + --text-dark: #333333; + --text-muted: #666666; } + +body { + font-family: 'Outfit', 'Cairo', sans-serif; + background-color: var(--bg-light); + color: var(--text-dark); + margin: 0; + overflow-x: hidden; +} + +[lang="ar"] body { + direction: rtl; + text-align: right; +} + +.navbar { + background-color: var(--white); + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + padding: 1rem 2rem; +} + +.navbar-brand { + font-weight: 700; + font-size: 1.5rem; + color: var(--primary-color) !important; +} + +.nav-link { + color: var(--primary-color) !important; + font-weight: 600; + margin: 0 10px; +} + +.btn-primary { + background-color: var(--secondary-color); + border: none; + padding: 10px 25px; + border-radius: 50px; + font-weight: 600; + transition: all 0.3s ease; +} + +.btn-primary:hover { + background-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3); +} + +.btn-outline-primary { + border: 2px solid var(--secondary-color); + color: var(--secondary-color); + padding: 10px 25px; + border-radius: 50px; + font-weight: 600; +} + +.hero-section { + padding: 100px 0; + background: linear-gradient(135deg, var(--white) 0%, #E3F2FD 100%); + position: relative; + overflow: hidden; +} + +.hero-title { + font-size: 3.5rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 20px; + line-height: 1.2; +} + +.hero-subtitle { + font-size: 1.25rem; + color: var(--text-muted); + margin-bottom: 40px; +} + +.role-card { + background: var(--white); + padding: 40px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0,0,0,0.05); + transition: all 0.3s ease; + border: 1px solid transparent; + height: 100%; + cursor: pointer; +} + +.role-card:hover { + border-color: var(--secondary-color); + transform: translateY(-10px); +} + +.role-icon { + font-size: 3rem; + color: var(--secondary-color); + margin-bottom: 20px; +} + +.role-title { + font-size: 1.5rem; + font-weight: 700; + color: var(--primary-color); +} + +.role-desc { + color: var(--text-muted); + margin-bottom: 25px; +} + +footer { + background-color: var(--primary-color); + color: var(--white); + padding: 50px 0; +} + +/* RTL Adjustments */ +[lang="ar"] .ms-auto { + margin-right: auto !important; + margin-left: 0 !important; +} \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 108056f..48834d9 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -1,21 +1,131 @@ +@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&family=Outfit:wght@400;600;700&display=swap'); :root { - --bg-color-start: #6a11cb; - --bg-color-end: #2575fc; - --text-color: #ffffff; - --card-bg-color: rgba(255, 255, 255, 0.01); - --card-border-color: rgba(255, 255, 255, 0.1); + --primary-color: #0A1D37; + --secondary-color: #2196F3; + --accent-color: #00BCD4; + --bg-light: #F4F7F6; + --white: #FFFFFF; + --text-dark: #333333; + --text-muted: #666666; } + body { + font-family: 'Outfit', 'Cairo', sans-serif; + background-color: var(--bg-light); + color: var(--text-dark); margin: 0; - font-family: 'Inter', sans-serif; - background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); - color: var(--text-color); - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - text-align: center; - overflow: hidden; - position: relative; + overflow-x: hidden; } + +[lang="ar"] body { + direction: rtl; + text-align: right; +} + +.navbar { + background-color: var(--white); + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + padding: 1rem 2rem; +} + +.navbar-brand { + font-weight: 700; + font-size: 1.5rem; + color: var(--primary-color) !important; +} + +.nav-link { + color: var(--primary-color) !important; + font-weight: 600; + margin: 0 10px; +} + +.btn-primary { + background-color: var(--secondary-color); + border: none; + padding: 10px 25px; + border-radius: 50px; + font-weight: 600; + transition: all 0.3s ease; +} + +.btn-primary:hover { + background-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3); +} + +.btn-outline-primary { + border: 2px solid var(--secondary-color); + color: var(--secondary-color); + padding: 10px 25px; + border-radius: 50px; + font-weight: 600; +} + +.hero-section { + padding: 100px 0; + background: linear-gradient(135deg, var(--white) 0%, #E3F2FD 100%); + position: relative; + overflow: hidden; +} + +.hero-title { + font-size: 3.5rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 20px; + line-height: 1.2; +} + +.hero-subtitle { + font-size: 1.25rem; + color: var(--text-muted); + margin-bottom: 40px; +} + +.role-card { + background: var(--white); + padding: 40px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0,0,0,0.05); + transition: all 0.3s ease; + border: 1px solid transparent; + height: 100%; + cursor: pointer; +} + +.role-card:hover { + border-color: var(--secondary-color); + transform: translateY(-10px); +} + +.role-icon { + font-size: 3rem; + color: var(--secondary-color); + margin-bottom: 20px; +} + +.role-title { + font-size: 1.5rem; + font-weight: 700; + color: var(--primary-color); +} + +.role-desc { + color: var(--text-muted); + margin-bottom: 25px; +} + +footer { + background-color: var(--primary-color); + color: var(--white); + padding: 50px 0; +} + +/* RTL Adjustments */ +[lang="ar"] .ms-auto { + margin-right: auto !important; + margin-left: 0 !important; +} \ No newline at end of file