major update
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-08-22 00:55
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0002_vote'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Comment',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='core.issue')),
|
||||||
|
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='core.comment')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-08-25 06:21
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0003_comment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Department',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100, unique=True)),
|
||||||
|
('description', models.TextField(blank=True, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='comment',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-08-25 06:43
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0004_department_alter_comment_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='department',
|
||||||
|
name='users',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='departments', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -92,3 +92,21 @@ 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)
|
||||||
|
|
||||||
|
users = models.ManyToManyField(
|
||||||
|
User,
|
||||||
|
related_name="departments",
|
||||||
|
blank=True,
|
||||||
|
limit_choices_to={'is_staff': True}
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["name"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name.name
|
||||||
|
|||||||
@@ -1,178 +1,168 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>
|
|
||||||
Civixfix - {% block title %}Community Issue Reporting Platform{% endblock %}
|
|
||||||
</title>
|
|
||||||
<!-- Bootstrap 5 CSS -->
|
|
||||||
<link
|
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<link rel="preload" as="image" href="https://a.tile.openstreetmap.org/13/4521/2632.png">
|
|
||||||
<link rel="preload" as="image" href="https://b.tile.openstreetmap.org/13/4521/2632.png">
|
|
||||||
|
|
||||||
<link rel="preload" as="font" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/webfonts/fa-solid-900.woff2" type="font/woff2" crossorigin>
|
<head>
|
||||||
<!-- Font Awesome -->
|
<meta charset="UTF-8" />
|
||||||
<link
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
rel="stylesheet"
|
<title>
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
Civixfix - {% block title %}Community Issue Reporting Platform{% endblock %}
|
||||||
/>
|
</title>
|
||||||
<!-- Custom CSS -->
|
<!-- Bootstrap 5 CSS -->
|
||||||
<style>
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||||
.hero-section {
|
<link rel="preload" as="image" href="https://a.tile.openstreetmap.org/13/4521/2632.png">
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),
|
<link rel="preload" as="image" href="https://b.tile.openstreetmap.org/13/4521/2632.png">
|
||||||
url("https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-4.0.3");
|
|
||||||
background-size: cover;
|
<link rel="preload" as="font"
|
||||||
background-position: center;
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/webfonts/fa-solid-900.woff2" type="font/woff2"
|
||||||
color: white;
|
crossorigin>
|
||||||
padding: 100px 0;
|
<!-- Font Awesome -->
|
||||||
margin-bottom: 30px;
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
|
||||||
}
|
<!-- Custom CSS -->
|
||||||
.feature-icon {
|
<style>
|
||||||
font-size: 2.5rem;
|
.hero-section {
|
||||||
margin-bottom: 1rem;
|
background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),
|
||||||
color: #0d6efd;
|
url("https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-4.0.3");
|
||||||
}
|
background-size: cover;
|
||||||
.issue-card {
|
background-position: center;
|
||||||
transition: transform 0.3s;
|
color: white;
|
||||||
}
|
padding: 100px 0;
|
||||||
.issue-card:hover {
|
margin-bottom: 30px;
|
||||||
transform: translateY(-5px);
|
}
|
||||||
}
|
|
||||||
</style>
|
.feature-icon {
|
||||||
{% block extra_css %}{% endblock %}
|
font-size: 2.5rem;
|
||||||
</head>
|
margin-bottom: 1rem;
|
||||||
<body>
|
color: #0d6efd;
|
||||||
<!-- Navigation -->
|
}
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
|
|
||||||
<div class="container">
|
.issue-card {
|
||||||
<a class="navbar-brand" href="{% url 'home' %}">
|
transition: transform 0.3s;
|
||||||
<i class="fas fa-bullhorn me-2"></i>Civixfix
|
}
|
||||||
</a>
|
|
||||||
<button
|
.issue-card:hover {
|
||||||
class="navbar-toggler"
|
transform: translateY(-5px);
|
||||||
type="button"
|
}
|
||||||
data-bs-toggle="collapse"
|
</style>
|
||||||
data-bs-target="#navbarNav"
|
{% block extra_css %}{% endblock %}
|
||||||
>
|
</head>
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
<body>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<!-- Navigation -->
|
||||||
<ul class="navbar-nav me-auto">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
|
||||||
<li class="nav-item">
|
<div class="container">
|
||||||
<a class="nav-link active" href="{% url 'home' %}">Home</a>
|
<a class="navbar-brand" href="{% url 'home' %}">
|
||||||
|
<i class="fas fa-bullhorn me-2"></i>Civixfix
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="{% url 'home' %}">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#features">Features</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#how-it-works">How It Works</a>
|
||||||
|
</li>
|
||||||
|
{% if user.is_authenticated and user.is_citizen %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'citizen_dashboard' %}">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- 🔥 ADD THE DROPDOWN CODE RIGHT HERE 🔥 -->
|
||||||
|
<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>
|
||||||
|
{% if user.is_superuser %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item text-danger fw-bold" href="{% url 'superadmin_dashboard' %}">
|
||||||
|
<i class="fas fa-crown me-1"></i> Super Admin
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main>{% block content %}{% endblock %}</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="text-black py-4 mt-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<h5><i class="fas fa-bullhorn me-2"></i>Civixfix</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Empowering communities through transparent issue reporting and
|
||||||
|
resolution.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<h5>Quick Links</h5>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>
|
||||||
|
<a href="#features" class="text-decoration-none text-muted">Features</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li>
|
||||||
<a class="nav-link" href="#features">Features</a>
|
<a href="#how-it-works" class="text-decoration-none text-muted">How It Works</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li>
|
||||||
<a class="nav-link" href="#how-it-works">How It Works</a>
|
<a href="#" class="text-decoration-none text-muted">Privacy Policy</a>
|
||||||
</li>
|
</li>
|
||||||
{% if user.is_authenticated and user.is_citizen %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'citizen_dashboard' %}"
|
|
||||||
>Dashboard</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
<!-- 🔥 ADD THE DROPDOWN CODE RIGHT HERE 🔥 -->
|
<div class="col-md-4 mb-3">
|
||||||
<div class="d-flex">
|
<h5>Contact</h5>
|
||||||
{% if user.is_authenticated %}
|
<ul class="list-unstyled text-muted">
|
||||||
<div class="dropdown">
|
<li>
|
||||||
<button
|
<i class="fas fa-envelope me-2"></i> gokuldevse2001@gmail.com
|
||||||
class="btn btn-outline-light dropdown-toggle"
|
</li>
|
||||||
type="button"
|
<li><i class="fas fa-phone me-2"></i> +91 8129329073</li>
|
||||||
data-bs-toggle="dropdown"
|
</ul>
|
||||||
>
|
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
<hr class="my-4 bg-secondary" />
|
||||||
|
<div class="text-center text-muted">
|
||||||
<!-- Main Content -->
|
<small>© {% now "Y" %} Civixfix. All rights reserved.</small>
|
||||||
<main>{% block content %}{% endblock %}</main>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="text-black py-4 mt-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<h5><i class="fas fa-bullhorn me-2"></i>Civixfix</h5>
|
|
||||||
<p class="text-muted">
|
|
||||||
Empowering communities through transparent issue reporting and
|
|
||||||
resolution.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<h5>Quick Links</h5>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>
|
|
||||||
<a href="#features" class="text-decoration-none text-muted"
|
|
||||||
>Features</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#how-it-works" class="text-decoration-none text-muted"
|
|
||||||
>How It Works</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" class="text-decoration-none text-muted"
|
|
||||||
>Privacy Policy</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 mb-3">
|
|
||||||
<h5>Contact</h5>
|
|
||||||
<ul class="list-unstyled text-muted">
|
|
||||||
<li>
|
|
||||||
<i class="fas fa-envelope me-2"></i> gokuldevse2001@gmail.com
|
|
||||||
</li>
|
|
||||||
<li><i class="fas fa-phone me-2"></i> +91 8129329073</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr class="my-4 bg-secondary" />
|
|
||||||
<div class="text-center text-muted">
|
|
||||||
<small>© {% now "Y" %} Civixfix. All rights reserved.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Bootstrap 5 JS Bundle with Popper -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- jQuery -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
{% block extra_js %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
|
||||||
<!-- Bootstrap 5 JS Bundle with Popper -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<!-- jQuery -->
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
||||||
{% block extra_js %}{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{% extends "core/base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||||
|
<h3>{{ department.name }} Department</h3>
|
||||||
|
<a href="{% url 'manage_departments' %}" class="btn btn-light btn-sm">⬅ Back</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<h5>Department Users</h5>
|
||||||
|
{% if users %}
|
||||||
|
<ul class="list-group mb-3">
|
||||||
|
{% for user in users %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
{{ user.username }} — {{ user.email }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted">No users registered in this department yet.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h5>Register New User for {{ department.name }}</h5>
|
||||||
|
<form method="post" class="row g-2">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="text" name="username" class="form-control" placeholder="Username" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="email" name="email" class="form-control" placeholder="Email">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="password" name="password" class="form-control" placeholder="Password" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Create User</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<!-- core/templates/core/manage_departments.html -->
|
||||||
|
{% extends "core/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
||||||
|
<h3>Manage Departments</h3>
|
||||||
|
<a href="{% url 'superadmin_dashboard' %}" class="btn btn-light btn-sm">Back to Dashboard</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<!-- List departments -->
|
||||||
|
{% if departments %}
|
||||||
|
<ul class="list-group mb-3">
|
||||||
|
{% for dept in departments %}
|
||||||
|
<a href="{% url 'department_detail' dept.pk %}"
|
||||||
|
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
|
||||||
|
<span>
|
||||||
|
<strong>{{ dept.name }}</strong><br>
|
||||||
|
<small class="text-muted">{{ dept.description }}</small>
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-primary rounded-pill">Manage</span>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted">No departments added yet.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Add new department form -->
|
||||||
|
<form method="post" class="mt-3">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" name="name" class="form-control" placeholder="Department name" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" name="description" class="form-control" placeholder="Description (optional)">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<button type="submit" class="btn btn-success w-100">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!-- core/templates/core/superadmin_dashboard.html -->
|
||||||
|
{% extends "core/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container my-5">
|
||||||
|
<div class="card shadow-lg">
|
||||||
|
<div class="card-header bg-dark text-white">
|
||||||
|
<h3>Super Admin Dashboard</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Welcome, {{ request.user.username }} 👑</p>
|
||||||
|
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-users me-2 text-primary"></i>
|
||||||
|
<a href="#">Manage Users</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<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="#">Manage Roles & Permissions</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="#">View Analytics & Reports</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="#">System Settings</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -4,6 +4,9 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.home, name='home'),
|
path('', views.home, name='home'),
|
||||||
|
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('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'),
|
||||||
|
|||||||
+60
-13
@@ -1,12 +1,13 @@
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
|
from django.contrib.auth.hashers import make_password
|
||||||
from django.db.models import Exists, OuterRef
|
from django.db.models import Exists, OuterRef
|
||||||
from django.http import JsonResponse
|
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
|
from .models import Issue, IssueCategory, User, Vote, Comment, Department
|
||||||
from .forms import CitizenRegistrationForm, IssueForm, CommentForm
|
from .forms import CitizenRegistrationForm, IssueForm, CommentForm
|
||||||
|
|
||||||
def home(request):
|
def home(request):
|
||||||
@@ -165,11 +166,9 @@ def vote_issue(request, issue_id):
|
|||||||
return JsonResponse({'success': False, 'error': str(e)})
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_comment(request, issue_id, parent_id=None):
|
def add_comment(request, pk, parent_id=None):
|
||||||
issue = get_object_or_404(Issue, id=issue_id)
|
issue = get_object_or_404(Issue, pk=pk)
|
||||||
parent = None
|
parent = get_object_or_404(Comment, pk=parent_id) if parent_id else None
|
||||||
if parent_id:
|
|
||||||
parent = get_object_or_404(Comment, id=parent_id)
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = CommentForm(request.POST)
|
form = CommentForm(request.POST)
|
||||||
@@ -179,11 +178,7 @@ def add_comment(request, issue_id, parent_id=None):
|
|||||||
comment.user = request.user
|
comment.user = request.user
|
||||||
comment.parent = parent
|
comment.parent = parent
|
||||||
comment.save()
|
comment.save()
|
||||||
return redirect("issue_detail", issue_id=issue.id)
|
return redirect("issue_detail", pk=pk)
|
||||||
else:
|
|
||||||
form = CommentForm()
|
|
||||||
|
|
||||||
return render(request, "comments/add_comment.html", {"form": form, "issue": issue})
|
|
||||||
|
|
||||||
def issue_detail(request, pk):
|
def issue_detail(request, pk):
|
||||||
issue = get_object_or_404(Issue, pk=pk)
|
issue = get_object_or_404(Issue, pk=pk)
|
||||||
@@ -198,4 +193,56 @@ def add_comment(request, pk, parent_id=None):
|
|||||||
if content:
|
if content:
|
||||||
parent = Comment.objects.get(pk=parent_id) if parent_id else None
|
parent = Comment.objects.get(pk=parent_id) if parent_id else None
|
||||||
Comment.objects.create(issue=issue, user=request.user, content=content, parent=parent)
|
Comment.objects.create(issue=issue, user=request.user, content=content, parent=parent)
|
||||||
return redirect("issue_detail", pk=pk)
|
return redirect("issue_detail", pk=pk)
|
||||||
|
|
||||||
|
def superadmin_check(user):
|
||||||
|
return user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def superadmin_dashboard(request):
|
||||||
|
return render(request, "core/superadmin_dashboard.html")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def manage_departments(request):
|
||||||
|
departments = Department.objects.all().order_by("name")
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
name = request.POST.get("name")
|
||||||
|
description = request.POST.get("description")
|
||||||
|
if name:
|
||||||
|
Department.objects.create(name=name, description=description)
|
||||||
|
return redirect("manage_departments")
|
||||||
|
|
||||||
|
return render(request, "core/manage_departments.html", {
|
||||||
|
"departments": departments
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_passes_test(superadmin_check)
|
||||||
|
def department_detail(request, pk):
|
||||||
|
department = get_object_or_404(Department, pk=pk)
|
||||||
|
users = department.users.all()
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
username = request.POST.get("username")
|
||||||
|
email = request.POST.get("email")
|
||||||
|
password = request.POST.get("password")
|
||||||
|
|
||||||
|
if username and password:
|
||||||
|
user = User.objects.create(
|
||||||
|
username=username,
|
||||||
|
email=email,
|
||||||
|
password=make_password(password), # hash the password
|
||||||
|
is_staff=True # mark as staff
|
||||||
|
)
|
||||||
|
department.users.add(user)
|
||||||
|
return redirect("department_detail", pk=department.id)
|
||||||
|
|
||||||
|
return render(request, "core/department_detail.html", {
|
||||||
|
"department": department,
|
||||||
|
"users": users,
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user