mirror of https://github.com/snachodog/mybuddy.git
commit
8545fb360b
|
@ -25,4 +25,4 @@ runs:
|
||||||
run: pipenv graph
|
run: pipenv graph
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches:
|
||||||
|
- master
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches:
|
||||||
|
- master
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '33 0 * * 3'
|
- cron: '33 0 * * 3'
|
||||||
jobs:
|
jobs:
|
||||||
|
|
4
Pipfile
4
Pipfile
|
@ -6,7 +6,7 @@ name = "pypi"
|
||||||
[packages]
|
[packages]
|
||||||
boto3 = "*"
|
boto3 = "*"
|
||||||
dj-database-url = "*"
|
dj-database-url = "*"
|
||||||
django = ">=4.0.6"
|
django = "*"
|
||||||
django-axes = "*"
|
django-axes = "*"
|
||||||
django-filter = "*"
|
django-filter = "*"
|
||||||
django-imagekit = "*"
|
django-imagekit = "*"
|
||||||
|
@ -25,6 +25,7 @@ uritemplate = "*"
|
||||||
whitenoise = "*"
|
whitenoise = "*"
|
||||||
django-taggit = "*"
|
django-taggit = "*"
|
||||||
django-qr-code = "*"
|
django-qr-code = "*"
|
||||||
|
django-dbsettings = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
coveralls = "*"
|
coveralls = "*"
|
||||||
|
@ -33,3 +34,4 @@ mkdocs = "*"
|
||||||
mkdocs-material = "*"
|
mkdocs-material = "*"
|
||||||
tblib = "*"
|
tblib = "*"
|
||||||
black = "*"
|
black = "*"
|
||||||
|
pysnooper = "*"
|
||||||
|
|
|
@ -99,7 +99,7 @@ class TemperatureFilter(TimeFieldFilter, TagsFieldFilter):
|
||||||
class TimerFilter(StartEndFieldFilter):
|
class TimerFilter(StartEndFieldFilter):
|
||||||
class Meta(StartEndFieldFilter.Meta):
|
class Meta(StartEndFieldFilter.Meta):
|
||||||
model = models.Timer
|
model = models.Timer
|
||||||
fields = sorted(StartEndFieldFilter.Meta.fields + ["active", "user"])
|
fields = sorted(StartEndFieldFilter.Meta.fields + ["user"])
|
||||||
|
|
||||||
|
|
||||||
class TummyTimeFilter(StartEndFieldFilter, TagsFieldFilter):
|
class TummyTimeFilter(StartEndFieldFilter, TagsFieldFilter):
|
||||||
|
|
|
@ -77,16 +77,12 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
||||||
timer = attrs["timer"]
|
timer = attrs["timer"]
|
||||||
attrs.pop("timer")
|
attrs.pop("timer")
|
||||||
|
|
||||||
if timer.end:
|
|
||||||
end = timer.end
|
|
||||||
else:
|
|
||||||
end = timezone.now()
|
|
||||||
if timer.child:
|
if timer.child:
|
||||||
attrs["child"] = timer.child
|
attrs["child"] = timer.child
|
||||||
|
|
||||||
# Overwrites values provided directly!
|
# Overwrites values provided directly!
|
||||||
attrs["start"] = timer.start
|
attrs["start"] = timer.start
|
||||||
attrs["end"] = end
|
attrs["end"] = timezone.now()
|
||||||
|
|
||||||
# The "child", "start", and "end" field should all be set at this
|
# The "child", "start", and "end" field should all be set at this
|
||||||
# point. If one is not, model validation will fail because they are
|
# point. If one is not, model validation will fail because they are
|
||||||
|
@ -103,7 +99,7 @@ class CoreModelWithDurationSerializer(CoreModelSerializer):
|
||||||
|
|
||||||
# Only actually stop the timer if all validation passed.
|
# Only actually stop the timer if all validation passed.
|
||||||
if timer:
|
if timer:
|
||||||
timer.stop(attrs["end"])
|
timer.stop()
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
@ -232,10 +228,11 @@ class TimerSerializer(CoreModelSerializer):
|
||||||
queryset=get_user_model().objects.all(),
|
queryset=get_user_model().objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
duration = serializers.DurationField(read_only=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Timer
|
model = models.Timer
|
||||||
fields = ("id", "child", "name", "start", "end", "duration", "active", "user")
|
fields = ("id", "child", "name", "start", "duration", "user")
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
attrs = super(TimerSerializer, self).validate(attrs)
|
attrs = super(TimerSerializer, self).validate(attrs)
|
||||||
|
|
41
api/tests.py
41
api/tests.py
|
@ -47,7 +47,6 @@ class TestBase:
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
timer.refresh_from_db()
|
timer.refresh_from_db()
|
||||||
self.assertTrue(timer.active)
|
|
||||||
child = models.Child.objects.first()
|
child = models.Child.objects.first()
|
||||||
|
|
||||||
self.timer_test_data["child"] = child.id
|
self.timer_test_data["child"] = child.id
|
||||||
|
@ -55,11 +54,9 @@ class TestBase:
|
||||||
self.endpoint, self.timer_test_data, format="json"
|
self.endpoint, self.timer_test_data, format="json"
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
timer.refresh_from_db()
|
|
||||||
self.assertFalse(timer.active)
|
|
||||||
obj = self.model.objects.get(pk=response.data["id"])
|
obj = self.model.objects.get(pk=response.data["id"])
|
||||||
self.assertEqual(obj.start, start)
|
self.assertEqual(obj.start, start)
|
||||||
self.assertEqual(obj.end, timer.end)
|
self.assertIsNotNone(obj.end)
|
||||||
|
|
||||||
def test_post_with_timer_with_child(self):
|
def test_post_with_timer_with_child(self):
|
||||||
if not self.timer_test_data:
|
if not self.timer_test_data:
|
||||||
|
@ -73,12 +70,10 @@ class TestBase:
|
||||||
self.endpoint, self.timer_test_data, format="json"
|
self.endpoint, self.timer_test_data, format="json"
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
timer.refresh_from_db()
|
|
||||||
self.assertFalse(timer.active)
|
|
||||||
obj = self.model.objects.get(pk=response.data["id"])
|
obj = self.model.objects.get(pk=response.data["id"])
|
||||||
self.assertEqual(obj.child, timer.child)
|
self.assertIsNotNone(obj.child)
|
||||||
self.assertEqual(obj.start, start)
|
self.assertEqual(obj.start, start)
|
||||||
self.assertEqual(obj.end, timer.end)
|
self.assertIsNotNone(obj.end)
|
||||||
|
|
||||||
|
|
||||||
class BMIAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
class BMIAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||||
|
@ -703,19 +698,7 @@ class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
response = self.client.get(self.endpoint)
|
response = self.client.get(self.endpoint)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(
|
self.assertEqual(response.data["results"][0]["id"], 1)
|
||||||
response.data["results"][0],
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"child": None,
|
|
||||||
"name": "Fake timer",
|
|
||||||
"start": "2017-11-17T23:30:00-05:00",
|
|
||||||
"end": "2017-11-18T00:30:00-05:00",
|
|
||||||
"duration": "01:00:00",
|
|
||||||
"active": False,
|
|
||||||
"user": 1,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_post(self):
|
def test_post(self):
|
||||||
data = {"name": "New fake timer", "user": 1}
|
data = {"name": "New fake timer", "user": 1}
|
||||||
|
@ -743,31 +726,19 @@ class TimerAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data, entry)
|
self.assertEqual(response.data["name"], entry["name"])
|
||||||
|
|
||||||
def test_start_stop_timer(self):
|
def test_start_restart_timer(self):
|
||||||
endpoint = "{}{}/".format(self.endpoint, 1)
|
endpoint = "{}{}/".format(self.endpoint, 1)
|
||||||
response = self.client.get(endpoint)
|
response = self.client.get(endpoint)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertFalse(response.data["active"])
|
|
||||||
|
|
||||||
response = self.client.patch(f"{endpoint}restart/")
|
response = self.client.patch(f"{endpoint}restart/")
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertTrue(response.data["active"])
|
|
||||||
|
|
||||||
# Restart twice is allowed
|
# Restart twice is allowed
|
||||||
response = self.client.patch(f"{endpoint}restart/")
|
response = self.client.patch(f"{endpoint}restart/")
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertTrue(response.data["active"])
|
|
||||||
|
|
||||||
response = self.client.patch(f"{endpoint}stop/")
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertFalse(response.data["active"])
|
|
||||||
|
|
||||||
# Stopping twice is allowed, too
|
|
||||||
response = self.client.patch(f"{endpoint}stop/")
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertFalse(response.data["active"])
|
|
||||||
|
|
||||||
|
|
||||||
class TummyTimeAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
class TummyTimeAPITestCase(TestBase.BabyBuddyAPITestCaseBase):
|
||||||
|
|
|
@ -118,12 +118,6 @@ class TimerViewSet(viewsets.ModelViewSet):
|
||||||
ordering_fields = ("duration", "end", "start")
|
ordering_fields = ("duration", "end", "start")
|
||||||
ordering = "-start"
|
ordering = "-start"
|
||||||
|
|
||||||
@action(detail=True, methods=["patch"])
|
|
||||||
def stop(self, request, pk=None):
|
|
||||||
timer = self.get_object()
|
|
||||||
timer.stop()
|
|
||||||
return Response(self.serializer_class(timer).data)
|
|
||||||
|
|
||||||
@action(detail=True, methods=["patch"])
|
@action(detail=True, methods=["patch"])
|
||||||
def restart(self, request, pk=None):
|
def restart(self, request, pk=None):
|
||||||
timer = self.get_object()
|
timer = self.get_object()
|
||||||
|
|
|
@ -393,9 +393,6 @@
|
||||||
{
|
{
|
||||||
"name": "Fake timer",
|
"name": "Fake timer",
|
||||||
"start": "2017-11-18T04:30:00Z",
|
"start": "2017-11-18T04:30:00Z",
|
||||||
"end": "2017-11-18T05:30:00Z",
|
|
||||||
"duration": "01:00:00",
|
|
||||||
"active": false,
|
|
||||||
"user": 1
|
"user": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Add custom "short" version of `MONTH_DAY_FORMAT`. This customization will
|
||||||
|
# only work with the locale format locale specified by this file.
|
||||||
|
SHORT_MONTH_DAY_FORMAT = "M j"
|
|
@ -1,11 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from django.conf.locale.pt import formats
|
|
||||||
|
|
||||||
# Limit datetime input formats to those support by moment.
|
|
||||||
formats_supported = list(
|
|
||||||
filter(
|
|
||||||
lambda dt_format: not dt_format.startswith("%Y-%m-%d"),
|
|
||||||
formats.DATETIME_INPUT_FORMATS,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
DATETIME_INPUT_FORMATS = formats_supported
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from django.conf.locale.tr import formats
|
|
||||||
|
|
||||||
# Add formats supported by moment.
|
|
||||||
DATETIME_INPUT_FORMATS = [
|
|
||||||
"%d.%m.%Y %H:%M:%S",
|
|
||||||
"%d.%m.%Y %H:%M",
|
|
||||||
*formats.DATETIME_INPUT_FORMATS,
|
|
||||||
]
|
|
|
@ -5,68 +5,9 @@ import pytz
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
from django.conf.locale.en import formats as formats_en_us
|
|
||||||
from django.conf.locale.en_GB import formats as formats_en_gb
|
|
||||||
from django.contrib.auth.middleware import RemoteUserMiddleware
|
from django.contrib.auth.middleware import RemoteUserMiddleware
|
||||||
|
|
||||||
|
|
||||||
def update_en_us_date_formats():
|
|
||||||
"""
|
|
||||||
Update the datetime formats for the en-US locale. This is handled here and
|
|
||||||
not using `FORMAT_MODULE_PATH` because the processing of format modules
|
|
||||||
does not allow us to distinguish appropriately between en-US and en-GB
|
|
||||||
based on user settings.
|
|
||||||
"""
|
|
||||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
|
||||||
formats_en_us.DATETIME_FORMAT = "N j, Y, H:i:s"
|
|
||||||
custom_input_formats = [
|
|
||||||
"%m/%d/%Y %H:%M:%S", # '10/25/2006 14:30:59'
|
|
||||||
"%m/%d/%Y %H:%M", # '10/25/2006 14:30'
|
|
||||||
]
|
|
||||||
formats_en_us.SHORT_DATETIME_FORMAT = "m/d/Y G:i:s"
|
|
||||||
formats_en_us.TIME_FORMAT = "H:i:s"
|
|
||||||
else:
|
|
||||||
# These formats are added to support the locale style of Baby Buddy's
|
|
||||||
# frontend library, which uses momentjs.
|
|
||||||
custom_input_formats = [
|
|
||||||
"%m/%d/%Y %I:%M:%S %p", # '10/25/2006 2:30:59 PM'
|
|
||||||
"%m/%d/%Y %I:%M %p", # '10/25/2006 2:30 PM'
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add custom "short" version of `MONTH_DAY_FORMAT`.
|
|
||||||
formats_en_us.SHORT_MONTH_DAY_FORMAT = "M j"
|
|
||||||
|
|
||||||
# Append all other input formats from the base locale.
|
|
||||||
formats_en_us.DATETIME_INPUT_FORMATS = (
|
|
||||||
custom_input_formats + formats_en_us.DATETIME_INPUT_FORMATS
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def update_en_gb_date_formats():
|
|
||||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
|
||||||
# 25 October 2006 14:30:00
|
|
||||||
formats_en_gb.DATETIME_FORMAT = "j F Y H:i:s"
|
|
||||||
custom_input_formats = [
|
|
||||||
"%d/%m/%Y %H:%M:%S", # '25/10/2006 14:30:59'
|
|
||||||
"%d/%m/%Y %H:%M", # '25/10/2006 14:30'
|
|
||||||
]
|
|
||||||
formats_en_gb.SHORT_DATETIME_FORMAT = "d/m/Y H:i"
|
|
||||||
formats_en_gb.TIME_FORMAT = "H:i"
|
|
||||||
else:
|
|
||||||
formats_en_gb.DATETIME_FORMAT = "j F Y f a" # 25 October 2006 2:30 p.m
|
|
||||||
# These formats are added to support the locale style of Baby Buddy's
|
|
||||||
# frontend library, which uses momentjs.
|
|
||||||
custom_input_formats = [
|
|
||||||
"%d/%m/%Y %I:%M:%S %p", # '25/10/2006 2:30:59 PM'
|
|
||||||
"%d/%m/%Y %I:%M %p", # '25/10/2006 2:30 PM'
|
|
||||||
]
|
|
||||||
|
|
||||||
# Append all other input formats from the base locale.
|
|
||||||
formats_en_gb.DATETIME_INPUT_FORMATS = (
|
|
||||||
custom_input_formats + formats_en_gb.DATETIME_INPUT_FORMATS
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserLanguageMiddleware:
|
class UserLanguageMiddleware:
|
||||||
"""
|
"""
|
||||||
Customizes settings based on user language setting.
|
Customizes settings based on user language setting.
|
||||||
|
@ -85,11 +26,6 @@ class UserLanguageMiddleware:
|
||||||
language = settings.LANGUAGE_CODE
|
language = settings.LANGUAGE_CODE
|
||||||
|
|
||||||
if language:
|
if language:
|
||||||
if language == "en-US":
|
|
||||||
update_en_us_date_formats()
|
|
||||||
elif language == "en-GB":
|
|
||||||
update_en_gb_date_formats()
|
|
||||||
|
|
||||||
# Set the language before generating the response.
|
# Set the language before generating the response.
|
||||||
translation.activate(language)
|
translation.activate(language)
|
||||||
|
|
||||||
|
@ -104,10 +40,9 @@ class UserLanguageMiddleware:
|
||||||
|
|
||||||
class UserTimezoneMiddleware:
|
class UserTimezoneMiddleware:
|
||||||
"""
|
"""
|
||||||
Sets the timezone based on a user specific setting that falls back on
|
Sets the timezone based on a user specific setting. This middleware must run after
|
||||||
`settings.TIME_ZONE`. This middleware must run after
|
`django.contrib.auth.middleware.AuthenticationMiddleware` because it uses the
|
||||||
`django.contrib.auth.middleware.AuthenticationMiddleware` because it uses
|
request.user object.
|
||||||
the request.user object.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
|
|
|
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
||||||
"storages",
|
"storages",
|
||||||
"import_export",
|
"import_export",
|
||||||
"qr_code",
|
"qr_code",
|
||||||
|
"dbsettings",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
|
@ -78,7 +79,7 @@ ROOT_URLCONF = "babybuddy.urls"
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
"DIRS": ["babybuddy/templates/error"],
|
"DIRS": ["babybuddy/templates", "babybuddy/templates/error"],
|
||||||
"APP_DIRS": True,
|
"APP_DIRS": True,
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"context_processors": [
|
"context_processors": [
|
||||||
|
@ -158,7 +159,7 @@ if REVERSE_PROXY_AUTH:
|
||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
TIME_ZONE = os.environ.get("TIME_ZONE") or "UTC"
|
TIME_ZONE = "UTC"
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
||||||
|
@ -201,16 +202,6 @@ USE_L10N = True
|
||||||
|
|
||||||
FORMAT_MODULE_PATH = ["babybuddy.formats"]
|
FORMAT_MODULE_PATH = ["babybuddy.formats"]
|
||||||
|
|
||||||
# Custom setting that can be used to override the locale-based time set by
|
|
||||||
# USE_L10N _for specific locales_ to use 24-hour format. In order for this to
|
|
||||||
# work with a given locale it must be set at the FORMAT_MODULE_PATH with
|
|
||||||
# conditionals on this setting. See babybuddy/forms/en/formats.py for an example
|
|
||||||
# implementation for the English locale.
|
|
||||||
|
|
||||||
USE_24_HOUR_TIME_FORMAT = bool(
|
|
||||||
strtobool(os.environ.get("USE_24_HOUR_TIME_FORMAT") or "False")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||||
|
@ -364,7 +355,5 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
||||||
BABY_BUDDY = {
|
BABY_BUDDY = {
|
||||||
"ALLOW_UPLOADS": bool(strtobool(os.environ.get("ALLOW_UPLOADS") or "True")),
|
"ALLOW_UPLOADS": bool(strtobool(os.environ.get("ALLOW_UPLOADS") or "True")),
|
||||||
"NAP_START_MAX": os.environ.get("NAP_START_MAX") or "18:00",
|
|
||||||
"NAP_START_MIN": os.environ.get("NAP_START_MIN") or "06:00",
|
|
||||||
"READ_ONLY_GROUP_NAME": "read_only",
|
"READ_ONLY_GROUP_NAME": "read_only",
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from datetime import time
|
||||||
|
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
import dbsettings
|
||||||
|
|
||||||
|
from .widgets import TimeInput
|
||||||
|
|
||||||
|
|
||||||
|
class NapSettings(dbsettings.Group):
|
||||||
|
nap_start_min = dbsettings.TimeValue(
|
||||||
|
default=time(6),
|
||||||
|
description=_("Default minimum nap start time"),
|
||||||
|
help_text=_(
|
||||||
|
"The minimum default time that a sleep entry is consider a nap. If set the "
|
||||||
|
"nap property will be preselected if the start time is within the bounds."
|
||||||
|
),
|
||||||
|
widget=TimeInput,
|
||||||
|
)
|
||||||
|
nap_start_max = dbsettings.TimeValue(
|
||||||
|
default=time(18),
|
||||||
|
description=_("Default maximum nap start time"),
|
||||||
|
help_text=_(
|
||||||
|
"The maximum default time that a sleep entry is consider a nap. If set the "
|
||||||
|
"nap property will be preselected if the start time is within the bounds."
|
||||||
|
),
|
||||||
|
widget=TimeInput,
|
||||||
|
)
|
|
@ -1,9 +1,6 @@
|
||||||
if (typeof jQuery === 'undefined') {
|
if (typeof jQuery === 'undefined') {
|
||||||
throw new Error('Baby Buddy requires jQuery.')
|
throw new Error('Baby Buddy requires jQuery.')
|
||||||
}
|
}
|
||||||
if (typeof moment === 'undefined') {
|
|
||||||
throw new Error('Baby Buddy requires moment.js.')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baby Buddy Namespace
|
* Baby Buddy Namespace
|
||||||
|
@ -16,42 +13,6 @@ var BabyBuddy = function () {
|
||||||
return {};
|
return {};
|
||||||
}();
|
}();
|
||||||
|
|
||||||
/**
|
|
||||||
* Datetime Picker.
|
|
||||||
*
|
|
||||||
* Provides modifications and defaults for the base datetime picker widget.
|
|
||||||
*
|
|
||||||
* @type {{init: BabyBuddy.DatetimePicker.init}}
|
|
||||||
*/
|
|
||||||
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'
|
|
||||||
},
|
|
||||||
viewMode: 'times',
|
|
||||||
};
|
|
||||||
element.datetimepicker($.extend(defaultOptions, options));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}(jQuery, moment);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pull to refresh.
|
* Pull to refresh.
|
||||||
*
|
*
|
||||||
|
|
|
@ -174,7 +174,7 @@
|
||||||
this.apiTagsUrl = widget.getAttribute('data-tags-url');
|
this.apiTagsUrl = widget.getAttribute('data-tags-url');
|
||||||
this.createTagInputs = widget.querySelector('.create-tag-inputs');
|
this.createTagInputs = widget.querySelector('.create-tag-inputs');
|
||||||
this.addTagInput = this.createTagInputs.querySelector('input[type="text"]');
|
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 = "";
|
this.addTagInput.value = "";
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@import '../../../node_modules/bootstrap/scss/functions';
|
|
||||||
|
|
||||||
// Baby Buddy site-wide custom functions.
|
|
|
@ -1,3 +0,0 @@
|
||||||
@import '../../../node_modules/bootstrap/scss/mixins';
|
|
||||||
|
|
||||||
// Baby Buddy site-wide custom mixins.
|
|
|
@ -1,5 +1,136 @@
|
||||||
@import 'functions';
|
@use 'sass:color';
|
||||||
@import '../../../node_modules/bootstrap/scss/variables';
|
|
||||||
@import 'themes/blueorange';
|
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
@import 'functions';
|
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
@import 'mixins';
|
|
||||||
@import '../../../node_modules/bootstrap/scss/bootstrap';
|
@import '../../../node_modules/bootstrap/scss/bootstrap';
|
||||||
@import '../../../node_modules/tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4';
|
|
||||||
|
|
||||||
@import '../../../**/static_src/scss/*';
|
@import '../../../**/static_src/scss/*';
|
||||||
@import '../fontello/css/babybuddy';
|
@import '../fontello/css/babybuddy';
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use 'sass:map';
|
||||||
|
|
||||||
// Baby Buddy form style customizations.
|
// Baby Buddy form style customizations.
|
||||||
|
|
||||||
// BB form fields do not follow typical BS4 style that enables this display.
|
// BB form fields do not follow typical BS4 style that enables this display.
|
||||||
|
@ -18,40 +20,6 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bootstrap-datetimepicker-widget {
|
|
||||||
// Set default text color for datetime picker.
|
|
||||||
color: $gray-400;
|
|
||||||
|
|
||||||
// Ensure widget appears above other elements (e.g. .submit-primary).
|
|
||||||
z-index: 1050;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Datetime picker input styles.
|
|
||||||
.input-group {
|
|
||||||
&.datetimepicker {
|
|
||||||
|
|
||||||
// Make calendar icon large and use the primary theme color.
|
|
||||||
.input-group-text {
|
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
color: theme-color('primary');
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Style readonly input to look less like an input element.
|
|
||||||
.datetimepicker-input {
|
|
||||||
&[readonly] {
|
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
color: $white;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a full width, fixed button on smaller screens.
|
// Use a full width, fixed button on smaller screens.
|
||||||
.submit-primary {
|
.submit-primary {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use 'sass:map';
|
||||||
|
|
||||||
// Baby Buddy site-wide custom styles.
|
// Baby Buddy site-wide custom styles.
|
||||||
|
|
||||||
// Remove extra margin below site breadcrumb.
|
// Remove extra margin below site breadcrumb.
|
||||||
|
@ -7,7 +9,7 @@
|
||||||
|
|
||||||
// Extra-small button.
|
// Extra-small button.
|
||||||
.btn-xs {
|
.btn-xs {
|
||||||
@include button-size(.2rem, .12rem, .75rem, 1, .2rem);
|
@include button-size(.2rem, .12rem, .75rem, .2rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right align main dropdown menu.
|
// Right align main dropdown menu.
|
||||||
|
@ -18,33 +20,10 @@
|
||||||
|
|
||||||
// PullToRefresh elements.
|
// PullToRefresh elements.
|
||||||
.ptr--ptr {
|
.ptr--ptr {
|
||||||
background: theme-color('dark');
|
background: map.get($theme-colors, 'dark');
|
||||||
.ptr--text, .ptr--icon {
|
.ptr--text, .ptr--icon {
|
||||||
// "!important" must be used to override inline styling from JS.
|
// "!important" must be used to override inline styling from JS.
|
||||||
color: theme-color('light') !important;
|
color: map.get($theme-colors, '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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,5 +34,5 @@
|
||||||
|
|
||||||
// All modals
|
// All modals
|
||||||
.modal-content {
|
.modal-content {
|
||||||
color: theme-color('dark');
|
color: map.get($theme-colors, 'dark');
|
||||||
}
|
}
|
|
@ -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;
|
|
|
@ -42,8 +42,6 @@
|
||||||
{% block page %}{% endblock %}
|
{% block page %}{% endblock %}
|
||||||
|
|
||||||
<script src="{% static "babybuddy/js/vendor.js" %}"></script>
|
<script src="{% static "babybuddy/js/vendor.js" %}"></script>
|
||||||
<script>moment.locale('{{ LOCALE }}');</script>
|
|
||||||
<script>moment.tz.setDefault('{{ TIMEZONE }}');</script>
|
|
||||||
<script src="{% static "babybuddy/js/app.js" %}"></script>
|
<script src="{% static "babybuddy/js/app.js" %}"></script>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<script>BabyBuddy.PullToRefresh.init()</script>
|
<script>BabyBuddy.PullToRefresh.init()</script>
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="col-xs-10 col-sm-auto">
|
<div class="col-xs-10 col-sm-auto">
|
||||||
{% if 'choice' or 'boolean' in field|field_type %}
|
{% 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 %}
|
{% else %}
|
||||||
{{ field|add_class:"form-control form-control-sm" }}
|
{{ field|add_class:"form-control form-control-sm" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="col-xs-12 col-sm-auto mt-3 mt-sm-0">
|
<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>
|
<a href="{{ request.path }}" class="btn btn-sm btn-error">{% trans "Reset" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-dark btn-sm"
|
<a class="btn btn-dark btn-sm"
|
||||||
data-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
href="#filter_form"
|
href="#filter_form"
|
||||||
role="button"
|
role="button"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
{{ field.widget }}
|
{{ field.widget }}
|
||||||
<div class="form-group row">
|
<div class="row">
|
||||||
{% include 'babybuddy/form_field.html' %}
|
{% include 'babybuddy/form_field.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,38 +1,24 @@
|
||||||
{% load widget_tweaks %}
|
{% 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 %}">
|
<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 }}
|
{{ field.label }}
|
||||||
{% endif %}
|
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10{% if field|widget_type == 'childradioselect' %} overflow-auto"{% endif %}">
|
<div class="col-sm-10{% if field|widget_type == 'childradioselect' %} overflow-auto"{% endif %}">
|
||||||
{% if field|field_type == "booleanfield" %}
|
{% 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 %}
|
{% if field.errors %}
|
||||||
{{ field|add_class:"is-invalid" }}
|
{{ field|add_class:"btn-check is-invalid" }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ field }}
|
{{ field|add_class:"btn-check" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<label for="id_{{ field.name }}" class="btn btn-outline-light btn-no-hover{% if field.value %} active{% endif %}">
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
</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>
|
|
||||||
{% if field.errors %}
|
|
||||||
{{ field|add_class:"datetimepicker-input form-control form-control-lg is-invalid" }}
|
|
||||||
{% else %}
|
|
||||||
{{ field|add_class:"datetimepicker-input form-control form-control-lg" }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% elif 'choice' in field|field_type %}
|
{% elif 'choice' in field|field_type %}
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
{{ field|add_class:"custom-select is-invalid" }}
|
{{ field|add_class:"form-select is-invalid" }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ field|add_class:"custom-select" }}
|
{{ field|add_class:"form-select" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
|
@ -48,3 +34,4 @@
|
||||||
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
|
<div class="invalid-feedback">{% for error in field.errors %}{{ error }}{% endfor %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert{% if message.tags %} alert-{{ message.tags }}{% endif %} alert-dismissible fade show" role="alert">
|
<div class="alert{% if message.tags %} alert-{{ message.tags }}{% endif %} alert-dismissible fade show" role="alert">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
|
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark sticky-top">
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark sticky-top">
|
||||||
<a class="navbar-brand mr-2" href={% url "babybuddy:root-router" %}>
|
<div class="container-fluid">
|
||||||
<img src="{% static "babybuddy/logo/icon-brand.png" %}" width="30" height="30" class="d-inline-block align-top" alt="">
|
<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="d-none d-lg-inline-block">
|
||||||
<span class="text-primary">Baby</span> Buddy
|
<span class="text-primary">Baby</span> Buddy
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</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>
|
<div>
|
||||||
<a class="text-muted"
|
<a class="text-muted"
|
||||||
href="{% url 'dashboard:dashboard' %}"
|
href="{% url 'dashboard:dashboard' %}"
|
||||||
|
@ -25,23 +27,19 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-lg-none d-md-none d-flex ms-auto p-0 me-2">
|
||||||
|
|
||||||
<div class="d-lg-none d-md-none d-flex ml-auto p-0 mr-2">
|
|
||||||
|
|
||||||
{% quick_timer_nav %}
|
{% quick_timer_nav %}
|
||||||
|
|
||||||
<div class="dropdown show">
|
<div class="dropdown show">
|
||||||
<a class="text-success"
|
<a class="text-success"
|
||||||
href="#"
|
href="#"
|
||||||
role="button"
|
role="button"
|
||||||
id="nav-quick-add-link"
|
id="nav-quick-add-link"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="icon-2x icon-add" aria-hidden="true"></i>
|
aria-expanded="false"><i class="icon-2x icon-add" aria-hidden="true"></i>
|
||||||
</a>
|
</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 %}
|
{% if perms.core.add_diaperchange %}
|
||||||
<a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}">
|
<a class="dropdown-item p-2" href="{% url 'core:diaperchange-add' %}">
|
||||||
<i class="icon-diaperchange" aria-hidden="true"></i>
|
<i class="icon-diaperchange" aria-hidden="true"></i>
|
||||||
|
@ -72,17 +70,18 @@
|
||||||
{% trans "Tummy Time" %}
|
{% trans "Tummy Time" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse"
|
|
||||||
data-target="#navbar-app" aria-controls="navbar-app"
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbar-app" aria-controls="navbar-app"
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbar-app">
|
<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 %}">
|
<li class="nav-item{% if request.path == '/' %} active{% endif %}">
|
||||||
<a class="nav-link" href="{% url 'dashboard:dashboard' %}">
|
<a class="nav-link" href="{% url 'dashboard:dashboard' %}">
|
||||||
<i class="icon-dashboard" aria-hidden="true"></i>
|
<i class="icon-dashboard" aria-hidden="true"></i>
|
||||||
|
@ -101,7 +100,7 @@
|
||||||
<a id="nav-children-menu-link"
|
<a id="nav-children-menu-link"
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
href="#"
|
href="#"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="icon-child" aria-hidden="true"></i>
|
aria-expanded="false"><i class="icon-child" aria-hidden="true"></i>
|
||||||
{% trans "Children" %}
|
{% trans "Children" %}
|
||||||
|
@ -116,7 +115,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_child %}
|
{% 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>
|
href="{% url 'core:child-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Child" %}
|
{% trans "Child" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -130,7 +129,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_note %}
|
{% 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>
|
href="{% url 'core:note-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Note" %}
|
{% trans "Note" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -143,7 +142,7 @@
|
||||||
<a id="nav-measurements-menu-link"
|
<a id="nav-measurements-menu-link"
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
href="#"
|
href="#"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="icon-measurements" aria-hidden="true"></i>
|
aria-expanded="false"><i class="icon-measurements" aria-hidden="true"></i>
|
||||||
{% trans "Measurements" %}
|
{% trans "Measurements" %}
|
||||||
|
@ -158,7 +157,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_bmi %}
|
{% 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>
|
href="{% url 'core:bmi-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "BMI entry" %}
|
{% trans "BMI entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -172,7 +171,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_head_circumference %}
|
{% 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>
|
href="{% url 'core:head-circumference-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Head Circumference entry" %}
|
{% trans "Head Circumference entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -186,7 +185,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_height %}
|
{% 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>
|
href="{% url 'core:height-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Height entry" %}
|
{% trans "Height entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -200,7 +199,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_temperature %}
|
{% if perms.core.add_temperature %}
|
||||||
<a class="dropdown-item pl-5{% if request.path == '/temperature/add/' %} active{% endif %}"
|
<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>
|
href="{% url 'core:temperature-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Temperature reading" %}
|
{% trans "Temperature reading" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -214,7 +213,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_weight %}
|
{% if perms.core.add_weight %}
|
||||||
<a class="dropdown-item pl-5{% if request.path == '/weight/add/' %} active{% endif %}"
|
<a class="dropdown-item ps-5{% if request.path == '/weight/add/' %} active{% endif %}"
|
||||||
href="{% url 'core:weight-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
href="{% url 'core:weight-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Weight entry" %}
|
{% trans "Weight entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -222,12 +221,11 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a id="nav-activity-menu-link"
|
<a id="nav-activity-menu-link"
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
href="#"
|
href="#"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="icon-activities" aria-hidden="true"></i>
|
aria-expanded="false"><i class="icon-activities" aria-hidden="true"></i>
|
||||||
{% trans "Activities" %}
|
{% trans "Activities" %}
|
||||||
|
@ -236,12 +234,13 @@
|
||||||
|
|
||||||
{% if perms.core.view_diaperchange %}
|
{% if perms.core.view_diaperchange %}
|
||||||
<a class="dropdown-item{% if request.path == '/changes/' %} active{% endif %}"
|
<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" %}
|
{% trans "Changes" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_diaperchange %}
|
{% 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>
|
href="{% url 'core:diaperchange-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Change" %}
|
{% trans "Change" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -254,7 +253,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_feeding %}
|
{% 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>
|
href="{% url 'core:feeding-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Feeding" %}
|
{% trans "Feeding" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -268,7 +267,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_pumping %}
|
{% 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>
|
href="{% url 'core:pumping-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Pumping entry" %}
|
{% trans "Pumping entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -281,7 +280,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_sleep %}
|
{% 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>
|
href="{% url 'core:sleep-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Sleep entry" %}
|
{% trans "Sleep entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -289,12 +288,13 @@
|
||||||
|
|
||||||
{% if perms.core.view_tummytime %}
|
{% if perms.core.view_tummytime %}
|
||||||
<a class="dropdown-item{% if request.path == '/tummy-time/' %} active{% endif %}"
|
<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" %}
|
{% trans "Tummy Time" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.core.add_tummytime %}
|
{% 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>
|
href="{% url 'core:tummytime-add' %}"><i class="icon-add" aria-hidden="true"></i>
|
||||||
{% trans "Tummy Time entry" %}
|
{% trans "Tummy Time entry" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -308,21 +308,23 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{% if request.user %}
|
{% if request.user %}
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ms-auto">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a id="nav-user-menu-link"
|
<a id="nav-user-menu-link"
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
href="#"
|
href="#"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false">
|
aria-expanded="false">
|
||||||
<i class="icon-user" aria-hidden="true"></i>
|
<i class="icon-user" aria-hidden="true"></i>
|
||||||
{% firstof user.get_full_name user.get_username %}
|
{% firstof user.get_full_name user.get_username %}
|
||||||
</a>
|
</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>
|
<h6 class="dropdown-header">{% trans "User" %}</h6>
|
||||||
<a href="{% url 'babybuddy:user-settings' %}" class="dropdown-item">{% trans "Settings" %}</a>
|
<a href="{% url 'babybuddy:user-settings' %}"
|
||||||
<a href="{% url 'babybuddy:user-password' %}" class="dropdown-item">{% trans "Password" %}</a>
|
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>
|
<a href="{% url 'babybuddy:user-add-device' %}" class="dropdown-item">{% trans "Add a device" %}</a>
|
||||||
<form action="{% url 'babybuddy:logout' %}" role="form" method="post">
|
<form action="{% url 'babybuddy:logout' %}" role="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -333,8 +335,12 @@
|
||||||
<h6 class="dropdown-header">{% trans "Site" %}</h6>
|
<h6 class="dropdown-header">{% trans "Site" %}</h6>
|
||||||
<a href="{% url 'api:api-root' %}" class="dropdown-item">{% trans "API Browser" %}</a>
|
<a href="{% url 'api:api-root' %}" class="dropdown-item">{% trans "API Browser" %}</a>
|
||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
<a href="{% url 'babybuddy:user-list' %}" class="dropdown-item">{% trans "Users" %}</a>
|
<a href="{% url 'babybuddy:site_settings' %}"
|
||||||
<a href="{% url 'admin:index' %}" class="dropdown-item">{% trans "Database Admin" %}</a>
|
class="dropdown-item">{% trans "Settings" %}</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 %}
|
{% endif %}
|
||||||
<h6 class="dropdown-header">{% trans "Support" %}</h6>
|
<h6 class="dropdown-header">{% trans "Support" %}</h6>
|
||||||
<a href="https://github.com/babybuddy/babybuddy" class="dropdown-item">
|
<a href="https://github.com/babybuddy/babybuddy" class="dropdown-item">
|
||||||
|
@ -347,5 +353,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link" href="{% relative_url 'page' page_obj.previous_page_number %}" aria-label="Previous">
|
<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>
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link" href="{% relative_url 'page' page_obj.next_page_number %}" aria-label="Next">
|
<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>
|
<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>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">{% trans "Users" %}</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'babybuddy:user-list' %}">{% trans "Users" %}</a></li>
|
||||||
{% if object %}
|
{% 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>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="breadcrumb-item active" aria-current="page">{% trans "Create User" %}</li>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Create User" %}</li>
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<h1>Users</h1>
|
<h1>Users</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover user-list">
|
<table class="table table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "User" %}</th>
|
<th>{% trans "User" %}</th>
|
||||||
<th>{% trans "First Name" %}</th>
|
<th>{% trans "First Name" %}</th>
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% 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>
|
<h1 class="display-3">{% trans "Welcome to Baby Buddy!" %}</h1>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
{% blocktrans trimmed%}
|
{% blocktrans trimmed%}
|
||||||
|
@ -17,7 +18,8 @@
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
<hr class="my-4">
|
<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 card-diaperchange">
|
||||||
<div class="card-header text-center">
|
<div class="card-header text-center">
|
||||||
<i class="icon-2x icon-diaperchange" aria-hidden="true"></i>
|
<i class="icon-2x icon-diaperchange" aria-hidden="true"></i>
|
||||||
|
@ -26,6 +28,8 @@
|
||||||
<h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
|
<h3 class="card-title text-center">{% trans "Diaper Changes" %}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="card card-feeding">
|
<div class="card card-feeding">
|
||||||
<div class="card-header text-center">
|
<div class="card-header text-center">
|
||||||
<i class="icon-2x icon-feeding" aria-hidden="true"></i>
|
<i class="icon-2x icon-feeding" aria-hidden="true"></i>
|
||||||
|
@ -34,6 +38,8 @@
|
||||||
<h3 class="card-title text-center">{% trans "Feedings" %}</h3>
|
<h3 class="card-title text-center">{% trans "Feedings" %}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="card card-sleep">
|
<div class="card card-sleep">
|
||||||
<div class="card-header text-center">
|
<div class="card-header text-center">
|
||||||
<i class="icon-2x icon-sleep" aria-hidden="true"></i>
|
<i class="icon-2x icon-sleep" aria-hidden="true"></i>
|
||||||
|
@ -42,6 +48,8 @@
|
||||||
<h3 class="card-title text-center">{% trans "Sleep" %}</h3>
|
<h3 class="card-title text-center">{% trans "Sleep" %}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="card card-tummytime">
|
<div class="card card-tummytime">
|
||||||
<div class="card-header text-center">
|
<div class="card-header text-center">
|
||||||
<i class="icon-2x icon-tummytime" aria-hidden="true"></i>
|
<i class="icon-2x icon-tummytime" aria-hidden="true"></i>
|
||||||
|
@ -51,6 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
|
@ -70,4 +79,5 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends 'babybuddy/page.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Site Settings" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<li class="breadcrumb-item">{% trans "Site" %}</li>
|
||||||
|
<li class="breadcrumb-item active">{% trans "Settings" %}</li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% trans "Site Settings" %}</h1>
|
||||||
|
{% include 'babybuddy/form.html' %}
|
||||||
|
{% endblock %}
|
|
@ -10,7 +10,8 @@
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
{{ main }} {{ reason }}
|
{{ main }} {{ reason }}
|
||||||
</div>
|
</div>
|
||||||
<div class="jumbotron">
|
<div class="px-2 py-5 bg-dark rounded-3">
|
||||||
|
<div class="container-fluid">
|
||||||
<h2>{% trans "How to Fix" %}</h2>
|
<h2>{% trans "How to Fix" %}</h2>
|
||||||
{% blocktrans trimmed with origin=origin %}
|
{% blocktrans trimmed with origin=origin %}
|
||||||
Add <samp>{{ origin }}</samp> to the <code>CSRF_TRUSTED_ORIGINS</code>
|
Add <samp>{{ origin }}</samp> to the <code>CSRF_TRUSTED_ORIGINS</code>
|
||||||
|
@ -18,4 +19,5 @@
|
||||||
with commas.
|
with commas.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-md-center">
|
||||||
<div class="col-lg-12 mb-4">
|
<div class="col-lg-12 mb-4">
|
||||||
<div class="d-sm-flex">
|
<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">
|
<div class="p-2 flex-grow-1">
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
<a href="{% url "babybuddy:root-router" %}" class="btn btn-outline-primary mt-3">{% trans "Return to Baby Buddy" %}</a>
|
<a href="{% url "babybuddy:root-router" %}" class="btn btn-outline-primary mt-3">{% trans "Return to Baby Buddy" %}</a>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div id="view-{{ request.resolver_match.view_name }}" class="container">
|
<div id="view-{{ request.resolver_match.view_name }}" class="container">
|
||||||
<div class="text-center pt-3">
|
<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="">
|
<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
|
<span class="text-primary">Baby</span> Buddy
|
||||||
</h1>
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -8,23 +8,19 @@
|
||||||
|
|
||||||
<input type="hidden" name="next" value="{{ next }}">
|
<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 }}
|
{{ form.username.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group mb-3 fade-in">
|
<div class="input-group mb-3 fade-in">
|
||||||
<div class="input-group-prepend">
|
<span class="input-group-text text-muted"><i class="icon-user" aria-hidden="true"></i></span>
|
||||||
<span class="input-group-text"><i class="icon-user" aria-hidden="true"></i></span>
|
|
||||||
</div>
|
|
||||||
{% render_field form.username name='username' class+='form-control' id='username-input-group' placeholder=form.username.label %}
|
{% render_field form.username name='username' class+='form-control' id='username-input-group' placeholder=form.username.label %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="sr-only" for="password-input-group">
|
<label class="visually-hidden" for="password-input-group">
|
||||||
{{ form.password.label }}
|
{{ form.password.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group mb-3 fade-in">
|
<div class="input-group mb-3 fade-in">
|
||||||
<div class="input-group-prepend">
|
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
|
||||||
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
|
|
||||||
</div>
|
|
||||||
{% render_field form.password name='password' class+='form-control' id='password-input-group' placeholder=form.password.label %}
|
{% render_field form.password name='password' class+='form-control' id='password-input-group' placeholder=form.password.label %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -22,23 +22,19 @@
|
||||||
<p class="mb-0">{% trans "Enter your new password in each field below." %}</p>
|
<p class="mb-0">{% trans "Enter your new password in each field below." %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="sr-only" for="password1-input-group">
|
<label class="visually-hidden" for="password1-input-group">
|
||||||
{{ form.new_password1.label }}
|
{{ form.new_password1.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group mb-3 fade-in">
|
<div class="input-group mb-3 fade-in">
|
||||||
<div class="input-group-prepend">
|
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
|
||||||
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
|
|
||||||
</div>
|
|
||||||
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
|
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="sr-only" for="password2-input-group">
|
<label class="visually-hidden" for="password2-input-group">
|
||||||
{{ form.new_password2.label }}
|
{{ form.new_password2.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group mb-3 fade-in">
|
<div class="input-group mb-3 fade-in">
|
||||||
<div class="input-group-prepend">
|
<span class="input-group-text text-muted"><i class="icon-lock" aria-hidden="true"></i></span>
|
||||||
<span class="input-group-text"><i class="icon-lock" aria-hidden="true"></i></span>
|
|
||||||
</div>
|
|
||||||
{% render_field form.new_password2 name='new_password2' class+='form-control' id='password2-input-group' %}
|
{% render_field form.new_password2 name='new_password2' class+='form-control' id='password2-input-group' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,11 @@
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<label class="sr-only" for="email-input-group">
|
<label class="visually-hidden" for="email-input-group">
|
||||||
{{ form.email.label }}
|
{{ form.email.label }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group mb-3 fade-in">
|
<div class="input-group mb-3 fade-in">
|
||||||
<div class="input-group-prepend">
|
<span class="input-group-text text-muted"><i class="icon-mail" aria-hidden="true"></i></span>
|
||||||
<span class="input-group-text"><i class="icon-mail" aria-hidden="true"></i></span>
|
|
||||||
</div>
|
|
||||||
{% render_field form.email name='email' class+='form-control' id='email-input-group' placeholder=form.email.label %}
|
{% render_field form.email name='email' class+='form-control' id='email-input-group' placeholder=form.email.label %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.formats import date_format
|
||||||
|
|
||||||
|
|
||||||
|
class FormatsTestCase(TestCase):
|
||||||
|
def test_short_month_day_format(self):
|
||||||
|
dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5, second=5)
|
||||||
|
self.assertEqual(date_format(dt, "SHORT_MONTH_DAY_FORMAT"), "Jul 31")
|
|
@ -1,70 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.forms.fields import DateTimeField
|
|
||||||
from django.test import TestCase, override_settings # , tag
|
|
||||||
from django.utils.formats import date_format, time_format
|
|
||||||
|
|
||||||
from babybuddy.middleware import update_en_gb_date_formats
|
|
||||||
|
|
||||||
|
|
||||||
class GbFormatsTestCase(TestCase):
|
|
||||||
@override_settings(LANGUAGE_CODE="en-GB")
|
|
||||||
def test_datetime_input_formats(self):
|
|
||||||
update_en_gb_date_formats()
|
|
||||||
field = DateTimeField()
|
|
||||||
supported_custom_examples = [
|
|
||||||
"20/01/2020",
|
|
||||||
"20/01/2020 9:30 AM",
|
|
||||||
"20/01/2020 9:30:03 AM",
|
|
||||||
"01/10/2020 11:30 PM",
|
|
||||||
"01/10/2020 11:30:03 AM",
|
|
||||||
]
|
|
||||||
|
|
||||||
for example in supported_custom_examples:
|
|
||||||
try:
|
|
||||||
result = field.to_python(example)
|
|
||||||
self.assertIsInstance(result, datetime.datetime)
|
|
||||||
except ValidationError:
|
|
||||||
self.fail('Format of "{}" not recognized!'.format(example))
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
field.to_python("invalid date string!")
|
|
||||||
|
|
||||||
# @tag('isolate')
|
|
||||||
@override_settings(LANGUAGE_CODE="en-GB", USE_24_HOUR_TIME_FORMAT=True)
|
|
||||||
def test_use_24_hour_time_format(self):
|
|
||||||
update_en_gb_date_formats()
|
|
||||||
field = DateTimeField()
|
|
||||||
supported_custom_examples = [
|
|
||||||
"25/10/2006 2:30:59",
|
|
||||||
"25/10/2006 2:30",
|
|
||||||
"25/10/2006 14:30:59",
|
|
||||||
"25/10/2006 14:30",
|
|
||||||
]
|
|
||||||
|
|
||||||
for example in supported_custom_examples:
|
|
||||||
try:
|
|
||||||
result = field.to_python(example)
|
|
||||||
self.assertIsInstance(result, datetime.datetime)
|
|
||||||
except ValidationError:
|
|
||||||
self.fail('Format of "{}" not recognized!'.format(example))
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
field.to_python("invalid date string!")
|
|
||||||
|
|
||||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5, second=59)
|
|
||||||
self.assertEqual(date_format(dt, "DATETIME_FORMAT"), "4 November 2011 23:05:59")
|
|
||||||
|
|
||||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5, second=59)
|
|
||||||
self.assertEqual(date_format(dt, "SHORT_DATETIME_FORMAT"), "04/11/2011 02:05")
|
|
||||||
|
|
||||||
t = datetime.time(hour=16, minute=2, second=25)
|
|
||||||
self.assertEqual(time_format(t), "16:02")
|
|
||||||
|
|
||||||
# def test_short_month_day_format(self):
|
|
||||||
# update_en_gb_date_formats()
|
|
||||||
# dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5,
|
|
||||||
# second=5)
|
|
||||||
# self.assertEqual(date_format(dt, 'SHORT_MONTH_DAY_FORMAT'), '31 Jul')
|
|
|
@ -1,67 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.forms.fields import DateTimeField
|
|
||||||
from django.test import TestCase, override_settings, tag
|
|
||||||
from django.utils.formats import date_format, time_format
|
|
||||||
|
|
||||||
from babybuddy.middleware import update_en_us_date_formats
|
|
||||||
|
|
||||||
|
|
||||||
class FormatsTestCase(TestCase):
|
|
||||||
def test_datetime_input_formats(self):
|
|
||||||
update_en_us_date_formats()
|
|
||||||
field = DateTimeField()
|
|
||||||
supported_custom_examples = [
|
|
||||||
"01/20/2020 9:30 AM",
|
|
||||||
"01/20/2020 9:30:03 AM",
|
|
||||||
"10/01/2020 11:30 PM",
|
|
||||||
"10/01/2020 11:30:03 AM",
|
|
||||||
]
|
|
||||||
|
|
||||||
for example in supported_custom_examples:
|
|
||||||
try:
|
|
||||||
result = field.to_python(example)
|
|
||||||
self.assertIsInstance(result, datetime.datetime)
|
|
||||||
except ValidationError:
|
|
||||||
self.fail('Format of "{}" not recognized!'.format(example))
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
field.to_python("invalid date string!")
|
|
||||||
|
|
||||||
@tag("isolate")
|
|
||||||
@override_settings(LANGUAGE_CODE="en-US", USE_24_HOUR_TIME_FORMAT=True)
|
|
||||||
def test_use_24_hour_time_format(self):
|
|
||||||
update_en_us_date_formats()
|
|
||||||
field = DateTimeField()
|
|
||||||
supported_custom_examples = [
|
|
||||||
"10/25/2006 2:30:59",
|
|
||||||
"10/25/2006 2:30",
|
|
||||||
"10/25/2006 14:30:59",
|
|
||||||
"10/25/2006 14:30",
|
|
||||||
]
|
|
||||||
|
|
||||||
for example in supported_custom_examples:
|
|
||||||
try:
|
|
||||||
result = field.to_python(example)
|
|
||||||
self.assertIsInstance(result, datetime.datetime)
|
|
||||||
except ValidationError:
|
|
||||||
self.fail('Format of "{}" not recognized!'.format(example))
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
field.to_python("invalid date string!")
|
|
||||||
|
|
||||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=23, minute=5, second=59)
|
|
||||||
self.assertEqual(date_format(dt, "DATETIME_FORMAT"), "Nov. 4, 2011, 23:05:59")
|
|
||||||
|
|
||||||
dt = datetime.datetime(year=2011, month=11, day=4, hour=2, minute=5, second=59)
|
|
||||||
self.assertEqual(date_format(dt, "SHORT_DATETIME_FORMAT"), "11/04/2011 2:05:59")
|
|
||||||
|
|
||||||
t = datetime.time(hour=16, minute=2, second=25)
|
|
||||||
self.assertEqual(time_format(t), "16:02:25")
|
|
||||||
|
|
||||||
def test_short_month_day_format(self):
|
|
||||||
update_en_us_date_formats()
|
|
||||||
dt = datetime.datetime(year=2021, month=7, day=31, hour=5, minute=5, second=5)
|
|
||||||
self.assertEqual(date_format(dt, "SHORT_MONTH_DAY_FORMAT"), "Jul 31")
|
|
|
@ -222,11 +222,10 @@ class FormsTestCase(TestCase):
|
||||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||||
self.assertContains(page, "Paramètres utilisateur")
|
self.assertContains(page, "Paramètres utilisateur")
|
||||||
|
|
||||||
@override_settings(TIME_ZONE="US/Eastern")
|
|
||||||
def test_user_settings_timezone(self):
|
def test_user_settings_timezone(self):
|
||||||
self.c.login(**self.credentials)
|
self.c.login(**self.credentials)
|
||||||
|
|
||||||
self.assertEqual(timezone.get_default_timezone_name(), "US/Eastern")
|
self.assertEqual(timezone.get_default_timezone_name(), "UTC")
|
||||||
params = self.settings_template.copy()
|
params = self.settings_template.copy()
|
||||||
params["timezone"] = "US/Pacific"
|
params["timezone"] = "US/Pacific"
|
||||||
page = self.c.post("/user/settings/", data=params, follow=True)
|
page = self.c.post("/user/settings/", data=params, follow=True)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.test import Client as HttpClient, TestCase
|
||||||
|
|
||||||
|
from faker import Faker
|
||||||
|
|
||||||
|
from core.models import Sleep
|
||||||
|
|
||||||
|
|
||||||
|
class SiteSettingsTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(SiteSettingsTestCase, cls).setUpClass()
|
||||||
|
fake = Faker()
|
||||||
|
call_command("migrate", verbosity=0)
|
||||||
|
call_command("fake", verbosity=0)
|
||||||
|
|
||||||
|
cls.c = HttpClient()
|
||||||
|
|
||||||
|
fake_user = fake.simple_profile()
|
||||||
|
cls.credentials = {
|
||||||
|
"username": fake_user["username"],
|
||||||
|
"password": fake.password(),
|
||||||
|
}
|
||||||
|
cls.user = get_user_model().objects.create_user(
|
||||||
|
is_superuser=True, is_staff=True, **cls.credentials
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_default_settings(self):
|
||||||
|
self.c.login(**self.credentials)
|
||||||
|
page = self.c.get("/settings/")
|
||||||
|
self.assertEqual(page.status_code, 200)
|
||||||
|
self.assertEqual(
|
||||||
|
page.context["form"]["core.models__Sleep__nap_start_max"].value(),
|
||||||
|
"18:00:00",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
page.context["form"]["core.models__Sleep__nap_start_min"].value(),
|
||||||
|
"06:00:00",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_nap_start_settings(self):
|
||||||
|
self.c.login(**self.credentials)
|
||||||
|
self.assert_naps()
|
||||||
|
params = {
|
||||||
|
"core.models__Sleep__nap_start_max": "20:00:00",
|
||||||
|
"core.models__Sleep__nap_start_min": "09:00:00",
|
||||||
|
}
|
||||||
|
page = self.c.post("/settings/", params, follow=True)
|
||||||
|
self.assertEqual(page.status_code, 200)
|
||||||
|
self.assertEqual(
|
||||||
|
Sleep.settings.nap_start_max.strftime("%H:%M:%S"),
|
||||||
|
params["core.models__Sleep__nap_start_max"],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Sleep.settings.nap_start_min.strftime("%H:%M:%S"),
|
||||||
|
params["core.models__Sleep__nap_start_min"],
|
||||||
|
)
|
||||||
|
self.assert_naps()
|
||||||
|
|
||||||
|
def assert_naps(self):
|
||||||
|
"""
|
||||||
|
Asserts sleep instances filtered with nap start min and max match nap instances.
|
||||||
|
"""
|
||||||
|
instances = Sleep.objects.filter(
|
||||||
|
start__time__range=(
|
||||||
|
Sleep.settings.nap_start_min.strftime("%H:%M:%S"),
|
||||||
|
Sleep.settings.nap_start_max.strftime("%H:%M:%S"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
naps = Sleep.naps.all()
|
||||||
|
self.assertQuerySetEqual(instances, naps)
|
|
@ -44,6 +44,7 @@ app_patterns = [
|
||||||
path("user/password/", views.UserPassword.as_view(), name="user-password"),
|
path("user/password/", views.UserPassword.as_view(), name="user-password"),
|
||||||
path("user/settings/", views.UserSettings.as_view(), name="user-settings"),
|
path("user/settings/", views.UserSettings.as_view(), name="user-settings"),
|
||||||
path("user/add-device/", views.UserAddDevice.as_view(), name="user-add-device"),
|
path("user/add-device/", views.UserAddDevice.as_view(), name="user-add-device"),
|
||||||
|
path("settings/", include("dbsettings.urls")),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.forms import widgets
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeBaseInput(widgets.DateTimeBaseInput):
|
||||||
|
def format_value(self, value):
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
value = value.isoformat()
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeInput(DateTimeBaseInput):
|
||||||
|
input_type = "datetime-local"
|
||||||
|
|
||||||
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
||||||
|
attrs = super().build_attrs(base_attrs, extra_attrs)
|
||||||
|
# Default to seconds granularity. Required for client validation in Safari.
|
||||||
|
if "step" not in attrs:
|
||||||
|
attrs["step"] = 1
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class DateInput(DateTimeBaseInput):
|
||||||
|
input_type = "date"
|
||||||
|
|
||||||
|
|
||||||
|
class TimeInput(DateTimeBaseInput):
|
||||||
|
input_type = "time"
|
|
@ -216,8 +216,8 @@ class TemperatureAdmin(ImportExportMixin, ExportActionMixin, admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(models.Timer)
|
@admin.register(models.Timer)
|
||||||
class TimerAdmin(admin.ModelAdmin):
|
class TimerAdmin(admin.ModelAdmin):
|
||||||
list_display = ("name", "child", "start", "end", "duration", "active", "user")
|
list_display = ("name", "child", "start", "duration", "user")
|
||||||
list_filter = ("child", "active", "user")
|
list_filter = ("child", "user")
|
||||||
search_fields = ("child__first_name", "child__last_name", "name", "user")
|
search_fields = ("child__first_name", "child__last_name", "name", "user")
|
||||||
|
|
||||||
|
|
||||||
|
|
117
core/forms.py
117
core/forms.py
|
@ -7,6 +7,7 @@ from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from taggit.forms import TagField
|
from taggit.forms import TagField
|
||||||
|
|
||||||
|
from babybuddy.widgets import DateInput, DateTimeInput
|
||||||
from core import models
|
from core import models
|
||||||
from core.widgets import TagsEditor, ChildRadioSelect
|
from core.widgets import TagsEditor, ChildRadioSelect
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ def set_initial_values(kwargs, form_type):
|
||||||
if timer_id:
|
if timer_id:
|
||||||
timer = models.Timer.objects.get(id=timer_id)
|
timer = models.Timer.objects.get(id=timer_id)
|
||||||
kwargs["initial"].update(
|
kwargs["initial"].update(
|
||||||
{"timer": timer, "start": timer.start, "end": timer.end or timezone.now()}
|
{"timer": timer, "start": timer.start, "end": timezone.now()}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set type and method values for Feeding instance based on last feed.
|
# Set type and method values for Feeding instance based on last feed.
|
||||||
|
@ -83,7 +84,7 @@ class CoreModelForm(forms.ModelForm):
|
||||||
instance = super(CoreModelForm, self).save(commit=False)
|
instance = super(CoreModelForm, self).save(commit=False)
|
||||||
if self.timer_id:
|
if self.timer_id:
|
||||||
timer = models.Timer.objects.get(id=self.timer_id)
|
timer = models.Timer.objects.get(id=self.timer_id)
|
||||||
timer.stop(instance.end)
|
timer.stop()
|
||||||
if commit:
|
if commit:
|
||||||
instance.save()
|
instance.save()
|
||||||
self.save_m2m()
|
self.save_m2m()
|
||||||
|
@ -97,12 +98,7 @@ class ChildForm(forms.ModelForm):
|
||||||
if settings.BABY_BUDDY["ALLOW_UPLOADS"]:
|
if settings.BABY_BUDDY["ALLOW_UPLOADS"]:
|
||||||
fields.append("picture")
|
fields.append("picture")
|
||||||
widgets = {
|
widgets = {
|
||||||
"birth_date": forms.DateInput(
|
"birth_date": DateInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_birth_date",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,12 +140,7 @@ class PumpingForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "amount", "time", "notes", "tags"]
|
fields = ["child", "amount", "time", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"time": forms.DateTimeInput(
|
"time": DateTimeInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_time",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,12 +151,7 @@ class DiaperChangeForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "time", "wet", "solid", "color", "amount", "notes", "tags"]
|
fields = ["child", "time", "wet", "solid", "color", "amount", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect(),
|
"child": ChildRadioSelect(),
|
||||||
"time": forms.DateTimeInput(
|
"time": DateTimeInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_time",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,18 +162,8 @@ class FeedingForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "start", "end", "type", "method", "amount", "notes", "tags"]
|
fields = ["child", "start", "end", "type", "method", "amount", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"start": forms.DateTimeInput(
|
"start": DateTimeInput(),
|
||||||
attrs={
|
"end": DateTimeInput(),
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_start",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"end": forms.DateTimeInput(
|
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_end",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,12 +174,7 @@ class NoteForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "note", "time", "tags"]
|
fields = ["child", "note", "time", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"time": forms.DateTimeInput(
|
"time": DateTimeInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_time",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,18 +184,8 @@ class SleepForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "start", "end", "notes", "tags"]
|
fields = ["child", "start", "end", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"start": forms.DateTimeInput(
|
"start": DateTimeInput(),
|
||||||
attrs={
|
"end": DateTimeInput(),
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_start",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"end": forms.DateTimeInput(
|
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_end",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,12 +196,7 @@ class TemperatureForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "temperature", "time", "notes", "tags"]
|
fields = ["child", "temperature", "time", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"time": forms.DateTimeInput(
|
"time": DateTimeInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_time",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,12 +207,7 @@ class TimerForm(CoreModelForm):
|
||||||
fields = ["child", "name", "start"]
|
fields = ["child", "name", "start"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"start": forms.DateTimeInput(
|
"start": DateTimeInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_start",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -276,18 +227,8 @@ class TummyTimeForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "start", "end", "milestone", "tags"]
|
fields = ["child", "start", "end", "milestone", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"start": forms.DateTimeInput(
|
"start": DateTimeInput(),
|
||||||
attrs={
|
"end": DateTimeInput(),
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_start",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"end": forms.DateTimeInput(
|
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_end",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -297,12 +238,7 @@ class WeightForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "weight", "date", "notes", "tags"]
|
fields = ["child", "weight", "date", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"date": forms.DateInput(
|
"date": DateInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_date",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,12 +249,7 @@ class HeightForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "height", "date", "notes", "tags"]
|
fields = ["child", "height", "date", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"date": forms.DateInput(
|
"date": DateInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_date",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,12 +260,7 @@ class HeadCircumferenceForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "head_circumference", "date", "notes", "tags"]
|
fields = ["child", "head_circumference", "date", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"date": forms.DateInput(
|
"date": DateInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_date",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,12 +271,7 @@ class BMIForm(CoreModelForm, TaggableModelForm):
|
||||||
fields = ["child", "bmi", "date", "notes", "tags"]
|
fields = ["child", "bmi", "date", "notes", "tags"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"child": ChildRadioSelect,
|
"child": ChildRadioSelect,
|
||||||
"date": forms.DateInput(
|
"date": DateInput(),
|
||||||
attrs={
|
|
||||||
"autocomplete": "off",
|
|
||||||
"data-target": "#datetimepicker_date",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"notes": forms.Textarea(attrs={"rows": 5}),
|
"notes": forms.Textarea(attrs={"rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,37 @@
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def delete_inactive_timers(apps, schema_editor):
|
||||||
|
from core import models
|
||||||
|
|
||||||
|
for timer in models.Timer.objects.filter(active=False):
|
||||||
|
timer.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0026_alter_feeding_end_alter_feeding_start_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="timer",
|
||||||
|
options={
|
||||||
|
"default_permissions": ("view", "add", "change", "delete"),
|
||||||
|
"ordering": ["-start"],
|
||||||
|
"verbose_name": "Timer",
|
||||||
|
"verbose_name_plural": "Timers",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="timer",
|
||||||
|
name="duration",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="timer",
|
||||||
|
name="end",
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
delete_inactive_timers, reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,7 +2,6 @@
|
||||||
import re
|
import re
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
|
@ -13,6 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from taggit.managers import TaggableManager as TaggitTaggableManager
|
from taggit.managers import TaggableManager as TaggitTaggableManager
|
||||||
from taggit.models import GenericTaggedItemBase, TagBase
|
from taggit.models import GenericTaggedItemBase, TagBase
|
||||||
|
|
||||||
|
from babybuddy.site_settings import NapSettings
|
||||||
from core.utils import random_color
|
from core.utils import random_color
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,8 +273,15 @@ class Feeding(models.Model):
|
||||||
related_name="feeding",
|
related_name="feeding",
|
||||||
verbose_name=_("Child"),
|
verbose_name=_("Child"),
|
||||||
)
|
)
|
||||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
start = models.DateTimeField(
|
||||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
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(
|
duration = models.DurationField(
|
||||||
editable=False, null=True, verbose_name=_("Duration")
|
editable=False, null=True, verbose_name=_("Duration")
|
||||||
)
|
)
|
||||||
|
@ -426,7 +433,9 @@ class Pumping(models.Model):
|
||||||
verbose_name=_("Child"),
|
verbose_name=_("Child"),
|
||||||
)
|
)
|
||||||
amount = models.FloatField(blank=False, null=False, verbose_name=_("Amount"))
|
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"))
|
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||||
tags = TaggableManager(blank=True, through=Tagged)
|
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")
|
"Child", on_delete=models.CASCADE, related_name="sleep", verbose_name=_("Child")
|
||||||
)
|
)
|
||||||
napping = models.BooleanField(editable=False, null=True, verbose_name=_("Napping"))
|
napping = models.BooleanField(editable=False, null=True, verbose_name=_("Napping"))
|
||||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
start = models.DateTimeField(
|
||||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
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(
|
duration = models.DurationField(
|
||||||
editable=False, null=True, verbose_name=_("Duration")
|
editable=False, null=True, verbose_name=_("Duration")
|
||||||
)
|
)
|
||||||
|
@ -461,6 +477,7 @@ class Sleep(models.Model):
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
naps = NapsManager()
|
naps = NapsManager()
|
||||||
|
settings = NapSettings(_("Nap settings"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
default_permissions = ("view", "add", "change", "delete")
|
default_permissions = ("view", "add", "change", "delete")
|
||||||
|
@ -473,14 +490,12 @@ class Sleep(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nap(self):
|
def nap(self):
|
||||||
nap_start_min = timezone.datetime.strptime(
|
|
||||||
settings.BABY_BUDDY["NAP_START_MIN"], "%H:%M"
|
|
||||||
).time()
|
|
||||||
nap_start_max = timezone.datetime.strptime(
|
|
||||||
settings.BABY_BUDDY["NAP_START_MAX"], "%H:%M"
|
|
||||||
).time()
|
|
||||||
local_start_time = timezone.localtime(self.start).time()
|
local_start_time = timezone.localtime(self.start).time()
|
||||||
return nap_start_min <= local_start_time <= nap_start_max
|
return (
|
||||||
|
Sleep.settings.nap_start_min
|
||||||
|
<= local_start_time
|
||||||
|
<= Sleep.settings.nap_start_max
|
||||||
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.start and self.end:
|
if self.start and self.end:
|
||||||
|
@ -543,12 +558,6 @@ class Timer(models.Model):
|
||||||
start = models.DateTimeField(
|
start = models.DateTimeField(
|
||||||
default=timezone.now, blank=False, verbose_name=_("Start time")
|
default=timezone.now, blank=False, verbose_name=_("Start time")
|
||||||
)
|
)
|
||||||
end = models.DateTimeField(
|
|
||||||
blank=True, editable=False, null=True, verbose_name=_("End time")
|
|
||||||
)
|
|
||||||
duration = models.DurationField(
|
|
||||||
editable=False, null=True, verbose_name=_("Duration")
|
|
||||||
)
|
|
||||||
active = models.BooleanField(default=True, editable=False, verbose_name=_("Active"))
|
active = models.BooleanField(default=True, editable=False, verbose_name=_("Active"))
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
"auth.User",
|
"auth.User",
|
||||||
|
@ -561,7 +570,7 @@ class Timer(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
default_permissions = ("view", "add", "change", "delete")
|
default_permissions = ("view", "add", "change", "delete")
|
||||||
ordering = ["-active", "-start", "-end"]
|
ordering = ["-start"]
|
||||||
verbose_name = _("Timer")
|
verbose_name = _("Timer")
|
||||||
verbose_name_plural = _("Timers")
|
verbose_name_plural = _("Timers")
|
||||||
|
|
||||||
|
@ -584,42 +593,24 @@ class Timer(models.Model):
|
||||||
return self.user.get_full_name()
|
return self.user.get_full_name()
|
||||||
return self.user.get_username()
|
return self.user.get_username()
|
||||||
|
|
||||||
@classmethod
|
def duration(self):
|
||||||
def from_db(cls, db, field_names, values):
|
return timezone.now() - self.start
|
||||||
instance = super(Timer, cls).from_db(db, field_names, values)
|
|
||||||
if not instance.duration:
|
|
||||||
instance.duration = timezone.now() - instance.start
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
"""Restart the timer."""
|
"""Restart the timer."""
|
||||||
self.start = timezone.now()
|
self.start = timezone.now()
|
||||||
self.end = None
|
|
||||||
self.duration = None
|
|
||||||
self.active = True
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def stop(self, end=None):
|
def stop(self):
|
||||||
"""Stop the timer."""
|
"""Stop (delete) the timer."""
|
||||||
if not end:
|
self.delete()
|
||||||
end = timezone.now()
|
|
||||||
self.end = end
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.active = self.end is None
|
|
||||||
self.name = self.name or None
|
self.name = self.name or None
|
||||||
if self.start and self.end:
|
|
||||||
self.duration = self.end - self.start
|
|
||||||
else:
|
|
||||||
self.duration = None
|
|
||||||
super(Timer, self).save(*args, **kwargs)
|
super(Timer, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
validate_time(self.start, "start")
|
validate_time(self.start, "start")
|
||||||
if self.end:
|
|
||||||
validate_time(self.end, "end")
|
|
||||||
validate_duration(self)
|
|
||||||
|
|
||||||
|
|
||||||
class TummyTime(models.Model):
|
class TummyTime(models.Model):
|
||||||
|
@ -630,8 +621,15 @@ class TummyTime(models.Model):
|
||||||
related_name="tummy_time",
|
related_name="tummy_time",
|
||||||
verbose_name=_("Child"),
|
verbose_name=_("Child"),
|
||||||
)
|
)
|
||||||
start = models.DateTimeField(blank=False, null=False, verbose_name=_("Start time"))
|
start = models.DateTimeField(
|
||||||
end = models.DateTimeField(blank=False, null=False, verbose_name=_("End time"))
|
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(
|
duration = models.DurationField(
|
||||||
editable=False, null=True, verbose_name=_("Duration")
|
editable=False, null=True, verbose_name=_("Duration")
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ BabyBuddy.Timer = function ($) {
|
||||||
var runIntervalId = null;
|
var runIntervalId = null;
|
||||||
var timerId = null;
|
var timerId = null;
|
||||||
var timerElement = null;
|
var timerElement = null;
|
||||||
var lastUpdate = moment();
|
var lastUpdate = new Date();
|
||||||
var hidden = null;
|
var hidden = null;
|
||||||
|
|
||||||
var Timer = {
|
var Timer = {
|
||||||
|
@ -18,14 +18,14 @@ BabyBuddy.Timer = function ($) {
|
||||||
timerId = timer_id;
|
timerId = timer_id;
|
||||||
timerElement = $('#' + element_id);
|
timerElement = $('#' + element_id);
|
||||||
|
|
||||||
if (timerElement.length == 0) {
|
if (timerElement.length === 0) {
|
||||||
console.error('BBTimer: Timer element not found.');
|
console.error('BBTimer: Timer element not found.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timerElement.find('.timer-seconds').length == 0
|
if (timerElement.find('.timer-seconds').length === 0
|
||||||
|| timerElement.find('.timer-minutes').length == 0
|
|| timerElement.find('.timer-minutes').length === 0
|
||||||
|| timerElement.find('.timer-hours').length == 0) {
|
|| timerElement.find('.timer-hours').length === 0) {
|
||||||
console.error('BBTimer: Element does not contain expected children.');
|
console.error('BBTimer: Element does not contain expected children.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ BabyBuddy.Timer = function ($) {
|
||||||
},
|
},
|
||||||
|
|
||||||
handleVisibilityChange: function() {
|
handleVisibilityChange: function() {
|
||||||
if (!document[hidden] && moment().diff(lastUpdate) > 10000) {
|
if (!document[hidden] && (new Date()) - lastUpdate > 1) {
|
||||||
Timer.update();
|
Timer.update();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -83,19 +83,18 @@ BabyBuddy.Timer = function ($) {
|
||||||
$.get('/api/timers/' + timerId + '/', function(data) {
|
$.get('/api/timers/' + timerId + '/', function(data) {
|
||||||
if (data && 'duration' in data) {
|
if (data && 'duration' in data) {
|
||||||
clearInterval(runIntervalId);
|
clearInterval(runIntervalId);
|
||||||
var duration = moment.duration(data.duration);
|
var duration = data.duration.split(/[\s:.]/)
|
||||||
timerElement.find('.timer-hours').text(duration.hours());
|
if (duration.length === 5) {
|
||||||
timerElement.find('.timer-minutes').text(duration.minutes());
|
duration[0] = parseInt(duration[0]) * 24 + parseInt(duration[1]);
|
||||||
timerElement.find('.timer-seconds').text(duration.seconds());
|
duration[1] = duration[2];
|
||||||
lastUpdate = moment();
|
duration[2] = duration[3];
|
||||||
|
}
|
||||||
if (data['active']) {
|
timerElement.find('.timer-hours').text(parseInt(duration[0]));
|
||||||
|
timerElement.find('.timer-minutes').text(parseInt(duration[1]));
|
||||||
|
timerElement.find('.timer-seconds').text(parseInt(duration[2]));
|
||||||
|
lastUpdate = new Date()
|
||||||
runIntervalId = setInterval(Timer.tick, 1000);
|
runIntervalId = setInterval(Timer.tick, 1000);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
timerElement.addClass('timer-stopped');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
@include media-breakpoint-up(md) {
|
||||||
#view-core\:child {
|
#view-core\:child {
|
||||||
.child-detail-column {
|
.child-detail-column {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use 'sass:map';
|
||||||
|
|
||||||
// Adapted for Bootstrap 4 from https://www.bootply.com/SzXin8KDZJ.
|
// Adapted for Bootstrap 4 from https://www.bootply.com/SzXin8KDZJ.
|
||||||
$card-shadow: rgba(0, 0, 0, .175);
|
$card-shadow: rgba(0, 0, 0, .175);
|
||||||
|
|
||||||
|
@ -12,7 +14,7 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
width: 3px;
|
width: 3px;
|
||||||
background-color: theme-color('primary');
|
background-color: map.get($theme-colors, 'primary');
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -1.5px;
|
margin-left: -1.5px;
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
width: 47%;
|
width: 46%;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 1px 6px $card-shadow;
|
box-shadow: 0 1px 6px $card-shadow;
|
||||||
|
@ -45,8 +47,8 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
right: -15px;
|
right: -15px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-top: 15px solid transparent;
|
border-top: 15px solid transparent;
|
||||||
border-left: 15px solid theme-color('dark');
|
border-left: 15px solid map.get($theme-colors, 'dark');
|
||||||
border-right: 0 solid theme-color('dark');
|
border-right: 0 solid map.get($theme-colors, 'dark');
|
||||||
border-bottom: 15px solid transparent;
|
border-bottom: 15px solid transparent;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
}
|
}
|
||||||
|
@ -57,8 +59,8 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
right: -14px;
|
right: -14px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-top: 14px solid transparent;
|
border-top: 14px solid transparent;
|
||||||
border-left: 14px solid theme-color('dark');
|
border-left: 14px solid map.get($theme-colors, 'dark');
|
||||||
border-right: 0 solid theme-color('dark');
|
border-right: 0 solid map.get($theme-colors, 'dark');
|
||||||
border-bottom: 14px solid transparent;
|
border-bottom: 14px solid transparent;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
top: 16px;
|
top: 16px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
background-color: theme-color('dark');
|
background-color: map.get($theme-colors, 'dark');
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
|
@ -110,7 +112,7 @@ $card-shadow: rgba(0, 0, 0, .175);
|
||||||
top: 26px;
|
top: 26px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
background-color: theme-color('dark');
|
background-color: map.get($theme-colors, 'dark');
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,7 @@
|
||||||
#timer-status {
|
@use 'sass:map';
|
||||||
font-size: $h1-font-size;
|
|
||||||
font-weight: $display1-weight;
|
|
||||||
|
|
||||||
|
#timer-status {
|
||||||
&.timer-stopped {
|
&.timer-stopped {
|
||||||
color: theme-color('danger');
|
color: map.get($theme-colors, 'dark');
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-breakpoint-up(sm) {
|
|
||||||
#view-core\:timer-detail {
|
|
||||||
#timer-status {
|
|
||||||
font-size: $display1-size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
|
|
||||||
format: 'L',
|
|
||||||
extraFormats: ['YYYY-MM-DD']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Date" %}</th>
|
<th>{% trans "Date" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for bmi in object_list %}
|
{% for bmi in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if bmi.notes %}
|
{% if bmi.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
<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>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
<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' %}
|
{% child_quick_switch object 'core:child' %}
|
||||||
</li>
|
</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
||||||
{% if object %}
|
{% 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>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="breadcrumb-item active" aria-current="page">{% trans "Add a Child" %}</li>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Add a Child" %}</li>
|
||||||
|
@ -29,13 +29,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_birth_date'), {
|
|
||||||
format: 'L',
|
|
||||||
viewMode: 'years',
|
|
||||||
extraFormats: ['YYYY-MM-DD']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,10 +18,10 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover child-list">
|
<table class="table table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<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 "First Name" %}</th>
|
||||||
<th>{% trans "Last Name" %}</th>
|
<th>{% trans "Last Name" %}</th>
|
||||||
<th>{% trans "Birth Date" %}</th>
|
<th>{% trans "Birth Date" %}</th>
|
||||||
|
@ -31,8 +31,12 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for child in object_list %}
|
{% for child in object_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="picture-column">
|
<td>
|
||||||
|
{% if child.picture %}
|
||||||
{% include "core/child_thumbnail.html" %}
|
{% 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>
|
</td>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<a href="{% url 'core:child' child.slug %}">{{ child.first_name }}</a>
|
<a href="{% url 'core:child' child.slug %}">{{ child.first_name }}</a>
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
<a href="{% url 'core:child' current_child.slug %}">{{ current_child }}</a>
|
<a href="{% url 'core:child' current_child.slug %}">{{ current_child }}</a>
|
||||||
{% if children.count > 0 %}
|
{% if children.count > 0 %}
|
||||||
<a href="#" class="ml-1 pl-1 pr-1 dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a href="#" class="ms-1 ps-1 pe-1 dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="sr-only">{% trans 'Switch child' %}</span>
|
<span class="visually-hidden">{% trans 'Switch child' %}</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M3.626 6.832A.5.5 0 0 1 4 6h8a.5.5 0 0 1 .374.832l-4 4.5a.5.5 0 0 1-.748 0l-4-4.5z"></path>
|
<path d="M3.626 6.832A.5.5 0 0 1 4 6h8a.5.5 0 0 1 .374.832l-4 4.5a.5.5 0 0 1-.748 0l-4-4.5z"></path>
|
||||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2z"></path>
|
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2z"></path>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
{% for child in children %}
|
{% for child in children %}
|
||||||
<a class="dropdown-item d-flex align-items-center" href="{% url target_url child.slug %}">
|
<a class="dropdown-item d-flex align-items-center" href="{% url target_url child.slug %}">
|
||||||
{% include "core/child_thumbnail.html" %}
|
{% include "core/child_thumbnail.html" %}
|
||||||
<span class="text-wrap ml-2">{{ child }}</span>
|
<span class="text-wrap ms-2">{{ child }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
{% load imagekit static %}
|
{% load imagekit static %}
|
||||||
|
|
||||||
{% if widget.wrap_label %}
|
|
||||||
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="btn btn-outline-light btn-no-hover">
|
|
||||||
{% endif %}
|
|
||||||
{% include "django/forms/widgets/input.html" %}
|
{% include "django/forms/widgets/input.html" %}
|
||||||
{% if widget.wrap_label %}
|
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="btn btn-outline-light btn-no-hover">
|
||||||
{% include "core/child_thumbnail.html" with child=widget %}
|
{% include "core/child_thumbnail.html" with child=widget %}
|
||||||
{{ widget.label }}</label>
|
{{ widget.label }}
|
||||||
{% endif %}
|
</label>
|
||||||
|
|
|
@ -28,11 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Time" %}</th>
|
<th>{% trans "Time" %}</th>
|
||||||
|
@ -34,8 +34,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for change in object_list %}
|
{% for change in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -69,7 +68,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if change.notes %}
|
{% if change.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -28,26 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_end'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
$('#id_type').change(function() {
|
|
||||||
var feed_type=$('#id_type').val();
|
|
||||||
if (feed_type === 'formula' || feed_type === 'fortified breast milk') {
|
|
||||||
$('#id_method').val('bottle');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$('#id_method').change(function() {
|
|
||||||
var method = $('#id_method').val();
|
|
||||||
if (['left breast', 'right breast', 'both breasts'].includes(method)) {
|
|
||||||
$('#id_type').val('breast milk');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Date" %}</th>
|
<th>{% trans "Date" %}</th>
|
||||||
|
@ -36,8 +36,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for feeding in object_list %}
|
{% for feeding in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if feeding.notes %}
|
{% if feeding.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -28,12 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
|
|
||||||
format: 'L',
|
|
||||||
extraFormats: ['YYYY-MM-DD']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Date" %}</th>
|
<th>{% trans "Date" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for head_circumference in object_list %}
|
{% for head_circumference in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if head_circumference.notes %}
|
{% if head_circumference.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -28,12 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
|
|
||||||
format: 'L',
|
|
||||||
extraFormats: ['YYYY-MM-DD']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Date" %}</th>
|
<th>{% trans "Date" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for height in object_list %}
|
{% for height in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if height.notes %}
|
{% if height.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -28,11 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Time" %}</th>
|
<th>{% trans "Time" %}</th>
|
||||||
|
|
|
@ -28,11 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Time" %}</th>
|
<th>{% trans "Time" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for pumping in object_list %}
|
{% for pumping in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if pumping.notes %}
|
{% if pumping.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
{% for child in children %}
|
{% for child in children %}
|
||||||
<button class="dropdown-item d-flex align-items-center" type="submit" name="child" value="{{ child.pk }}">
|
<button class="dropdown-item d-flex align-items-center" type="submit" name="child" value="{{ child.pk }}">
|
||||||
{% include "core/child_thumbnail.html" %}
|
{% include "core/child_thumbnail.html" %}
|
||||||
<span class="text-wrap ml-2">{{ child }}</span>
|
<span class="text-wrap ms-2">{{ child }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<label class="sr-only">{% trans "Quick Start Timer" %}</label>
|
<label class="visually-hidden">{% trans "Quick Start Timer" %}</label>
|
||||||
<button class="btn m-0 p-0 text-success">
|
<button class="btn m-0 p-0 text-success">
|
||||||
<i class="icon-2x icon-timer" aria-hidden="true"></i>
|
<i class="icon-2x icon-timer" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -28,15 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
|
|
||||||
defaultDate: false,
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_end'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Start" %}</th>
|
<th>{% trans "Start" %}</th>
|
||||||
|
@ -34,8 +34,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for sleep in object_list %}
|
{% for sleep in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if sleep.notes %}
|
{% if sleep.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -28,11 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_time'), {
|
|
||||||
format: '{% datetimepicker_format %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Time" %}</th>
|
<th>{% trans "Time" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for temperature in object_list %}
|
{% for temperature in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if temperature.notes %}
|
{% if temperature.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
|
<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>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Delete" %}</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
{% extends 'babybuddy/page.html' %}
|
|
||||||
{% load humanize i18n widget_tweaks %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% blocktrans %}Delete All Inactive Timers{% endblocktrans %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
|
|
||||||
<li class="breadcrumb-item active" aria-current="page">{% trans "Delete Inactive" %}</li>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<form role="form" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<h1>
|
|
||||||
{% blocktrans trimmed with number=timer_count|apnumber|intcomma count counter=timer_count %}
|
|
||||||
Are you sure you want to delete {{ number }} inactive timer?
|
|
||||||
{% plural %}
|
|
||||||
Are you sure you want to delete {{ number }} inactive timers?
|
|
||||||
{% endblocktrans %}
|
|
||||||
</h1>
|
|
||||||
<input type="submit" value="{% trans "Delete" %}" class="btn btn-danger" />
|
|
||||||
<a href={% url "babybuddy:root-router" %} class="btn btn-default">{% trans "Cancel" %}</a>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -6,13 +6,14 @@
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
|
<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 %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="jumbotron text-center">
|
<div class="p-5 mb-4 bg-dark rounded-3 text-center">
|
||||||
|
<div class="container-fluid py-1">
|
||||||
<h1 {% if not object.active %}class="timer-stopped" {% endif %}id="timer-status">
|
<h1 id="timer-status"
|
||||||
|
class="display-1">
|
||||||
<span class="timer-hours">{{ object.duration|hours }}</span>h
|
<span class="timer-hours">{{ object.duration|hours }}</span>h
|
||||||
<span class="timer-minutes">{{ object.duration|minutes }}</span>m
|
<span class="timer-minutes">{{ object.duration|minutes }}</span>m
|
||||||
<span class="timer-seconds">{{ object.duration|seconds }}</span>s
|
<span class="timer-seconds">{{ object.duration|seconds }}</span>s
|
||||||
|
@ -26,9 +27,6 @@
|
||||||
|
|
||||||
<p class="lead text-secondary">
|
<p class="lead text-secondary">
|
||||||
{% trans "Started" %} {{ object.start }}
|
{% trans "Started" %} {{ object.start }}
|
||||||
{% if not object.active %}
|
|
||||||
/ {% trans "Stopped" %} {{ object.end }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
</p>
|
||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
{% blocktrans trimmed with user=object.user_username %}
|
{% blocktrans trimmed with user=object.user_username %}
|
||||||
|
@ -36,8 +34,9 @@
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div class="d-grid gap-4 mb-4">
|
||||||
{% if perms.core.add_feeding %}
|
{% 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' %}"
|
href="{% instance_add_url 'core:feeding-add' %}"
|
||||||
role="button"><i class="icon-feeding" aria-hidden="true"></i>
|
role="button"><i class="icon-feeding" aria-hidden="true"></i>
|
||||||
{% trans "Feeding" %}
|
{% trans "Feeding" %}
|
||||||
|
@ -45,7 +44,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.core.add_sleep %}
|
{% 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' %}"
|
href="{% instance_add_url 'core:sleep-add' %}"
|
||||||
role="button"><i class="icon-sleep" aria-hidden="true"></i>
|
role="button"><i class="icon-sleep" aria-hidden="true"></i>
|
||||||
{% trans "Sleep" %}
|
{% trans "Sleep" %}
|
||||||
|
@ -53,12 +52,13 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.core.add_tummytime %}
|
{% 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' %}"
|
href="{% instance_add_url 'core:tummytime-add' %}"
|
||||||
role="button"><i class="icon-tummytime" aria-hidden="true"></i>
|
role="button"><i class="icon-tummytime" aria-hidden="true"></i>
|
||||||
{% trans "Tummy Time" %}
|
{% trans "Tummy Time" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="center-block" role="group" aria-label="{% trans "Timer actions" %}">
|
<div class="center-block" role="group" aria-label="{% trans "Timer actions" %}">
|
||||||
{% if perms.core.delete_timer %}
|
{% if perms.core.delete_timer %}
|
||||||
|
@ -74,26 +74,17 @@
|
||||||
|
|
||||||
<form action="{% url 'core:timer-restart' timer.id %}" role="form" method="post" class="d-inline">
|
<form action="{% url 'core:timer-restart' timer.id %}" role="form" method="post" class="d-inline">
|
||||||
{% csrf_token %}
|
{% 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>
|
<button type="submit" class="btn btn-lg btn-secondary"><i class="icon-refresh" aria-hidden="true"></i></button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if object.active %}
|
|
||||||
<form action="{% url 'core:timer-stop' timer.id %}" role="form" method="post" class="d-inline">
|
|
||||||
{% csrf_token %}
|
|
||||||
<label class="sr-only">{% trans "Delete timer" %}</label>
|
|
||||||
<button type="submit" class="btn btn-lg btn-warning"><i class="icon-stop" aria-hidden="true"></i></button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
{% if object.active %}
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
BabyBuddy.Timer.run({{ timer.id }}, 'timer-status');
|
BabyBuddy.Timer.run({{ timer.id }}, 'timer-status');
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -6,7 +6,7 @@
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
|
<li class="breadcrumb-item"><a href="{% url 'core:timer-list' %}">{% trans "Timers" %}</a></li>
|
||||||
{% if object %}
|
{% 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>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Update" %}</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="breadcrumb-item active" aria-current="page">{% trans "Start" %}</li>
|
<li class="breadcrumb-item active" aria-current="page">{% trans "Start" %}</li>
|
||||||
|
@ -23,11 +23,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_start'), {
|
|
||||||
format: '{% datetimepicker_format 'L LTS' %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,17 +18,14 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Start" %}</th>
|
<th>{% trans "Start" %}</th>
|
||||||
<th>{% trans "Name" %}</th>
|
<th>{% trans "Name" %}</th>
|
||||||
{% if not unique_child %}
|
{% if not unique_child %}
|
||||||
<th>{% trans "Child" %}</th>
|
<th>{% trans "Child" %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th>{% trans "Duration" %}</th>
|
|
||||||
<th>{% trans "End" %}</th>
|
|
||||||
<th>{% trans "Active" %}</th>
|
|
||||||
<th>{% trans "User" %}</th>
|
<th>{% trans "User" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -44,13 +41,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>{{ timer.duration|duration_string }}</td>
|
|
||||||
<td>
|
|
||||||
{% if timer.end %}
|
|
||||||
{{ timer.end|datetime_short }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{ timer.active|bool_icon }}</td>
|
|
||||||
<td>{{ timer.user_username }}</td>
|
<td>{{ timer.user_username }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
@ -62,11 +52,4 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% include 'babybuddy/paginator.html' %}
|
{% include 'babybuddy/paginator.html' %}
|
||||||
|
|
||||||
{% if object_list and perms.core.delete_timer %}
|
|
||||||
<a href="{% url 'core:timer-delete-inactive' %}" class="btn btn-sm btn-danger">
|
|
||||||
<i class="icon-delete" aria-hidden="true"></i> {% trans "Delete Inactive Timers" %}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -4,7 +4,7 @@
|
||||||
<a id="nav-timer-menu-link"
|
<a id="nav-timer-menu-link"
|
||||||
class="nav-link dropdown-toggle"
|
class="nav-link dropdown-toggle"
|
||||||
href="#"
|
href="#"
|
||||||
data-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="icon-timer" aria-hidden="true"></i>
|
aria-expanded="false"><i class="icon-timer" aria-hidden="true"></i>
|
||||||
{% trans "Timers" %}
|
{% trans "Timers" %}
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
{% for child in children %}
|
{% for child in children %}
|
||||||
<button class="dropdown-item d-flex align-items-center" type="submit" name="child" value="{{ child.pk }}">
|
<button class="dropdown-item d-flex align-items-center" type="submit" name="child" value="{{ child.pk }}">
|
||||||
{% include "core/child_thumbnail.html" %}
|
{% include "core/child_thumbnail.html" %}
|
||||||
<span class="text-wrap ml-2">{{ child }}</span>
|
<span class="text-wrap ms-2">{{ child }}</span>
|
||||||
</button>
|
</button>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if timers %}
|
{% if timers %}
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<h6 class="dropdown-header">{% trans "Active Timers" %}</h6>
|
<h6 class="dropdown-header">{% trans "Timers" %}</h6>
|
||||||
{% for timer in timers %}
|
{% for timer in timers %}
|
||||||
<a class="dropdown-item" href="{% url 'core:timer-detail' timer.id %}">
|
<a class="dropdown-item" href="{% url 'core:timer-detail' timer.id %}">
|
||||||
{{ timer.title_with_child }}
|
{{ timer.title_with_child }}
|
||||||
|
|
|
@ -28,15 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% 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' %}'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -17,8 +17,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Start" %}</th>
|
<th>{% trans "Start" %}</th>
|
||||||
|
|
|
@ -28,12 +28,3 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'babybuddy/form.html' %}
|
{% include 'babybuddy/form.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
BabyBuddy.DatetimePicker.init($('#datetimepicker_date'), {
|
|
||||||
format: 'L',
|
|
||||||
extraFormats: ['YYYY-MM-DD']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -18,8 +18,8 @@
|
||||||
</h1>
|
</h1>
|
||||||
{% include 'babybuddy/filter.html' %}
|
{% include 'babybuddy/filter.html' %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-instances table-striped table-hover">
|
<table class="table table-instances table-borderless table-striped table-hover align-middle">
|
||||||
<thead class="thead-inverse">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Actions" %}</th>
|
<th>{% trans "Actions" %}</th>
|
||||||
<th>{% trans "Date" %}</th>
|
<th>{% trans "Date" %}</th>
|
||||||
|
@ -32,8 +32,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for weight in object_list %}
|
{% for weight in object_list %}
|
||||||
{% cycle "odd" "even" as row_class silent %}
|
<tr>
|
||||||
<tr class="{{ row_class }}">
|
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
<div class="btn-group btn-group-sm" role="group" aria-label="{% trans "Actions" %}">
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% if weight.notes %}
|
{% if weight.notes %}
|
||||||
<tr class="{{ row_class }} row-details">
|
<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>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -5,39 +5,31 @@
|
||||||
{{ k }}="{{ v }}"
|
{{ k }}="{{ v }}"
|
||||||
{% endfor %}>
|
{% endfor %}>
|
||||||
{% csrf_token %}
|
{% 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
|
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>
|
</span>
|
||||||
<div class="current_tags" style="min-height: 2em;">
|
<div class="current_tags" style="min-height: 2em;">
|
||||||
{% for t in widget.value %}
|
{% 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 }}
|
{{ t.name }}
|
||||||
<span class="add-remove-icon pl-1 pr-1">-</span>
|
<span class="add-remove-icon ps-1 pe-1">-</span>
|
||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="new-tags">
|
<div class="new-tags">
|
||||||
<div class="create-tag-inputs input-group">
|
<div class="create-tag-inputs input-group">
|
||||||
<input class="form-control" type="text" name="" placeholder="{% trans "Tag name" %}">
|
<input class="form-control" type="text" name="" placeholder="{% trans "Tag name" %}">
|
||||||
<div class="input-group-append">
|
<button id="add-tag" class="btn btn-outline-primary bg-dark" type="button">{% trans "Add" %}</button>
|
||||||
<button class="btn btn-outline-primary bg-dark btn-add-new-tag" type="button">{% trans "Add" %}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% if widget.tag_suggestions.quick %}
|
{% if widget.tag_suggestions.quick %}
|
||||||
<span>{% trans "Recently used:" %}</span>
|
<span>{% trans "Recently used:" %}</span>
|
||||||
{% for t in widget.tag_suggestions.quick %}
|
{% 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 }}
|
{{ t.name }}
|
||||||
<span class="add-remove-icon pl-1 pr-1">+</span>
|
<span class="add-remove-icon ps-1 pe-1">+</span>
|
||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
|
||||||
<style>
|
|
||||||
.help-block{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{%endif%}
|
{%endif%}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
@ -51,7 +43,7 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">{% trans "Error" context "Error modal" %}</h4>
|
<h4 class="modal-title">{% trans "Error" context "Error modal" %}</h4>
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
@ -62,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<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" %}
|
{% trans "Close" context "Error modal" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
{% if date_previous %}
|
{% if date_previous %}
|
||||||
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "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>
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ date|date }}
|
{{ date|date }}
|
||||||
{% if date_next %}
|
{% if date_next %}
|
||||||
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "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>
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</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 %}">
|
<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>
|
<i class="icon-{{ object.model_name }}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="card text-right">
|
<div class="card text-end">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ object.event }}
|
{{ object.event }}
|
||||||
{% for detail in object.details %}
|
{% for detail in object.details %}
|
||||||
|
@ -70,14 +70,14 @@
|
||||||
{% if date_previous %}
|
{% if date_previous %}
|
||||||
<a class="btn btn-sm btn-default" href="?date={{ date_previous|date:"Y-m-d" }}" aria-label="{% trans "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>
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ date|date }}
|
{{ date|date }}
|
||||||
{% if date_next %}
|
{% if date_next %}
|
||||||
<a class="btn btn-sm btn-default" href="?date={{ date_next|date:"Y-m-d" }}" aria-label="{% trans "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>
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% block title %}{% trans "Timeline" %}{% endblock %}
|
{% block title %}{% trans "Timeline" %}{% endblock %}
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item font-weight-bold">{% trans "Timeline" %}</li>
|
<li class="breadcrumb-item fw-bold">{% trans "Timeline" %}</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -7,40 +7,6 @@ from django.utils.translation import gettext_lazy as _
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
|
||||||
def datetimepicker_format(context, format_string="L LT"):
|
|
||||||
"""
|
|
||||||
Return a datetime format string for momentjs, with support for 24 hour time
|
|
||||||
override setting.
|
|
||||||
:param context: caller context data
|
|
||||||
:param format_string: the default format string (locale based)
|
|
||||||
:return: the format string to use, as 24 hour time if configured.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
user = context["request"].user
|
|
||||||
if hasattr(user, "settings") and user.settings.language:
|
|
||||||
language = user.settings.language
|
|
||||||
else:
|
|
||||||
language = settings.LANGUAGE_CODE
|
|
||||||
except KeyError:
|
|
||||||
language = None
|
|
||||||
|
|
||||||
if settings.USE_24_HOUR_TIME_FORMAT:
|
|
||||||
if format_string == "L LT":
|
|
||||||
format_string = "L HH:mm"
|
|
||||||
elif format_string == "L LTS":
|
|
||||||
format_string = "L HH:mm:ss"
|
|
||||||
elif language and language == "en-GB":
|
|
||||||
# Force 12-hour format if 24 hour format is not configured for en-GB
|
|
||||||
# (Django default is 12H, momentjs default is 24H).
|
|
||||||
if format_string == "L LT":
|
|
||||||
format_string = "L h:mm a"
|
|
||||||
elif format_string == "L LTS":
|
|
||||||
format_string = "L h:mm:ss a"
|
|
||||||
|
|
||||||
return format_string
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter()
|
@register.filter()
|
||||||
def datetime_short(date):
|
def datetime_short(date):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,15 +8,14 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag("core/timer_nav.html", takes_context=True)
|
@register.inclusion_tag("core/timer_nav.html", takes_context=True)
|
||||||
def timer_nav(context, active=True):
|
def timer_nav(context):
|
||||||
"""
|
"""
|
||||||
Get a list of active Timer instances to include in the nav menu.
|
Get a list of Timer instances to include in the nav menu.
|
||||||
:param context: Django's context data.
|
:param context: Django's context data.
|
||||||
:param active: the state of Timers to filter.
|
|
||||||
:returns: a dictionary with timers data.
|
:returns: a dictionary with timers data.
|
||||||
"""
|
"""
|
||||||
request = context["request"] or None
|
request = context["request"] or None
|
||||||
timers = Timer.objects.filter(active=active)
|
timers = Timer.objects.filter()
|
||||||
children = Child.objects.all()
|
children = Child.objects.all()
|
||||||
perms = context["perms"] or None
|
perms = context["perms"] or None
|
||||||
# The 'next' parameter is currently not used.
|
# The 'next' parameter is currently not used.
|
||||||
|
|
|
@ -122,32 +122,13 @@ class InitialValuesTestCase(FormsTestCaseBase):
|
||||||
self.assertEqual(page.context["form"].initial["type"], f_three.type)
|
self.assertEqual(page.context["form"].initial["type"], f_three.type)
|
||||||
self.assertEqual(page.context["form"].initial["method"], f_three.method)
|
self.assertEqual(page.context["form"].initial["method"], f_three.method)
|
||||||
|
|
||||||
def test_timer_set(self):
|
def test_timer_form_field_set(self):
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
|
|
||||||
page = self.c.get("/sleep/add/")
|
page = self.c.get("/sleep/add/")
|
||||||
self.assertTrue("start" not in page.context["form"].initial)
|
self.assertTrue("start" not in page.context["form"].initial)
|
||||||
self.assertTrue("end" not in page.context["form"].initial)
|
self.assertTrue("end" not in page.context["form"].initial)
|
||||||
|
|
||||||
page = self.c.get("/sleep/add/?timer={}".format(self.timer.id))
|
|
||||||
self.assertEqual(page.context["form"].initial["start"], self.timer.start)
|
|
||||||
self.assertEqual(page.context["form"].initial["end"], self.timer.end)
|
|
||||||
|
|
||||||
def test_timer_stop_on_save(self):
|
|
||||||
end = timezone.localtime()
|
|
||||||
params = {
|
|
||||||
"child": self.child.id,
|
|
||||||
"start": self.localtime_string(self.timer.start),
|
|
||||||
"end": self.localtime_string(end),
|
|
||||||
}
|
|
||||||
page = self.c.post(
|
|
||||||
"/sleep/add/?timer={}".format(self.timer.id), params, follow=True
|
|
||||||
)
|
|
||||||
self.assertEqual(page.status_code, 200)
|
|
||||||
self.timer.refresh_from_db()
|
|
||||||
self.assertFalse(self.timer.active)
|
|
||||||
self.assertEqual(self.localtime_string(self.timer.end), params["end"])
|
|
||||||
|
|
||||||
|
|
||||||
class BMIFormsTestCase(FormsTestCaseBase):
|
class BMIFormsTestCase(FormsTestCaseBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -783,29 +764,6 @@ class TimerFormsTestCase(FormsTestCaseBase):
|
||||||
self.timer.refresh_from_db()
|
self.timer.refresh_from_db()
|
||||||
self.assertEqual(self.localtime_string(self.timer.start), params["start"])
|
self.assertEqual(self.localtime_string(self.timer.start), params["start"])
|
||||||
|
|
||||||
def test_edit_stopped(self):
|
|
||||||
self.timer.stop()
|
|
||||||
params = {
|
|
||||||
"name": "Edit stopped timer",
|
|
||||||
"start": self.localtime_string(self.timer.start),
|
|
||||||
"end": self.localtime_string(self.timer.end),
|
|
||||||
}
|
|
||||||
page = self.c.post(
|
|
||||||
"/timers/{}/edit/".format(self.timer.id), params, follow=True
|
|
||||||
)
|
|
||||||
self.assertEqual(page.status_code, 200)
|
|
||||||
|
|
||||||
def test_delete_inactive(self):
|
|
||||||
models.Timer.objects.create(user=self.user)
|
|
||||||
self.assertEqual(models.Timer.objects.count(), 2)
|
|
||||||
self.timer.stop()
|
|
||||||
page = self.c.post("/timers/delete-inactive/", follow=True)
|
|
||||||
self.assertEqual(page.status_code, 200)
|
|
||||||
messages = list(page.context["messages"])
|
|
||||||
self.assertEqual(len(messages), 1)
|
|
||||||
self.assertEqual(str(messages[0]), "All inactive timers deleted.")
|
|
||||||
self.assertEqual(models.Timer.objects.count(), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class ValidationsTestCase(FormsTestCaseBase):
|
class ValidationsTestCase(FormsTestCaseBase):
|
||||||
def test_validate_date(self):
|
def test_validate_date(self):
|
||||||
|
|
|
@ -270,11 +270,9 @@ class TimerTestCase(TestCase):
|
||||||
)
|
)
|
||||||
self.user = get_user_model().objects.first()
|
self.user = get_user_model().objects.first()
|
||||||
self.named = models.Timer.objects.create(
|
self.named = models.Timer.objects.create(
|
||||||
name="Named", end=timezone.localtime(), user=self.user, child=child
|
name="Named", user=self.user, child=child
|
||||||
)
|
|
||||||
self.unnamed = models.Timer.objects.create(
|
|
||||||
end=timezone.localtime(), user=self.user
|
|
||||||
)
|
)
|
||||||
|
self.unnamed = models.Timer.objects.create(user=self.user)
|
||||||
|
|
||||||
def test_timer_create(self):
|
def test_timer_create(self):
|
||||||
self.assertEqual(self.named, models.Timer.objects.get(name="Named"))
|
self.assertEqual(self.named, models.Timer.objects.get(name="Named"))
|
||||||
|
@ -302,19 +300,7 @@ class TimerTestCase(TestCase):
|
||||||
|
|
||||||
def test_timer_restart(self):
|
def test_timer_restart(self):
|
||||||
self.named.restart()
|
self.named.restart()
|
||||||
self.assertIsNone(self.named.end)
|
self.assertGreaterEqual(timezone.localtime(), self.named.start)
|
||||||
self.assertIsNone(self.named.duration)
|
|
||||||
self.assertTrue(self.named.active)
|
|
||||||
|
|
||||||
def test_timer_stop(self):
|
|
||||||
stop_time = timezone.localtime()
|
|
||||||
self.unnamed.stop(end=stop_time)
|
|
||||||
self.assertEqual(self.unnamed.end, stop_time)
|
|
||||||
self.assertEqual(
|
|
||||||
self.unnamed.duration.seconds,
|
|
||||||
(self.unnamed.end - self.unnamed.start).seconds,
|
|
||||||
)
|
|
||||||
self.assertFalse(self.unnamed.active)
|
|
||||||
|
|
||||||
def test_timer_duration(self):
|
def test_timer_duration(self):
|
||||||
timer = models.Timer.objects.create(user=get_user_model().objects.first())
|
timer = models.Timer.objects.create(user=get_user_model().objects.first())
|
||||||
|
@ -322,9 +308,9 @@ class TimerTestCase(TestCase):
|
||||||
timer.save()
|
timer.save()
|
||||||
timer.refresh_from_db()
|
timer.refresh_from_db()
|
||||||
|
|
||||||
self.assertEqual(timer.duration.seconds, timezone.timedelta(minutes=30).seconds)
|
self.assertEqual(
|
||||||
timer.stop()
|
timer.duration().seconds, timezone.timedelta(minutes=30).seconds
|
||||||
self.assertEqual(timer.duration.seconds, timezone.timedelta(minutes=30).seconds)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TummyTimeTestCase(TestCase):
|
class TummyTimeTestCase(TestCase):
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue