major updation

This commit is contained in:
2025-08-20 11:35:11 +05:30
parent a6f62de2c4
commit 27d013e853
19 changed files with 578 additions and 60 deletions
+7 -5
View File
@@ -26,7 +26,7 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'core', 'core.apps.CoreConfig',
] ]
AUTH_USER_MODEL = 'core.User' AUTH_USER_MODEL = 'core.User'
@@ -67,9 +67,9 @@ WSGI_APPLICATION = 'civicfix.wsgi.application'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',
'NAME': 'civixfix_db', 'NAME': 'civicfix',
'USER': 'civicfix_user', 'USER': 'admin',
'PASSWORD': 'Gokul@2001', 'PASSWORD': 'qwerty123',
'HOST': 'localhost', 'HOST': 'localhost',
'PORT': '5432', 'PORT': '5432',
} }
@@ -116,5 +116,7 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = 'home' # Add these settings
LOGIN_REDIRECT_URL = 'citizen_dashboard'
LOGIN_URL = 'login'
LOGOUT_REDIRECT_URL = 'home' LOGOUT_REDIRECT_URL = 'home'
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+30 -2
View File
@@ -1,6 +1,6 @@
from django import forms from django import forms
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from .models import User from .models import User, Issue
class CitizenRegistrationForm(UserCreationForm): class CitizenRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True) email = forms.EmailField(required=True)
@@ -18,4 +18,32 @@ class CitizenRegistrationForm(UserCreationForm):
if commit: if commit:
user.save() user.save()
return user return user
class CitizenRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
phone = forms.CharField(max_length=15, required=False)
class Meta:
model = User
fields = ['username', 'email', 'phone', 'password1', 'password2']
def save(self, commit=True):
user = super().save(commit=False)
user.email = self.cleaned_data['email']
user.phone = self.cleaned_data['phone']
user.is_citizen = True
if commit:
user.save()
return user
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ['title', 'description', 'category', 'location', 'latitude', 'longitude', 'photo']
widgets = {
'latitude': forms.HiddenInput(),
'longitude': forms.HiddenInput(),
'description': forms.Textarea(attrs={'rows': 4, 'placeholder': 'Describe the issue in detail...'}),
}
+32 -3
View File
@@ -1,8 +1,11 @@
# Generated by Django 5.2.5 on 2025-08-18 15:07 # Generated by Django 5.2.5 on 2025-08-20 06:30
import django.contrib.auth.models import django.contrib.auth.models
import django.contrib.auth.validators import django.contrib.auth.validators
import django.core.validators
import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@@ -15,6 +18,15 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.CreateModel(
name='IssueCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(blank=True)),
('icon', models.CharField(default='fas fa-exclamation-circle', max_length=50)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='User', name='User',
fields=[ fields=[
@@ -33,8 +45,8 @@ class Migration(migrations.Migration):
('is_moderator', models.BooleanField(default=False)), ('is_moderator', models.BooleanField(default=False)),
('is_resolver', models.BooleanField(default=False)), ('is_resolver', models.BooleanField(default=False)),
('phone', models.CharField(blank=True, max_length=15, null=True)), ('phone', models.CharField(blank=True, max_length=15, null=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to.', related_name='core_user_groups', related_query_name='core_user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='core_user_permissions', related_query_name='core_user', to='auth.permission', verbose_name='user permissions')),
], ],
options={ options={
'verbose_name': 'user', 'verbose_name': 'user',
@@ -45,4 +57,21 @@ class Migration(migrations.Migration):
('objects', django.contrib.auth.models.UserManager()), ('objects', django.contrib.auth.models.UserManager()),
], ],
), ),
migrations.CreateModel(
name='Issue',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('description', models.TextField()),
('location', models.CharField(max_length=200)),
('latitude', models.FloatField()),
('longitude', models.FloatField()),
('photo', models.ImageField(blank=True, null=True, upload_to='issue_photos/', validators=[django.core.validators.FileExtensionValidator(['jpg', 'jpeg', 'png', 'gif'])])),
('status', models.CharField(choices=[('reported', 'Reported'), ('acknowledged', 'Acknowledged'), ('in_progress', 'In Progress'), ('resolved', 'Resolved')], default='reported', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('reporter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reported_issues', to=settings.AUTH_USER_MODEL)),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.issuecategory')),
],
),
] ]
+49 -2
View File
@@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.urls import reverse from django.urls import reverse
from django.core.validators import FileExtensionValidator
class User(AbstractUser): class User(AbstractUser):
is_citizen = models.BooleanField(default=False) is_citizen = models.BooleanField(default=False)
@@ -8,5 +9,51 @@ class User(AbstractUser):
is_resolver = models.BooleanField(default=False) is_resolver = models.BooleanField(default=False)
phone = models.CharField(max_length=15, blank=True, null=True) phone = models.CharField(max_length=15, blank=True, null=True)
def get_absolute_url(self): groups = models.ManyToManyField(
return reverse('home') 'auth.Group',
verbose_name='groups',
blank=True,
help_text='The groups this user belongs to.',
related_name='core_user_groups',
related_query_name='core_user',
)
user_permissions = models.ManyToManyField(
'auth.Permission',
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name='core_user_permissions',
related_query_name='core_user',
)
class IssueCategory(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
icon = models.CharField(max_length=50, default='fas fa-exclamation-circle')
def __str__(self):
return self.name
class Issue(models.Model):
STATUS_CHOICES = [
('reported', 'Reported'),
('acknowledged', 'Acknowledged'),
('in_progress', 'In Progress'),
('resolved', 'Resolved'),
]
title = models.CharField(max_length=200)
description = models.TextField()
category = models.ForeignKey(IssueCategory, on_delete=models.SET_NULL, null=True, blank=True)
reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reported_issues')
location = models.CharField(max_length=200)
latitude = models.FloatField()
longitude = models.FloatField()
photo = models.ImageField(upload_to='issue_photos/', blank=True, null=True,
validators=[FileExtensionValidator(['jpg', 'jpeg', 'png', 'gif'])])
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='reported')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
+49 -25
View File
@@ -35,32 +35,56 @@
<body> <body>
<!-- Navigation --> <!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
<div class="container"> <div class="container">
<a class="navbar-brand" href="{% url 'home' %}"> <a class="navbar-brand" href="{% url 'home' %}">
<i class="fas fa-bullhorn me-2"></i>Civixfix <i class="fas fa-bullhorn me-2"></i>Civixfix
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto"> <ul class="navbar-nav me-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="{% url 'home' %}">Home</a> <a class="nav-link active" href="{% url 'home' %}">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#features">Features</a> <a class="nav-link" href="#features">Features</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#how-it-works">How It Works</a> <a class="nav-link" href="#how-it-works">How It Works</a>
</li> </li>
</ul> {% if user.is_authenticated and user.is_citizen %}
<div class="d-flex"> <li class="nav-item">
<a href="{% url 'login' %}" class="btn btn-outline-light me-2">Login</a> <a class="nav-link" href="{% url 'citizen_dashboard' %}">Dashboard</a>
<a href="{% url 'register' %}" class="btn btn-primary">Register</a> </li>
</div> {% endif %}
</div> </ul>
<div class="d-flex">
{% if user.is_authenticated %}
<div class="dropdown">
<button class="btn btn-outline-light dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-user me-1"></i> {{ user.username }}
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'citizen_dashboard' %}">Dashboard</a></li>
<li><a class="dropdown-item" href="#">Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<form method="post" action="{% url 'logout' %}">
{% csrf_token %}
<button type="submit" class="dropdown-item">Logout</button>
</form>
</li>
</ul>
</div>
{% else %}
<a href="{% url 'login' %}" class="btn btn-outline-light me-2">Login</a>
<a href="{% url 'register' %}" class="btn btn-primary">Register</a>
{% endif %}
</div>
</div> </div>
</nav> </div>
</nav>
<!-- Main Content --> <!-- Main Content -->
<main> <main>
@@ -0,0 +1,240 @@
{% extends "core/base.html" %}
{% block title %}Citizen Dashboard - CivixFix{% endblock %}
{% block extra_css %}
<style>
.dashboard-card {
transition: transform 0.2s;
}
.dashboard-card:hover {
transform: translateY(-2px);
}
.map-container {
height: 300px;
border-radius: 8px;
overflow: hidden;
border: 2px dashed #dee2e6;
}
.issue-status {
font-size: 0.8rem;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="row">
<!-- Sidebar - Quick Actions -->
<div class="col-md-3">
<div class="card shadow-sm dashboard-card">
<div class="card-body text-center">
<h5 class="card-title">Welcome, {{ user.username }}</h5>
<p class="text-muted">Citizen Dashboard</p>
<div class="d-grid gap-2 mb-3">
<button class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#reportIssueModal">
<i class="fas fa-plus me-2"></i>Report New Issue
</button>
</div>
<hr>
<h6>Quick Stats</h6>
<div class="row text-center">
<div class="col-6">
<div class="bg-light p-2 rounded">
<h4 class="mb-0 text-primary">{{ user_issues.count }}</h4>
<small>My Reports</small>
</div>
</div>
<div class="col-6">
<div class="bg-light p-2 rounded">
<h4 class="mb-0 text-success">
{{ resolved_count }}
</h4>
<small>Resolved</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content - Recent Issues -->
<div class="col-md-9">
<!-- Welcome Message -->
<div class="card shadow-sm dashboard-card mb-4">
<div class="card-body">
<h4>Welcome to CivixFix!</h4>
<p class="text-muted mb-0">
Report community issues, track their progress, and help make your neighborhood better.
Start by reporting an issue using the button on the left.
</p>
</div>
</div>
<!-- My Recent Issues -->
<div class="card shadow-sm dashboard-card">
<div class="card-header">
<h5 class="mb-0">My Recent Reports</h5>
</div>
<div class="card-body">
{% for issue in user_issues %}
<div class="card mb-3">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<h6 class="card-title mb-1">{{ issue.title }}</h6>
<p class="card-text text-muted mb-2 small">
{{ issue.description|truncatewords:15 }}
</p>
<div class="d-flex gap-2 align-items-center">
<span class="badge bg-secondary">{{ issue.category.name|default:"No Category" }}</span>
<span class="badge bg-{% if issue.status == 'resolved' %}success{% elif issue.status == 'in_progress' %}warning{% else %}primary{% endif %} issue-status">
{{ issue.get_status_display }}
</span>
<small class="text-muted">{{ issue.created_at|date:"M d, Y" }}</small>
</div>
</div>
{% if issue.photo %}
<div class="ms-3">
<img src="{{ issue.photo.url }}" alt="Issue photo"
class="img-fluid rounded" style="max-height: 60px; max-width: 80px;">
</div>
{% endif %}
</div>
</div>
</div>
{% empty %}
<div class="text-center py-4">
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
<h5>No issues reported yet</h5>
<p class="text-muted">Click "Report New Issue" to get started!</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
<!-- Report Issue Modal -->
<div class="modal fade" id="reportIssueModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Report New Issue</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="issueForm" method="post" enctype="multipart/form-data" action="{% url 'report_issue' %}">
{% csrf_token %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show mb-3">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Issue Title*</label>
{{ issue_form.title }}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Category</label>
{{ issue_form.category }}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description*</label>
{{ issue_form.description }}
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Location*</label>
{{ issue_form.location }}
<small class="text-muted">Click on the map to select location</small>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Photo (Optional)</label>
{{ issue_form.photo }}
</div>
</div>
</div>
<!-- Hidden latitude/longitude fields -->
{{ issue_form.latitude }}
{{ issue_form.longitude }}
<div class="map-container mb-3">
<div id="map" style="height: 100%;"></div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" form="issueForm" class="btn btn-primary">
<i class="fas fa-paper-plane me-2"></i>Report Issue
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
<script>
$(document).ready(function() {
// Initialize map
const map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
let marker = null;
// Add click event to map
map.on('click', function(e) {
if (marker) {
map.removeLayer(marker);
}
marker = L.marker(e.latlng).addTo(map)
.bindPopup('Selected location').openPopup();
// Update form fields
$('#id_latitude').val(e.latlng.lat);
$('#id_longitude').val(e.latlng.lng);
// Reverse geocode to get address
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${e.latlng.lat}&lon=${e.latlng.lng}`)
.then(response => response.json())
.then(data => {
$('#id_location').val(data.display_name || 'Selected location');
});
});
// Add Bootstrap classes to form fields
$('input:not([type="file"]), select, textarea').addClass('form-control');
$('input[type="file"]').addClass('form-control-file');
// Add specific classes to description field
$('#id_description').addClass('form-control').attr('rows', '3');
});
</script>
{% endblock %}
+54
View File
@@ -0,0 +1,54 @@
{% extends "core/base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow">
<div class="card-body p-5">
<h2 class="card-title text-center mb-4">Login to Your Account</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="id_username" class="form-label">Username</label>
<input type="text" name="username" class="form-control" id="id_username" required>
</div>
<div class="mb-4">
<label for="id_password" class="form-label">Password</label>
<input type="password" name="password" class="form-control" id="id_password" required>
</div>
<div class="d-grid mb-3">
<button type="submit" class="btn btn-primary btn-lg">Login</button>
</div>
<div class="text-center">
<a href="#" class="text-decoration-none">Forgot password?</a>
</div>
</form>
<hr class="my-4">
<p class="text-center text-muted">
Don't have an account? <a href="{% url 'register' %}" class="text-decoration-none">Register here</a>
</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
+32 -17
View File
@@ -1,5 +1,4 @@
{% extends "core/base.html" %} {% extends "core/base.html" %}
{% block title %}Register as Citizen{% endblock %} {% block title %}Register as Citizen{% endblock %}
{% block content %} {% block content %}
@@ -9,38 +8,50 @@
<div class="card shadow"> <div class="card shadow">
<div class="card-body p-5"> <div class="card-body p-5">
<h2 class="card-title text-center mb-4">Create Citizen Account</h2> <h2 class="card-title text-center mb-4">Create Citizen Account</h2>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{# Messages block - FIXED SYNTAX #}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{# Form errors block - FIXED SYNTAX #}
{% if form.errors %} {% if form.errors %}
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Error!</strong> Please correct the following: <strong>Error!</strong> Please correct the following:
<ul> <ul>
{% for field, errors in form.errors.items %} {% for field, errors in form.errors.items %}
{% for error in errors %} {% for error in errors %}
<li>{{ error }}</li> <li>{{ error }}</li>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
<div class="mb-3"> <div class="mb-3">
<label for="id_username" class="form-label">Username</label> <label for="id_username" class="form-label">Username</label>
{{ form.username }} {{ form.username }}
<small class="text-muted">Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small> <small class="text-muted">Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</small>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="id_email" class="form-label">Email</label> <label for="id_email" class="form-label">Email</label>
{{ form.email }} {{ form.email }}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="id_phone" class="form-label">Phone (Optional)</label> <label for="id_phone" class="form-label">Phone (Optional)</label>
{{ form.phone }} {{ form.phone }}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="id_password1" class="form-label">Password</label> <label for="id_password1" class="form-label">Password</label>
{{ form.password1 }} {{ form.password1 }}
@@ -53,20 +64,20 @@
</ul> </ul>
</small> </small>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<label for="id_password2" class="form-label">Password Confirmation</label> <label for="id_password2" class="form-label">Password Confirmation</label>
{{ form.password2 }} {{ form.password2 }}
<small class="text-muted">Enter the same password as before, for verification.</small> <small class="text-muted">Enter the same password as before, for verification.</small>
</div> </div>
<div class="d-grid"> <div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Register</button> <button type="submit" class="btn btn-primary btn-lg">Register</button>
</div> </div>
</form> </form>
<hr class="my-4"> <hr class="my-4">
<p class="text-center text-muted"> <p class="text-center text-muted">
Already have an account? <a href="{% url 'login' %}" class="text-decoration-none">Login here</a> Already have an account? <a href="{% url 'login' %}" class="text-decoration-none">Login here</a>
</p> </p>
@@ -79,10 +90,14 @@
{% block extra_js %} {% block extra_js %}
<script> <script>
// Add Bootstrap classes to form fields $(document).ready(function () {
$(document).ready(function() { // Add Bootstrap classes to all input fields
$('input').addClass('form-control'); $('input:not([type="checkbox"]):not([type="radio"])').addClass('form-control');
$('input[type="checkbox"]').removeClass('form-control').addClass('form-check-input'); $('input[type="checkbox"]').removeClass('form-control').addClass('form-check-input');
$('input[type="radio"]').removeClass('form-control').addClass('form-check-input');
// Add Bootstrap classes to select fields
$('select').addClass('form-select');
}); });
</script> </script>
{% endblock %} {% endblock %}
+3 -1
View File
@@ -5,6 +5,8 @@ from . import views
urlpatterns = [ urlpatterns = [
path('', views.home, name='home'), path('', views.home, name='home'),
path('register/', views.register, name='register'), path('register/', views.register, name='register'),
path('login/', auth_views.LoginView.as_view(template_name='core/login.html'), name='login'), path('login/', views.custom_login, name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('dashboard/', views.citizen_dashboard, name='citizen_dashboard'),
path('report-issue/', views.report_issue, name='report_issue'),
] ]
+82 -5
View File
@@ -1,17 +1,94 @@
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.contrib.auth import login from django.contrib import messages
from .forms import CitizenRegistrationForm from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm
from .models import Issue, IssueCategory
from .forms import CitizenRegistrationForm, IssueForm
def home(request): def home(request):
return render(request, 'core/index.html') return render(request, 'core/index.html')
def home(request):
if request.user.is_authenticated and request.user.is_citizen:
return redirect('citizen_dashboard')
return render(request, 'core/index.html')
def home(request):
if request.user.is_authenticated and request.user.is_citizen:
return redirect('citizen_dashboard')
return render(request, 'core/index.html')
@login_required
def citizen_dashboard(request):
if not request.user.is_citizen:
messages.error(request, 'Access denied. Citizen role required.')
return redirect('home')
# Get only basic data for now
all_user_issues = Issue.objects.filter(reporter=request.user)
user_issues_display = all_user_issues.order_by('-created_at')[:5]
resolved_count = all_user_issues.filter(status='resolved').count()
categories = IssueCategory.objects.all()
context = {
'user_issues': user_issues_display,
'resolved_count': resolved_count,
'categories': categories,
'issue_form': IssueForm(),
}
return render(request, 'core/citizen_dashboard.html', context)
@login_required
def report_issue(request):
if not request.user.is_citizen:
messages.error(request, 'Access denied. Citizen role required.')
return redirect('home')
if request.method == 'POST':
form = IssueForm(request.POST, request.FILES)
if form.is_valid():
issue = form.save(commit=False)
issue.reporter = request.user
issue.save()
messages.success(request, 'Issue reported successfully!')
return redirect('citizen_dashboard')
else:
messages.error(request, 'Please correct the errors below.')
else:
form = IssueForm()
return render(request, 'core/report_issue.html', {'form': form})
def register(request): def register(request):
if request.method == 'POST': if request.method == 'POST':
form = CitizenRegistrationForm(request.POST) form = CitizenRegistrationForm(request.POST)
if form.is_valid(): if form.is_valid():
user = form.save() user = form.save()
login(request, user) messages.success(request, 'Registration successful! Please login.')
return redirect('home') return redirect('login')
else: else:
form = CitizenRegistrationForm() form = CitizenRegistrationForm()
return render(request, 'core/register.html', {'form': form})
return render(request, 'core/register.html', {'form': form})
def custom_login(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
messages.success(request, f'Welcome back, {username}!')
return redirect('home')
else:
messages.error(request, 'Invalid username or password.')
else:
messages.error(request, 'Invalid username or password.')
else:
form = AuthenticationForm()
return render(request, 'core/login.html', {'form': form})