minor update
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
+54
-19
@@ -34,37 +34,72 @@ class IssueCategory(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
# issues/models.py
|
||||
class Issue(models.Model):
|
||||
STATUS_REPORTED = 'reported'
|
||||
STATUS_ACKNOWLEDGED = 'acknowledged'
|
||||
STATUS_IN_PROGRESS = 'in_progress'
|
||||
STATUS_RESOLVED = 'resolved'
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('reported', 'Reported'),
|
||||
('acknowledged', 'Acknowledged'),
|
||||
('in_progress', 'In Progress'),
|
||||
('resolved', 'Resolved'),
|
||||
(STATUS_REPORTED, 'Reported'),
|
||||
(STATUS_ACKNOWLEDGED, 'Acknowledged'),
|
||||
(STATUS_IN_PROGRESS, 'In Progress'),
|
||||
(STATUS_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')
|
||||
|
||||
category = models.ForeignKey(
|
||||
"IssueCategory",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="issues"
|
||||
)
|
||||
|
||||
reporter = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="reported_issues"
|
||||
)
|
||||
|
||||
location = models.CharField(max_length=200, blank=True)
|
||||
latitude = models.FloatField(null=True, blank=True)
|
||||
longitude = models.FloatField(null=True, blank=True)
|
||||
|
||||
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=STATUS_REPORTED
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"] # 🔹 latest issues first by default
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
return f"{self.title} ({self.get_status_display()})"
|
||||
|
||||
# 🔹 Helpers
|
||||
def vote_count(self):
|
||||
return self.votes.count()
|
||||
|
||||
return self.votes.count() if hasattr(self, "votes") else 0
|
||||
|
||||
def has_user_voted(self, user):
|
||||
if user.is_authenticated:
|
||||
if user.is_authenticated and hasattr(self, "votes"):
|
||||
return self.votes.filter(user=user).exists()
|
||||
return False
|
||||
|
||||
|
||||
class Vote(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<i class="fas fa-building me-2 text-success"></i>
|
||||
<a href="{% url 'manage_departments' %}">Manage Departments</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'manage_issues' %}">Manage Issues</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<a href="#">Manage Roles & Permissions</a>
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "core/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-5">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h3>Manage Issues</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if issues %}
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Title</th>
|
||||
<th>Category</th>
|
||||
<th>Reported By</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for issue in issues %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ issue.title }}</td>
|
||||
<td>{{ issue.category.name|default:"—" }}</td>
|
||||
<td>{{ issue.reporter.username }}</td>
|
||||
<td>
|
||||
{% if issue.status == "reported" %}
|
||||
<span class="badge bg-danger">Reported</span>
|
||||
{% elif issue.status == "acknowledged" %}
|
||||
<span class="badge bg-info text-dark">Acknowledged</span>
|
||||
{% elif issue.status == "in_progress" %}
|
||||
<span class="badge bg-warning text-dark">In Progress</span>
|
||||
{% elif issue.status == "resolved" %}
|
||||
<span class="badge bg-success">Resolved</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Unknown</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ issue.created_at|date:"M d, Y H:i" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">No issues reported yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -7,6 +7,8 @@ urlpatterns = [
|
||||
path("superadmin/", views.superadmin_dashboard, name="superadmin_dashboard"),
|
||||
path("superadmin/departments/", views.manage_departments, name="manage_departments"),
|
||||
path("superadmin/departments/<int:pk>/", views.department_detail, name="department_detail"),
|
||||
path("superadmin/manage/", views.manage_issues, name="manage_issues"),
|
||||
|
||||
path('register/', views.register, name='register'),
|
||||
path('login/', views.custom_login, name='login'),
|
||||
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
|
||||
|
||||
@@ -266,4 +266,10 @@ def department_detail(request, pk):
|
||||
return render(request, "department/department_detail.html", {
|
||||
"department": department,
|
||||
"users": users,
|
||||
})
|
||||
})
|
||||
|
||||
@login_required
|
||||
@user_passes_test(superadmin_check)
|
||||
def manage_issues(request):
|
||||
issues = Issue.objects.all().order_by("-created_at")
|
||||
return render(request, "issues/manage_issues.html", {"issues": issues})
|
||||
Reference in New Issue
Block a user