added hire and another features

main
Babit Shrestha 6 months ago
parent 6b9504eedb
commit 3ae01ed458
  1. BIN
      sahara/db.sqlite3
  2. BIN
      sahara/main/__pycache__/models.cpython-312.pyc
  3. BIN
      sahara/main/__pycache__/urls.cpython-312.pyc
  4. BIN
      sahara/main/__pycache__/views.cpython-312.pyc
  5. 18
      sahara/main/migrations/0006_servicerequest_phone_number.py
  6. 22
      sahara/main/migrations/0007_remove_servicerequest_phone_number_user_phone_number.py
  7. 18
      sahara/main/migrations/0008_alter_servicerequest_is_approved.py
  8. 18
      sahara/main/migrations/0009_alter_servicerequest_is_approved.py
  9. BIN
      sahara/main/migrations/__pycache__/0006_servicerequest_phone_number.cpython-312.pyc
  10. BIN
      sahara/main/migrations/__pycache__/0007_remove_servicerequest_phone_number_user_phone_number.cpython-312.pyc
  11. BIN
      sahara/main/migrations/__pycache__/0008_alter_servicerequest_is_approved.cpython-312.pyc
  12. BIN
      sahara/main/migrations/__pycache__/0009_alter_servicerequest_is_approved.cpython-312.pyc
  13. 10
      sahara/main/models.py
  14. 116
      sahara/main/templates/main/home.html
  15. 126
      sahara/main/templates/main/profile.html
  16. 164
      sahara/main/templates/main/services1.html
  17. 4
      sahara/main/urls.py
  18. 146
      sahara/main/views.py
  19. 56
      sahara/static/css/styles.css
  20. 144
      sahara/static/css/styles.css_1
  21. BIN
      sahara/static/img/background.jpg
  22. BIN
      sahara/static/img/dummy pic.png
  23. BIN
      sahara/static/img/grandmother.png
  24. BIN
      sahara/static/profile-images/pic1.jpg
  25. 26
      sahara/templates/base.html

Binary file not shown.

@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-01-12 03:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0005_user_login_profile_alter_servicerequest_service_and_more'),
]
operations = [
migrations.AddField(
model_name='servicerequest',
name='phone_number',
field=models.CharField(blank=True, max_length=15, null=True),
),
]

@ -0,0 +1,22 @@
# Generated by Django 5.1.4 on 2025-01-12 03:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0006_servicerequest_phone_number'),
]
operations = [
migrations.RemoveField(
model_name='servicerequest',
name='phone_number',
),
migrations.AddField(
model_name='user',
name='phone_number',
field=models.CharField(blank=True, max_length=15, null=True),
),
]

@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-01-12 10:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0007_remove_servicerequest_phone_number_user_phone_number'),
]
operations = [
migrations.AlterField(
model_name='servicerequest',
name='is_approved',
field=models.BooleanField(blank=True, null=True),
),
]

@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-01-12 10:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0008_alter_servicerequest_is_approved'),
]
operations = [
migrations.AlterField(
model_name='servicerequest',
name='is_approved',
field=models.BooleanField(blank=True, default=False, null=True),
),
]

@ -55,7 +55,6 @@ class UserManager(BaseUserManager):
return self.create_user(first_name, last_name, email, password, **extra_fields)
def send_verification_email(self, user):
# Generate the token and the uid for the verification URL
token = default_token_generator.make_token(user)
uid = urlsafe_base64_encode(force_bytes(user.pk))
verification_url = f"{settings.FRONTEND_URL}/verify-email/{uid}/{token}/"
@ -93,6 +92,7 @@ class User(AbstractBaseUser, PermissionsMixin):
bio = models.CharField(max_length=200, blank=True, null=True)
member_since = models.DateTimeField(auto_now_add=True)
updated_on = models.DateField(auto_now=True)
phone_number = models.CharField(max_length=15, blank=True, null=True)
service_offered = models.ForeignKey(Service, on_delete=models.CASCADE,blank=True,null=True)
login_profile = models.CharField(
max_length=15,
@ -112,6 +112,11 @@ class User(AbstractBaseUser, PermissionsMixin):
def get_short_name(self):
return self.first_name
@property
def url(self):
if self.profile_picture:
return self.profile_picture.url
return None
class ServiceRequest(models.Model):
client = models.ForeignKey(User,on_delete=models.CASCADE,related_name="client")
@ -119,12 +124,13 @@ class ServiceRequest(models.Model):
agreed_price = models.DecimalField(max_digits=999,decimal_places=2 , default=0)
service_hour = models.PositiveIntegerField(default=1)
agreed_on = models.DateField(auto_now_add=True)
is_approved = models.DateTimeField(blank=True, null=True)
is_approved = models.BooleanField(blank=True, null=True,default=False)
remarks = models.TextField(blank=True, null=True)
is_completed = models.BooleanField(default=False)
completed_date = models.DateTimeField(null=True,blank=True)
service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name="service_offered")
def __str__(self):
return self.service.name

@ -5,7 +5,7 @@
<header class="bg-primary text-white text-center py-5">
<div class="container">
<h1>Welcome to Sahara</h1>
<p>Your one-stop platform for elder care services.</p>
<p>Your one-stop platform for elder cure serviecs.</p>
</div>
</header>
@ -24,26 +24,6 @@
<li><a href="{% url 'home' %}?category={{ service.id }}" class="category-link">{{ service.name }}</a></li>
{% endfor %}
</ul>
<h4>Price</h4>
<input
type="number"
class="form-control"
id="basePriceInput"
value="100"
min="1"
step="0.01">
<input
type="range"
class="form-range"
min="0.5"
max="2"
step="0.01"
value="1"
id="ratioSlider">
<p class="fs-5">
<strong>Price:</strong> Rs.<span id="price">100.00</span>
(<span id="ratio">1.00</span>x)
</p>
</div>
</div>
@ -67,7 +47,7 @@
<div class="d-flex align-items-center">
<p class="price mb-0">Rs. {{ user.price|floatformat:2 }}</p>
<!-- Hire button -->
<button class="btn btn-primary btn-sm ms-auto" data-bs-toggle="modal" data-bs-target="#hireModal" data-name="{{ user.first_name }} {{ user.last_name }}" data-bio="{{ user.bio }}" data-price="{{ user.price|floatformat:2 }}" data-id="{{ user.id }}">Hire</button>
<button class="btn btn-primary btn-sm ms-auto" data-bs-toggle="modal" data-bs-target="#hireModal" data-id="{{user.id}}" data-name="{{ user.first_name }} {{ user.last_name }}" data-email="{{ user.email }}" data-contact = "{{ user.phone_number }}" data-bio="{{ user.bio }}" data-price="{{ user.price|floatformat:2 }}" data-id="{{ user.id }}">Hire</button>
</div>
</div>
</div>
@ -90,24 +70,44 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="hireForm">
<form id="hireForm" method="POST">
{% csrf_token %}
<div class="mb-3">
<label for="caretakerName" class="form-label">Caretaker Name</label>
<input type="text" class="form-control" id="caretakerName" disabled>
<input type="text" class="form-control" name="caretakerName" id="caretakerName" disabled>
</div>
<div class="mb-3">
<label for="caretakerBio" class="form-label">Bio</label>
<textarea class="form-control" id="caretakerBio" rows="3" disabled></textarea>
<textarea class="form-control" id="caretakerBio" name="caretakerBio" rows="3" disabled></textarea>
</div>
<div class="mb-3">
<label for="caretakeremail" class="form-label">Email Address</label>
<textarea class="form-control" id="caretakeremail" name = "caretakeremail" rows="3" disabled></textarea>
</div>
<div class="mb-3">
<label for="caretakercontact" class="form-label">Contact no</label>
<textarea class="form-control" id="caretakercontact" name="caretakercontact" rows="3" disabled></textarea>
</div>
<div class="mb-3">
<label for="caretakerPrice" class="form-label">Price</label>
<input type="text" class="form-control" id="caretakerPrice" disabled>
<input type="text" class="form-control" id="caretakerPrice" name="caretakercontact" price="caretakercontact">
</div>
<div class="mb-3">
<label for="serviceHour" class="form-label">Hours to Hire</label>
<input type="number" class="form-control" id="serviceHour" name="serviceHour">
</div>
<div class="mb-3">
<label for="service" class="form-label">Service </label>
<select class="form-control" id="caretakerservice" name="caretakerservice">
{% for service in services %}
<option value={{service.id}}>{{service.name}}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="caretakerService" class="form-label">Service</label>
<input type="text" class="form-control" id="caretakerService" disabled>
<input type="hidden" class="form-control" id="caretakerid" name="caretakerid" disabled>
</div>
<button type="submit" class="btn btn-primary">Confirm Hire</button>
<a "btn btn-primary" href="javascript:sendRequest()" >Send Request</a>
</form>
</div>
</div>
@ -127,26 +127,68 @@
const bio = button.getAttribute('data-bio');
const price = button.getAttribute('data-price');
const serviceId = button.getAttribute('data-id');
const contact = button.getAttribute('data-contact');
const email = button.getAttribute('data-email');
const id = button.getAttribute('data-id');
// Populate the modal fields
document.getElementById('caretakerName').value = name;
document.getElementById('caretakerBio').value = bio;
document.getElementById('caretakerPrice').value = price;
// Fetch service name based on service ID (optional, if needed)
fetch(`/get-service-name/${serviceId}/`)
.then(response => response.json())
.then(data => {
document.getElementById('caretakerService').value = data.service_name;
});
document.getElementById('caretakeremail').value = email;
document.getElementById('caretakercontact').value = contact;
document.getElementById('caretakerid').value = id;
});
const sendRequest = async () => {
try {
const price = document.getElementById('caretakerPrice').value;
const serviceHour = document.getElementById('serviceHour').value;
const serviceProviderId = document.getElementById('caretakerid').value;
const caretakerservice = document.getElementById('caretakerservice').value;
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const hireUrl = "{% url 'hire' %}";
const initiatorId = "{{ request.user.id }}";
const response = await fetch(hireUrl, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
"X-CSRFToken": csrfToken,
},
body: JSON.stringify({
price: price,
initiator: initiatorId,
serviceProvider: serviceProviderId,
serviceHour: serviceHour,
caretakerservice: caretakerservice,
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
const data = await response.json();
console.log('Request was successful:', data);
window.location.href = window.location.href;
} catch (error) {
console.error('There was an error with the request:', error.message);
alert('There was an error with your request: ' + error.message);
}
};
const sendSearch = () => {
let url = window.location.href;
const searchTerm = document.getElementById("search").value;
url = url.split('?')[0] + "?search=" + encodeURIComponent(searchTerm);
window.location.href = url;
}
</script>
{% endblock %}

@ -0,0 +1,126 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<style>
body {
background-color: #f8f9fa;
}
.profile-header {
background-color: #007bff;
color: white;
padding: 20px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.profile-header .left {
display: flex;
align-items: center;
}
.profile-header img {
border-radius: 50%;
width: 100px;
height: 100px;
margin-right: 20px;
}
.profile-details {
margin-bottom: 20px;
}
.card {
border: none;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.status-badge {
padding: 5px 10px;
border-radius: 5px;
color: white;
}
.status-active {
background-color: #28a745;
}
.status-complete {
background-color: #17a2b8;
}
</style>
<div class="container mt-4">
<!-- Profile Header -->
<div class="profile-header">
<div class="left">
{% if user.profile and user.profile.url %}
<img src="{{ user.profile.url }}" alt="Profile Picture">
{% else %}
<img src="{% static 'img/dummypic.png' %}" alt="Profile Picture">
{% endif %}
<div>
<h2>{{ user.first_name|capfirst }} {{ user.last_name|capfirst }}</h2>
<p>Customer ID: {{ user.id }}</p>
</div>
</div>
<div>
<p>Member Since: {{ user.member_since|date:"b - Y" }}</p>
<p>Status: Active</p>
</div>
</div>
<!-- Profile Details -->
<div class="profile-details">
<div class="row">
<div class="col-md-12">
<div class="card p-6">
<h5>Contact Information</h5>
<p>Email: {{ user.email }}</p>
<p>Phone: (+977) {{ user.phone_number }}</p>
<p>Address: {{ user.address }}</p>
</div>
</div>
</div>
</div>
{% if request.user == user %}
<!-- Pending/Active Requests -->
<div class="recent-activities">
<h4 class="mb-3">Pending/Active Requests</h4>
<div class="card p-3">
<ul class="list-group list-group-flush">
{% for req in pending_requests %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ req.service_offered }} request from {{ req.client.first_name|capfirst }} {{ req.client.last_name|capfirst }}
<a class="status-badge status-active">Accept</a>
</li>
{% empty %}
<li class="list-group-item d-flex justify-content-between align-items-center">
Hurray! There are no Pending Requests till now.
</li>
{% endfor %}
</ul>
</div>
</div>
<!-- Completed Services -->
<div class="recent-activities mt-4">
<h4 class="my-3">Completed/Active Requests</h4>
<div class="card p-3">
<ul class="list-group list-group-flush">
{% for service in current %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ service.service_offered }} request from {{ service.client.first_name|capfirst }} {{ service.client.last_name|capfirst }}
<a class="status-badge status-complete">Complete</a>
</li>
{% empty %}
<li class="list-group-item d-flex justify-content-between align-items-center">
There are no active tasks right now.
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
{% endblock %}

@ -0,0 +1,164 @@
{% extends 'base.html' %}
{% block content %}
<style>
/* General Styles */
body {
margin: 0;
font-family: Arial, sans-serif;
}
h1, h2 {
text-align: center;
color: #333;
}
.section {
padding: 50px 20px;
background-color: #fdf6e3; /* Beige background */
margin: 20px auto;
width: 80%;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateX(-100px);
transition: all 0.8s ease-in-out;
}
.section.visible {
opacity: 1;
transform: translateX(0);
}
.service {
display: flex;
align-items: center;
margin-top: 20px;
}
.service img {
width: 100px;
height: 100px;
border-radius: 8px;
margin-right: 20px;
}
.service p {
font-size: 16px;
line-height: 1.6;
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 40px;
}
.grid-item {
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
text-align: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
opacity: 0;
transform: translateY(100px);
transition: all 0.8s ease-in-out;
}
.grid-item.visible {
opacity: 1;
transform: translateY(0);
}
.grid-item img {
width: 80px;
height: 80px;
margin-bottom: 10px;
}
.grid-item h3 {
margin: 10px 0;
font-size: 18px;
}
.grid-item p {
font-size: 14px;
color: #555;
}
</style>
</head>
<body>
<h1>Services</h1>
<section class="section" id="service-1">
<h2>Our Services</h2>
<div class="service">
<img src="https://via.placeholder.com/100" alt="Service Image">
<p>I am a tech-savvy individual with a Bachelor's degree in Software Development, seeking employment as a mobile game developer. I am passionate about consistently advancing my knowledge and skills. I have attended multiple seminars and boot camps on coding and game development.</p>
</div>
</section>
<section class="section" id="service-2">
<h2>What We Offer</h2>
<div class="service">
<img src="https://via.placeholder.com/100" alt="Service Image">
<p>I am a tech-savvy individual with a Bachelor's degree in Software Development, seeking employment as a mobile game developer. I am passionate about consistently advancing my knowledge and skills. I have attended multiple seminars and boot camps on coding and game development.</p>
</div>
</section>
<section class="section" id="services-grid">
<h2>Our Web App Provides</h2>
<div class="services-grid">
<div class="grid-item">
<img src="https://via.placeholder.com/80" alt="Feature 1">
<h3>Feature 1</h3>
<p>Detail about the first feature of the web app.</p>
</div>
<div class="grid-item">
<img src="https://via.placeholder.com/80" alt="Feature 2">
<h3>Feature 2</h3>
<p>Detail about the second feature of the web app.</p>
</div>
<div class="grid-item">
<img src="https://via.placeholder.com/80" alt="Feature 3">
<h3>Feature 3</h3>
<p>Detail about the third feature of the web app.</p>
</div>
<div class="grid-item">
<img src="https://via.placeholder.com/80" alt="Feature 4">
<h3>Feature 4</h3>
<p>Detail about the fourth feature of the web app.</p>
</div>
</div>
</section>
<script>
// Function to check if element is in viewport
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// Function to add "visible" class to sections
function handleScroll() {
const sections = document.querySelectorAll('.section, .grid-item');
sections.forEach(section => {
if (isInViewport(section)) {
section.classList.add('visible');
}
});
}
// Listen for scroll event
window.addEventListener('scroll', handleScroll);
// Trigger scroll event on load to show elements already in view
window.addEventListener('load', handleScroll);
</script>
{% endblock %}

@ -5,7 +5,9 @@ urlpatterns = [
path('', views.home, name="home"),
path('register/', views.register, name='register'),
path('activate/<str:uidb64>/<str:token>/', views.activate_account, name='activate_account'),
# path('verify-email/<str:uidb64>/<str:token>/', views.verify_email, name='verify_email'),
path('login/', views.user_login, name='login'),
path('logout/', views.user_logout, name='logout'),
path('hire/', views.hire_view, name='hire'),
path('services/',views.services,name='services'),
path('profiles/<pk>',views.profie_View, name="profiles")
]

@ -1,35 +1,49 @@
from django.shortcuts import render, redirect,HttpResponse,get_list_or_404
from django.shortcuts import render, redirect,HttpResponse,get_object_or_404
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_decode
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import User,Service
from .models import User,Service,ServiceRequest
from .forms import UserRegistrationForm
import logging
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_protect
logger = logging.getLogger('main')
def home(request):
category = request.GET.get('category')
services = Service.objects.all()
if category:
try:
service = Service.objects.get(id=category)
service_providers = User.objects.filter(service_offered=service)
except Service.DoesNotExist:
service_providers = []
else:
service_providers = User.objects.filter(service_offered__isnull=False)
context = {
'service_providers': service_providers,
'services': services
}
if(request.method == "GET"):
category = request.GET.get('category')
search = request.GET.get('search')
services = Service.objects.all()
if category:
try:
service = Service.objects.get(id=category)
service_providers = User.objects.filter(service_offered=service)
except Service.DoesNotExist:
service_providers = []
elif search:
try:
service_providers = User.objects.filter(first_name__startswith=search)
except Service.DoesNotExist:
service_providers = []
else:
service_providers = User.objects.filter(service_offered__isnull=False)
context = {
'service_providers': service_providers,
'services': services
}
return render(request, "main/home.html", context)
return render(request, "main/home.html", context)
print(request.POST)
return HttpResponse("<h1>tjisfjsa<h1>")
def register(request):
if request.user.is_authenticated:
messages.warning(request,"You are already logged in. Please Logout first to register ")
return redirect('home')
if request.method == 'POST' and request.FILES:
form = UserRegistrationForm(request.POST,request.FILES, instance=request.user)
if form.is_valid():
@ -38,7 +52,7 @@ def register(request):
user.save()
try:
user.send_verification_email()
user.send_verification_email(user)
messages.success(request, 'Registration successful! Please check your email to activate your account.')
except Exception as e:
logger.error(f"Error sending verification email: {e}")
@ -67,24 +81,29 @@ def activate_account(request, uidb64, token):
return redirect('register')
def user_login(request):
print(request.user.is_authenticated)
if request.user.is_authenticated:
messages.info(request, f'You are already logged in as {request.user}')
return redirect('home')
else:
if request.method == 'POST':
email = request.POST['email']
password = request.POST['password']
user = authenticate(request, email = email, password = password)
# Authenticate the user using the email and password
user = authenticate(request, email=email, password=password)
if user is not None:
form = login(request, user)
messages.success(request, f' welcome {user} !!')
# Log the user in
login(request, user)
messages.success(request, f'Welcome {user}!')
return redirect('home')
else:
messages.info(request, f'Something went wrong')
messages.info(request, f'You are Already Logged in as {request.user}')
return render(request, 'main/login.html', {'title':'log in'})
else:
# If authentication fails
messages.error(request, 'Invalid email or password. Please try again.')
# Handle GET request by initializing a blank form
form = None # Optionally, you can replace this with your login form
return render(request, 'main/login.html', {'form': form})
@login_required
def user_logout(request):
@ -92,7 +111,70 @@ def user_logout(request):
messages.success(request, 'You have been logged out.')
return redirect('login')
# def verify_email(request, uidb64, token):
# # This view is redundant if it does the same as activate_account
# # Consider removing or assigning a different purpose
# pass
def services(request):
return render(request,'main/services1.html')
@login_required
@csrf_protect
def hire_view(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
price = data.get('price')
initiator_id = data.get('initiator')
service_provider_id = data.get('serviceProvider')
service_hour = data.get('serviceHour')
caretakerservice_id = data.get('caretakerservice')
# Validate required fields
if not price or not initiator_id or not service_provider_id or not service_hour or not caretakerservice_id:
return JsonResponse({'error': 'Missing required fields'}, status=400)
# Validate initiator
if str(request.user.id) != str(initiator_id):
return JsonResponse({'error': 'Unauthorized initiator'}, status=401)
# Retrieve service provider and service
service_provider = get_object_or_404(User, id=service_provider_id)
service = get_object_or_404(Service, id=caretakerservice_id)
# Create and save the service request
service_request = ServiceRequest(
agreed_price=price,
service_provider=service_provider,
client=request.user,
service_hour=service_hour,
service=service
)
service_request.save()
messages.success(request, 'Request submitted successfully!')
return JsonResponse({'success': 'Request submitted successfully!'}, status=201)
except json.JSONDecodeError:
messages.error(request, 'Invalid JSON format')
return JsonResponse({'error': 'Invalid JSON format'}, status=400)
except User.DoesNotExist:
messages.error(request, 'Service provider does not exist')
return JsonResponse({'error': 'Service provider does not exist'}, status=404)
except Service.DoesNotExist:
messages.error(request, 'Service does not exist')
return JsonResponse({'error': 'Service does not exist'}, status=404)
except Exception as e:
messages.error(request, f'An error occurred: {str(e)}')
return JsonResponse({'error': 'An error occurred'}, status=500)
else:
messages.error(request, 'Invalid request method')
return JsonResponse({'error': 'Invalid request method'}, status=405)
def profie_View(request,pk):
if request.method == "GET":
user = get_object_or_404(User,id=pk)
pending_requests = ServiceRequest.objects.filter(service_provider=user, is_approved=False)
current = ServiceRequest.objects.filter(service_provider=user, is_approved=True)
completed = ServiceRequest.objects.filter(service_provider=user, is_completed=True)
context = {'user':user,
'pending_requests':pending_requests,
'completed_orders' : completed
}
return render(request,'main/profile.html', context)

@ -142,3 +142,59 @@ body {
min-height: 100vh;
background: url('img/backgroung-form.jpg') no-repeat center center/cover;
}
/* status for history */
.status-badge {
font-size: 0.9em;
padding: 0.25em 0.6em;
border-radius: 10px;
margin-left: 10px;
}
.status-active {
background-color: #28a745;
color: white;
}
.status-inactive {
background-color: #dc3545;
color: white;
}
footer {
margin-top: 50px;
}
/* darken */
header {
position: relative;
background: url('../img/background.jpg') no-repeat center center/cover;
height: 300px;
width: 100%;
}
header::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5); /* Dark overlay */
z-index: 1; /* Make sure the overlay is above the image but below the text */
}
header .container {
position: relative;
z-index: 2; /* Ensure the text appears above the overlay */
}
header h1 {
font-size: 2.5rem; /* Larger text for better visibility */
font-weight: bold;
color: #fff; /* White text for contrast */
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); /* Adding shadow to make text pop */
}
header p {
font-size: 1.2rem;
color: #fff; /* White text for contrast */
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7); /* Subtle shadow for better legibility */
}

@ -0,0 +1,144 @@
body {
background-color:#ffffff;
}
.navbar .navbar-brand {
margin-right: auto;
}
.navbar .nav {
margin: 0 auto;
text-align: center;
}
.navbar .login {
margin-left: auto;
}
.search-bar {
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
}
.search-bar input {
width: 50%;
border-radius: 50px;
}
.profile-card {
border: 1px solid #ddd;
border-radius: 10px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.profile-card:hover {
transform: translateY(-10px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.profile-card img {
width: 100%;
height: 200px;
object-fit: cover;
object-position: 10%;
}
.profile-card .card-body {
padding: 15px;
}
.profile-card .card-title {
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 10px;
}
.profile-card .card-text {
font-size: 0.9rem;
color: #555;
}
.profile-card .price {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
.profile-card .availability {
font-size: 0.9rem;
color: #28a745;
}
.category-bar {
background-color: #f8f9fa; /* Light background */
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* Subtle shadow */
}
.category-bar h4 {
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.category-bar ul {
padding-left: 0;
}
.category-bar .category-link {
display: block;
padding: 10px 15px;
margin: 5px 0;
color: #555;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease, color 0.3s ease;
}
.category-bar .category-link:hover {
background-color: #007bff; /* Highlight on hover */
color: #fff;
}
/* odd */
.form-container {
font-family: Arial, sans-serif;
margin: auto;
padding: 30px;
background: rgba(255, 255, 255, 0.9);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
width: 600px;
}
.form-container h2 {
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.form-group input[type="file"] {
padding: 5px;
}
.form-group button {
width: 100%;
padding: 10px;
background-color: #007BFF;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
.form-group button:hover {
background-color: #0056b3;
}
.form-wrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: url('img/backgroung-form.jpg') no-repeat center center/cover;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

@ -10,30 +10,42 @@
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="bg-body-tertiary">
<nav class="navbar container navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="#"><img src="{% static 'img/logofinal.png' %}" height="50px" width="75px" alt="Logo"></a>
<a class="navbar-brand" href="{% url 'home' %}"><img src="{% static 'img/logofinal.png' %}" height="50px" width="75px" alt="Logo"></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="nav justify-content-center">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link active" aria-current="page" href="{% url 'home' %}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About us</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Services</a>
<a class="nav-link" href="{% url 'services' %}">Services</a>
</li>
{% if request.user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'profiles' request.user.id %}">Profile</a>
</li>
{% endif %}
</ul>
<div class="login">
<a href="#" class="btn btn-outline-primary">Login</a>
{% if request.user.is_authenticated %}
<a href="{% url 'logout' %}" class="btn btn-outline-primary">Log Out</a>
{% else %}
<a href="{% url 'register' %}" class="btn btn-outline-success">SignUp</a>
<a href="{% url 'login' %}" class="btn btn-outline-primary">Log In</a>
{% endif %}
</div>
</div>
</div>
</nav>
</div>
<div class="container mt-3">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
@ -47,3 +59,7 @@
{% block content %}
{% endblock %}
<footer class="bg-dark text-white text-center py-3">
<p>&copy; 2025 Sahara. All Rights Reserved. | <a href="#" class="text-white">Privacy Policy</a> | <a href="#" class="text-white">Terms of Service</a></p>
</footer>
Loading…
Cancel
Save