Skip to content
This repository was archived by the owner on Aug 29, 2023. It is now read-only.

Commit eb1e85d

Browse files
committed
feat: add basic users list
1 parent 7905828 commit eb1e85d

6 files changed

Lines changed: 257 additions & 3 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{% extends 'backend/_layout.html' %}
2+
{% block content %}
3+
<div class="row">
4+
<div class="col">
5+
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-1 pb-1 mb-1 border-bottom">
6+
<h3>{{ page_title }}</h3>
7+
</div>
8+
</div>
9+
</div>
10+
<div class="row">
11+
<div class="col-sm-10 offset-sm-1 col-md-8 offset-md-2 py-3">
12+
<form method="post">
13+
{% csrf_token %}
14+
<div class="card">
15+
<div class="card-body">
16+
<div class="row mb-3">
17+
<label for="{{ form.username.id_for_label }}" class="col-sm-4 col-form-label">Username</label>
18+
<div class="col-sm-8">
19+
<input type="text" class="form-control" name="{{ form.username.name }}" id="{{ form.username.id_for_label }}" value="{{ form.username.value }}">
20+
</div>
21+
</div>
22+
<div class="row mb-3">
23+
<label for="{{ form.email.id_for_label }}" class="col-sm-4 col-form-label">Email</label>
24+
<div class="col-sm-8">
25+
<input type="email" class="form-control" name="{{ form.email.name }}" id="{{ form.email.id_for_label }}" value="{{ form.email.value }}">
26+
</div>
27+
</div>
28+
<div class="row mb-3">
29+
<label for="{{ form.first_name.id_for_label }}" class="col-sm-4 col-form-label">First name</label>
30+
<div class="col-sm-8">
31+
<input type="text" class="form-control" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}" value="{{ form.first_name.value }}">
32+
</div>
33+
</div>
34+
<div class="row mb-3">
35+
<label for="{{ form.last_name.id_for_label }}" class="col-sm-4 col-form-label">Surname</label>
36+
<div class="col-sm-8">
37+
<input type="number" class="form-control" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}" value="{{ form.last_name.value }}">
38+
</div>
39+
</div>
40+
</div>
41+
<div class="card-footer">
42+
<button type="submit" class="btn btn-primary">Save Changes</button>
43+
<a href="{% url 'users:users_list' %}" class="btn btn-secondary">Cancel</a>
44+
</div>
45+
</div>
46+
</form>
47+
</div>
48+
</div>
49+
{% endblock content %}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{% extends 'backend/_layout.html' %}
2+
{% load staticfiles %}
3+
{% block content %}
4+
<div class="row">
5+
<div class="col">
6+
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-1 pb-1 mb-1 border-bottom">
7+
<h3>{{ page_title }}</h3>
8+
</div>
9+
</div>
10+
</div>
11+
<div class="row">
12+
<div class="col">
13+
<div class="card border-light mb-3">
14+
<div class="card-body">
15+
<div class="row g-3">
16+
<div class="col">
17+
<a href="#" class="btn btn-success mt-3">
18+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" width="20" height="20">
19+
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
20+
</svg>
21+
Add new user
22+
</a>
23+
</div>
24+
</div>
25+
</div>
26+
</div>
27+
</div>
28+
</div>
29+
<div class="row">
30+
<div class="col">
31+
<div class="card">
32+
<div class="card-body">
33+
<div class="table-responsive">
34+
<table class="table table-hover table-sm table-striped">
35+
<thead>
36+
<tr>
37+
<th></th>
38+
<th>Full name</th>
39+
<th>Username</th>
40+
<th>Email</th>
41+
<th>Active?</th>
42+
</tr>
43+
</thead>
44+
<tbody>
45+
{% for user in users %}
46+
<tr>
47+
<td>
48+
<a href="">
49+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" width="20" height="20">
50+
<title>Edit this user</title>
51+
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
52+
</svg>
53+
</a>
54+
</td>
55+
<td>{{ user.get_full_name }}</td>
56+
<td>{{ user.username }}</td>
57+
<td>{{ user.email|default:'N/A' }}</td>
58+
<td>
59+
{% if user.is_active %}
60+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" width="20" height="20">
61+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
62+
</svg>
63+
{% else %}
64+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" width="20" height="20">
65+
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
66+
</svg>
67+
{% endif %}
68+
</td>
69+
</tr>
70+
{% empty %}
71+
<tr>
72+
<td class="table-warning" colspan="4">No records found</td>
73+
</tr>
74+
{% endfor %}
75+
</tbody>
76+
</table>
77+
</div>
78+
</div>
79+
</div>
80+
</div>
81+
</div>
82+
{% include 'backend/pagination.html' %}
83+
{% endblock content %}
84+
{% block scripts %}
85+
<script src="{% static 'vendor/flatpickr/flatpickr.min.js' %}"></script>
86+
<script>
87+
let loader = function () {
88+
flatpickr('.date-picker', {
89+
dateFormat: 'm/d/Y'
90+
});
91+
};
92+
93+
document.addEventListener('DOMContentLoaded', loader);
94+
</script>
95+
{% endblock scripts %}

profiles/forms.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,58 @@
11
# -*- coding: utf-8 -*-
22
from django import forms
3+
from django.contrib.auth.models import User
4+
from django.core.exceptions import ObjectDoesNotExist
35

46
from locations.models import Location
57
from profiles.models import Profile
68

79

810
class ProfileAdminForm(forms.ModelForm):
911
locations = forms.ModelMultipleChoiceField(
10-
queryset=Location.objects.filter(type__name=u'State'))
12+
queryset=Location.objects.filter(type__name=u'State'))
1113

1214
class Meta:
1315
model = Profile
1416
fields = (u'user', u'locations')
17+
18+
19+
class UserForm(forms.ModelForm):
20+
locations = forms.ModelMultipleChoiceField(
21+
queryset=Location.objects.filter(type__name=u'State'))
22+
can_add_locations = forms.BooleanField(required=False)
23+
can_change_br_reports = forms.BooleanField(required=False)
24+
can_change_dr_reports = forms.BooleanField(required=False)
25+
can_change_mnchw_reports = forms.BooleanField(required=False)
26+
can_change_reporters = forms.BooleanField(required=False)
27+
28+
class Meta:
29+
model = User
30+
fields = (
31+
'username', 'email', 'first_name', 'last_name', 'is_active',
32+
'is_superuser'
33+
)
34+
35+
def __init__(self, *args, **kwargs):
36+
user = kwargs.get('instance')
37+
if user is not None:
38+
initial_data = kwargs.get('initial', {}).copy()
39+
40+
# set up locations for subscription
41+
try:
42+
profile = user.profile
43+
except ObjectDoesNotExist:
44+
profile = None
45+
46+
if profile is not None:
47+
initial_data['locations'] = profile.locations
48+
49+
# set up permissions
50+
initial_data['can_add_locations'] = user.has_perm('locations.add_location')
51+
initial_data['can_change_br_reports'] = user.has_perm('br.change_birthregistration')
52+
initial_data['can_change_dr_reports'] = user.has_perm('dr.change_deathreport')
53+
initial_data['can_change_mnchw_reports'] = user.has_perm('ipd.change_report')
54+
initial_data['can_change_reporters'] = user.has_perm('reporters.change_reporter')
55+
56+
kwargs['initial'] = initial_data
57+
58+
super(UserForm, self).__init__(*args, **kwargs)

profiles/urls.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
from django.conf.urls import url
3+
4+
from profiles.views import UserListView
5+
6+
urlpatterns = [
7+
url(r'^$', UserListView.as_view(), name='users_list'),
8+
# url(r'^new/?$', UserCreateView.as_view(), name='user_create'),
9+
# url(r'^(?P<pk>\d+)/?$', UserUpdateView.as_view(), name='user_edit'),
10+
]

profiles/views.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,58 @@
1-
from django.shortcuts import render
1+
# -*- coding: utf-8 -*-
2+
from braces.views import LoginRequiredMixin, PermissionRequiredMixin
3+
from django.conf import settings
4+
from django.contrib.auth.models import User
5+
from django.views.generic import CreateView, ListView, UpdateView
26

3-
# Create your views here.
7+
from profiles.forms import UserForm
8+
from profiles.models import Profile
9+
10+
PROTECTED_VIEW_PERMISSION = 'auth.change_user'
11+
12+
13+
class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
14+
context_object_name = 'users'
15+
model = User
16+
ordering = ('-pk',)
17+
page_title = 'Users'
18+
paginate_by = settings.PAGE_SIZE
19+
permission_required = PROTECTED_VIEW_PERMISSION
20+
template_name = 'backend/user_list.html'
21+
22+
def get_context_data(self, **kwargs):
23+
context = super(UserListView, self).get_context_data(**kwargs)
24+
context['page_title'] = self.page_title
25+
return context
26+
27+
28+
class UserCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
29+
form_class = UserForm
30+
model = User
31+
page_title = 'Create User'
32+
permission_required = PROTECTED_VIEW_PERMISSION
33+
template_name = 'backend/user_create.html'
34+
35+
def form_valid(self, form):
36+
pass
37+
38+
def get_context_data(self, **kwargs):
39+
context = super(UserCreateView, self).get_context_data(**kwargs)
40+
context['page_title'] = self.page_title
41+
return context
42+
43+
44+
class UserUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
45+
form_class = UserForm
46+
model = User
47+
page_title = 'Edit User'
48+
permission_required = PROTECTED_VIEW_PERMISSION
49+
template_name = 'backend/user_edit.html'
50+
51+
def form_valid(self, form):
52+
pass
53+
54+
def get_context_data(self, **kwargs):
55+
context = super(UserUpdateView, self).get_context_data(**kwargs)
56+
context['page_title'] = self.page_title
57+
return context
58+

unicefng/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
url(r'^mnchw/', include('campaigns.urls', namespace=u'mnchw')),
2727
url(r'incoming/', HttpBackendView.as_view(backend_name='polling')),
2828
url(r'^messages/', include('messagebox.urls', namespace='messaging')),
29+
url(r'^users/', include('profiles.urls', namespace='users')),
2930
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
3031

3132
# authentication urls

0 commit comments

Comments
 (0)