@ -0,0 +1,19 @@ |
||||
# Generated by Django 5.1.4 on 2025-01-11 20:13 |
||||
|
||||
import django.db.models.deletion |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('main', '0003_service_alter_user_bio_alter_user_price_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='user', |
||||
name='service_offered', |
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='main.service'), |
||||
), |
||||
] |
@ -0,0 +1,29 @@ |
||||
# Generated by Django 5.1.4 on 2025-01-12 01:26 |
||||
|
||||
import django.db.models.deletion |
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('main', '0004_user_service_offered'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='user', |
||||
name='login_profile', |
||||
field=models.CharField(choices=[('NORMAL', 'Normal User'), ('SERVICE', 'Service Provider'), ('ADMIN', 'Admin User')], default='NORMAL', max_length=15), |
||||
), |
||||
migrations.AlterField( |
||||
model_name='servicerequest', |
||||
name='service', |
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='service_offered', to='main.service'), |
||||
), |
||||
migrations.AlterField( |
||||
model_name='user', |
||||
name='profile', |
||||
field=models.ImageField(blank=True, null=True, upload_to='profile-images/'), |
||||
), |
||||
] |
@ -1,63 +1,152 @@ |
||||
{% extends 'base.html' %} |
||||
{% load static %} |
||||
|
||||
{% block content %} |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>Login Form</title> |
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> |
||||
<style> |
||||
.login-container { |
||||
height: 100vh; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
background: #f4f7fc; |
||||
} |
||||
.login-card { |
||||
padding: 2rem; |
||||
border-radius: 8px; |
||||
background: white; |
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
||||
width: 100%; |
||||
max-width: 400px; |
||||
} |
||||
.login-card h2 { |
||||
text-align: center; |
||||
margin-bottom: 1rem; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div class="login-container"> |
||||
<div class="login-card"> |
||||
<h2>Login</h2> |
||||
<form action="#" method="post"> |
||||
{% csrf_token %} |
||||
<div class="mb-3"> |
||||
<label for="email" class="form-label">Email</label> |
||||
<input type="text" class="form-control" id="email" name="email" placeholder="Enter your email" required> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label for="password" class="form-label">Password</label> |
||||
<input type="password" class="form-control" id="password" name="password" placeholder="Enter your password" required> |
||||
<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> |
||||
</div> |
||||
</header> |
||||
|
||||
<div class="search-bar"> |
||||
<input type="text" class="form-control" name=search id=search placeholder="Search Your Caretaker..."> |
||||
<button class="btn btn-primary ms-1" onclick=sendSearch()>Search</button> |
||||
</div> |
||||
|
||||
<section class="container my-5"> |
||||
<div class="row"> |
||||
<div class="col-md-3"> |
||||
<div class="category-bar"> |
||||
<h4>Services</h4> |
||||
<ul class="list-unstyled"> |
||||
{% for service in services %} |
||||
<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 class="d-flex justify-content-between align-items-center"> |
||||
<div class="form-check"> |
||||
<input type="checkbox" class="form-check-input" id="rememberMe"> |
||||
<label class="form-check-label" for="rememberMe">Remember Me</label> |
||||
</div> |
||||
|
||||
<div class="col-md-9"> |
||||
<h2 class="text-center mb-4">Available Caretakers</h2> |
||||
<div class="row row-cols-1 row-cols-md-3 g-4"> |
||||
|
||||
{% for user in service_providers %} |
||||
<div class="col"> |
||||
<div class="profile-card"> |
||||
{% if user.profile %} |
||||
<img src="{{ user.profile.url }}" alt="{{ user.first_name }} {{ user.last_name }}'s Image" class="img-fluid"> |
||||
{% else %} |
||||
<img src="{% static 'img/dummypic.png' %}" alt="Avatar Image" class="img-fluid"> |
||||
{% endif %} |
||||
|
||||
<div class="card-body"> |
||||
<h5 class="card-title">{{ user.first_name|capfirst }} {{ user.last_name|capfirst }}</h5> |
||||
<p class="card-text">Bio: {{ user.bio }}</p> |
||||
<p class="availability">Member Since: {{ user.member_since|date:"F j, Y" }}</p> |
||||
<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> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<a href="#" class="small">Forgot password?</a> |
||||
{% empty %} |
||||
<div class="mx-auto">Oops! No Caretaker Found Currently.</div> |
||||
{% endfor %} |
||||
|
||||
</div> |
||||
<button type="submit" class="btn btn-primary w-100 mt-3">Login</button> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</section> |
||||
|
||||
<!-- Modal --> |
||||
<div class="modal fade" id="hireModal" tabindex="-1" aria-labelledby="hireModalLabel" aria-hidden="true"> |
||||
<div class="modal-dialog"> |
||||
<div class="modal-content"> |
||||
<div class="modal-header"> |
||||
<h5 class="modal-title" id="hireModalLabel">Hire Caretaker</h5> |
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
</div> |
||||
<div class="modal-body"> |
||||
<form id="hireForm"> |
||||
<div class="mb-3"> |
||||
<label for="caretakerName" class="form-label">Caretaker Name</label> |
||||
<input type="text" class="form-control" 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> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label for="caretakerPrice" class="form-label">Price</label> |
||||
<input type="text" class="form-control" id="caretakerPrice" disabled> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label for="caretakerService" class="form-label">Service</label> |
||||
<input type="text" class="form-control" id="caretakerService" disabled> |
||||
</div> |
||||
<button type="submit" class="btn btn-primary">Confirm Hire</button> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> |
||||
</body> |
||||
</html> |
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> |
||||
<script> |
||||
// Handle the modal data population |
||||
const hireModal = document.getElementById('hireModal'); |
||||
hireModal.addEventListener('show.bs.modal', function (event) { |
||||
// Button that triggered the modal |
||||
const button = event.relatedTarget; |
||||
|
||||
// Extract information from data-* attributes |
||||
const name = button.getAttribute('data-name'); |
||||
const bio = button.getAttribute('data-bio'); |
||||
const price = button.getAttribute('data-price'); |
||||
const serviceId = 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; |
||||
}); |
||||
}); |
||||
|
||||
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 %} |
||||
{% endblock %} |
||||
|
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 113 KiB |
@ -1,7 +1,13 @@ |
||||
from django.contrib import admin |
||||
from django.urls import path, include |
||||
from django.urls import path,include |
||||
from django.conf import settings |
||||
from django.conf.urls.static import static |
||||
|
||||
urlpatterns = [ |
||||
path('admin/', admin.site.urls), |
||||
path('', include('main.urls')), |
||||
] |
||||
] |
||||
|
||||
|
||||
if settings.DEBUG: |
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
@ -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; |
||||
} |
After Width: | Height: | Size: 266 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 492 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 218 KiB |