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,27 +1,27 @@
{% load widget_tweaks %}
<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 %}">
{% if field|field_type != "booleanfield" %}
{{ 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 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" }}
{{ field|add_class:"btn-check is-invalid" }}
{% else %}
{{ field }}
{{ field|add_class:"btn-check" }}
{% endif %}
<label for="id_{{ field.name }}" class="btn btn-outline-light btn-no-hover{% if field.value %} active{% 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>
<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 %}
@ -30,9 +30,9 @@
</div>
{% elif 'choice' in field|field_type %}
{% if field.errors %}
{{ field|add_class:"custom-select is-invalid" }}
{{ field|add_class:"form-select is-invalid" }}
{% else %}
{{ field|add_class:"custom-select" }}
{{ field|add_class:"form-select" }}
{% endif %}
{% else %}
{% if field.errors %}
@ -48,3 +48,4 @@
<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,13 +3,15 @@
{% 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="">
<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 mr-auto p-0 ml-2">
<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' %}"
@ -25,23 +27,19 @@
&nbsp;
</div>
</div>
<div class="d-lg-none d-md-none d-flex ml-auto p-0 mr-2">
<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-toggle="dropdown"
data-bs-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">
<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>
@ -72,17 +70,18 @@
{% trans "Tummy Time" %}
</a>
{% endif %}
</div>
</div>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse"
</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">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar-app">
<ul class="navbar-nav mr-auto">
<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,7 +8,8 @@
{% endblock %}
{% block content %}
<div class="jumbotron">
<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%}
@ -17,7 +18,8 @@
{% endblocktrans %}
</p>
<hr class="my-4">
<div class="card-deck">
<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>
@ -26,6 +28,8 @@
<h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
</div>
</div>
</div>
<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>
@ -34,6 +38,8 @@
<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>
@ -42,6 +48,8 @@
<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>
@ -51,6 +59,7 @@
</div>
</div>
</div>
</div>
<hr class="my-4">
<p class="lead">
{% blocktrans trimmed %}
@ -70,4 +79,5 @@
{% endif %}
</p>
</div>
</div>
{% endblock %}

View File

@ -10,7 +10,8 @@
<div class="alert alert-danger" role="alert">
{{ main }} {{ reason }}
</div>
<div class="jumbotron">
<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>
@ -18,4 +19,5 @@
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 @@
#timer-status {
font-size: $h1-font-size;
font-weight: $display1-weight;
@use 'sass:map';
#timer-status {
&.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">
<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,13 +6,14 @@
{% 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">
<h1 {% if not object.active %}class="timer-stopped" {% endif %}id="timer-status">
<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
@ -36,8 +37,9 @@
{% endblocktrans %}
</p>
<div class="d-grid gap-4 mb-4">
{% if perms.core.add_feeding %}
<a class="btn btn-success btn-lg btn-block mb-3"
<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" %}
@ -45,7 +47,7 @@
{% endif %}
{% if perms.core.add_sleep %}
<a class="btn btn-success btn-lg btn-block mb-3"
<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" %}
@ -53,12 +55,13 @@
{% endif %}
{% if perms.core.add_tummytime %}
<a class="btn btn-success btn-lg btn-block mb-3"
<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 %}
@ -74,20 +77,21 @@
<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>
<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="sr-only">{% trans "Delete timer" %}</label>
<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 %}
{% block javascript %}

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,27 +5,51 @@
{% 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">
<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 %}
{% card_pumping_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 %}
{% block javascript %}

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',

2764
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,11 +12,13 @@
{% if html %}
{{ html|safe }}
{% else %}
<div class="jumbotron text-center">
<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>
{% endblock %}

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