mirror of https://github.com/snachodog/mybuddy.git
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:
parent
7955cd04a7
commit
e2733e6b1e
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
#view-child {
|
||||||
|
.child-photo {
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -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="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-xs-2 mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<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">
|
<a href="{% url 'child' object.slug %}">
|
||||||
<div class="card-body">
|
<img src="{% static 'babybuddy/img/core/child-placeholder.png' %}"
|
||||||
<h4 class="card-title">
|
class="child-photo img-fluid" />
|
||||||
{{ child.name }}
|
|
||||||
<small class="text-muted">{{ child.birth_date }}</small>
|
|
||||||
</h4>
|
|
||||||
<p class="card-text">
|
|
||||||
{% if perms.core.view_child %}
|
|
||||||
<a href="{% url 'dashboard-child' child.slug %}" class="btn btn-success">
|
|
||||||
<i class="icon icon-dashboard" aria-hidden="true"></i>
|
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
<div class="card-body text-center">
|
||||||
|
<h4 class="card-title">{{ object }}</h4>
|
||||||
|
<div class="card-text">
|
||||||
|
<p class="lead">
|
||||||
|
Born <span class="text-secondary">{{ object.birth_date }}</span><br/>
|
||||||
|
Age <span class="text-secondary">{{ object.birth_date|timesince }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
{% include 'dashboard/child_button_group.html' %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
|
||||||
<small class="text-muted">...</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -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/')
|
|
||||||
self.assertEqual(page.status_code, 200)
|
|
||||||
|
|
||||||
"""Dashboard handles the root URL and redirects based on zero, one, or
|
"""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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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/*'
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue