Compare commits
10 Commits
4b245d4884
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ab7373fc34 | |||
| 2767fdf674 | |||
| be2dc882b3 | |||
| 345887d56f | |||
| 0c838571a2 | |||
| 2d0bbe73bd | |||
| 70ae15064c | |||
| 143dd17e6b | |||
| c67538d474 | |||
| 716461af49 |
+2
-2
@@ -1,2 +1,2 @@
|
||||
civicenv
|
||||
civicProject.code-workspace
|
||||
.env
|
||||
issue_photos/
|
||||
@@ -1,10 +0,0 @@
|
||||
SECRET_KEY="django-insecure-wa6p9d+go#+evjql%m(+e5eti$%z7yx2o#cbq8bsh!==icxua3"
|
||||
DEBUG="False"
|
||||
CLOUD_NAME="dkxbfoesf"
|
||||
API_KEY="658671916285379"
|
||||
API_SECRET="_CwNDj4L2dE9yH90Ynj7slPlbo0"
|
||||
CLOUDINARY_URL="cloudinary://658671916285379:_CwNDj4L2dE9yH90Ynj7slPlbo0@dkxbfoesf"
|
||||
DATABASE_URL="postgresql://civicfix_user:YG56PWj9Xj1DvYIKF35TKmIEjrsfis6d@dpg-d2mpapripnbc73f5vaj0-a.oregon-postgres.render.com/civicfix"
|
||||
SUPERUSER_USERNAME="admin"
|
||||
SUPERUSER_PASSWORD="82c96bb18606401630ab9d2836325fbd"
|
||||
SUPERUSER_EMAIL="gokuldevse2001@gmail.com"
|
||||
@@ -1,2 +0,0 @@
|
||||
.env
|
||||
issue_photos/
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,78 +0,0 @@
|
||||
{% extends "core/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-5">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-header bg-warning text-black d-flex justify-content-between align-items-center">
|
||||
<h3>Manage Issues</h3>
|
||||
<a href="{% url 'superadmin_dashboard' %}" class="btn btn-light btn-sm">Back to Dashboard</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if issues %}
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Title</th>
|
||||
<th>Reported By</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for issue in issues %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ issue.title }}</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>
|
||||
<td>
|
||||
{% if issue.department %}
|
||||
<span class="fw-bold text-primary">{{ issue.department.name }}</span>
|
||||
{% else %}
|
||||
<!-- Assign Department Form -->
|
||||
<form method="post" action="" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="issue_id" value="{{ issue.id }}">
|
||||
<select name="department" class="form-select form-select-sm d-inline w-auto">
|
||||
<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">Assign</button>
|
||||
</form>
|
||||
|
||||
<!-- Report Fake Button -->
|
||||
<a href="{% url 'delete_fake_issue' issue.id %}"
|
||||
class="btn btn-sm btn-danger ms-1"
|
||||
onclick="return confirm('Are you sure you want to delete this issue as FAKE?');">
|
||||
<i class="fas fa-ban"></i> Report Fake
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">No issues reported yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Binary file not shown.
@@ -24,17 +24,19 @@ INSTALLED_APPS = [
|
||||
'django.contrib.humanize',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# Third-party
|
||||
'cloudinary',
|
||||
'cloudinary_storage',
|
||||
'django.contrib.staticfiles',
|
||||
'whitenoise.runserver_nostatic',
|
||||
|
||||
# Local apps
|
||||
'core.apps.CoreConfig',
|
||||
]
|
||||
|
||||
CLOUDINARY_STORAGE = {
|
||||
'CLOUD_NAME': os.getenv('CLOUD_NAME'),
|
||||
'API_KEY': os.getenv('API_KEY'),
|
||||
'API_SECRET': os.getenv('API_SECRET'),
|
||||
}
|
||||
|
||||
AUTH_USER_MODEL = 'core.User'
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -93,17 +95,20 @@ USE_TZ = True
|
||||
# Static & Media files
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
|
||||
|
||||
cloudinary.config(
|
||||
cloud_name = os.getenv("CLOUD_NAME"),
|
||||
api_key = os.getenv("API_KEY"),
|
||||
api_secret = os.getenv("API_SECRET")
|
||||
cloud_name=os.getenv("CLOUD_NAME"),
|
||||
api_key=os.getenv("API_KEY"),
|
||||
api_secret=os.getenv("API_SECRET"),
|
||||
secure=True
|
||||
)
|
||||
|
||||
MEDIA_URL = f"https://res.cloudinary.com/{os.getenv('CLOUD_NAME')}/"
|
||||
# Default storage (Cloudinary)
|
||||
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
|
||||
|
||||
|
||||
# Auth redirects
|
||||
LOGIN_REDIRECT_URL = 'citizen_dashboard'
|
||||
LOGIN_URL = 'login'
|
||||
@@ -1,16 +0,0 @@
|
||||
<!-- base.html -->
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Civixfix - {% block title %}{% endblock %}</title>
|
||||
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
from cloudinary.models import CloudinaryField
|
||||
from datetime import timedelta
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.core.validators import FileExtensionValidator
|
||||
@@ -116,11 +117,10 @@ class Issue(models.Model):
|
||||
latitude = models.FloatField(null=True, blank=True)
|
||||
longitude = models.FloatField(null=True, blank=True)
|
||||
|
||||
photo = models.ImageField(
|
||||
upload_to="issue_photos/",
|
||||
photo = CloudinaryField(
|
||||
'images',
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[FileExtensionValidator(['jpg', 'jpeg', 'png', 'gif'])]
|
||||
)
|
||||
|
||||
status = models.CharField(
|
||||
+3
@@ -22,6 +22,7 @@
|
||||
<th>#</th>
|
||||
<th>Title</th>
|
||||
<th>Reported By</th>
|
||||
<th>Location</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
<th>Action</th>
|
||||
@@ -33,6 +34,7 @@
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ issue.title }}</td>
|
||||
<td>{{ issue.reporter.username }}</td>
|
||||
<td>{{ issue.location }}</td>
|
||||
<td>
|
||||
{% if issue.status == "reported" %}
|
||||
<span class="badge bg-danger">Reported</span>
|
||||
@@ -46,6 +48,7 @@
|
||||
<span class="badge bg-secondary">Unknown</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>{{ issue.created_at|date:"M d, Y H:i" }}</td>
|
||||
<td>
|
||||
{% if issue.status in "reported,acknowledged" %}
|
||||
@@ -0,0 +1,81 @@
|
||||
{% extends "core/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-5">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-header bg-warning text-black d-flex justify-content-between align-items-center">
|
||||
<h3>Manage Issues</h3>
|
||||
<a href="{% url 'superadmin_dashboard' %}" class="btn btn-light btn-sm">Back to Dashboard</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if issues %}
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Title</th>
|
||||
<th>Reported By</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for issue in issues %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ issue.title }}</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>
|
||||
<td>
|
||||
{% if issue.department %}
|
||||
<span class="fw-bold text-primary">{{ issue.department.name }}</span>
|
||||
{% else %}
|
||||
<!-- Assign Department Form -->
|
||||
<form method="post" action="" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="issue_id" value="{{ issue.id }}">
|
||||
<select name="department" class="form-select form-select-sm d-inline w-auto">
|
||||
<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">Assign</button>
|
||||
</form>
|
||||
|
||||
<!-- Report Fake Button -->
|
||||
<a href="{% url 'delete_fake_issue' issue.id %}" class="btn btn-sm btn-danger ms-1"
|
||||
onclick="return confirm('Are you sure you want to delete this issue as FAKE?');">
|
||||
<i class="fas fa-ban"></i> Report Fake
|
||||
</a>
|
||||
<a href="{% url 'delete_issue' issue.id %}" class="btn btn-sm btn-outline-danger ms-1"
|
||||
onclick="return confirm('Are you sure you want to permanently delete this issue? This cannot be undone.');">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">No issues reported yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Binary file not shown.
BIN
Binary file not shown.
@@ -26,4 +26,5 @@ urlpatterns = [
|
||||
path('unban-user/<int:user_id>/', views.unban_user, name='unban_user'),
|
||||
path('issues/<int:issue_id>/delete_fake/', views.delete_fake_issue, name='delete_fake_issue'),
|
||||
path("reports/", views.superadmin_reports, name="superadmin_reports"),
|
||||
path('delete-issue/<int:issue_id>/', views.delete_issue, name='delete_issue'),
|
||||
]
|
||||
@@ -76,8 +76,8 @@ def report_issue(request):
|
||||
|
||||
@login_required
|
||||
def view_all_issues(request):
|
||||
if not request.user.is_citizen:
|
||||
messages.error(request, 'Access denied. Citizen role required.')
|
||||
if not request.user.is_active:
|
||||
messages.error(request, 'Access denied.')
|
||||
return redirect('home')
|
||||
|
||||
# Efficiently annotate whether the current user has voted
|
||||
@@ -384,6 +384,14 @@ def superadmin_reports(request):
|
||||
}
|
||||
return render(request, "core/superadmin_reports.html", context)
|
||||
|
||||
@login_required
|
||||
@user_passes_test(superadmin_check)
|
||||
def delete_issue(request, issue_id):
|
||||
issue = get_object_or_404(Issue, id=issue_id)
|
||||
issue.delete()
|
||||
messages.success(request, "Issue deleted successfully.")
|
||||
return redirect('manage_issues')
|
||||
|
||||
def resolver_check(user):
|
||||
return user.is_resolver
|
||||
|
||||
Reference in New Issue
Block a user