minor update

This commit is contained in:
2025-08-25 14:26:58 +05:30
parent 17f309dcda
commit 7272f1726f
13 changed files with 163 additions and 33 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8
View File
@@ -37,3 +37,11 @@ class CommentForm(forms.ModelForm):
widgets = {
"content": forms.Textarea(attrs={"rows": 2, "placeholder": "Add a comment..."})
}
class IssueAssignForm(forms.ModelForm):
class Meta:
model = Issue
fields = ['department']
widgets = {
'department': forms.Select(attrs={'class': 'form-select form-select-sm'}),
}
@@ -0,0 +1,38 @@
# Generated by Django 5.2.5 on 2025-08-25 08:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_department_users'),
]
operations = [
migrations.AlterModelOptions(
name='issue',
options={'ordering': ['-created_at']},
),
migrations.AlterField(
model_name='issue',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issues', to='core.issuecategory'),
),
migrations.AlterField(
model_name='issue',
name='latitude',
field=models.FloatField(blank=True, null=True),
),
migrations.AlterField(
model_name='issue',
name='location',
field=models.CharField(blank=True, max_length=200),
),
migrations.AlterField(
model_name='issue',
name='longitude',
field=models.FloatField(blank=True, null=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 5.2.5 on 2025-08-25 08:28
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_alter_issue_options_alter_issue_category_and_more'),
]
operations = [
migrations.AddField(
model_name='issue',
name='department',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issues', to='core.department'),
),
]
+45 -29
View File
@@ -26,6 +26,33 @@ class User(AbstractUser):
related_query_name='core_user',
)
class Department(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
# Each department can have many users
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name="departments",
blank=True
)
# One admin per department
admin = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name="admin_of_department",
null=True,
blank=True
)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class IssueCategory(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
@@ -65,6 +92,15 @@ class Issue(models.Model):
related_name="reported_issues"
)
# 🔹 Add relation to department
department = models.ForeignKey(
Department,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="issues"
)
location = models.CharField(max_length=200, blank=True)
latitude = models.FloatField(null=True, blank=True)
longitude = models.FloatField(null=True, blank=True)
@@ -98,7 +134,14 @@ class Issue(models.Model):
def has_user_voted(self, user):
if user.is_authenticated and hasattr(self, "votes"):
return self.votes.filter(user=user).exists()
return False
return
def assign_to_department(self, department):
"""Assign issue to a department and auto-update status to acknowledged"""
self.department = department
self.status = self.STATUS_ACKNOWLEDGED
self.save()
class Vote(models.Model):
@@ -127,31 +170,4 @@ class Comment(models.Model):
@property
def is_reply(self):
return self.parent is not None
class Department(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
# Each department can have many users
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name="departments",
blank=True
)
# One admin per department
admin = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name="admin_of_department",
null=True,
blank=True
)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
return self.parent is not None
@@ -17,6 +17,7 @@
<th>Reported By</th>
<th>Status</th>
<th>Created At</th>
<th>Assign Department</th>
</tr>
</thead>
<tbody>
@@ -40,6 +41,24 @@
{% endif %}
</td>
<td>{{ issue.created_at|date:"M d, Y H:i" }}</td>
<td>
{% if issue.department %}
<span class="fw-bold text-primary">{{ issue.department.name }}</span>
{% else %}
<form method="post" action="">
{% csrf_token %}
<input type="hidden" name="issue_id" value="{{ issue.id }}">
<select name="department" class="form-select form-select-sm">
<option value="">— Select Department —</option>
{% for dept in departments %}
<option value="{{ dept.id }}">{{ dept.name }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-sm btn-primary mt-1">Assign</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
+2 -1
View File
@@ -8,7 +8,8 @@ urlpatterns = [
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("superadmin/assign-department/<int:issue_id>/", views.assign_department, name="assign_department"),
path('register/', views.register, name='register'),
path('login/', views.custom_login, name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
+32 -3
View File
@@ -9,7 +9,7 @@ from django.http import JsonResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from .models import Issue, IssueCategory, User, Vote, Comment, Department
from .forms import CitizenRegistrationForm, IssueForm, CommentForm
from .forms import CitizenRegistrationForm, IssueForm, CommentForm, IssueAssignForm
def home(request):
total_issues = Issue.objects.count()
@@ -271,5 +271,34 @@ def department_detail(request, pk):
@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})
issues = Issue.objects.all().order_by('-created_at')
if request.method == "POST":
issue_id = request.POST.get("issue_id")
dept_id = request.POST.get("department")
issue = get_object_or_404(Issue, id=issue_id)
if dept_id: # Only assign if a department is selected
department = get_object_or_404(Department, id=dept_id)
issue.assign_to_department(department) # 🔹 uses helper
return redirect("manage_issues") # refresh page after save
return render(request, "issues/manage_issues.html", {
"issues": issues,
"departments": Department.objects.all()
})
@user_passes_test(superadmin_check)
def assign_department(request, issue_id):
if request.method == "POST":
issue = get_object_or_404(Issue, id=issue_id)
dept_id = request.POST.get("department_id")
if dept_id:
department = get_object_or_404(Department, id=dept_id)
issue.assign_to_department(department)
messages.success(request, f"Issue '{issue.title}' assigned to {department.name}.")
else:
messages.error(request, "Please select a department.")
return redirect("manage_issues") # redirect back to the issues page