Improve Dashboard view.

This commit adds a placeholder image for Child objects (in the future, hopefully, photo uploads will be added) and reworks the Dashboard view to look better when more than one child is in the database. There are also some minor tweaks to the Child detail view and one new Gulp requirement to improve the flow of copying static image assets from apps.
This commit is contained in:
Christopher Charbonneau Wells 2017-11-06 16:24:21 -05:00
parent 7955cd04a7
commit e2733e6b1e
12 changed files with 117 additions and 78 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -0,0 +1,5 @@
#view-child {
.child-photo {
max-width: 150px;
}
}

View File

@ -1,4 +1,5 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load static %}
{% block title %}{{ object }}{% endblock %} {% block title %}{{ object }}{% endblock %}
@ -9,48 +10,12 @@
{% block content %} {% block content %}
<div class="jumbotron text-center"> <div class="jumbotron text-center">
<div class="display-3">{{ object }}</div> <img src="{% static 'babybuddy/img/core/child-placeholder.png' %}" class="child-photo img-fluid rounded-circle" />
<div class="child-name display-4">{{ object }}</div>
<p class="lead"> <p class="lead">
Born <span class="text-secondary">{{ object.birth_date }}</span><br/> Born <span class="text-secondary">{{ object.birth_date }}</span><br/>
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span> Age <span class="text-secondary">{{ object.birth_date|timesince }}</span>
</p> </p>
{% include 'dashboard/child_button_group.html' %}
<div class="btn-group btn-group-lg center-block" role="group" aria-label="Timer actions">
{% if perms.core.view_child %}
<a href="{% url 'dashboard-child' object.slug %}" class="btn btn-success">
<i class="icon icon-dashboard" aria-hidden="true"></i>
</a>
{% endif %}
<div class="btn-group" role="group">
<button id="reports-dropdown"
class="btn btn-primary dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon icon-graph" aria-hidden="true"></i></button>
<div class="dropdown-menu" aria-labelledby="reports-dropdown">
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-types-child' object.slug %}">Diaper Change Types</a>
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">Diaper Lifetimes</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">Sleep Pattern</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">Sleep Totals</a>
<a class="dropdown-item" href="{% url 'reports:report-timeline-child' object.slug %}">Timeline</a>
</div>
</div>
{% if perms.core.change_child %}
<a class="btn btn-warning"
href="{% url 'child-update' object.slug %}"
role="button"><i class="icon icon-update" aria-hidden="true"></i></a>
{% endif %}
{% if perms.core.delete_child %}
<a class="btn btn-danger"
href="{% url 'child-delete' object.slug %}"
role="button"><i class="icon icon-delete" aria-hidden="true"></i></a>
{% endif %}
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

View File

@ -0,0 +1,37 @@
<div class="child-actions btn-group btn-group-lg center-block" role="group" aria-label="Child actions">
{% if perms.core.view_child %}
<a href="{% url 'dashboard-child' object.slug %}" class="btn btn-success">
<i class="icon icon-dashboard" aria-hidden="true"></i>
</a>
{% endif %}
<div class="btn-group" role="group">
<button id="reports-dropdown"
class="btn btn-primary dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon icon-graph" aria-hidden="true"></i></button>
<div class="dropdown-menu" aria-labelledby="reports-dropdown">
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-types-child' object.slug %}">Diaper Change Types</a>
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">Diaper Lifetimes</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">Sleep Pattern</a>
<a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">Sleep Totals</a>
<a class="dropdown-item" href="{% url 'reports:report-timeline-child' object.slug %}">Timeline</a>
</div>
</div>
{% if perms.core.change_child %}
<a class="btn btn-warning"
href="{% url 'child-update' object.slug %}"
role="button"><i class="icon icon-update" aria-hidden="true"></i></a>
{% endif %}
{% if perms.core.delete_child %}
<a class="btn btn-danger"
href="{% url 'child-delete' object.slug %}"
role="button"><i class="icon icon-delete" aria-hidden="true"></i></a>
{% endif %}
</div>

View File

@ -1,27 +1,31 @@
{% extends 'babybuddy/page.html' %} {% extends 'babybuddy/page.html' %}
{% load static %}
{% block title %}Welcome!{% endblock %} {% block title %}Dashboard{% endblock %}
{% block breadcrumbs %}
<li class="breadcrumb-item font-weight-bold">Dashboard</li>
{% endblock %}
{% block content %} {% block content %}
<div class="card-group"> <div class="row">
{% for child in objects %} {% for object in objects %}
<div class="card"> <div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-xs-2 mb-4">
<img class="card-img-top" style="height: 180px; width: 100%; display: block;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22259%22%20height%3D%22180%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20259%20180%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15e152b1929%20text%20%7B%20fill%3Argba(255%2C255%2C255%2C.75)%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A13pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15e152b1929%22%3E%3Crect%20width%3D%22259%22%20height%3D%22180%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2296.26666641235352%22%20y%3D%2296%22%3E259x180%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true"> <div class="card">
<div class="card-body"> <a href="{% url 'child' object.slug %}">
<h4 class="card-title"> <img src="{% static 'babybuddy/img/core/child-placeholder.png' %}"
{{ child.name }} class="child-photo img-fluid" />
<small class="text-muted">{{ child.birth_date }}</small> </a>
</h4> <div class="card-body text-center">
<p class="card-text"> <h4 class="card-title">{{ object }}</h4>
{% if perms.core.view_child %} <div class="card-text">
<a href="{% url 'dashboard-child' child.slug %}" class="btn btn-success"> <p class="lead">
<i class="icon icon-dashboard" aria-hidden="true"></i> Born <span class="text-secondary">{{ object.birth_date }}</span><br/>
</a> Age <span class="text-secondary">{{ object.birth_date|timesince }}</span>
{% endif %} </p>
</p> {% include 'dashboard/child_button_group.html' %}
</div> </div>
<div class="card-footer"> </div>
<small class="text-muted">...</small>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@ -31,17 +31,20 @@ class ViewsTestCase(TestCase):
cls.c.login(**cls.credentials) cls.c.login(**cls.credentials)
def test_dashboard_views(self): def test_dashboard_views(self):
page = self.c.get('/dashboard/') """Dashboard handles the root URL and redirects based on zero, one, or
self.assertEqual(page.status_code, 200)
""" Dashboard handles the root URL and redirects based on zero, one, or
more than one child is in the database.""" more than one child is in the database."""
page = self.c.get('/') page = self.c.get('/')
self.assertEqual(page.url, '/dashboard/')
page = self.c.get('/dashboard/')
self.assertEqual(page.url, '/children/add/') self.assertEqual(page.url, '/children/add/')
call_command('fake', verbosity=0, children=1) call_command('fake', verbosity=0, children=1, days=1)
child = Child.objects.first() child = Child.objects.first()
page = self.c.get('/') page = self.c.get('/dashboard/')
self.assertEqual(
page.url, '/children/{}/dashboard/'.format(child.slug))
page = self.c.get('/dashboard/')
self.assertEqual( self.assertEqual(
page.url, '/children/{}/dashboard/'.format(child.slug)) page.url, '/children/{}/dashboard/'.format(child.slug))
# Test the actual child dashboard (including cards). # Test the actual child dashboard (including cards).
@ -54,5 +57,5 @@ class ViewsTestCase(TestCase):
last_name='Child', last_name='Child',
birth_date='2000-01-01' birth_date='2000-01-01'
) )
page = self.c.get('/') page = self.c.get('/dashboard/')
self.assertEqual(page.url, '/dashboard/') self.assertEqual(page.status_code, 200)

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
from django.contrib.auth.mixins import (LoginRequiredMixin, from django.contrib.auth.mixins import (LoginRequiredMixin,
PermissionRequiredMixin) PermissionRequiredMixin)
from django.http import HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from django.views.generic.base import TemplateView, RedirectView from django.views.generic.base import TemplateView, RedirectView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
@ -11,22 +12,23 @@ from core.models import Child
class DashboardRedirect(LoginRequiredMixin, RedirectView): class DashboardRedirect(LoginRequiredMixin, RedirectView):
url = '/dashboard/'
class Dashboard(LoginRequiredMixin, TemplateView):
# TODO: Use .card-deck in this template once BS4 is finalized.
template_name = 'dashboard/dashboard.html'
# Show the overall dashboard or a child dashboard if one Child instance. # Show the overall dashboard or a child dashboard if one Child instance.
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
children = Child.objects.count() children = Child.objects.count()
if children == 0: if children == 0:
# TODO: Create some sort of welcome page. # TODO: Create some sort of welcome page.
self.url = reverse('child-add') return HttpResponseRedirect(reverse('child-add'))
elif children == 1: elif children == 1:
child_instance = Child.objects.first() return HttpResponseRedirect(
self.url = reverse('dashboard-child', args={child_instance.slug}) reverse('dashboard-child', args={Child.objects.first().slug}))
else: return super(Dashboard, self).get(request, *args, **kwargs)
self.url = reverse('dashboard')
return super(DashboardRedirect, self).get(request, *args, **kwargs)
class Dashboard(LoginRequiredMixin, TemplateView):
template_name = 'dashboard/dashboard.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(Dashboard, self).get_context_data(**kwargs) context = super(Dashboard, self).get_context_data(**kwargs)

View File

@ -15,6 +15,10 @@ module.exports = {
dest: basePath + 'fonts/', dest: basePath + 'fonts/',
files: 'node_modules/font-awesome/fonts/*' files: 'node_modules/font-awesome/fonts/*'
}, },
images: {
dest: basePath + 'img/',
files: '**/static_src/img/**/*'
},
root: { root: {
dest: basePath + 'root/', dest: basePath + 'root/',
files: 'babybuddy/static_src/root/*' files: 'babybuddy/static_src/root/*'

View File

@ -1,11 +1,12 @@
var gulp = require('gulp'); var gulp = require('gulp');
var flatten = require('gulp-flatten');
var pump = require('pump'); var pump = require('pump');
var extrasConfig = require('../config.js').extrasConfig; var extrasConfig = require('../config.js').extrasConfig;
gulp.task('extras', ['extras:fonts', 'extras:root']); gulp.task('extras', ['extras:fonts', 'extras:images', 'extras:root']);
gulp.task('extras:fonts', function(cb) { gulp.task('extras:fonts', function(cb) {
pump([ pump([
@ -14,6 +15,14 @@ gulp.task('extras:fonts', function(cb) {
], cb); ], cb);
}); });
gulp.task('extras:images', function(cb) {
pump([
gulp.src(extrasConfig.images.files),
flatten({ subPath: 3 }),
gulp.dest(extrasConfig.images.dest)
], cb);
});
gulp.task('extras:root', function(cb) { gulp.task('extras:root', function(cb) {
pump([ pump([
gulp.src(extrasConfig.root.files), gulp.src(extrasConfig.root.files),

9
package-lock.json generated
View File

@ -4000,6 +4000,15 @@
"vinyl-sourcemaps-apply": "0.2.1" "vinyl-sourcemaps-apply": "0.2.1"
} }
}, },
"gulp-flatten": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.3.1.tgz",
"integrity": "sha1-Uef+wTozxARXjRjBWJ0bW8Rf4dY=",
"requires": {
"gulp-util": "3.0.8",
"through2": "2.0.3"
}
},
"gulp-sass": { "gulp-sass": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-3.1.0.tgz", "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-3.1.0.tgz",

View File

@ -24,6 +24,7 @@
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-csso": "^3.0.0", "gulp-csso": "^3.0.0",
"gulp-flatten": "^0.3.1",
"gulp-sass": "^3.1.0", "gulp-sass": "^3.1.0",
"gulp-sass-glob": "^1.0.8", "gulp-sass-glob": "^1.0.8",
"gulp-sass-lint": "^1.3.4", "gulp-sass-lint": "^1.3.4",