Upgrade to Bootstrap 5 (#479)

* Update Tempus Dominus to v6 (WIP)

* Re-add calendar input prepend container

* Normalize setting of default date values

* Migrate to Bootstrap 5 (WIP)

* Remove deprecated card columns implementation from dashboard

* Update BS5 data props

* Use Masonry layout for dashboard cards (for now)

This doesn't seem particularly ideal so may need a more involved refactor

* Update styles for Bootstrap 5 deprecations

* Remove resolutions crap

* Refactor deprecated BS4 classes

* Update list table styles

* Refactor deprecated jumbotron class usages

* Update close button structure

* Add `v2` branch to standard CI workflows
This commit is contained in:
Christopher C. Wells 2023-03-25 12:57:12 -07:00
parent 77d4ba1920
commit 34fe811a54
105 changed files with 19654 additions and 18521 deletions

View File

@ -3,9 +3,11 @@ on:
push:
branches:
- master
- v2
pull_request:
branches:
- master
- v2
jobs:
test:
runs-on: ubuntu-latest

View File

@ -1,9 +1,13 @@
name: "CodeQL"
on:
push:
branches: [ master ]
branches:
- master
- v2
pull_request:
branches: [ master ]
branches:
- master
- v2
schedule:
- cron: '33 0 * * 3'
jobs:

View File

@ -23,34 +23,48 @@ var BabyBuddy = function () {
*
* @type {{init: BabyBuddy.DatetimePicker.init}}
*/
BabyBuddy.DatetimePicker = function ($, moment) {
BabyBuddy.DatetimePicker = function (moment) {
return {
init: function (element, options) {
var defaultOptions = {
buttons: { showToday: true, showClose: true },
defaultDate: 'now',
focusOnShow: false,
format: 'L LT',
ignoreReadonly: true,
locale: moment.locale(),
useCurrent: false,
icons: {
time: 'icon-clock',
date: 'icon-calendar',
up: 'icon-arrow-up',
down: 'icon-arrow-down',
previous: 'icon-angle-circled-left',
next: 'icon-angle-circled-right',
today: 'icon-today',
clear: 'icon-delete',
close: 'icon-cancel'
let defaultOptions = {
display: {
buttons: {
close: true,
today: true,
},
components: {
calendar: true,
clock: true,
date: true,
decades: true,
hours: true,
minutes: true,
month: true,
seconds: false,
useTwentyfourHour: false,
year: true,
},
icons: {
clear: 'icon-delete',
close: 'icon-cancel',
date: 'icon-calendar',
down: 'icon-arrow-down',
next: 'icon-angle-circled-right',
previous: 'icon-angle-circled-left',
time: 'icon-clock',
today: 'icon-today',
up: 'icon-arrow-up',
},
viewMode: 'clock',
},
localization: {
locale: moment.locale(),
},
viewMode: 'times',
};
element.datetimepicker($.extend(defaultOptions, options));
new tempusDominus.TempusDominus(element, Object.assign(defaultOptions, options));
}
};
}(jQuery, moment);
}(moment);
/**
* Pull to refresh.

View File

@ -174,7 +174,7 @@
this.apiTagsUrl = widget.getAttribute('data-tags-url');
this.createTagInputs = widget.querySelector('.create-tag-inputs');
this.addTagInput = this.createTagInputs.querySelector('input[type="text"]');
this.addTagButton = this.createTagInputs.querySelector('.btn-add-new-tag');
this.addTagButton = this.createTagInputs.querySelector('#add-tag');
this.addTagInput.value = "";

View File

@ -1,3 +0,0 @@
@import '../../../node_modules/bootstrap/scss/functions';
// Baby Buddy site-wide custom functions.

View File

@ -1,3 +0,0 @@
@import '../../../node_modules/bootstrap/scss/mixins';
// Baby Buddy site-wide custom mixins.

View File

@ -1,5 +1,136 @@
@import 'functions';
@import '../../../node_modules/bootstrap/scss/variables';
@import 'themes/blueorange';
@use 'sass:color';
// Baby Buddy site-wide variables.
// Theme variables.
// Color system
$white: #fff;
$gray-100: #f8f9fa;
$gray-200: #e9ecef;
$gray-300: #dee2e6;
$gray-400: #ced4da;
$gray-500: #adb5bd;
$gray-600: #6c757d;
$gray-700: #495057;
$gray-800: #343a40;
$gray-900: #212529;
$black: #000;
$primary: #37abe9;
$danger: #a72431;
$error: $danger;
$secondary: #ff8f00;
$warning: #ffbe42;
$success: #239556;
$debug: #5abccc;
$info: #44c4dd;
$light: $gray-100;
$dark: $gray-800;
// Body
// Settings for the `<body>` element.
$body-bg: $gray-900;
$body-color: $gray-400;
// Links
// Style anchor elements.
$link-color: $info;
$link-decoration: none;
$link-hover-color: color.adjust($link-color, $lightness: -15%);
// Components
// Define common padding and border radius sizes and more.
$border-color: $gray-200;
$component-active-color: $gray-400;
$component-active-bg: $primary;
// Fonts
// Font, line-height, and color for body text, headings, and more.
$text-muted: $gray-600 !default;
$blockquote-small-color: $gray-600 !default;
$hr-border-color: rgba($black, .1) !default;
$mark-bg: #fcf8e3 !default;
// Forms
$input-bg: $white;
$input-disabled-bg: $gray-600;
$input-color: $black;
$input-border-color: rgba($gray-600, .15);
$input-group-addon-bg: $gray-500;
// Tables
$table-cell-padding-y: 0.75rem;
$table-cell-padding-x: 0.75rem;
$table-striped-bg: $dark;
// Dropdowns
// Dropdown menu container and contents.
$dropdown-bg: $gray-700;
$dropdown-divider-bg: $gray-800;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: color.adjust($body-color, $lightness: -5%);
$dropdown-link-hover-bg: $primary;
$dropdown-link-active-color: $component-active-color;
$dropdown-link-active-bg: $component-active-bg;
$dropdown-header-color: color.adjust($body-color, $lightness: -25%);
// Pagination
$pagination-color: $link-color;
$pagination-bg: $dark;
$pagination-border-color: color.adjust($dark, $lightness: 5%);
$pagination-hover-color: $link-hover-color;
$pagination-hover-bg: $gray-900;
$pagination-hover-border-color: $gray-800;
$pagination-active-color: $body-color;
$pagination-active-bg: $primary;
$pagination-active-border-color: $primary;
$pagination-disabled-color: $gray-600;
$pagination-disabled-bg: $pagination-bg;
$pagination-disabled-border-color: $pagination-border-color;
// Cards
$card-bg: $dark;
$card-cap-bg: rgba($light, .05);
// Progress bars
$progress-bg: $gray-600;
// List group
$list-group-bg: $dark;
$list-group-hover-bg: color.adjust($list-group-bg, $lightness: -5%);
$list-group-active-color: $component-active-color !default;
$list-group-active-bg: $component-active-bg !default;
$list-group-active-border-color: $list-group-active-bg !default;
$list-group-action-color: $gray-400;
$list-group-action-hover-color: $list-group-action-color;
// Breadcrumbs
$breadcrumb-active-color: $gray-600;
$breadcrumb-bg: none;
$breadcrumb-padding-y: 0.75rem;
$breadcrumb-padding-x: 1rem;

View File

@ -1,8 +1,7 @@
@import 'functions';
@import 'variables';
@import 'mixins';
@import '../../../node_modules/bootstrap/scss/bootstrap';
@import '../../../node_modules/tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4';
@import '../../../node_modules/@eonasdan/tempus-dominus/src/sass/variables';
@import '../../../node_modules/@eonasdan/tempus-dominus/src/sass/tempus-dominus';
@import '../../../**/static_src/scss/*';
@import '../fontello/css/babybuddy';

View File

@ -1,3 +1,5 @@
@use 'sass:map';
// Baby Buddy form style customizations.
// BB form fields do not follow typical BS4 style that enables this display.
@ -34,7 +36,7 @@
.input-group-text {
background: none;
border: 0;
color: theme-color('primary');
color: map.get($theme-colors, 'primary');
padding-left: 0;
padding-right: 0;
}

View File

@ -1,3 +1,5 @@
@use 'sass:map';
// Baby Buddy site-wide custom styles.
// Remove extra margin below site breadcrumb.
@ -7,7 +9,7 @@
// Extra-small button.
.btn-xs {
@include button-size(.2rem, .12rem, .75rem, 1, .2rem);
@include button-size(.2rem, .12rem, .75rem, .2rem);
}
// Right align main dropdown menu.
@ -18,33 +20,10 @@
// PullToRefresh elements.
.ptr--ptr {
background: theme-color('dark');
background: map.get($theme-colors, 'dark');
.ptr--text, .ptr--icon {
// "!important" must be used to override inline styling from JS.
color: theme-color('light') !important;
}
}
// Basic table of model instances.
.table-instances {
tr {
td, th {
vertical-align: middle;
}
&.odd {
background: $table-accent-bg;
}
&.even {
background: none;
}
&.row-details {
td {
color: $gray-500;
padding-top: 0;
border-top: 0;
}
}
color: map.get($theme-colors, 'light') !important;
}
}
@ -55,5 +34,5 @@
// All modals
.modal-content {
color: theme-color('dark');
color: map.get($theme-colors, 'dark');
}

View File

@ -1,149 +0,0 @@
@use 'sass:color';
// Blue Orange theme variables.
// Color system
$blue: #37abe9;
$indigo: #472395;
$purple: #712395;
$pink: #952393;
$red: #a72431;
$orange: #ff8f00;
$yellow: #ffbe42;
$green: #239556;
$teal: #5abccc;
$cyan: #44c4dd;
$theme-colors: (
primary: $blue,
secondary: $orange,
success: $green,
info: $cyan,
debug: $cyan,
warning: $yellow,
danger: $red,
error: $red,
light: $gray-100,
dark: $gray-800
);
// Body
// Settings for the `<body>` element.
$body-bg: $gray-900;
$body-color: $gray-400;
// Links
// Style anchor elements.
$link-color: theme-color('info');
$link-hover-color: color.adjust($link-color, $lightness: -15%);
// Components
// Define common padding and border radius sizes and more.
$border-color: $gray-200;
$component-active-color: $gray-400;
$component-active-bg: theme-color('primary');
// Fonts
// Font, line-height, and color for body text, headings, and more.
$text-muted: $gray-600 !default;
$blockquote-small-color: $gray-600 !default;
$hr-border-color: rgba($black, .1) !default;
$mark-bg: #fcf8e3 !default;
// Tables
// Customizes the `.table` component with basic values, each used across all table variations.
$table-border-color: $gray-800;
$table-color: $body-color;
$table-head-bg: theme-color('primary');
$table-hover-color: $body-color;
$table-inverse-bg: theme-color('primary');
$table-accent-bg: rgba(theme-color('primary'), .1);
// Forms
$input-bg: $white;
$input-disabled-bg: $gray-600;
$input-color: $black;
$input-border-color: rgba($gray-600, .15);
$input-group-addon-bg: $gray-500;
// Dropdowns
// Dropdown menu container and contents.
$dropdown-bg: $gray-700;
$dropdown-divider-bg: $gray-800;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: color.adjust($body-color, $lightness: -5%);
$dropdown-link-hover-bg: theme-color('primary');
$dropdown-link-active-color: $component-active-color;
$dropdown-link-active-bg: $component-active-bg;
$dropdown-header-color: color.adjust($body-color, $lightness: -25%);
// Pagination
$pagination-color: $link-color;
$pagination-bg: theme-color('dark');
$pagination-border-color: color.adjust(theme-color('dark'), $lightness: 5%);
$pagination-hover-color: $link-hover-color;
$pagination-hover-bg: $gray-900;
$pagination-hover-border-color: $gray-800;
$pagination-active-color: $body-color;
$pagination-active-bg: theme-color('primary');
$pagination-active-border-color: theme-color('primary');
$pagination-disabled-color: $gray-600;
$pagination-disabled-bg: $pagination-bg;
$pagination-disabled-border-color: $pagination-border-color;
// Jumbotron
$jumbotron-bg: theme-color('dark');
// Cards
$card-bg: theme-color('dark');
$card-cap-bg: rgba(theme-color('light'), .05);
// Progress bars
$progress-bg: $gray-600;
// List group
$list-group-bg: theme-color('dark');
$list-group-hover-bg: color.adjust($list-group-bg, $lightness: -5%);
$list-group-active-color: $component-active-color !default;
$list-group-active-bg: $component-active-bg !default;
$list-group-active-border-color: $list-group-active-bg !default;
$list-group-action-color: $gray-400;
$list-group-action-hover-color: $list-group-action-color;
// Breadcrumbs
$breadcrumb-bg: none;
$breadcrumb-active-color: $gray-600;
// Datetimepicker library (Tempus Dominus)
$bs-datetimepicker-btn-hover-bg: $gray-800;

View File

@ -8,14 +8,14 @@
</label>
<div class="col-xs-10 col-sm-auto">
{% if 'choice' or 'boolean' in field|field_type %}
{{ field|add_class:"custom-select custom-select-sm" }}
{{ field|add_class:"form-select form-select-sm" }}
{% else %}
{{ field|add_class:"form-control form-control-sm" }}
{% endif %}
</div>
{% endfor %}
<div class="col-xs-12 col-sm-auto mt-3 mt-sm-0">
<button type="submit" class="btn btn-sm btn-primary mr-2">{% trans "Filter" %}</button>
<button type="submit" class="btn btn-sm btn-primary me-2">{% trans "Filter" %}</button>
<a href="{{ request.path }}" class="btn btn-sm btn-error">{% trans "Reset" %}</a>
</div>
</div>
@ -24,7 +24,7 @@
<p>
<a class="btn btn-dark btn-sm"
data-toggle="collapse"
data-bs-toggle="collapse"
href="#filter_form"
role="button"
aria-expanded="false"

View File

@ -7,7 +7,7 @@
{% csrf_token %}
{% for field in form %}
{{ field.widget }}
<div class="form-group row">
<div class="row">
{% include 'babybuddy/form_field.html' %}
</div>
{% endfor %}

View File

@ -1,50 +1,51 @@
{% load widget_tweaks %}
<label for="id_{{ field.name }}" class="col-sm-2 col-form-label{% if field|field_type == 'booleanfield' %} boolean-label{% endif %}">
{% if field|field_type != "booleanfield" %}
<div class="row mb-3">
<label for="id_{{ field.name }}" class="col-sm-2 col-form-label{% if field|field_type == 'booleanfield' %} boolean-label{% endif %}">
{{ field.label }}
{% endif %}
</label>
<div class="col-sm-10{% if field|widget_type == 'childradioselect' %} overflow-auto"{% endif %}">
{% if field|field_type == "booleanfield" %}
<div class="btn-group-toggle" data-toggle="buttons">
</label>
<div class="col-sm-10{% if field|widget_type == 'childradioselect' %} overflow-auto"{% endif %}">
{% if field|field_type == "booleanfield" %}
{% if field.errors %}
{{ field|add_class:"btn-check is-invalid" }}
{% else %}
{{ field|add_class:"btn-check" }}
{% endif %}
<label for="id_{{ field.name }}" class="btn btn-outline-light btn-no-hover{% if field.value %} active{% endif %}">
{% if field.errors %}
{{ field|add_class:"is-invalid" }}
{% else %}
{{ field }}
{% endif %}
{{ field.label }}
</label>
</div>
{% elif field|field_type == "datetimefield" or field|field_type == "datefield" %}
<div class="input-group input-group-lg datetimepicker" id="datetimepicker_{{ field.name }}" data-target-input="nearest">
<div class="input-group-prepend px-2 rounded-left bg-dark" data-target="#datetimepicker_{{ field.name }}" data-toggle="datetimepicker">
<span class="input-group-text"><i class="icon-calendar"></i></span>
</div>
{% elif field|field_type == "datetimefield" or field|field_type == "datefield" %}
<div class="input-group input-group-lg datetimepicker"
id="datetimepicker_{{ field.name }}"
data-td-target-input="nearest"
data-td-target-toggle="nearest">
<span class="input-group-text px-2 rounded-start bg-dark"
data-td-target="#datetimepicker_{{ field.name }}"
data-td-toggle="datetimepicker"><i class="icon-calendar"></i></span>
{% if field.errors %}
{{ field|add_class:"datetimepicker-input form-control form-control-lg is-invalid" }}
{% else %}
{{ field|add_class:"datetimepicker-input form-control form-control-lg" }}
{% endif %}
</div>
{% elif 'choice' in field|field_type %}
{% if field.errors %}
{{ field|add_class:"datetimepicker-input form-control form-control-lg is-invalid" }}
{{ field|add_class:"form-select is-invalid" }}
{% else %}
{{ field|add_class:"datetimepicker-input form-control form-control-lg" }}
{{ field|add_class:"form-select" }}
{% endif %}
</div>
{% elif 'choice' in field|field_type %}
{% if field.errors %}
{{ field|add_class:"custom-select is-invalid" }}
{% else %}
{{ field|add_class:"custom-select" }}
{% if field.errors %}
{{ field|add_class:"form-control is-invalid" }}
{% else %}
{{ field|add_class:"form-control" }}
{% endif %}
{% endif %}
{% if field.help_text %}
<div class="help-block"><small>{{ field.help_text }}</small></div>
{% endif %}
{% else %}
{% if field.errors %}
{{ field|add_class:"form-control is-invalid" }}
{% else %}
{{ field|add_class:"form-control" }}
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
{% endif %}
{% endif %}
{% if field.help_text %}
<div class="help-block"><small>{{ field.help_text }}</small></div>
{% endif %}
{% if field.errors %}
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
{% endif %}
</div>
</div>

View File

@ -5,9 +5,7 @@
{% for message in messages %}
<div class="alert{% if message.tags %} alert-{{ message.tags }}{% endif %} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}

View File

@ -3,86 +3,85 @@
{% block nav %}
<nav class="navbar navbar-expand-md navbar-dark bg-dark sticky-top">
<a class="navbar-brand mr-2" href={% url "babybuddy:root-router" %}>
<img src="{% static "babybuddy/logo/icon-brand.png" %}" width="30" height="30" class="d-inline-block align-top" alt="">
<span class="d-none d-lg-inline-block">
<span class="text-primary">Baby</span> Buddy
</span>
</a>
<div class="d-lg-none d-md-none d-flex mr-auto p-0 ml-2">
<div>
<a class="text-muted"
href="{% url 'dashboard:dashboard' %}"
aria-expanded="false"><i class="icon-2x icon-dashboard" aria-hidden="true"></i>
</a>
&nbsp;
</div>
<div>
<a class="text-muted"
href="{% url 'core:timeline' %}"
aria-expanded="false"><i class="icon-2x icon-timeline" aria-hidden="true"></i>
</a>
&nbsp;
</div>
</div>
<div class="d-lg-none d-md-none d-flex ml-auto p-0 mr-2">
{% quick_timer_nav %}
<div class="dropdown show">
<a class="text-success"
href="#"
role="button"
id="nav-quick-add-link"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-2x icon-add" aria-hidden="true"></i>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-quick-add-link">
{% if perms.core.add_diaperchange %}
<a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}">
<i class="icon-diaperchange" aria-hidden="true"></i>
{% trans "Diaper Change" %}
</a>
{% endif %}
{% if perms.core.add_feeding %}
<a class="dropdown-item p-2" href="{% url 'core:feeding-add' %}">
<i class="icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %}
{% if perms.core.add_note %}
<a class="dropdown-item p-2" href="{% url 'core:note-add' %}">
<i class="icon-note" aria-hidden="true"></i>
{% trans "Note" %}
</a>
{% endif %}
{% if perms.core.add_sleep %}
<a class="dropdown-item p-2" href="{% url 'core:sleep-add' %}">
<i class="icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %}
{% if perms.core.add_tummytime %}
<a class="dropdown-item p-2" href="{% url 'core:tummytime-add' %}">
<i class="icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %}
<div class="container-fluid">
<a class="navbar-brand me-2" href={% url "babybuddy:root-router" %}>
<img src="{% static "babybuddy/logo/icon-brand.png" %}" width="30" height="30"
class="d-inline-block align-top" alt="">
<span class="d-none d-lg-inline-block">
<span class="text-primary">Baby</span> Buddy
</span>
</a>
<div class="d-lg-none d-md-none d-flex me-auto p-0 ms-2">
<div>
<a class="text-muted"
href="{% url 'dashboard:dashboard' %}"
aria-expanded="false"><i class="icon-2x icon-dashboard" aria-hidden="true"></i>
</a>
&nbsp;
</div>
<div>
<a class="text-muted"
href="{% url 'core:timeline' %}"
aria-expanded="false"><i class="icon-2x icon-timeline" aria-hidden="true"></i>
</a>
&nbsp;
</div>
</div>
</div>
<div class="d-lg-none d-md-none d-flex ms-auto p-0 me-2">
{% quick_timer_nav %}
<div class="dropdown show">
<a class="text-success"
href="#"
role="button"
id="nav-quick-add-link"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-2x icon-add" aria-hidden="true"></i>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="nav-quick-add-link">
{% if perms.core.add_diaperchange %}
<a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}">
<i class="icon-diaperchange" aria-hidden="true"></i>
{% trans "Diaper Change" %}
</a>
{% endif %}
{% if perms.core.add_feeding %}
<a class="dropdown-item p-2" href="{% url 'core:feeding-add' %}">
<i class="icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %}
{% if perms.core.add_note %}
<a class="dropdown-item p-2" href="{% url 'core:note-add' %}">
<i class="icon-note" aria-hidden="true"></i>
{% trans "Note" %}
</a>
{% endif %}
{% if perms.core.add_sleep %}
<a class="dropdown-item p-2" href="{% url 'core:sleep-add' %}">
<i class="icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %}
{% if perms.core.add_tummytime %}
<a class="dropdown-item p-2" href="{% url 'core:tummytime-add' %}">
<i class="icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %}
</div>
</div>
</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-target="#navbar-app" aria-controls="navbar-app"
aria-expanded="false" aria-label="Toggle navigation">
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar-app">
<ul class="navbar-nav mr-auto">
<div class="collapse navbar-collapse" id="navbar-app">
<ul class="navbar-nav me-auto">
<li class="nav-item{% if request.path == '/' %} active{% endif %}">
<a class="nav-link" href="{% url 'dashboard:dashboard' %}">
<i class="icon-dashboard" aria-hidden="true"></i>
@ -101,7 +100,7 @@
<a id="nav-children-menu-link"
class="nav-link dropdown-toggle"
href="#"
data-toggle="dropdown"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-child" aria-hidden="true"></i>
{% trans "Children" %}
@ -116,7 +115,7 @@
</a>
{% endif %}
{% if perms.core.add_child %}
<a class="dropdown-item pl-5{% if request.path == '/children/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/children/add/' %} active{% endif %}"
href="{% url 'core:child-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Child" %}
</a>
@ -130,7 +129,7 @@
</a>
{% endif %}
{% if perms.core.add_note %}
<a class="dropdown-item pl-5{% if request.path == '/notes/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/notes/add/' %} active{% endif %}"
href="{% url 'core:note-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Note" %}
</a>
@ -143,7 +142,7 @@
<a id="nav-measurements-menu-link"
class="nav-link dropdown-toggle"
href="#"
data-toggle="dropdown"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-measurements" aria-hidden="true"></i>
{% trans "Measurements" %}
@ -158,7 +157,7 @@
</a>
{% endif %}
{% if perms.core.add_bmi %}
<a class="dropdown-item pl-5{% if request.path == '/bmi/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/bmi/add/' %} active{% endif %}"
href="{% url 'core:bmi-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "BMI entry" %}
</a>
@ -172,7 +171,7 @@
</a>
{% endif %}
{% if perms.core.add_head_circumference %}
<a class="dropdown-item pl-5{% if request.path == '/head-circumference/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/head-circumference/add/' %} active{% endif %}"
href="{% url 'core:head-circumference-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Head Circumference entry" %}
</a>
@ -186,7 +185,7 @@
</a>
{% endif %}
{% if perms.core.add_height %}
<a class="dropdown-item pl-5{% if request.path == '/height/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/height/add/' %} active{% endif %}"
href="{% url 'core:height-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Height entry" %}
</a>
@ -200,8 +199,9 @@
</a>
{% endif %}
{% if perms.core.add_temperature %}
<a class="dropdown-item pl-5{% if request.path == '/temperature/add/' %} active{% endif %}"
href="{% url 'core:temperature-add' %}"><i class="icon-add" aria-hidden="true"></i>
<a class="dropdown-item ps-5{% if request.path == '/temperature/add/' %} active{% endif %}"
href="{% url 'core:temperature-add' %}"><i class="icon-add"
aria-hidden="true"></i>
{% trans "Temperature reading" %}
</a>
{% endif %}
@ -222,12 +222,11 @@
</div>
</li>
<li class="nav-item dropdown">
<a id="nav-activity-menu-link"
class="nav-link dropdown-toggle"
href="#"
data-toggle="dropdown"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-activities" aria-hidden="true"></i>
{% trans "Activities" %}
@ -236,12 +235,13 @@
{% if perms.core.view_diaperchange %}
<a class="dropdown-item{% if request.path == '/changes/' %} active{% endif %}"
href="{% url 'core:diaperchange-list' %}"><i class="icon-diaperchange" aria-hidden="true"></i>
href="{% url 'core:diaperchange-list' %}"><i class="icon-diaperchange"
aria-hidden="true"></i>
{% trans "Changes" %}
</a>
{% endif %}
{% if perms.core.add_diaperchange %}
<a class="dropdown-item pl-5{% if request.path == '/changes/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/changes/add/' %} active{% endif %}"
href="{% url 'core:diaperchange-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Change" %}
</a>
@ -254,7 +254,7 @@
</a>
{% endif %}
{% if perms.core.add_feeding %}
<a class="dropdown-item pl-5{% if request.path == '/feedings/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/feedings/add/' %} active{% endif %}"
href="{% url 'core:feeding-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
@ -268,7 +268,7 @@
</a>
{% endif %}
{% if perms.core.add_pumping %}
<a class="dropdown-item pl-5{% if request.path == '/pumping/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/pumping/add/' %} active{% endif %}"
href="{% url 'core:pumping-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Pumping entry" %}
</a>
@ -281,7 +281,7 @@
</a>
{% endif %}
{% if perms.core.add_sleep %}
<a class="dropdown-item pl-5{% if request.path == '/sleep/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/sleep/add/' %} active{% endif %}"
href="{% url 'core:sleep-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Sleep entry" %}
</a>
@ -289,12 +289,13 @@
{% if perms.core.view_tummytime %}
<a class="dropdown-item{% if request.path == '/tummy-time/' %} active{% endif %}"
href="{% url 'core:tummytime-list' %}"><i class="icon-tummytime" aria-hidden="true"></i>
href="{% url 'core:tummytime-list' %}"><i class="icon-tummytime"
aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %}
{% if perms.core.add_tummytime %}
<a class="dropdown-item pl-5{% if request.path == '/tummy-time/add/' %} active{% endif %}"
<a class="dropdown-item ps-5{% if request.path == '/tummy-time/add/' %} active{% endif %}"
href="{% url 'core:tummytime-add' %}"><i class="icon-add" aria-hidden="true"></i>
{% trans "Tummy Time entry" %}
</a>
@ -308,21 +309,23 @@
</ul>
{% if request.user %}
<ul class="navbar-nav ml-auto">
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a id="nav-user-menu-link"
class="nav-link dropdown-toggle"
href="#"
data-toggle="dropdown"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="icon-user" aria-hidden="true"></i>
{% firstof user.get_full_name user.get_username %}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-user-menu-link">
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="nav-user-menu-link">
<h6 class="dropdown-header">{% trans "User" %}</h6>
<a href="{% url 'babybuddy:user-settings' %}" class="dropdown-item">{% trans "Settings" %}</a>
<a href="{% url 'babybuddy:user-password' %}" class="dropdown-item">{% trans "Password" %}</a>
<a href="{% url 'babybuddy:user-settings' %}"
class="dropdown-item">{% trans "Settings" %}</a>
<a href="{% url 'babybuddy:user-password' %}"
class="dropdown-item">{% trans "Password" %}</a>
<a href="{% url 'babybuddy:user-add-device' %}" class="dropdown-item">{% trans "Add a device" %}</a>
<form action="{% url 'babybuddy:logout' %}" role="form" method="post">
{% csrf_token %}
@ -333,8 +336,10 @@
<h6 class="dropdown-header">{% trans "Site" %}</h6>
<a href="{% url 'api:api-root' %}" class="dropdown-item">{% trans "API Browser" %}</a>
{% if request.user.is_staff %}
<a href="{% url 'babybuddy:user-list' %}" class="dropdown-item">{% trans "Users" %}</a>
<a href="{% url 'admin:index' %}" class="dropdown-item">{% trans "Database Admin" %}</a>
<a href="{% url 'babybuddy:user-list' %}"
class="dropdown-item">{% trans "Users" %}</a>
<a href="{% url 'admin:index' %}"
class="dropdown-item">{% trans "Database Admin" %}</a>
{% endif %}
<h6 class="dropdown-header">{% trans "Support" %}</h6>
<a href="https://github.com/babybuddy/babybuddy" class="dropdown-item">
@ -347,5 +352,6 @@
</ul>
{% endif %}
</div>
</div>
</nav>
{% endblock %}

View File

@ -8,7 +8,7 @@
<li class="page-item">
<a class="page-link" href="{% relative_url 'page' page_obj.previous_page_number %}" aria-label="Previous">
<i class="icon-angle-circled-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
</li>
{% endif %}
@ -25,7 +25,7 @@
<li class="page-item">
<a class="page-link" href="{% relative_url 'page' page_obj.next_page_number %}" aria-label="Next">
<i class="icon-angle-circled-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
</li>
{% endif %}

View File

@ -12,7 +12,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">{% trans "Users" %}</a></li>
{% if object %}
<li class="breadcrumb-item font-weight-bold">{{ object }}</li>
<li class="breadcrumb-item fw-bold">{{ object }}</li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %}
<li class="breadcrumb-item active" aria-current="page">{% trans "Create User" %}</li>

View File

@ -11,8 +11,8 @@
<h1>Users</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-striped table-hover user-list">
<thead class="thead-inverse">
<table class="table table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "First Name" %}</th>

View File

@ -8,66 +8,76 @@
{% endblock %}
{% block content %}
<div class="jumbotron">
<h1 class="display-3">{% trans "Welcome to Baby Buddy!" %}</h1>
<p class="lead">
{% blocktrans trimmed%}
Learn about and predict baby's needs without (<em>as much</em>)
guess work by using Baby Buddy to track &mdash;
{% endblocktrans %}
</p>
<hr class="my-4">
<div class="card-deck">
<div class="card card-diaperchange">
<div class="card-header text-center">
<i class="icon-2x icon-diaperchange" aria-hidden="true"></i>
<div class="px-2 py-5 bg-dark rounded-3">
<div class="container-fluid">
<h1 class="display-3">{% trans "Welcome to Baby Buddy!" %}</h1>
<p class="lead">
{% blocktrans trimmed%}
Learn about and predict baby's needs without (<em>as much</em>)
guess work by using Baby Buddy to track &mdash;
{% endblocktrans %}
</p>
<hr class="my-4">
<div class="row gy-4">
<div class="col-12 col-md-6 col-lg-3">
<div class="card card-diaperchange">
<div class="card-header text-center">
<i class="icon-2x icon-diaperchange" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
</div>
</div>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
</div>
</div>
<div class="card card-feeding">
<div class="card-header text-center">
<i class="icon-2x icon-feeding" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Feedings" %}</h3>
</div>
</div>
<div class="card card-sleep">
<div class="card-header text-center">
<i class="icon-2x icon-sleep" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Sleep" %}</h3>
</div>
</div>
<div class="card card-tummytime">
<div class="card-header text-center">
<i class="icon-2x icon-tummytime" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Tummy Time" %}</h3>
<div class="col-12 col-md-6 col-lg-3">
<div class="card card-feeding">
<div class="card-header text-center">
<i class="icon-2x icon-feeding" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Feedings" %}</h3>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card card-sleep">
<div class="card-header text-center">
<i class="icon-2x icon-sleep" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Sleep" %}</h3>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card card-tummytime">
<div class="card-header text-center">
<i class="icon-2x icon-tummytime" aria-hidden="true"></i>
</div>
<div class="card-body">
<h3 class="card-title text-center">{% trans "Tummy Time" %}</h3>
</div>
</div>
</div>
</div>
<hr class="my-4">
<p class="lead">
{% blocktrans trimmed %}
As the amount of entries grows, Baby Buddy will help parents
and caregivers to identify small patterns in baby's habits
using the dashboard and graphs. Baby Buddy is mobile-friendly
and uses a dark theme to help weary moms and dads with 2AM
feedings and changings. To get started, just click the button
below to add your first (or second, third, etc.) child!
{% endblocktrans %}
</p>
<p class="text-center">
{% if perms.core.add_child %}
<a href="{% url 'core:child-add' %}" class="btn btn-lg btn-success">
<i class="icon-child" aria-hidden="true"></i> {% trans "Add a Child" %}
</a>
{% endif %}
</p>
</div>
<hr class="my-4">
<p class="lead">
{% blocktrans trimmed %}
As the amount of entries grows, Baby Buddy will help parents
and caregivers to identify small patterns in baby's habits
using the dashboard and graphs. Baby Buddy is mobile-friendly
and uses a dark theme to help weary moms and dads with 2AM
feedings and changings. To get started, just click the button
below to add your first (or second, third, etc.) child!
{% endblocktrans %}
</p>
<p class="text-center">
{% if perms.core.add_child %}
<a href="{% url 'core:child-add' %}" class="btn btn-lg btn-success">
<i class="icon-child" aria-hidden="true"></i> {% trans "Add a Child" %}
</a>
{% endif %}
</p>
</div>
{% endblock %}

View File

@ -10,12 +10,14 @@
<div class="alert alert-danger" role="alert">
{{ main }} {{ reason }}
</div>
<div class="jumbotron">
<h2>{% trans "How to Fix" %}</h2>
{% blocktrans trimmed with origin=origin %}
Add <samp>{{ origin }}</samp> to the <code>CSRF_TRUSTED_ORIGINS</code>
environment variable. If multiple origins are required separate
with commas.
{% endblocktrans %}
<div class="px-2 py-5 bg-dark rounded-3">
<div class="container-fluid">
<h2>{% trans "How to Fix" %}</h2>
{% blocktrans trimmed with origin=origin %}
Add <samp>{{ origin }}</samp> to the <code>CSRF_TRUSTED_ORIGINS</code>
environment variable. If multiple origins are required separate
with commas.
{% endblocktrans %}
</div>
</div>
{% endblock %}

View File

@ -8,7 +8,7 @@
<div class="row justify-content-md-center">
<div class="col-lg-12 mb-4">
<div class="d-sm-flex">
<img class="d-inline-block align-self-top mr-2 mt-2 text-center" src="{% static "babybuddy/logo/logo-sad.png" %}" width="65" height="65">
<img class="d-inline-block align-self-top me-2 mt-2 text-center" src="{% static "babybuddy/logo/logo-sad.png" %}" width="65" height="65">
<div class="p-2 flex-grow-1">
{% block content %}{% endblock %}
<a href="{% url "babybuddy:root-router" %}" class="btn btn-outline-primary mt-3">{% trans "Return to Baby Buddy" %}</a>

View File

@ -7,7 +7,7 @@
<div id="view-{{ request.resolver_match.view_name }}" class="container">
<div class="text-center pt-3">
<img src="{% static "babybuddy/logo/icon-brand.png" %}" width="65" height="65" class="d-inline-block align-top" alt="">
<h1 class="d-none d-md-inline-block display-4 ml-2">
<h1 class="d-none d-md-inline-block display-4 ms-2">
<span class="text-primary">Baby</span> Buddy
</h1>
</a>

View File

@ -8,23 +8,19 @@
<input type="hidden" name="next" value="{{ next }}">
<label class="sr-only" for="username-input-group">
<label class="visually-hidden" for="username-input-group">
{{ form.username.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-user" aria-hidden="true"></i></span>
</div>
<span class="input-group-text text-muted"><i class="icon-user" aria-hidden="true"></i></span>
{% render_field form.username name='username' class+='form-control' id='username-input-group' placeholder=form.username.label %}
</div>
<label class="sr-only" for="password-input-group">
<label class="visually-hidden" for="password-input-group">
{{ form.password.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
</div>
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
{% render_field form.password name='password' class+='form-control' id='password-input-group' placeholder=form.password.label %}
</div>

View File

@ -22,23 +22,19 @@
<p class="mb-0">{% trans "Enter your new password in each field below." %}</p>
</div>
<label class="sr-only" for="password1-input-group">
<label class="visually-hidden" for="password1-input-group">
{{ form.new_password1.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
</div>
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
</div>
<label class="sr-only" for="password2-input-group">
<label class="visually-hidden" for="password2-input-group">
{{ form.new_password2.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
</div>
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
{% render_field form.new_password2 name='new_password2' class+='form-control' id='password2-input-group' %}
</div>

View File

@ -17,13 +17,11 @@
<form method="post">
{% csrf_token %}
<label class="sr-only" for="email-input-group">
<label class="visually-hidden" for="email-input-group">
{{ form.email.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-mail" aria-hidden="true"></i></span>
</div>
<span class="input-group-text text-muted"><i class="icon-mail" aria-hidden="true"></i></span>
{% render_field form.email name='email' class+='form-control' id='email-input-group' placeholder=form.email.label %}
</div>

View File

@ -100,7 +100,7 @@ class ChildForm(forms.ModelForm):
"birth_date": forms.DateInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_date",
"data-td-target": "#datetimepicker_date",
}
),
}
@ -147,7 +147,7 @@ class PumpingForm(CoreModelForm, TaggableModelForm):
"time": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_time",
"data-td-target": "#datetimepicker_time",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -163,7 +163,7 @@ class DiaperChangeForm(CoreModelForm, TaggableModelForm):
"time": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_time",
"data-td-target": "#datetimepicker_time",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -179,13 +179,13 @@ class FeedingForm(CoreModelForm, TaggableModelForm):
"start": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_start",
"data-td-target": "#datetimepicker_start",
}
),
"end": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_end",
"data-td-target": "#datetimepicker_end",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -201,7 +201,7 @@ class NoteForm(CoreModelForm, TaggableModelForm):
"time": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_time",
"data-td-target": "#datetimepicker_time",
}
),
}
@ -216,13 +216,13 @@ class SleepForm(CoreModelForm, TaggableModelForm):
"start": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_start",
"data-td-target": "#datetimepicker_start",
}
),
"end": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_end",
"data-td-target": "#datetimepicker_end",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -238,7 +238,7 @@ class TemperatureForm(CoreModelForm, TaggableModelForm):
"time": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_time",
"data-td-target": "#datetimepicker_time",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -254,7 +254,7 @@ class TimerForm(CoreModelForm):
"start": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_start",
"data-td-target": "#datetimepicker_start",
}
),
}
@ -279,13 +279,13 @@ class TummyTimeForm(CoreModelForm, TaggableModelForm):
"start": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_start",
"data-td-target": "#datetimepicker_start",
}
),
"end": forms.DateTimeInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_end",
"data-td-target": "#datetimepicker_end",
}
),
}
@ -300,7 +300,7 @@ class WeightForm(CoreModelForm, TaggableModelForm):
"date": forms.DateInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_date",
"data-td-target": "#datetimepicker_date",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -316,7 +316,7 @@ class HeightForm(CoreModelForm, TaggableModelForm):
"date": forms.DateInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_date",
"data-td-target": "#datetimepicker_date",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -332,7 +332,7 @@ class HeadCircumferenceForm(CoreModelForm, TaggableModelForm):
"date": forms.DateInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_date",
"data-td-target": "#datetimepicker_date",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),
@ -348,7 +348,7 @@ class BMIForm(CoreModelForm, TaggableModelForm):
"date": forms.DateInput(
attrs={
"autocomplete": "off",
"data-target": "#datetimepicker_date",
"data-td-target": "#datetimepicker_date",
}
),
"notes": forms.Textarea(attrs={"rows": 5}),

View File

@ -0,0 +1,62 @@
# Generated by Django 4.0.4 on 2022-06-10 03:34
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("core", "0025_pumping_tags"),
]
operations = [
migrations.AlterField(
model_name="feeding",
name="end",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="End time"
),
),
migrations.AlterField(
model_name="feeding",
name="start",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="Start time"
),
),
migrations.AlterField(
model_name="pumping",
name="time",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="Time"
),
),
migrations.AlterField(
model_name="sleep",
name="end",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="End time"
),
),
migrations.AlterField(
model_name="sleep",
name="start",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="Start time"
),
),
migrations.AlterField(
model_name="tummytime",
name="end",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="End time"
),
),
migrations.AlterField(
model_name="tummytime",
name="start",
field=models.DateTimeField(
default=django.utils.timezone.localtime, verbose_name="Start time"
),
),
]

View File

@ -273,8 +273,15 @@ class Feeding(models.Model):
related_name="feeding",
verbose_name=_("Child"),
)
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
duration = models.DurationField(
editable=False, null=True, verbose_name=_("Duration")
)
@ -426,7 +433,9 @@ class Pumping(models.Model):
verbose_name=_("Child"),
)
amount = models.FloatField(blank=False, null=False, verbose_name=_("Amount"))
time = models.DateTimeField(blank=False, null=False, verbose_name=_("Time"))
time = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("Time")
)
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
tags = TaggableManager(blank=True, through=Tagged)
@ -451,8 +460,15 @@ class Sleep(models.Model):
"Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child")
)
napping = models.BooleanField(editable=False, null=True, verbose_name=_("Napping"))
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
duration = models.DurationField(
editable=False, null=True, verbose_name=_("Duration")
)
@ -630,8 +646,15 @@ class TummyTime(models.Model):
related_name="tummy_time",
verbose_name=_("Child"),
)
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
start = models.DateTimeField(
blank=False,
default=timezone.localtime,
null=False,
verbose_name=_("Start time"),
)
end = models.DateTimeField(
blank=False, default=timezone.localtime, null=False, verbose_name=_("End time")
)
duration = models.DurationField(
editable=False, null=True, verbose_name=_("Duration")
)

View File

@ -4,21 +4,6 @@
}
}
#view-core\:child-list {
.picture-column {
text-align: center;
width: 65px;
min-width: 65px;
}
.child-list {
th,
td {
vertical-align: middle;
}
}
}
@include media-breakpoint-up(md) {
#view-core\:child {
.child-detail-column {

View File

@ -1,3 +1,5 @@
@use 'sass:map';
// Adapted for Bootstrap 4 from https://www.bootply.com/SzXin8KDZJ.
$card-shadow: rgba(0, 0, 0, .175);
@ -12,7 +14,7 @@ $card-shadow: rgba(0, 0, 0, .175);
position: absolute;
content: ' ';
width: 3px;
background-color: theme-color('primary');
background-color: map.get($theme-colors, 'primary');
left: 50%;
margin-left: -1.5px;
@ -34,7 +36,7 @@ $card-shadow: rgba(0, 0, 0, .175);
}
.card {
width: 47%;
width: 46%;
float: left;
position: relative;
box-shadow: 0 1px 6px $card-shadow;
@ -45,8 +47,8 @@ $card-shadow: rgba(0, 0, 0, .175);
right: -15px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid theme-color('dark');
border-right: 0 solid theme-color('dark');
border-left: 15px solid map.get($theme-colors, 'dark');
border-right: 0 solid map.get($theme-colors, 'dark');
border-bottom: 15px solid transparent;
content: ' ';
}
@ -57,8 +59,8 @@ $card-shadow: rgba(0, 0, 0, .175);
right: -14px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid theme-color('dark');
border-right: 0 solid theme-color('dark');
border-left: 14px solid map.get($theme-colors, 'dark');
border-right: 0 solid map.get($theme-colors, 'dark');
border-bottom: 14px solid transparent;
content: ' ';
}
@ -95,7 +97,7 @@ $card-shadow: rgba(0, 0, 0, .175);
top: 16px;
left: 50%;
margin-left: -25px;
background-color: theme-color('dark');
background-color: map.get($theme-colors, 'dark');
z-index: 100;
border-radius: 50%;
@ -110,7 +112,7 @@ $card-shadow: rgba(0, 0, 0, .175);
top: 26px;
left: 50%;
margin-left: -25px;
background-color: theme-color('dark');
background-color: map.get($theme-colors, 'dark');
z-index: 100;
border-radius: 50%;
}

View File

@ -1,16 +1,7 @@
@use 'sass:map';
#timer-status {
font-size: $h1-font-size;
font-weight: $display1-weight;
&.timer-stopped {
color: theme-color('danger');
}
}
@include media-breakpoint-up(sm) {
#view-core\:timer-detail {
#timer-status {
font-size: $display1-size;
}
color: map.get($theme-colors, 'dark');
}
}

View File

@ -31,9 +31,14 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
format: 'L',
extraFormats: ['YYYY-MM-DD']
BabyBuddy.DatetimePicker.init(document.querySelector('#datetimepicker_date'), {
display: {
components: {
hours: false,
minutes: false,
seconds: false,
}
}
});
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Date" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for bmi in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if bmi.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="5"><i class="icon-note mr-2" aria-hidden="true"></i>{{ bmi.notes }}</td>
<td colspan="5"><i class="icon-note me-2" aria-hidden="true"></i>{{ bmi.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -5,7 +5,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item fw-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %}

View File

@ -5,7 +5,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold">
<li class="breadcrumb-item fw-bold">
{% child_quick_switch object 'core:child' %}
</li>
{% endblock %}

View File

@ -12,7 +12,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
{% if object %}
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item fw-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %}
<li class="breadcrumb-item active" aria-current="page">{% trans "Add a Child" %}</li>
@ -32,10 +32,15 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_birth_date'), {
format: 'L',
viewMode: 'years',
extraFormats: ['YYYY-MM-DD']
BabyBuddy.DatetimePicker.init(document.querySelector('#datetimepicker_birth_date'), {
display: {
components: {
hours: false,
minutes: false,
seconds: false,
},
viewMode: 'years'
}
});
</script>
{% endblock %}

View File

@ -18,10 +18,10 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-striped table-hover child-list">
<thead class="thead-inverse">
<table class="table table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th class="picture-column"><i class="icon-camera" aria-hidden="true"></i></th>
<th><i class="icon-camera" aria-hidden="true"></i></th>
<th>{% trans "First Name" %}</th>
<th>{% trans "Last Name" %}</th>
<th>{% trans "Birth Date" %}</th>
@ -31,8 +31,12 @@
<tbody>
{% for child in object_list %}
<tr>
<td class="picture-column">
{% include "core/child_thumbnail.html" %}
<td>
{% if child.picture %}
{% include "core/child_thumbnail.html" %}
{% else %}
<img src="{% static 'babybuddy/img/core/child-placeholder.png' %}" width="40" height="40" class="img-fluid rounded-circle" />
{% endif %}
</td>
<th scope="row">
<a href="{% url 'core:child' child.slug %}">{{ child.first_name }}</a>

View File

@ -31,8 +31,6 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_time'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Time" %}</th>
@ -34,8 +34,7 @@
</thead>
<tbody>
{% for change in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -69,7 +68,7 @@
</tr>
{% if change.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="7"><i class="icon-note mr-2" aria-hidden="true"></i>{{ change.notes }}</td>
<td colspan="7"><i class="icon-note me-2" aria-hidden="true"></i>{{ change.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,12 +31,8 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init($('#datetimepicker_end'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_start'));
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_end'));
$('#id_type').change(function() {
var feed_type=$('#id_type').val();
if (feed_type === 'formula' || feed_type === 'fortified breast milk') {

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Date" %}</th>
@ -36,8 +36,7 @@
</thead>
<tbody>
{% for feeding in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -73,7 +72,7 @@
</tr>
{% if feeding.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="8"><i class="icon-note mr-2" aria-hidden="true"></i>{{ feeding.notes }}</td>
<td colspan="8"><i class="icon-note me-2" aria-hidden="true"></i>{{ feeding.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,9 +31,14 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
format: 'L',
extraFormats: ['YYYY-MM-DD']
BabyBuddy.DatetimePicker.init(document.querySelector('#datetimepicker_date'), {
display: {
components: {
hours: false,
minutes: false,
seconds: false,
}
}
});
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Date" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for head_circumference in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if head_circumference.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="5"><i class="icon-note mr-2" aria-hidden="true"></i>{{ head_circumference.notes }}</td>
<td colspan="5"><i class="icon-note me-2" aria-hidden="true"></i>{{ head_circumference.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,9 +31,14 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
format: 'L',
extraFormats: ['YYYY-MM-DD']
BabyBuddy.DatetimePicker.init(document.querySelector('#datetimepicker_date'), {
display: {
components: {
hours: false,
minutes: false,
seconds: false,
}
}
});
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Date" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for height in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if height.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="5"><i class="icon-note mr-2" aria-hidden="true"></i>{{ height.notes }}</td>
<td colspan="5"><i class="icon-note me-2" aria-hidden="true"></i>{{ height.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,8 +31,6 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('#datetimepicker_time'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Time" %}</th>

View File

@ -31,8 +31,6 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_time'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Time" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for pumping in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if pumping.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="4"><i class="icon-note mr-2" aria-hidden="true"></i>{{ pumping.notes }}</td>
<td colspan="4"><i class="icon-note me-2" aria-hidden="true"></i>{{ pumping.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,12 +31,7 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
defaultDate: false,
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init($('#datetimepicker_end'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_start'));
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_end'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Start" %}</th>
@ -34,8 +34,7 @@
</thead>
<tbody>
{% for sleep in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -66,7 +65,7 @@
</tr>
{% if sleep.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="7"><i class="icon-note mr-2" aria-hidden="true"></i>{{ sleep.notes }}</td>
<td colspan="7"><i class="icon-note me-2" aria-hidden="true"></i>{{ sleep.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -31,8 +31,6 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
format: '{% datetimepicker_format %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_time'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Time" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for temperature in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if temperature.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="5"><i class="icon-note mr-2" aria-hidden="true"></i>{{ temperature.notes }}</td>
<td colspan="5"><i class="icon-note me-2" aria-hidden="true"></i>{{ temperature.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -7,7 +7,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item fw-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
{% endblock %}

View File

@ -6,86 +6,90 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
<li class="breadcrumb-item font-weight-bold">{{ object }}</li>
<li class="breadcrumb-item fw-bold">{{ object }}</li>
{% endblock %}
{% block content %}
<div class="jumbotron text-center">
<div class="p-5 mb-4 bg-dark rounded-3 text-center">
<div class="container-fluid py-1">
<h1 id="timer-status"
class="display-1 {% if not object.active %} timer-stopped{% endif %}">
<span class="timer-hours">{{ object.duration|hours }}</span>h
<span class="timer-minutes">{{ object.duration|minutes }}</span>m
<span class="timer-seconds">{{ object.duration|seconds }}</span>s
</h1>
<h1 {% if not object.active %}class="timer-stopped" {% endif %}id="timer-status">
<span class="timer-hours">{{ object.duration|hours }}</span>h
<span class="timer-minutes">{{ object.duration|minutes }}</span>m
<span class="timer-seconds">{{ object.duration|seconds }}</span>s
</h1>
{% if timer.child and CHILD_COUNT > 1 %}
<h2 class="text-muted">
{{ timer.child }}
</h2>
{% endif %}
<p class="lead text-secondary">
{% trans "Started" %} {{ object.start }}
{% if not object.active %}
/ {% trans "Stopped" %} {{ object.end }}
{% endif %}
</p>
<p class="text-muted">
{% blocktrans trimmed with user=object.user_username %}
{{ timer }} created by {{ user }}
{% endblocktrans %}
</p>
{% if perms.core.add_feeding %}
<a class="btn btn-success btn-lg btn-block mb-3"
href="{% instance_add_url 'core:feeding-add' %}"
role="button"><i class="icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %}
{% if perms.core.add_sleep %}
<a class="btn btn-success btn-lg btn-block mb-3"
href="{% instance_add_url 'core:sleep-add' %}"
role="button"><i class="icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %}
{% if perms.core.add_tummytime %}
<a class="btn btn-success btn-lg btn-block mb-3"
href="{% instance_add_url 'core:tummytime-add' %}"
role="button"><i class="icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %}
<div class="center-block" role="group" aria-label="{% trans "Timer actions" %}">
{% if perms.core.delete_timer %}
<a class="btn btn-lg btn-danger"
href="{% url 'core:timer-delete' timer.id %}"
role="button"><i class="icon-delete" aria-hidden="true"></i></a>
{% if timer.child and CHILD_COUNT > 1 %}
<h2 class="text-muted">
{{ timer.child }}
</h2>
{% endif %}
{% if perms.core.change_timer %}
<a class="btn btn-lg btn-primary"
href="{% url 'core:timer-update' timer.id %}"
role="button"><i class="icon-update" aria-hidden="true"></i></a>
<form action="{% url 'core:timer-restart' timer.id %}" role="form" method="post" class="d-inline">
{% csrf_token %}
<label class="sr-only">{% trans "Restart timer" %}</label>
<button type="submit" class="btn btn-lg btn-secondary"><i class="icon-refresh" aria-hidden="true"></i></button>
</form>
{% if object.active %}
<form action="{% url 'core:timer-stop' timer.id %}" role="form" method="post" class="d-inline">
{% csrf_token %}
<label class="sr-only">{% trans "Delete timer" %}</label>
<button type="submit" class="btn btn-lg btn-warning"><i class="icon-stop" aria-hidden="true"></i></button>
</form>
<p class="lead text-secondary">
{% trans "Started" %} {{ object.start }}
{% if not object.active %}
/ {% trans "Stopped" %} {{ object.end }}
{% endif %}
{% endif %}
</p>
<p class="text-muted">
{% blocktrans trimmed with user=object.user_username %}
{{ timer }} created by {{ user }}
{% endblocktrans %}
</p>
<div class="d-grid gap-4 mb-4">
{% if perms.core.add_feeding %}
<a class="btn btn-success btn-lg"
href="{% instance_add_url 'core:feeding-add' %}"
role="button"><i class="icon-feeding" aria-hidden="true"></i>
{% trans "Feeding" %}
</a>
{% endif %}
{% if perms.core.add_sleep %}
<a class="btn btn-success btn-lg"
href="{% instance_add_url 'core:sleep-add' %}"
role="button"><i class="icon-sleep" aria-hidden="true"></i>
{% trans "Sleep" %}
</a>
{% endif %}
{% if perms.core.add_tummytime %}
<a class="btn btn-success btn-lg"
href="{% instance_add_url 'core:tummytime-add' %}"
role="button"><i class="icon-tummytime" aria-hidden="true"></i>
{% trans "Tummy Time" %}
</a>
{% endif %}
</div>
<div class="center-block" role="group" aria-label="{% trans "Timer actions" %}">
{% if perms.core.delete_timer %}
<a class="btn btn-lg btn-danger"
href="{% url 'core:timer-delete' timer.id %}"
role="button"><i class="icon-delete" aria-hidden="true"></i></a>
{% endif %}
{% if perms.core.change_timer %}
<a class="btn btn-lg btn-primary"
href="{% url 'core:timer-update' timer.id %}"
role="button"><i class="icon-update" aria-hidden="true"></i></a>
<form action="{% url 'core:timer-restart' timer.id %}" role="form" method="post" class="d-inline">
{% csrf_token %}
<label class="visually-hidden">{% trans "Restart timer" %}</label>
<button type="submit" class="btn btn-lg btn-secondary"><i class="icon-refresh" aria-hidden="true"></i></button>
</form>
{% if object.active %}
<form action="{% url 'core:timer-stop' timer.id %}" role="form" method="post" class="d-inline">
{% csrf_token %}
<label class="visually-hidden">{% trans "Delete timer" %}</label>
<button type="submit" class="btn btn-lg btn-warning"><i class="icon-stop" aria-hidden="true"></i></button>
</form>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -6,7 +6,7 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
{% if object %}
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item fw-bold"><a href="{% url 'core:timer-detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
{% else %}
<li class="breadcrumb-item active" aria-current="page">{% trans "Start" %}</li>
@ -26,8 +26,6 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
format: '{% datetimepicker_format 'L LTS' %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_start'));
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Start" %}</th>
<th>{% trans "Name" %}</th>

View File

@ -4,13 +4,21 @@
<a id="nav-timer-menu-link"
class="nav-link dropdown-toggle"
href="#"
data-toggle="dropdown"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"><i class="icon-timer" aria-hidden="true"></i>
{% trans "Timers" %}
</a>
<div class="dropdown-menu" aria-labelledby="nav-timer-menu-link">
{% if perms.core.add_timer %}
<div class="dropdown-item">
<form action="{% url 'core:timer-add-quick' %}" role="form" method="post" class="d-inline">
{% csrf_token %}
<button class="btn text-start p-0">
<i class="icon-timer" aria-hidden="true"></i> {% trans "Quick Start Timer" %}
</button>
</form>
</div>
<a class="dropdown-item" href="{% url 'core:timer-add' %}">
<i class="icon-add" aria-hidden="true"></i> {% trans "Start Timer" %}
</a>

View File

@ -31,12 +31,7 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
defaultDate: false,
format: '{% datetimepicker_format 'L LTS' %}'
});
BabyBuddy.DatetimePicker.init($('#datetimepicker_end'), {
format: '{% datetimepicker_format 'L LTS' %}'
});
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_start'));
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_end'));
</script>
{% endblock %}

View File

@ -17,8 +17,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Start" %}</th>

View File

@ -31,9 +31,14 @@
{% block javascript %}
<script type="text/javascript">
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
format: 'L',
extraFormats: ['YYYY-MM-DD']
BabyBuddy.DatetimePicker.init(document.getElementById('datetimepicker_date'), {
display: {
components: {
hours: false,
minutes: false,
seconds: false,
}
}
});
</script>
{% endblock %}

View File

@ -18,8 +18,8 @@
</h1>
{% include 'babybuddy/filter.html' %}
<div class="table-responsive">
<table class="table table-instances table-striped table-hover">
<thead class="thead-inverse">
<table class="table table-instances table-borderless table-striped table-hover align-middle">
<thead>
<tr>
<th>{% trans "Actions" %}</th>
<th>{% trans "Date" %}</th>
@ -32,8 +32,7 @@
</thead>
<tbody>
{% for weight in object_list %}
{% cycle "odd" "even" as row_class silent %}
<tr class="{{ row_class }}">
<tr>
<td>
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
@ -62,7 +61,7 @@
</tr>
{% if weight.notes %}
<tr class="{{ row_class }} row-details">
<td colspan="5"><i class="icon-note mr-2" aria-hidden="true"></i>{{ weight.notes }}</td>
<td colspan="5"><i class="icon-note me-2" aria-hidden="true"></i>{{ weight.notes }}</td>
</tr>
{% endif %}
{% empty %}

View File

@ -5,39 +5,31 @@
{{ k }}="{{ v }}"
{% endfor %}>
{% csrf_token %}
<span class="prototype-tag btn badge badge-pill cursor-pointer mr-1" style="display: none;">
<span class="prototype-tag btn badge badge-pill cursor-pointer me-1" style="display: none;">
UNINITIALIZED PROTOTYPE
<span class="add-remove-icon pl-1 pr-1">+ or -</span>
<span class="add-remove-icon ps-1 pe-1">+ or -</span>
</span>
<div class="current_tags" style="min-height: 2em;">
{% for t in widget.value %}
<span data-value="{{ t.name }}" data-color="{{ t.color }}" class="tag btn badge badge-pill cursor-pointer mr-1" style="background-color: {{ t.color }};">
<span data-value="{{ t.name }}" data-color="{{ t.color }}" class="tag btn badge badge-pill cursor-pointer me-1" style="background-color: {{ t.color }};">
{{ t.name }}
<span class="add-remove-icon pl-1 pr-1">-</span>
<span class="add-remove-icon ps-1 pe-1">-</span>
</span>
{% endfor %}
</div>
<div class="new-tags">
<div class="create-tag-inputs input-group">
<input class="form-control" type="text" name="" placeholder="{% trans "Tag name" %}">
<div class="input-group-append">
<button class="btn btn-outline-primary bg-dark btn-add-new-tag" type="button">{% trans "Add" %}</button>
</div>
<button id="add-tag" class="btn btn-outline-primary bg-dark" type="button">{% trans "Add" %}</button>
</div>
{% if widget.tag_suggestions.quick %}
<span>{% trans "Recently used:" %}</span>
{% for t in widget.tag_suggestions.quick %}
<span data-value="{{ t.name }}" data-color="{{ t.color }}" class="tag btn badge badge-pill cursor-pointer mr-1" style="background-color: {{ t.color }};">
<span data-value="{{ t.name }}" data-color="{{ t.color }}" class="tag btn badge badge-pill cursor-pointer me-1" style="background-color: {{ t.color }};">
{{ t.name }}
<span class="add-remove-icon pl-1 pr-1">+</span>
<span class="add-remove-icon ps-1 pe-1">+</span>
</span>
{% endfor %}
{% else %}
<style>
.help-block{
display: none;
}
</style>
{%endif%}
</div>
<input
@ -51,7 +43,7 @@
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{% trans "Error" context "Error modal" %}</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
@ -62,7 +54,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">
{% trans "Close" context "Error modal" %}
</button>
</div>

View File

@ -4,14 +4,14 @@
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon-2x icon-angle-circled-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon-2x icon-angle-circled-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>
@ -22,7 +22,7 @@
<div class="timeline-badge {% if object.type == "start" %}bg-success{% elif object.type == "end" %}bg-danger{% else %}bg-info{% endif %}">
<i class="icon-{{ object.model_name }}"></i>
</div>
<div class="card text-right">
<div class="card text-end">
<div class="card-body">
{{ object.event }}
{% for detail in object.details %}
@ -70,14 +70,14 @@
{% if date_previous %}
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "Previous" %}">
<i class="icon-2x icon-angle-circled-left" aria-hidden="true"></i>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
{% endif %}
{{ date|date }}
{% if date_next %}
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "Next" %}">
<i class="icon-2x icon-angle-circled-right" aria-hidden="true"></i>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
{% endif %}
</h3>

View File

@ -4,7 +4,7 @@
{% block title %}{% trans "Timeline" %}{% endblock %}
{% block breadcrumbs %}
<li class="breadcrumb-item font-weight-bold">{% trans "Timeline" %}</li>
<li class="breadcrumb-item fw-bold">{% trans "Timeline" %}</li>
{% endblock %}
{% block content %}

View File

@ -1,3 +1,5 @@
@use 'sass:map';
%card-header-text {
font-size: $h4-font-size;
font-weight: $headings-font-weight;
@ -29,18 +31,24 @@
padding-right: $card-spacer-x;
}
}
.list-group {
.list-group-item {
color: $white;
}
}
}
.card-diaperchange {
border-color: theme-color('danger');
border-color: map.get($theme-colors, 'danger');
.card-header, .card-header a {
background-color: theme-color('danger');
color: theme-color('light');
background-color: map.get($theme-colors, 'danger');
color: map.get($theme-colors, 'light');
}
.card-body {
color: theme-color('danger');
color: map.get($theme-colors, 'danger');
}
.progress {
@ -50,15 +58,15 @@
.card-feeding,
.card-pumping {
border-color: theme-color('primary');
border-color: map.get($theme-colors, 'primary');
.card-header, .card-header a {
background-color: theme-color('primary');
color: theme-color('light');
background-color: map.get($theme-colors, 'primary');
color: map.get($theme-colors, 'light');
}
.card-body {
color: theme-color('primary');
color: map.get($theme-colors, 'primary');
// Last feeding method header in card.
.last-feeding-method {
@ -68,28 +76,28 @@
}
.card-sleep {
border-color: theme-color('secondary');
border-color: map.get($theme-colors, 'secondary');
.card-header, .card-header a {
background-color: theme-color('secondary');
color: theme-color('light');
background-color: map.get($theme-colors, 'secondary');
color: map.get($theme-colors, 'light');
}
}
.card-statistics, .card-timer {
border-color: theme-color('light');
border-color: map.get($theme-colors, 'light');
.card-header {
background-color: theme-color('light');
color: theme-color('dark');
background-color: map.get($theme-colors, 'light');
color: map.get($theme-colors, 'dark');
a {
color: theme-color('dark');
color: map.get($theme-colors, 'dark');
}
}
.card-body {
color: theme-color('light');
color: map.get($theme-colors, 'light');
.container {
padding: 0;
@ -98,14 +106,14 @@
}
.card-tummytime {
border-color: theme-color('success');
border-color: map.get($theme-colors, 'success');
.card-header, .card-header a {
background-color: theme-color('success');
color: theme-color('light');
background-color: map.get($theme-colors, 'success');
color: map.get($theme-colors, 'light');
}
.card-body {
color: theme-color('success');
color: map.get($theme-colors, 'success');
}
}

View File

@ -1,11 +1,3 @@
.child-actions {
flex-wrap: wrap;
}
@include media-breakpoint-between(sm, md) {
#dashboard-child {
&.card-columns {
column-count: 2;
}
}
}

View File

@ -9,7 +9,7 @@
{% block title %}
{% if feedings|length > 0 %}
<div id="feeding-days-carousel" class="carousel slide" data-interval="false">
<div id="feeding-days-carousel" class="carousel slide" data-bs-interval="false">
<div class="carousel-inner">
{% for feeding in feedings %}
<div class="carousel-item{% if forloop.counter == 1 %} active{% endif %}">
@ -38,13 +38,19 @@
{% endfor %}
</div>
{% if feedings|length > 1 %}
<a class="carousel-control-prev" href="#feeding-days-carousel" role="button" data-slide="prev">
<a class="carousel-control-prev"
href="#feeding-days-carousel"
role="button"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
<a class="carousel-control-next" href="#feeding-days-carousel" role="button" data-slide="next">
<a class="carousel-control-next"
href="#feeding-days-carousel"
role="button"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
{% endif %}
</div>

View File

@ -9,7 +9,7 @@
{% block title %}
{% if feedings|length > 0 %}
<div id="feeding-methods-carousel" class="carousel slide" data-interval="false">
<div id="feeding-methods-carousel" class="carousel slide" data-bs-interval="false">
<div class="carousel-inner">
{% for feeding in feedings %}
<div class="carousel-item{% if forloop.counter == feedings|length %} active{% endif %}">
@ -29,13 +29,19 @@
{% endfor %}
</div>
{% if feedings|length > 1 %}
<a class="carousel-control-prev" href="#feeding-methods-carousel" role="button" data-slide="prev">
<a class="carousel-control-prev"
href="#feeding-methods-carousel"
role="button"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
<a class="carousel-control-next" href="#feeding-methods-carousel" role="button" data-slide="next">
<a class="carousel-control-next"
href="#feeding-methods-carousel"
role="button"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
{% endif %}
</div>

View File

@ -8,7 +8,7 @@
</div>
<div class="card-body text-center">
{% if stats|length > 0 %}
<div id="statistics-carousel" class="carousel slide" data-interval="false">
<div id="statistics-carousel" class="carousel slide" data-bs-interval="false">
<div class="carousel-inner">
{% for stat in stats %}
<div class="carousel-item{% if forloop.counter == 1 %} active{% endif %}">
@ -29,13 +29,19 @@
</div>
{% endfor %}
</div>
<a class="carousel-control-prev" href="#statistics-carousel" role="button" data-slide="prev">
<a class="carousel-control-prev"
href="#statistics-carousel"
role="button"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Previous" %}</span>
<span class="visually-hidden">{% trans "Previous" %}</span>
</a>
<a class="carousel-control-next" href="#statistics-carousel" role="button" data-slide="next">
<a class="carousel-control-next"
href="#statistics-carousel"
role="button"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">{% trans "Next" %}</span>
<span class="visually-hidden">{% trans "Next" %}</span>
</a>
</div>
{% else %}

View File

@ -5,26 +5,50 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item font-weight-bold">
<li class="breadcrumb-item fw-bold">
{% child_quick_switch object 'dashboard:dashboard-child' %}
</li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Dashboard" %}</li>
{% endblock %}
{% block content %}
<div id="dashboard-child" class="card-columns">
{% card_timer_list object %}
{% card_feeding_last object %}
{% card_pumping_last object %}
{% card_diaperchange_last object %}
{% card_sleep_last object %}
{% card_feeding_last_method object %}
{% card_feeding_day object %}
{% card_statistics object %}
{% card_sleep_recent object %}
{% card_sleep_naps_day object %}
{% card_tummytime_day object %}
{% card_diaperchange_types object %}
<div id="dashboard-child" class="row" data-masonry='{"percentPosition": true }'>
<div class="col-sm-6 col-lg-4">
{% card_timer_list object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_feeding_last object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_diaperchange_last object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_pumping_last object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_sleep_last object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_feeding_last_method object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_feeding_day object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_statistics object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_sleep_recent object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_sleep_naps_day object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_tummytime_day object %}
</div>
<div class="col-sm-6 col-lg-4">
{% card_diaperchange_types object %}
</div>
</div>
{% endblock %}

View File

@ -4,7 +4,7 @@
{% block title %}{% trans "Dashboard" %}{% endblock %}
{% block breadcrumbs %}
<li class="breadcrumb-item font-weight-bold">{% trans "Dashboard" %}</li>
<li class="breadcrumb-item fw-bold">{% trans "Dashboard" %}</li>
{% endblock %}
{% block content %}

View File

@ -29,8 +29,9 @@ module.exports = {
vendor: [
'node_modules/pulltorefreshjs/dist/index.umd.js',
'node_modules/jquery/dist/jquery.js',
'node_modules/popper.js/dist/umd/popper.js',
'node_modules/@popperjs/core/dist/umd/popper.js',
'node_modules/bootstrap/dist/js/bootstrap.js',
'node_modules/masonry-layout/dist/masonry.pkgd.js',
'node_modules/moment/moment.js',
'node_modules/moment/locale/ca.js',
'node_modules/moment/locale/cs.js',
@ -50,7 +51,7 @@ module.exports = {
'node_modules/moment/locale/tr.js',
'node_modules/moment/locale/zh-cn.js',
'node_modules/moment-timezone/builds/moment-timezone-with-data-10-year-range.js',
'node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.js'
'node_modules/@eonasdan/tempus-dominus/dist/js/tempus-dominus.js'
],
graph: [
'node_modules/plotly.js/dist/plotly-cartesian.js',

2774
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,10 @@
"url": "https://github.com/babybuddy/babybuddy.git"
},
"devDependencies": {
"@ronilaukkarinen/gulp-stylelint": "^14.1.1",
"bootstrap": "^4.6.2",
"@eonasdan/tempus-dominus": "^6.0.0-beta7",
"@popperjs/core": "^2.11.5",
"@ronilaukkarinen/gulp-stylelint": "^14.0.6",
"bootstrap": "^5.1.3",
"del": "^6.1.1",
"gulp": "^4.0.2",
"gulp-all": "^1.1.0",
@ -23,18 +25,17 @@
"gulp-spawn": "^2.0.0",
"gulp-uglify": "^3.0.2",
"jquery": "^3.6.4",
"masonry-layout": "^4.2.2",
"moment": "^2.29.4",
"moment-timezone": "^0.5.41",
"npm-force-resolutions": "^0.0.10",
"plotly.js": "^2.20.0",
"popper.js": "^1.16.1",
"pulltorefreshjs": "^0.1.22",
"sass": "^1.59.3",
"stylelint": "^15.3.0",
"stylelint-config-recommended-scss": "^9.0.1",
"stylelint-order": "^6.0.3",
"stylelint-scss": "^4.5.0",
"tempusdominus-bootstrap-4": "5.39.2",
"tempusdominus-core": "5.19.3"
"pump": "^3.0.0",
"sass": "^1.52.3",
"stylelint": "^14.9.0",
"stylelint-config-recommended-scss": "^6.0.0",
"stylelint-order": "^5.0.0",
"stylelint-scss": "^4.2.0"
}
}

View File

@ -0,0 +1,5 @@
.reports-list {
.list-group-item {
color: $white;
}
}

View File

@ -5,4 +5,6 @@
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
<li class="breadcrumb-item fw-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
<li class="breadcrumb-item"><a href="{% url 'reports:report-list' object.slug %}">{% trans "Reports" %}</a></li>
{% endblock %}

View File

@ -12,10 +12,12 @@
{% if html %}
{{ html|safe }}
{% else %}
<div class="jumbotron text-center">
<i class="icon-sad" aria-hidden="true"></i>
{% trans "There is not enough data to generate this report." %}
<i class="icon-sad" aria-hidden="true"></i>
<div class="px-2 py-5 bg-dark rounded-3 text-center display-5">
<div class="container-fluid">
<i class="icon-sad" aria-hidden="true"></i>
{% trans "There is not enough data to generate this report." %}
<i class="icon-sad" aria-hidden="true"></i>
</div>
</div>
{% endif %}
</div>

View File

@ -15,7 +15,7 @@
{% block content %}
<div class="container-fluid">
<h1>Reports</h1>
<div class="list-group">
<div class="reports-list list-group">
<a href="{% url 'reports:report-bmi-change-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Body Mass Index (BMI)" %}</a>
<a href="{% url 'reports:report-diaperchange-amounts-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Diaper Change Amounts" %}</a>
<a href="{% url 'reports:report-diaperchange-types-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Diaper Change Types" %}</a>

File diff suppressed because it is too large Load Diff

Binary file not shown.

10947
static/babybuddy/css/app.c7ae9e0dd7de.css generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1 +0,0 @@
if("undefined"==typeof jQuery)throw new Error("Baby Buddy requires jQuery.");if("undefined"==typeof moment)throw new Error("Baby Buddy requires moment.js.");var BabyBuddy={};function preventDoubleSubmit(){return!1}BabyBuddy.DatetimePicker=function(e,n){return{init:function(t,i){var o={buttons:{showToday:!0,showClose:!0},defaultDate:"now",focusOnShow:!1,format:"L LT",ignoreReadonly:!0,locale:n.locale(),useCurrent:!1,icons:{time:"icon-clock",date:"icon-calendar",up:"icon-arrow-up",down:"icon-arrow-down",previous:"icon-angle-circled-left",next:"icon-angle-circled-right",today:"icon-today",clear:"icon-delete",close:"icon-cancel"},viewMode:"times"};t.datetimepicker(e.extend(o,i))}}}(jQuery,moment),BabyBuddy.PullToRefresh=function(e){return{init:function(){e.init({mainElement:"body",onRefresh:this.onRefresh})},onRefresh:function(){window.location.reload()}}}(PullToRefresh),$("form").off("submit",preventDoubleSubmit),$("form").on("submit",function(){$(this).on("submit",preventDoubleSubmit)}),BabyBuddy.Timer=function(e){var n=null,t=null,i=null,o=moment(),d=null,r={run:function(o,u){return t=o,0==(i=e("#"+u)).length?(console.error("BBTimer: Timer element not found."),!1):0==i.find(".timer-seconds").length||0==i.find(".timer-minutes").length||0==i.find(".timer-hours").length?(console.error("BBTimer: Element does not contain expected children."),!1):(n=setInterval(this.tick,1e3),void 0!==document.hidden?d="hidden":void 0!==document.msHidden?d="msHidden":void 0!==document.webkitHidden&&(d="webkitHidden"),void window.addEventListener("focus",r.handleVisibilityChange,!1))},handleVisibilityChange:function(){!document[d]&&moment().diff(o)>1e4&&r.update()},tick:function(){var e=i.find(".timer-seconds"),n=Number(e.text());if(n<59)e.text(n+1);else{e.text(0);var t=i.find(".timer-minutes"),o=Number(t.text());if(o<59)t.text(o+1);else{t.text(0);var d=i.find(".timer-hours"),r=Number(d.text());d.text(r+1)}}},update:function(){e.get("/api/timers/"+t+"/",function(e){if(e&&"duration"in e){clearInterval(n);var t=moment.duration(e.duration);i.find(".timer-hours").text(t.hours()),i.find(".timer-minutes").text(t.minutes()),i.find(".timer-seconds").text(t.seconds()),o=moment(),e.active?n=setInterval(r.tick,1e3):i.addClass("timer-stopped")}})}};return r}(jQuery),BabyBuddy.Dashboard=function(e){var n=null,t={watch:function(i,o){if(0==e("#"+i).length)return console.error("Baby Buddy: Dashboard element not found."),!1;void 0!==document.hidden?n="hidden":void 0!==document.msHidden?n="msHidden":void 0!==document.webkitHidden&&(n="webkitHidden"),void 0===window.addEventListener||void 0===document.hidden?o&&setInterval(this.update,o):(window.addEventListener("focus",t.handleVisibilityChange,!1),o&&setInterval(t.handleVisibilityChange,o))},handleVisibilityChange:function(){document[n]||t.update()},update:function(){location.reload()}};return t}(jQuery);

Binary file not shown.

1
static/babybuddy/js/app.d025d9d0940e.js generated Normal file
View File

@ -0,0 +1 @@
if("undefined"==typeof jQuery)throw new Error("Baby Buddy requires jQuery.");if("undefined"==typeof moment)throw new Error("Baby Buddy requires moment.js.");var BabyBuddy={};function preventDoubleSubmit(){return!1}BabyBuddy.DatetimePicker=function(e){return{init:function(n,t){let i={display:{buttons:{close:!0,today:!0},components:{calendar:!0,clock:!0,date:!0,decades:!0,hours:!0,minutes:!0,month:!0,seconds:!1,useTwentyfourHour:!1,year:!0},icons:{clear:"icon-delete",close:"icon-cancel",date:"icon-calendar",down:"icon-arrow-down",next:"icon-angle-circled-right",previous:"icon-angle-circled-left",time:"icon-clock",today:"icon-today",up:"icon-arrow-up"},viewMode:"clock"},localization:{locale:e.locale()}};new tempusDominus.TempusDominus(n,Object.assign(i,t))}}}(moment),BabyBuddy.PullToRefresh=function(e){return{init:function(){e.init({mainElement:"body",onRefresh:this.onRefresh})},onRefresh:function(){window.location.reload()}}}(PullToRefresh),$("form").off("submit",preventDoubleSubmit),$("form").on("submit",function(){$(this).on("submit",preventDoubleSubmit)}),BabyBuddy.Timer=function(e){var n=null,t=null,i=null,o=moment(),d=null,r={run:function(o,u){return t=o,0==(i=e("#"+u)).length?(console.error("BBTimer: Timer element not found."),!1):0==i.find(".timer-seconds").length||0==i.find(".timer-minutes").length||0==i.find(".timer-hours").length?(console.error("BBTimer: Element does not contain expected children."),!1):(n=setInterval(this.tick,1e3),void 0!==document.hidden?d="hidden":void 0!==document.msHidden?d="msHidden":void 0!==document.webkitHidden&&(d="webkitHidden"),void window.addEventListener("focus",r.handleVisibilityChange,!1))},handleVisibilityChange:function(){!document[d]&&moment().diff(o)>1e4&&r.update()},tick:function(){var e=i.find(".timer-seconds"),n=Number(e.text());if(n<59)e.text(n+1);else{e.text(0);var t=i.find(".timer-minutes"),o=Number(t.text());if(o<59)t.text(o+1);else{t.text(0);var d=i.find(".timer-hours"),r=Number(d.text());d.text(r+1)}}},update:function(){e.get("/api/timers/"+t+"/",function(e){if(e&&"duration"in e){clearInterval(n);var t=moment.duration(e.duration);i.find(".timer-hours").text(t.hours()),i.find(".timer-minutes").text(t.minutes()),i.find(".timer-seconds").text(t.seconds()),o=moment(),e.active?n=setInterval(r.tick,1e3):i.addClass("timer-stopped")}})}};return r}(jQuery),BabyBuddy.Dashboard=function(e){var n=null,t={watch:function(i,o){if(0==e("#"+i).length)return console.error("Baby Buddy: Dashboard element not found."),!1;void 0!==document.hidden?n="hidden":void 0!==document.msHidden?n="msHidden":void 0!==document.webkitHidden&&(n="webkitHidden"),void 0===window.addEventListener||void 0===document.hidden?o&&setInterval(this.update,o):(window.addEventListener("focus",t.handleVisibilityChange,!1),o&&setInterval(t.handleVisibilityChange,o))},handleVisibilityChange:function(){document[n]||t.update()},update:function(){location.reload()}};return t}(jQuery);

BIN
static/babybuddy/js/app.d025d9d0940e.js.gz generated Normal file

Binary file not shown.

View File

@ -1 +1 @@
if("undefined"==typeof jQuery)throw new Error("Baby Buddy requires jQuery.");if("undefined"==typeof moment)throw new Error("Baby Buddy requires moment.js.");var BabyBuddy={};function preventDoubleSubmit(){return!1}BabyBuddy.DatetimePicker=function(e,n){return{init:function(t,i){var o={buttons:{showToday:!0,showClose:!0},defaultDate:"now",focusOnShow:!1,format:"L LT",ignoreReadonly:!0,locale:n.locale(),useCurrent:!1,icons:{time:"icon-clock",date:"icon-calendar",up:"icon-arrow-up",down:"icon-arrow-down",previous:"icon-angle-circled-left",next:"icon-angle-circled-right",today:"icon-today",clear:"icon-delete",close:"icon-cancel"},viewMode:"times"};t.datetimepicker(e.extend(o,i))}}}(jQuery,moment),BabyBuddy.PullToRefresh=function(e){return{init:function(){e.init({mainElement:"body",onRefresh:this.onRefresh})},onRefresh:function(){window.location.reload()}}}(PullToRefresh),$("form").off("submit",preventDoubleSubmit),$("form").on("submit",function(){$(this).on("submit",preventDoubleSubmit)}),BabyBuddy.Timer=function(e){var n=null,t=null,i=null,o=moment(),d=null,r={run:function(o,u){return t=o,0==(i=e("#"+u)).length?(console.error("BBTimer: Timer element not found."),!1):0==i.find(".timer-seconds").length||0==i.find(".timer-minutes").length||0==i.find(".timer-hours").length?(console.error("BBTimer: Element does not contain expected children."),!1):(n=setInterval(this.tick,1e3),void 0!==document.hidden?d="hidden":void 0!==document.msHidden?d="msHidden":void 0!==document.webkitHidden&&(d="webkitHidden"),void window.addEventListener("focus",r.handleVisibilityChange,!1))},handleVisibilityChange:function(){!document[d]&&moment().diff(o)>1e4&&r.update()},tick:function(){var e=i.find(".timer-seconds"),n=Number(e.text());if(n<59)e.text(n+1);else{e.text(0);var t=i.find(".timer-minutes"),o=Number(t.text());if(o<59)t.text(o+1);else{t.text(0);var d=i.find(".timer-hours"),r=Number(d.text());d.text(r+1)}}},update:function(){e.get("/api/timers/"+t+"/",function(e){if(e&&"duration"in e){clearInterval(n);var t=moment.duration(e.duration);i.find(".timer-hours").text(t.hours()),i.find(".timer-minutes").text(t.minutes()),i.find(".timer-seconds").text(t.seconds()),o=moment(),e.active?n=setInterval(r.tick,1e3):i.addClass("timer-stopped")}})}};return r}(jQuery),BabyBuddy.Dashboard=function(e){var n=null,t={watch:function(i,o){if(0==e("#"+i).length)return console.error("Baby Buddy: Dashboard element not found."),!1;void 0!==document.hidden?n="hidden":void 0!==document.msHidden?n="msHidden":void 0!==document.webkitHidden&&(n="webkitHidden"),void 0===window.addEventListener||void 0===document.hidden?o&&setInterval(this.update,o):(window.addEventListener("focus",t.handleVisibilityChange,!1),o&&setInterval(t.handleVisibilityChange,o))},handleVisibilityChange:function(){document[n]||t.update()},update:function(){location.reload()}};return t}(jQuery);
if("undefined"==typeof jQuery)throw new Error("Baby Buddy requires jQuery.");if("undefined"==typeof moment)throw new Error("Baby Buddy requires moment.js.");var BabyBuddy={};function preventDoubleSubmit(){return!1}BabyBuddy.DatetimePicker=function(e){return{init:function(n,t){let i={display:{buttons:{close:!0,today:!0},components:{calendar:!0,clock:!0,date:!0,decades:!0,hours:!0,minutes:!0,month:!0,seconds:!1,useTwentyfourHour:!1,year:!0},icons:{clear:"icon-delete",close:"icon-cancel",date:"icon-calendar",down:"icon-arrow-down",next:"icon-angle-circled-right",previous:"icon-angle-circled-left",time:"icon-clock",today:"icon-today",up:"icon-arrow-up"},viewMode:"clock"},localization:{locale:e.locale()}};new tempusDominus.TempusDominus(n,Object.assign(i,t))}}}(moment),BabyBuddy.PullToRefresh=function(e){return{init:function(){e.init({mainElement:"body",onRefresh:this.onRefresh})},onRefresh:function(){window.location.reload()}}}(PullToRefresh),$("form").off("submit",preventDoubleSubmit),$("form").on("submit",function(){$(this).on("submit",preventDoubleSubmit)}),BabyBuddy.Timer=function(e){var n=null,t=null,i=null,o=moment(),d=null,r={run:function(o,u){return t=o,0==(i=e("#"+u)).length?(console.error("BBTimer: Timer element not found."),!1):0==i.find(".timer-seconds").length||0==i.find(".timer-minutes").length||0==i.find(".timer-hours").length?(console.error("BBTimer: Element does not contain expected children."),!1):(n=setInterval(this.tick,1e3),void 0!==document.hidden?d="hidden":void 0!==document.msHidden?d="msHidden":void 0!==document.webkitHidden&&(d="webkitHidden"),void window.addEventListener("focus",r.handleVisibilityChange,!1))},handleVisibilityChange:function(){!document[d]&&moment().diff(o)>1e4&&r.update()},tick:function(){var e=i.find(".timer-seconds"),n=Number(e.text());if(n<59)e.text(n+1);else{e.text(0);var t=i.find(".timer-minutes"),o=Number(t.text());if(o<59)t.text(o+1);else{t.text(0);var d=i.find(".timer-hours"),r=Number(d.text());d.text(r+1)}}},update:function(){e.get("/api/timers/"+t+"/",function(e){if(e&&"duration"in e){clearInterval(n);var t=moment.duration(e.duration);i.find(".timer-hours").text(t.hours()),i.find(".timer-minutes").text(t.minutes()),i.find(".timer-seconds").text(t.seconds()),o=moment(),e.active?n=setInterval(r.tick,1e3):i.addClass("timer-stopped")}})}};return r}(jQuery),BabyBuddy.Dashboard=function(e){var n=null,t={watch:function(i,o){if(0==e("#"+i).length)return console.error("Baby Buddy: Dashboard element not found."),!1;void 0!==document.hidden?n="hidden":void 0!==document.msHidden?n="msHidden":void 0!==document.webkitHidden&&(n="webkitHidden"),void 0===window.addEventListener||void 0===document.hidden?o&&setInterval(this.update,o):(window.addEventListener("focus",t.handleVisibilityChange,!1),o&&setInterval(t.handleVisibilityChange,o))},handleVisibilityChange:function(){document[n]||t.update()},update:function(){location.reload()}};return t}(jQuery);

Binary file not shown.

View File

@ -0,0 +1 @@
!function(){const t=document.querySelector('input[name="csrfmiddlewaretoken"]').value;function e(e,s,a,i,n){const o=new XMLHttpRequest;o.addEventListener("load",()=>{o.status>=200&&o.status<300?i(o.responseText,o):n(o.responseText,o)});for(const t of["error","timeout","abort"])o.addEventListener(t,()=>{n(o.responseText,o)});o.timeout=2e4,o.open(e,s),o.setRequestHeader("Content-Type","application/json"),o.setRequestHeader("Accept","application/json"),o.setRequestHeader("X-CSRFTOKEN",t),o.send(a)}class s{constructor(t){this.prototype=t.querySelector(".prototype-tag"),this.listeners=[],this.modalElement=t.querySelector(".tag-editor-error-modal"),this.modalBodyNode=this.modalElement.querySelector(".modal-body");for(const t of this.modalBodyNode.childNodes)t.nodeType===Node.TEXT_NODE&&this.modalBodyNode.removeChild(t)}showModal(t){const e=this.modalBodyNode.querySelector(`span[data-message='${t}']`);e||(e=this.modalBodyNode.childNodes[0]);for(const t of this.modalBodyNode.childNodes)t.classList.add("d-none");e.classList.remove("d-none"),jQuery(this.modalElement).modal("show")}addTagListUpdatedListener(t){this.listeners.push(t)}callTagListUpdatedListeners(){for(const t of this.listeners)t()}updateTag(t,e,s,a){const i=t.querySelector(".add-remove-icon").childNodes[0];e=e||t.getAttribute("data-value"),s=s||t.getAttribute("data-color"),a=a||i.textContent,t.childNodes[0].textContent=e,t.setAttribute("data-value",e),t.setAttribute("data-color",s);const n=("#"===(o=s).slice(0,1)&&(o=o.slice(1)),3===o.length&&(o=o.split("").map(function(t){return t+t}).join("")),(299*parseInt(o.substr(0,2),16)+587*parseInt(o.substr(2,2),16)+114*parseInt(o.substr(4,2),16))/1e3>=128?"#101010":"#EFEFEF");var o;t.setAttribute("style",`background-color: ${s}; color: ${n};`),i.textContent=a}createNewTag(t,e,s){const a=this.prototype.cloneNode(!0);return a.classList.remove("prototype-tag"),a.classList.add("tag"),this.updateTag(a,t,e,s),a}insertTag(t,e){t.appendChild(e),this.callTagListUpdatedListeners()}}class a{constructor(t,e,s){this.widget=t,this.taggingBase=e,this.apiTagsUrl=t.getAttribute("data-tags-url"),this.createTagInputs=t.querySelector(".create-tag-inputs"),this.addTagInput=this.createTagInputs.querySelector('input[type="text"]'),this.addTagButton=this.createTagInputs.querySelector("#add-tag"),this.addTagInput.value="",this.onInsertNewTag=s,this.addTagButton.addEventListener("click",()=>this.onCreateTagClicked()),this.addTagInput.addEventListener("keydown",t=>{"enter"===t.key.toLowerCase()&&(t.preventDefault(),this.onCreateTagClicked())})}onCreateTagClicked(){const t=this.addTagInput.value.trim(),s=encodeURIComponent(t),a=t=>{this.addTagInput.select(),this.taggingBase.showModal(t||"generic")};if(!t)return void a("invalid-tag-name");const i=(t,e)=>{const s=this.taggingBase.createNewTag(t,e,"-");this.addTagInput.value="",this.onInsertNewTag(s)},n=JSON.stringify({name:this.addTagInput.value});e("GET",`${this.apiTagsUrl}?name=${s}`,null,t=>{const s=JSON.parse(t);if(s.count){const t=s.results[0];i(t.name,t.color)}else e("POST",this.apiTagsUrl,n,t=>{const e=JSON.parse(t);i(e.name,e.color)},()=>a("tag-creation-failed"))},()=>a("tag-checking-failed"))}}class i{constructor(t){this.widget=t,this.taggingBase=new s(this.widget),this.addTagControl=new a(this.widget,this.taggingBase,t=>this.insertNewTag(t)),this.currentTags=this.widget.querySelector(".current_tags"),this.newTags=this.widget.querySelector(".new-tags"),this.inputElement=this.widget.querySelector('input[type="hidden"]');for(const t of this.newTags.querySelectorAll(".tag"))this.configureAddTag(t);for(const t of this.currentTags.querySelectorAll(".tag"))this.configureRemoveTag(t);this.updateInputList(),this.taggingBase.addTagListUpdatedListener(()=>this.updateInputList())}insertNewTag(t){const e=t.getAttribute("data-value"),s=this.widget.querySelector(`span[data-value="${e}"]`);s&&s.parentNode.removeChild(s),this.taggingBase.insertTag(this.currentTags,t),this.configureRemoveTag(t)}registerNewCallback(t,e,s){t.addEventListener("click",function a(i){t.parentNode.removeChild(t),this.taggingBase.insertTag(e,t),t.removeEventListener("click",a),s(t)}.bind(this))}updateInputList(){const t=[];for(const e of this.currentTags.querySelectorAll(".tag")){const s=e.getAttribute("data-value");t.push(`"${s}"`)}this.inputElement.value=t.join(",")}configureAddTag(t){this.taggingBase.updateTag(t,null,null,"+"),this.registerNewCallback(t,this.currentTags,()=>this.configureRemoveTag(t)),this.updateInputList()}configureRemoveTag(t){this.taggingBase.updateTag(t,null,null,"-"),this.registerNewCallback(t,this.newTags,()=>this.configureAddTag(t)),this.updateInputList()}}window.addEventListener("load",()=>{for(const t of document.querySelectorAll(".babybuddy-tags-editor"))new i(t)})}();

Binary file not shown.

View File

@ -1 +0,0 @@
!function(){const t=document.querySelector('input[name="csrfmiddlewaretoken"]').value;function e(e,s,a,i,n){const o=new XMLHttpRequest;o.addEventListener("load",()=>{o.status>=200&&o.status<300?i(o.responseText,o):n(o.responseText,o)});for(const t of["error","timeout","abort"])o.addEventListener(t,()=>{n(o.responseText,o)});o.timeout=2e4,o.open(e,s),o.setRequestHeader("Content-Type","application/json"),o.setRequestHeader("Accept","application/json"),o.setRequestHeader("X-CSRFTOKEN",t),o.send(a)}class s{constructor(t){this.prototype=t.querySelector(".prototype-tag"),this.listeners=[],this.modalElement=t.querySelector(".tag-editor-error-modal"),this.modalBodyNode=this.modalElement.querySelector(".modal-body");for(const t of this.modalBodyNode.childNodes)t.nodeType===Node.TEXT_NODE&&this.modalBodyNode.removeChild(t)}showModal(t){const e=this.modalBodyNode.querySelector(`span[data-message='${t}']`);e||(e=this.modalBodyNode.childNodes[0]);for(const t of this.modalBodyNode.childNodes)t.classList.add("d-none");e.classList.remove("d-none"),jQuery(this.modalElement).modal("show")}addTagListUpdatedListener(t){this.listeners.push(t)}callTagListUpdatedListeners(){for(const t of this.listeners)t()}updateTag(t,e,s,a){const i=t.querySelector(".add-remove-icon").childNodes[0];e=e||t.getAttribute("data-value"),s=s||t.getAttribute("data-color"),a=a||i.textContent,t.childNodes[0].textContent=e,t.setAttribute("data-value",e),t.setAttribute("data-color",s);const n=("#"===(o=s).slice(0,1)&&(o=o.slice(1)),3===o.length&&(o=o.split("").map(function(t){return t+t}).join("")),(299*parseInt(o.substr(0,2),16)+587*parseInt(o.substr(2,2),16)+114*parseInt(o.substr(4,2),16))/1e3>=128?"#101010":"#EFEFEF");var o;t.setAttribute("style",`background-color: ${s}; color: ${n};`),i.textContent=a}createNewTag(t,e,s){const a=this.prototype.cloneNode(!0);return a.classList.remove("prototype-tag"),a.classList.add("tag"),this.updateTag(a,t,e,s),a}insertTag(t,e){t.appendChild(e),this.callTagListUpdatedListeners()}}class a{constructor(t,e,s){this.widget=t,this.taggingBase=e,this.apiTagsUrl=t.getAttribute("data-tags-url"),this.createTagInputs=t.querySelector(".create-tag-inputs"),this.addTagInput=this.createTagInputs.querySelector('input[type="text"]'),this.addTagButton=this.createTagInputs.querySelector(".btn-add-new-tag"),this.addTagInput.value="",this.onInsertNewTag=s,this.addTagButton.addEventListener("click",()=>this.onCreateTagClicked()),this.addTagInput.addEventListener("keydown",t=>{"enter"===t.key.toLowerCase()&&(t.preventDefault(),this.onCreateTagClicked())})}onCreateTagClicked(){const t=this.addTagInput.value.trim(),s=encodeURIComponent(t),a=t=>{this.addTagInput.select(),this.taggingBase.showModal(t||"generic")};if(!t)return void a("invalid-tag-name");const i=(t,e)=>{const s=this.taggingBase.createNewTag(t,e,"-");this.addTagInput.value="",this.onInsertNewTag(s)},n=JSON.stringify({name:this.addTagInput.value});e("GET",`${this.apiTagsUrl}?name=${s}`,null,t=>{const s=JSON.parse(t);if(s.count){const t=s.results[0];i(t.name,t.color)}else e("POST",this.apiTagsUrl,n,t=>{const e=JSON.parse(t);i(e.name,e.color)},()=>a("tag-creation-failed"))},()=>a("tag-checking-failed"))}}class i{constructor(t){this.widget=t,this.taggingBase=new s(this.widget),this.addTagControl=new a(this.widget,this.taggingBase,t=>this.insertNewTag(t)),this.currentTags=this.widget.querySelector(".current_tags"),this.newTags=this.widget.querySelector(".new-tags"),this.inputElement=this.widget.querySelector('input[type="hidden"]');for(const t of this.newTags.querySelectorAll(".tag"))this.configureAddTag(t);for(const t of this.currentTags.querySelectorAll(".tag"))this.configureRemoveTag(t);this.updateInputList(),this.taggingBase.addTagListUpdatedListener(()=>this.updateInputList())}insertNewTag(t){const e=t.getAttribute("data-value"),s=this.widget.querySelector(`span[data-value="${e}"]`);s&&s.parentNode.removeChild(s),this.taggingBase.insertTag(this.currentTags,t),this.configureRemoveTag(t)}registerNewCallback(t,e,s){t.addEventListener("click",function a(i){t.parentNode.removeChild(t),this.taggingBase.insertTag(e,t),t.removeEventListener("click",a),s(t)}.bind(this))}updateInputList(){const t=[];for(const e of this.currentTags.querySelectorAll(".tag")){const s=e.getAttribute("data-value");t.push(`"${s}"`)}this.inputElement.value=t.join(",")}configureAddTag(t){this.taggingBase.updateTag(t,null,null,"+"),this.registerNewCallback(t,this.currentTags,()=>this.configureRemoveTag(t)),this.updateInputList()}configureRemoveTag(t){this.taggingBase.updateTag(t,null,null,"-"),this.registerNewCallback(t,this.newTags,()=>this.configureAddTag(t)),this.updateInputList()}}window.addEventListener("load",()=>{for(const t of document.querySelectorAll(".babybuddy-tags-editor"))new i(t)})}();

Binary file not shown.

View File

@ -1 +1 @@
!function(){const t=document.querySelector('input[name="csrfmiddlewaretoken"]').value;function e(e,s,a,i,n){const o=new XMLHttpRequest;o.addEventListener("load",()=>{o.status>=200&&o.status<300?i(o.responseText,o):n(o.responseText,o)});for(const t of["error","timeout","abort"])o.addEventListener(t,()=>{n(o.responseText,o)});o.timeout=2e4,o.open(e,s),o.setRequestHeader("Content-Type","application/json"),o.setRequestHeader("Accept","application/json"),o.setRequestHeader("X-CSRFTOKEN",t),o.send(a)}class s{constructor(t){this.prototype=t.querySelector(".prototype-tag"),this.listeners=[],this.modalElement=t.querySelector(".tag-editor-error-modal"),this.modalBodyNode=this.modalElement.querySelector(".modal-body");for(const t of this.modalBodyNode.childNodes)t.nodeType===Node.TEXT_NODE&&this.modalBodyNode.removeChild(t)}showModal(t){const e=this.modalBodyNode.querySelector(`span[data-message='${t}']`);e||(e=this.modalBodyNode.childNodes[0]);for(const t of this.modalBodyNode.childNodes)t.classList.add("d-none");e.classList.remove("d-none"),jQuery(this.modalElement).modal("show")}addTagListUpdatedListener(t){this.listeners.push(t)}callTagListUpdatedListeners(){for(const t of this.listeners)t()}updateTag(t,e,s,a){const i=t.querySelector(".add-remove-icon").childNodes[0];e=e||t.getAttribute("data-value"),s=s||t.getAttribute("data-color"),a=a||i.textContent,t.childNodes[0].textContent=e,t.setAttribute("data-value",e),t.setAttribute("data-color",s);const n=("#"===(o=s).slice(0,1)&&(o=o.slice(1)),3===o.length&&(o=o.split("").map(function(t){return t+t}).join("")),(299*parseInt(o.substr(0,2),16)+587*parseInt(o.substr(2,2),16)+114*parseInt(o.substr(4,2),16))/1e3>=128?"#101010":"#EFEFEF");var o;t.setAttribute("style",`background-color: ${s}; color: ${n};`),i.textContent=a}createNewTag(t,e,s){const a=this.prototype.cloneNode(!0);return a.classList.remove("prototype-tag"),a.classList.add("tag"),this.updateTag(a,t,e,s),a}insertTag(t,e){t.appendChild(e),this.callTagListUpdatedListeners()}}class a{constructor(t,e,s){this.widget=t,this.taggingBase=e,this.apiTagsUrl=t.getAttribute("data-tags-url"),this.createTagInputs=t.querySelector(".create-tag-inputs"),this.addTagInput=this.createTagInputs.querySelector('input[type="text"]'),this.addTagButton=this.createTagInputs.querySelector(".btn-add-new-tag"),this.addTagInput.value="",this.onInsertNewTag=s,this.addTagButton.addEventListener("click",()=>this.onCreateTagClicked()),this.addTagInput.addEventListener("keydown",t=>{"enter"===t.key.toLowerCase()&&(t.preventDefault(),this.onCreateTagClicked())})}onCreateTagClicked(){const t=this.addTagInput.value.trim(),s=encodeURIComponent(t),a=t=>{this.addTagInput.select(),this.taggingBase.showModal(t||"generic")};if(!t)return void a("invalid-tag-name");const i=(t,e)=>{const s=this.taggingBase.createNewTag(t,e,"-");this.addTagInput.value="",this.onInsertNewTag(s)},n=JSON.stringify({name:this.addTagInput.value});e("GET",`${this.apiTagsUrl}?name=${s}`,null,t=>{const s=JSON.parse(t);if(s.count){const t=s.results[0];i(t.name,t.color)}else e("POST",this.apiTagsUrl,n,t=>{const e=JSON.parse(t);i(e.name,e.color)},()=>a("tag-creation-failed"))},()=>a("tag-checking-failed"))}}class i{constructor(t){this.widget=t,this.taggingBase=new s(this.widget),this.addTagControl=new a(this.widget,this.taggingBase,t=>this.insertNewTag(t)),this.currentTags=this.widget.querySelector(".current_tags"),this.newTags=this.widget.querySelector(".new-tags"),this.inputElement=this.widget.querySelector('input[type="hidden"]');for(const t of this.newTags.querySelectorAll(".tag"))this.configureAddTag(t);for(const t of this.currentTags.querySelectorAll(".tag"))this.configureRemoveTag(t);this.updateInputList(),this.taggingBase.addTagListUpdatedListener(()=>this.updateInputList())}insertNewTag(t){const e=t.getAttribute("data-value"),s=this.widget.querySelector(`span[data-value="${e}"]`);s&&s.parentNode.removeChild(s),this.taggingBase.insertTag(this.currentTags,t),this.configureRemoveTag(t)}registerNewCallback(t,e,s){t.addEventListener("click",function a(i){t.parentNode.removeChild(t),this.taggingBase.insertTag(e,t),t.removeEventListener("click",a),s(t)}.bind(this))}updateInputList(){const t=[];for(const e of this.currentTags.querySelectorAll(".tag")){const s=e.getAttribute("data-value");t.push(`"${s}"`)}this.inputElement.value=t.join(",")}configureAddTag(t){this.taggingBase.updateTag(t,null,null,"+"),this.registerNewCallback(t,this.currentTags,()=>this.configureRemoveTag(t)),this.updateInputList()}configureRemoveTag(t){this.taggingBase.updateTag(t,null,null,"-"),this.registerNewCallback(t,this.newTags,()=>this.configureAddTag(t)),this.updateInputList()}}window.addEventListener("load",()=>{for(const t of document.querySelectorAll(".babybuddy-tags-editor"))new i(t)})}();
!function(){const t=document.querySelector('input[name="csrfmiddlewaretoken"]').value;function e(e,s,a,i,n){const o=new XMLHttpRequest;o.addEventListener("load",()=>{o.status>=200&&o.status<300?i(o.responseText,o):n(o.responseText,o)});for(const t of["error","timeout","abort"])o.addEventListener(t,()=>{n(o.responseText,o)});o.timeout=2e4,o.open(e,s),o.setRequestHeader("Content-Type","application/json"),o.setRequestHeader("Accept","application/json"),o.setRequestHeader("X-CSRFTOKEN",t),o.send(a)}class s{constructor(t){this.prototype=t.querySelector(".prototype-tag"),this.listeners=[],this.modalElement=t.querySelector(".tag-editor-error-modal"),this.modalBodyNode=this.modalElement.querySelector(".modal-body");for(const t of this.modalBodyNode.childNodes)t.nodeType===Node.TEXT_NODE&&this.modalBodyNode.removeChild(t)}showModal(t){const e=this.modalBodyNode.querySelector(`span[data-message='${t}']`);e||(e=this.modalBodyNode.childNodes[0]);for(const t of this.modalBodyNode.childNodes)t.classList.add("d-none");e.classList.remove("d-none"),jQuery(this.modalElement).modal("show")}addTagListUpdatedListener(t){this.listeners.push(t)}callTagListUpdatedListeners(){for(const t of this.listeners)t()}updateTag(t,e,s,a){const i=t.querySelector(".add-remove-icon").childNodes[0];e=e||t.getAttribute("data-value"),s=s||t.getAttribute("data-color"),a=a||i.textContent,t.childNodes[0].textContent=e,t.setAttribute("data-value",e),t.setAttribute("data-color",s);const n=("#"===(o=s).slice(0,1)&&(o=o.slice(1)),3===o.length&&(o=o.split("").map(function(t){return t+t}).join("")),(299*parseInt(o.substr(0,2),16)+587*parseInt(o.substr(2,2),16)+114*parseInt(o.substr(4,2),16))/1e3>=128?"#101010":"#EFEFEF");var o;t.setAttribute("style",`background-color: ${s}; color: ${n};`),i.textContent=a}createNewTag(t,e,s){const a=this.prototype.cloneNode(!0);return a.classList.remove("prototype-tag"),a.classList.add("tag"),this.updateTag(a,t,e,s),a}insertTag(t,e){t.appendChild(e),this.callTagListUpdatedListeners()}}class a{constructor(t,e,s){this.widget=t,this.taggingBase=e,this.apiTagsUrl=t.getAttribute("data-tags-url"),this.createTagInputs=t.querySelector(".create-tag-inputs"),this.addTagInput=this.createTagInputs.querySelector('input[type="text"]'),this.addTagButton=this.createTagInputs.querySelector("#add-tag"),this.addTagInput.value="",this.onInsertNewTag=s,this.addTagButton.addEventListener("click",()=>this.onCreateTagClicked()),this.addTagInput.addEventListener("keydown",t=>{"enter"===t.key.toLowerCase()&&(t.preventDefault(),this.onCreateTagClicked())})}onCreateTagClicked(){const t=this.addTagInput.value.trim(),s=encodeURIComponent(t),a=t=>{this.addTagInput.select(),this.taggingBase.showModal(t||"generic")};if(!t)return void a("invalid-tag-name");const i=(t,e)=>{const s=this.taggingBase.createNewTag(t,e,"-");this.addTagInput.value="",this.onInsertNewTag(s)},n=JSON.stringify({name:this.addTagInput.value});e("GET",`${this.apiTagsUrl}?name=${s}`,null,t=>{const s=JSON.parse(t);if(s.count){const t=s.results[0];i(t.name,t.color)}else e("POST",this.apiTagsUrl,n,t=>{const e=JSON.parse(t);i(e.name,e.color)},()=>a("tag-creation-failed"))},()=>a("tag-checking-failed"))}}class i{constructor(t){this.widget=t,this.taggingBase=new s(this.widget),this.addTagControl=new a(this.widget,this.taggingBase,t=>this.insertNewTag(t)),this.currentTags=this.widget.querySelector(".current_tags"),this.newTags=this.widget.querySelector(".new-tags"),this.inputElement=this.widget.querySelector('input[type="hidden"]');for(const t of this.newTags.querySelectorAll(".tag"))this.configureAddTag(t);for(const t of this.currentTags.querySelectorAll(".tag"))this.configureRemoveTag(t);this.updateInputList(),this.taggingBase.addTagListUpdatedListener(()=>this.updateInputList())}insertNewTag(t){const e=t.getAttribute("data-value"),s=this.widget.querySelector(`span[data-value="${e}"]`);s&&s.parentNode.removeChild(s),this.taggingBase.insertTag(this.currentTags,t),this.configureRemoveTag(t)}registerNewCallback(t,e,s){t.addEventListener("click",function a(i){t.parentNode.removeChild(t),this.taggingBase.insertTag(e,t),t.removeEventListener("click",a),s(t)}.bind(this))}updateInputList(){const t=[];for(const e of this.currentTags.querySelectorAll(".tag")){const s=e.getAttribute("data-value");t.push(`"${s}"`)}this.inputElement.value=t.join(",")}configureAddTag(t){this.taggingBase.updateTag(t,null,null,"+"),this.registerNewCallback(t,this.currentTags,()=>this.configureRemoveTag(t)),this.updateInputList()}configureRemoveTag(t){this.taggingBase.updateTag(t,null,null,"-"),this.registerNewCallback(t,this.newTags,()=>this.configureAddTag(t)),this.updateInputList()}}window.addEventListener("load",()=>{for(const t of document.querySelectorAll(".babybuddy-tags-editor"))new i(t)})}();

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More