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 = { widgets = {
"content": forms.Textarea(attrs={"rows": 2, "placeholder": "Add a comment..."}) "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', 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): class IssueCategory(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
description = models.TextField(blank=True) description = models.TextField(blank=True)
@@ -65,6 +92,15 @@ class Issue(models.Model):
related_name="reported_issues" 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) location = models.CharField(max_length=200, blank=True)
latitude = models.FloatField(null=True, blank=True) latitude = models.FloatField(null=True, blank=True)
longitude = 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): def has_user_voted(self, user):
if user.is_authenticated and hasattr(self, "votes"): if user.is_authenticated and hasattr(self, "votes"):
return self.votes.filter(user=user).exists() 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): class Vote(models.Model):
@@ -127,31 +170,4 @@ class Comment(models.Model):
@property @property
def is_reply(self): def is_reply(self):
return self.parent is not None 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
@@ -17,6 +17,7 @@
<th>Reported By</th> <th>Reported By</th>
<th>Status</th> <th>Status</th>
<th>Created At</th> <th>Created At</th>
<th>Assign Department</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -40,6 +41,24 @@
{% endif %} {% endif %}
</td> </td>
<td>{{ issue.created_at|date:"M d, Y H:i" }}</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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
+2 -1
View File
@@ -8,7 +8,8 @@ urlpatterns = [
path("superadmin/departments/", views.manage_departments, name="manage_departments"), path("superadmin/departments/", views.manage_departments, name="manage_departments"),
path("superadmin/departments/<int:pk>/", views.department_detail, name="department_detail"), path("superadmin/departments/<int:pk>/", views.department_detail, name="department_detail"),
path("superadmin/manage/", views.manage_issues, name="manage_issues"), 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('register/', views.register, name='register'),
path('login/', views.custom_login, 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'),
+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.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from .models import Issue, IssueCategory, User, Vote, Comment, Department 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): def home(request):
total_issues = Issue.objects.count() total_issues = Issue.objects.count()
@@ -271,5 +271,34 @@ def department_detail(request, pk):
@login_required @login_required
@user_passes_test(superadmin_check) @user_passes_test(superadmin_check)
def manage_issues(request): def manage_issues(request):
issues = Issue.objects.all().order_by("-created_at") issues = Issue.objects.all().order_by('-created_at')
return render(request, "issues/manage_issues.html", {"issues": issues})
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